]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - support/doc/plugins/guide.md
Exclude 0p from auto webtorrent quality
[github/Chocobozzz/PeerTube.git] / support / doc / plugins / guide.md
CommitLineData
662e5d4f
C
1# Plugins & Themes
2
d8e9a42c
C
3<!-- START doctoc generated TOC please keep comment here to allow auto update -->
4<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
5
6
7- [Concepts](#concepts)
8 - [Hooks](#hooks)
9 - [Static files](#static-files)
10 - [CSS](#css)
11 - [Server helpers (only for plugins)](#server-helpers-only-for-plugins)
12 - [Settings](#settings)
13 - [Storage](#storage)
7545a094 14 - [Update video constants](#update-video-constants)
5e2b2e27 15 - [Add custom routes](#add-custom-routes)
1a2820e6 16 - [Add external auth methods](#add-external-auth-methods)
7545a094 17 - [Client helpers (themes & plugins)](#client-helpers-themes--plugins)
b3af2601
C
18 - [Plugin static route](#plugin-static-route)
19 - [Notifier](#notifier)
20 - [Markdown Renderer](#markdown-renderer)
21 - [Custom Modal](#custom-modal)
7545a094
C
22 - [Translate](#translate)
23 - [Get public settings](#get-public-settings)
d8e9a42c
C
24 - [Publishing](#publishing)
25- [Write a plugin/theme](#write-a-plugintheme)
26 - [Clone the quickstart repository](#clone-the-quickstart-repository)
27 - [Configure your repository](#configure-your-repository)
28 - [Update README](#update-readme)
29 - [Update package.json](#update-packagejson)
30 - [Write code](#write-code)
7545a094 31 - [Add translations](#add-translations)
d8e9a42c
C
32 - [Test your plugin/theme](#test-your-plugintheme)
33 - [Publish](#publish)
7545a094 34- [Plugin & Theme hooks/helpers API](#plugin--theme-hookshelpers-api)
d8e9a42c
C
35- [Tips](#tips)
36 - [Compatibility with PeerTube](#compatibility-with-peertube)
37 - [Spam/moderation plugin](#spammoderation-plugin)
112be80e 38 - [Other plugin examples](#other-plugin-examples)
d8e9a42c
C
39
40<!-- END doctoc generated TOC please keep comment here to allow auto update -->
41
662e5d4f
C
42## Concepts
43
32d7f2b7 44Themes are exactly the same as plugins, except that:
662e5d4f
C
45 * Their name starts with `peertube-theme-` instead of `peertube-plugin-`
46 * They cannot declare server code (so they cannot register server hooks or settings)
47 * CSS files are loaded by client only if the theme is chosen by the administrator or the user
48
49### Hooks
50
51A plugin registers functions in JavaScript to execute when PeerTube (server and client) fires events. There are 3 types of hooks:
5831dbcb 52 * `filter`: used to filter functions parameters or return values.
662e5d4f
C
53 For example to replace words in video comments, or change the videos list behaviour
54 * `action`: used to do something after a certain trigger. For example to send a hook every time a video is published
55 * `static`: same than `action` but PeerTube waits their execution
662e5d4f
C
56
57On server side, these hooks are registered by the `library` file defined in `package.json`.
58
59```json
60{
61 ...,
62 "library": "./main.js",
63 ...,
64}
65```
66
7545a094
C
67And `main.js` defines a `register` function:
68
69Example:
70
71```js
72async function register ({
73 registerHook,
5831dbcb 74
7545a094
C
75 registerSetting,
76 settingsManager,
5831dbcb 77
7545a094 78 storageManager,
5831dbcb 79
7545a094
C
80 videoCategoryManager,
81 videoLicenceManager,
5e2b2e27 82 videoLanguageManager,
5831dbcb 83
5e2b2e27 84 peertubeHelpers,
5831dbcb
C
85
86 getRouter,
87
88 registerExternalAuth,
89 unregisterExternalAuth,
90 registerIdAndPassAuth,
91 unregisterIdAndPassAuth
7545a094
C
92}) {
93 registerHook({
94 target: 'action:application.listening',
95 handler: () => displayHelloWorld()
96 })
97}
98```
99
662e5d4f
C
100
101On client side, these hooks are registered by the `clientScripts` files defined in `package.json`.
102All client scripts have scopes so PeerTube client only loads scripts it needs:
103
104```json
105{
106 ...,
107 "clientScripts": [
108 {
109 "script": "client/common-client-plugin.js",
110 "scopes": [ "common" ]
111 },
112 {
113 "script": "client/video-watch-client-plugin.js",
114 "scopes": [ "video-watch" ]
115 }
116 ],
117 ...
118}
119```
120
7545a094
C
121And these scripts also define a `register` function:
122
123```js
124function register ({ registerHook, peertubeHelpers }) {
125 registerHook({
126 target: 'action:application.init',
127 handler: () => onApplicationInit(peertubeHelpers)
128 })
129}
130```
131
662e5d4f
C
132### Static files
133
5831dbcb
C
134Plugins can declare static directories that PeerTube will serve (images for example)
135from `/plugins/{plugin-name}/{plugin-version}/static/`
662e5d4f
C
136or `/themes/{theme-name}/{theme-version}/static/` routes.
137
138### CSS
139
140Plugins can declare CSS files that PeerTube will automatically inject in the client.
7545a094
C
141If you need to override existing style, you can use the `#custom-css` selector:
142
143```
144body#custom-css {
145 color: red;
146}
147
148#custom-css .header {
149 background-color: red;
150}
151```
662e5d4f 152
9fa6ca16 153### Server helpers (only for plugins)
662e5d4f
C
154
155#### Settings
156
157Plugins can register settings, that PeerTube will inject in the administration interface.
158
159Example:
160
161```js
162registerSetting({
163 name: 'admin-name',
164 label: 'Admin name',
165 type: 'input',
8c7725dc 166 // type: input | input-checkbox | input-textarea | markdown-text | markdown-enhanced
662e5d4f
C
167 default: 'my super name'
168})
169
170const adminName = await settingsManager.getSetting('admin-name')
2db9c70f
C
171
172const result = await settingsManager.getSettings([ 'admin-name', 'admin-password' ])
173result['admin-name]
5831dbcb
C
174
175settingsManager.onSettingsChange(settings => {
176 settings['admin-name])
177})
662e5d4f
C
178```
179
d8e9a42c 180#### Storage
662e5d4f
C
181
182Plugins can store/load JSON data, that PeerTube will store in its database (so don't put files in there).
183
184Example:
185
186```js
187const value = await storageManager.getData('mykey')
9fa6ca16 188await storageManager.storeData('mykey', { subkey: 'value' })
662e5d4f
C
189```
190
7545a094
C
191#### Update video constants
192
193You can add/delete video categories, licences or languages using the appropriate managers:
194
195```js
196videoLanguageManager.addLanguage('al_bhed', 'Al Bhed')
197videoLanguageManager.deleteLanguage('fr')
198
199videoCategoryManager.addCategory(42, 'Best category')
200videoCategoryManager.deleteCategory(1) // Music
201
202videoLicenceManager.addLicence(42, 'Best licence')
203videoLicenceManager.deleteLicence(7) // Public domain
b3af2601
C
204
205videoPrivacyManager.deletePrivacy(2) // Remove Unlisted video privacy
206playlistPrivacyManager.deletePlaylistPrivacy(3) // Remove Private video playlist privacy
7545a094
C
207```
208
5e2b2e27
C
209#### Add custom routes
210
211You can create custom routes using an [express Router](https://expressjs.com/en/4x/api.html#router) for your plugin:
212
213```js
214const router = getRouter()
215router.get('/ping', (req, res) => res.json({ message: 'pong' }))
216```
217
218The `ping` route can be accessed using:
219 * `/plugins/:pluginName/:pluginVersion/router/ping`
220 * Or `/plugins/:pluginName/router/ping`
221
222
5831dbcb
C
223#### Add external auth methods
224
225If you want to add a classic username/email and password auth method (like [LDAP](https://framagit.org/framasoft/peertube/official-plugins/-/tree/master/peertube-plugin-auth-ldap) for example):
226
227```js
228registerIdAndPassAuth({
229 authName: 'my-auth-method',
230
231 // PeerTube will try all id and pass plugins in the weight DESC order
232 // Exposing this value in the plugin settings could be interesting
233 getWeight: () => 60,
234
235 // Optional function called by PeerTube when the user clicked on the logout button
236 onLogout: user => {
237 console.log('User %s logged out.', user.username')
238 },
239
240 // Optional function called by PeerTube when the access token or refresh token are generated/refreshed
241 hookTokenValidity: ({ token, type }) => {
242 if (type === 'access') return { valid: true }
243 if (type === 'refresh') return { valid: false }
244 },
245
246 // Used by PeerTube when the user tries to authenticate
247 login: ({ id, password }) => {
248 if (id === 'user' && password === 'super password') {
249 return {
250 username: 'user'
251 email: 'user@example.com'
252 role: 2
253 displayName: 'User display name'
254 }
255 }
256
257 // Auth failed
258 return null
259 }
260})
261
262// Unregister this auth method
263unregisterIdAndPassAuth('my-auth-method')
264```
265
266You can also add an external auth method (like [OpenID](https://framagit.org/framasoft/peertube/official-plugins/-/tree/master/peertube-plugin-auth-openid-connect), [SAML2](https://framagit.org/framasoft/peertube/official-plugins/-/tree/master/peertube-plugin-auth-saml2) etc):
267
268```js
269// result contains the userAuthenticated auth method you can call to authenticate a user
270const result = registerExternalAuth({
271 authName: 'my-auth-method',
272
273 // Will be displayed in a button next to the login form
274 authDisplayName: () => 'Auth method'
275
276 // If the user click on the auth button, PeerTube will forward the request in this function
277 onAuthRequest: (req, res) => {
278 res.redirect('https://external-auth.example.com/auth')
279 },
280
281 // Same than registerIdAndPassAuth option
282 // onLogout: ...
283
284 // Same than registerIdAndPassAuth option
285 // hookTokenValidity: ...
286})
287
288router.use('/external-auth-callback', (req, res) => {
289 // Forward the request to PeerTube
290 result.userAuthenticated({
291 req,
292 res,
293 username: 'user'
294 email: 'user@example.com'
295 role: 2
296 displayName: 'User display name'
297 })
298})
299
300// Unregister this external auth method
301unregisterExternalAuth('my-auth-method)
302```
303
7545a094
C
304### Client helpers (themes & plugins)
305
74c2dece 306#### Plugin static route
7545a094
C
307
308To get your plugin static route:
309
310```js
311const baseStaticUrl = peertubeHelpers.getBaseStaticRoute()
312const imageUrl = baseStaticUrl + '/images/chocobo.png'
313```
314
74c2dece
K
315#### Notifier
316
317To notify the user with the PeerTube ToastModule:
318
319```js
320const { notifier } = peertubeHelpers
321notifier.success('Success message content.')
322notifier.error('Error message content.')
323```
324
8c7725dc
K
325#### Markdown Renderer
326
327To render a formatted markdown text to HTML:
328
329```js
330const { markdownRenderer } = peertubeHelpers
331
332await markdownRenderer.textMarkdownToHTML('**My Bold Text**')
333// return <strong>My Bold Text</strong>
334
335await markdownRenderer.enhancedMarkdownToHTML('![alt-img](http://.../my-image.jpg)')
336// return <img alt=alt-img src=http://.../my-image.jpg />
337```
338
437e8e06
K
339#### Custom Modal
340
341To show a custom modal:
342
343```js
344 peertubeHelpers.showModal({
345 title: 'My custom modal title',
346 content: '<p>My custom modal content</p>',
347 // Optionals parameters :
348 // show close icon
349 close: true,
350 // show cancel button and call action() after hiding modal
351 cancel: { value: 'cancel', action: () => {} },
352 // show confirm button and call action() after hiding modal
353 confirm: { value: 'confirm', action: () => {} },
354 })
355```
356
7545a094
C
357#### Translate
358
359You can translate some strings of your plugin (PeerTube will use your `translations` object of your `package.json` file):
360
361```js
362peertubeHelpers.translate('User name')
363 .then(translation => console.log('Translated User name by ' + translation))
364```
365
366#### Get public settings
367
368To get your public plugin settings:
369
370```js
371peertubeHelpers.getSettings()
372 .then(s => {
373 if (!s || !s['site-id'] || !s['url']) {
374 console.error('Matomo settings are not set.')
375 return
376 }
5831dbcb 377
7545a094
C
378 // ...
379 })
5831dbcb 380```
7545a094
C
381
382
662e5d4f
C
383### Publishing
384
385PeerTube plugins and themes should be published on [NPM](https://www.npmjs.com/) so that PeerTube indexes
386take into account your plugin (after ~ 1 day). An official PeerTube index is available on https://packages.joinpeertube.org/ (it's just a REST API, so don't expect a beautiful website).
387
388## Write a plugin/theme
389
390Steps:
391 * Find a name for your plugin or your theme (must not have spaces, it can only contain lowercase letters and `-`)
392 * Add the appropriate prefix:
393 * If you develop a plugin, add `peertube-plugin-` prefix to your plugin name (for example: `peertube-plugin-mysupername`)
394 * If you develop a theme, add `peertube-theme-` prefix to your theme name (for example: `peertube-theme-mysupertheme`)
395 * Clone the quickstart repository
396 * Configure your repository
397 * Update `README.md`
398 * Update `package.json`
399 * Register hooks, add CSS and static files
400 * Test your plugin/theme with a local PeerTube installation
401 * Publish your plugin/theme on NPM
402
403### Clone the quickstart repository
404
405If you develop a plugin, clone the `peertube-plugin-quickstart` repository:
406
407```
408$ git clone https://framagit.org/framasoft/peertube/peertube-plugin-quickstart.git peertube-plugin-mysupername
409```
410
411If you develop a theme, clone the `peertube-theme-quickstart` repository:
412
413```
414$ git clone https://framagit.org/framasoft/peertube/peertube-theme-quickstart.git peertube-theme-mysupername
415```
416
417### Configure your repository
418
419Set your repository URL:
420
421```
422$ cd peertube-plugin-mysupername # or cd peertube-theme-mysupername
423$ git remote set-url origin https://your-git-repo
424```
425
426### Update README
427
428Update `README.md` file:
429
430```
431$ $EDITOR README.md
432```
433
434### Update package.json
435
436Update the `package.json` fields:
437 * `name` (should start with `peertube-plugin-` or `peertube-theme-`)
438 * `description`
439 * `homepage`
440 * `author`
441 * `bugs`
442 * `engine.peertube` (the PeerTube version compatibility, must be `>=x.y.z` and nothing else)
5831dbcb 443
662e5d4f 444**Caution:** Don't update or remove other keys, or PeerTube will not be able to index/install your plugin.
5831dbcb 445If you don't need static directories, use an empty `object`:
662e5d4f
C
446
447```json
448{
449 ...,
450 "staticDirs": {},
451 ...
452}
453```
454
9fa6ca16 455And if you don't need CSS or client script files, use an empty `array`:
662e5d4f
C
456
457```json
458{
459 ...,
460 "css": [],
9fa6ca16 461 "clientScripts": [],
662e5d4f
C
462 ...
463}
464```
465
466### Write code
467
468Now you can register hooks or settings, write CSS and add static directories to your plugin or your theme :)
469
5831dbcb 470**Caution:** It's up to you to check the code you write will be compatible with the PeerTube NodeJS version,
662e5d4f
C
471and will be supported by web browsers.
472If you want to write modern JavaScript, please use a transpiler like [Babel](https://babeljs.io/).
473
7545a094
C
474### Add translations
475
476If you want to translate strings of your plugin (like labels of your registered settings), create a file and add it to `package.json`:
477
478```json
479{
480 ...,
481 "translations": {
482 "fr-FR": "./languages/fr.json",
483 "pt-BR": "./languages/pt-BR.json"
484 },
485 ...
486}
487```
488
489The key should be one of the locales defined in [i18n.ts](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/i18n/i18n.ts).
490You **must** use the complete locales (`fr-FR` instead of `fr`).
491
112be80e
C
492Translation files are just objects, with the english sentence as the key and the translation as the value.
493`fr.json` could contain for example:
494
495```json
496{
497 "Hello world": "Hello le monde"
498}
499```
500
662e5d4f
C
501### Test your plugin/theme
502
503You'll need to have a local PeerTube instance:
5831dbcb 504 * Follow the [dev prerequisites](https://github.com/Chocobozzz/PeerTube/blob/develop/.github/CONTRIBUTING.md#prerequisites)
662e5d4f 505 (to clone the repository, install dependencies and prepare the database)
5831dbcb 506 * Build PeerTube (`--light` to only build the english language):
662e5d4f
C
507
508```
509$ npm run build -- --light
9fa6ca16
C
510```
511
512 * Build the CLI:
5831dbcb 513
9fa6ca16
C
514```
515$ npm run setup:cli
662e5d4f 516```
5831dbcb
C
517
518 * Run PeerTube (you can access to your instance on http://localhost:9000):
662e5d4f
C
519
520```
521$ NODE_ENV=test npm start
522```
523
5831dbcb 524 * Register the instance via the CLI:
662e5d4f
C
525
526```
527$ node ./dist/server/tools/peertube.js auth add -u 'http://localhost:9000' -U 'root' --password 'test'
528```
529
530Then, you can install or reinstall your local plugin/theme by running:
531
532```
533$ node ./dist/server/tools/peertube.js plugins install --path /your/absolute/plugin-or-theme/path
534```
535
536### Publish
537
538Go in your plugin/theme directory, and run:
539
540```
541$ npm publish
542```
543
544Every time you want to publish another version of your plugin/theme, just update the `version` key from the `package.json`
545and republish it on NPM. Remember that the PeerTube index will take into account your new plugin/theme version after ~24 hours.
546
d8e9a42c 547
bfa1a32b
C
548## Plugin & Theme hooks/helpers API
549
195474f9 550See the dedicated documentation: https://docs.joinpeertube.org/#/api-plugins
bfa1a32b
C
551
552
d8e9a42c
C
553## Tips
554
555### Compatibility with PeerTube
556
557Unfortunately, we don't have enough resources to provide hook compatibility between minor releases of PeerTube (for example between `1.2.x` and `1.3.x`).
558So please:
559 * Don't make assumptions and check every parameter you want to use. For example:
560
561```js
562registerHook({
563 target: 'filter:api.video.get.result',
564 handler: video => {
565 // We check the parameter exists and the name field exists too, to avoid exceptions
566 if (video && video.name) video.name += ' <3'
567
568 return video
569 }
570})
571```
51326912 572 * Don't try to require parent PeerTube modules, only use `peertubeHelpers`. If you need another helper or a specific hook, please [create an issue](https://github.com/Chocobozzz/PeerTube/issues/new)
5831dbcb 573 * Don't use PeerTube dependencies. Use your own :)
d8e9a42c 574
51326912 575If your plugin is broken with a new PeerTube release, update your code and the `peertubeEngine` field of your `package.json` field.
5831dbcb 576This way, older PeerTube versions will still use your old plugin, and new PeerTube versions will use your updated plugin.
d8e9a42c
C
577
578### Spam/moderation plugin
579
580If you want to create an antispam/moderation plugin, you could use the following hooks:
581 * `filter:api.video.upload.accept.result`: to accept or not local uploads
582 * `filter:api.video-thread.create.accept.result`: to accept or not local thread
583 * `filter:api.video-comment-reply.create.accept.result`: to accept or not local replies
584 * `filter:api.video-threads.list.result`: to change/hide the text of threads
585 * `filter:api.video-thread-comments.list.result`: to change/hide the text of replies
586 * `filter:video.auto-blacklist.result`: to automatically blacklist local or remote videos
5831dbcb 587
112be80e
C
588### Other plugin examples
589
590You can take a look to "official" PeerTube plugins if you want to take inspiration from them: https://framagit.org/framasoft/peertube/official-plugins