A PeerTube Plugin to Cleanup Unviewed Videos
"Cleanup Unviewed Videos" is a PeerTube plugin to easily identify videos that have not been viewed for a long time in order to delete them. Its code is hosted on both Worteks Github and my personal code hosting platform.
This article will present the plugin, how to develop a plugin and how to install it. It is based on 1.x versions of the plugin (you should navigate though Git tags to get relevant code).
PeerTube has strict naming rules for plugins. They can be found in online documentation.
Overall Behavior #
The plugin can be accessed from the main menu when connected with an account that has an "Administrator" role.
Search is done through a form on which the user can set the minimum age for last view:
Videos matching search criteria are displayed and pre-selected for mass deletion:
Plugin Architecture and Code #
PeerTube is developed in TypeScript (an addition to JavaScript to enable static typing) and its interface is based on Angular framework.
Because of the plugin simplicity, it has been decided not to use TypeScript and Angular and to prefer "Vanilla" JavaScript and CSS.
The plugin structure is described in package.json
file. Its contents will be
used by PeerTube at the installation and startup stages. File extract:
{
"library": "./main.js",
"staticDirs": {
"images": "public/images"
},
"css": ["assets/style.css"],
"clientScripts": [
{
"script": "client/common-client-plugin.js",
"scopes": ["common"]
}
],
"translations": {
"fr": "./languages/fr.json"
}
}
The following keys are referencing paths related to client code (interface):
clientScripts
, staticDirs
& css
.
The supported languages and their associated translation files are referenced
through the translations
key.
On both server and client sides, PeerTube will get the code to execute through
the register
export.
Server Code #
On the server side, the code only declares plugin preferences so they appear in PeerTube settings interface (extract):
registerSetting({
name: "enable-deletion",
label: "Enable deletion",
type: "input-checkbox",
descriptionHTML: "When this disabled, videos won't be actually deleted.",
private: false,
});
PeerTube handles preferences in a really simple way. All the keys that are declared in the plugin code are shown in the settings interface, then their value is stored into database.
There is no cleanup when the plugin deprecates settings keys or when a plugin is deinstalled. This behavior can be helpful during development phase as it gives the possibility to deinstall then reinstall the plugin without losing its configuration.
Client Code #
The client code declares the following hook handers:
- process main menu contents to add an entry
- show/hide the menu entry according to user role
// Add our menu entry
registerHook({
target: "filter:left-menu.links.create.result",
handler: async (menu_entries) => {
// ...
},
});
// Toggle menu visibility according to connected user role
registerHook({
target: "action:auth-user.information-loaded",
handler: ({ user }) => {
// ...
},
});
});
Then it registers a new route on the application that will lead to the plugin interface:
/**
* Register route
*/
registerClientRoute({
route: "cleanup-unviewed-videos",
title: await helpers.translate("Cleanup Unviewed Videos"),
onMount: ({ rootEl }) => {
// ...
},
The onMount
function will run when the user navigates to /p/cleanup-unviewed-videos
.
It will:
- create search form
- call PeerTube API to search videos matching search criteria
- display search results in a list
- call PeerTube API to delete selected videos
Interface Creation #
To keep the plugin lightweight, it has been decided to use JavaScript "template literals" (extract):
rootEl.innerHTML = `
<div class="cleanup-unviewed-videos-main">
<h1>${await helpers.translate("Cleanup Unviewed Videos")}</h1>
<form id="${prefix}-form" class="pt-two-cols mt-4">
<div class="title-col">
<h2>${await helpers.translate("Last view age")}</h2>
</div>
<!-- ... -->
</form>
</div>`;
Videos Search #
As the plugin is running on the client side, server-side API functions are not available. Videos serach will be achieved through calls to PeerTube HTTP API:
while (has_more_data) {
const json = await request_api(
`/api/v1/videos?isLocal=true&privacyOneOf=1&privacyOneOf=4&privacyOneOf=3&privacyOneOf=2&privacyOneOf=5&start=${page++ * pagination_count}&count=${pagination_count}`,
);
has_more_data = json.data.length === pagination_count;
// ...
}
Counting views on the search window is achieved through HTTP calls as well:
const json = await request_api(
`/api/v1/videos/${uuid}/stats/overall?startDate=${ref_date.toISOString()}`,
);
So does the video deletion:
await request_api(`/api/v1/videos/${uuid}`, "DELETE");
PeerTube architecture allows plugins to declare server-side routes. This plugin architecture could be refactored so it runs on the server for a better efficiency in search and deletion.
Plugin Publishing #
A plugin can be published by uploading it to NPM.
This is an easy task because of the NPM ecosystem. After creating an account on the repository, the archive publishing is handled though the following command:
npm publish
Plugin Installation #
Depending on the plugin maturity, two installation methods may be used:
- during development phases
- after plugin has been published
During development phases #
While developing, the plugin installation will be more efficient though the command-line.
The plugin directory tree must be copied to a location that's accessible to PeerTube server application. If the application runs into a container (Docker-like), the plugin could be dropped into a volume mount.
The installation itself is performet through the peertube-cli
command. If this
command is not available on the PeerTube server, it can be installed:
npm install -g @peertube/peertube-cli
While being run locally on the server itself, the command will issue HTTP requests. Authentication is required before trying to install the plugin:
PEERTUBE_HOST=...
LOGIN=...
PASSWORD=...
peertube-cli auth add -u "https://${PEERTUBE_HOST}" -U "$LOGIN" --password "$PASSWORD"
To install the plugin, instruct the command on the path to the directory where the plugin has been copied:
PATH_TO_PLUGIN_DIR=...
peertube-cli plugins install --path "$PATH_TO_PLUGIN_DIR"
During the development phases, when the CSS file is not yet stabilised, older versions of the rules may override newer changes as they are not automatically cleaned. It may then be necessary to deinstall the plugin before deploying a new version.
Deinstalling the plugin can be done through npm
:
PLUGIN_NAME=...
npm run plugin:uninstall -- --npm-name "$PLUGIN_NAME"
After the plugin has been published #
When the plugin is available on NPM, it can be installed either:
- from the command-line without requiring the directory tree to be copied first,
- from the plugin management Web UI
The command-line installation can be done through the peertube-cli
command. As
the plugin is available online, the command doesn't need to be run on the server
itself and can be executed from a workstation:
PEERTUBE_HOST=...
peertube-cli plugins install -u "https://${PEERTUBE_HOST}" -n "peertube-plugin-cleanup-unviewed-videos"
For the installation to be possible from the Web UI, the plugin must be referenced on the plugins directory Web site. Plugins are automatically referenced by a cron job. A one-day delay is necessary before the plugin appears in the directory.
peertube-plugin-cleanup-unviewed-videos
plugin is not yet available in the plugins directory because of a technical issue with naming.
## Mise à jour du plugin
Plugin update #
The plugin update will be possible through the plugin management Web UI.
Conclusion #
peertube-plugin-cleanup-unviewed-videos
plugin development was an interesting
introduction to PeerTube ecosystem. It implements different concepts on a quite
limited playground in order to address a useful functional scope.
The code of this plugin involves the following aspects:
- adding elements to the Web UI menu
- checking the connected user role
- creating a HTTP route
- querying the HTTP API
- translating the Web UI
Depending on the popularity the plugin will get, it may be necessary to consider an architecture update in order to move some processing on the backend side for an increased efficiency.
Let's discuss about this on Mastodon!- Previous: Making a Simple PWA