]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - support/doc/plugins/guide.md
Add server config helper in plugin guide
[github/Chocobozzz/PeerTube.git] / support / doc / plugins / guide.md
index 81691c8c14f309b484bc983c38323f4783dce5d4..20cbec5c739bff8a88e97a106831386567631c7a 100644 (file)
@@ -13,6 +13,8 @@
     - [Storage](#storage)
     - [Update video constants](#update-video-constants)
     - [Add custom routes](#add-custom-routes)
+    - [Add external auth methods](#add-external-auth-methods)
+    - [Add new transcoding profiles](#add-new-transcoding-profiles)
   - [Client helpers (themes & plugins)](#client-helpers-themes--plugins)
     - [Plugin static route](#plugin-static-route)
     - [Notifier](#notifier)
@@ -20,6 +22,8 @@
     - [Custom Modal](#custom-modal)
     - [Translate](#translate)
     - [Get public settings](#get-public-settings)
+    - [Get server config](#get-server-config)
+    - [Add custom fields to video form](#add-custom-fields-to-video-form)
   - [Publishing](#publishing)
 - [Write a plugin/theme](#write-a-plugintheme)
   - [Clone the quickstart repository](#clone-the-quickstart-repository)
@@ -28,6 +32,7 @@
   - [Update package.json](#update-packagejson)
   - [Write code](#write-code)
   - [Add translations](#add-translations)
+  - [Build your plugin](#build-your-plugin)
   - [Test your plugin/theme](#test-your-plugintheme)
   - [Publish](#publish)
 - [Plugin & Theme hooks/helpers API](#plugin--theme-hookshelpers-api)
@@ -48,7 +53,7 @@ Themes are exactly the same as plugins, except that:
 ### Hooks
 
 A plugin registers functions in JavaScript to execute when PeerTube (server and client) fires events. There are 3 types of hooks:
- * `filter`: used to filter functions parameters or return values. 
+ * `filter`: used to filter functions parameters or return values.
  For example to replace words in video comments, or change the videos list behaviour
  * `action`: used to do something after a certain trigger. For example to send a hook every time a video is published
  * `static`: same than `action` but PeerTube waits their execution
@@ -70,14 +75,24 @@ Example:
 ```js
 async function register ({
   registerHook,
+
   registerSetting,
   settingsManager,
+
   storageManager,
+
   videoCategoryManager,
   videoLicenceManager,
   videoLanguageManager,
+
   peertubeHelpers,
-  getRouter
+
+  getRouter,
+
+  registerExternalAuth,
+  unregisterExternalAuth,
+  registerIdAndPassAuth,
+  unregisterIdAndPassAuth
 }) {
   registerHook({
     target: 'action:application.listening',
@@ -120,8 +135,8 @@ function register ({ registerHook, peertubeHelpers }) {
 
 ### Static files
 
-Plugins can declare static directories that PeerTube will serve (images for example) 
-from `/plugins/{plugin-name}/{plugin-version}/static/` 
+Plugins can declare static directories that PeerTube will serve (images for example)
+from `/plugins/{plugin-name}/{plugin-version}/static/`
 or `/themes/{theme-name}/{theme-version}/static/` routes.
 
 ### CSS
@@ -152,7 +167,7 @@ registerSetting({
   name: 'admin-name',
   label: 'Admin name',
   type: 'input',
-  // type: input | input-checkbox | input-textarea | markdown-text | markdown-enhanced
+  // type: input | input-checkbox | input-password | input-textarea | markdown-text | markdown-enhanced
   default: 'my super name'
 })
 
@@ -160,6 +175,10 @@ const adminName = await settingsManager.getSetting('admin-name')
 
 const result = await settingsManager.getSettings([ 'admin-name', 'admin-password' ])
 result['admin-name]
+
+settingsManager.onSettingsChange(settings => {
+  settings['admin-name])
+})
 ```
 
 #### Storage
@@ -205,6 +224,175 @@ The `ping` route can be accessed using:
  * Or `/plugins/:pluginName/router/ping`
 
 
+#### Add external auth methods
+
+If 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):
+
+```js
+registerIdAndPassAuth({
+  authName: 'my-auth-method',
+
+  // PeerTube will try all id and pass plugins in the weight DESC order
+  // Exposing this value in the plugin settings could be interesting
+  getWeight: () => 60,
+
+  // Optional function called by PeerTube when the user clicked on the logout button
+  onLogout: user => {
+    console.log('User %s logged out.', user.username')
+  },
+
+  // Optional function called by PeerTube when the access token or refresh token are generated/refreshed
+  hookTokenValidity: ({ token, type }) => {
+    if (type === 'access') return { valid: true }
+    if (type === 'refresh') return { valid: false }
+  },
+
+  // Used by PeerTube when the user tries to authenticate
+  login: ({ id, password }) => {
+    if (id === 'user' && password === 'super password') {
+      return {
+        username: 'user'
+        email: 'user@example.com'
+        role: 2
+        displayName: 'User display name'
+      }
+    }
+
+    // Auth failed
+    return null
+  }
+})
+
+// Unregister this auth method
+unregisterIdAndPassAuth('my-auth-method')
+```
+
+You 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):
+
+```js
+// result contains the userAuthenticated auth method you can call to authenticate a user
+const result = registerExternalAuth({
+  authName: 'my-auth-method',
+
+  // Will be displayed in a button next to the login form
+  authDisplayName: () => 'Auth method'
+
+  // If the user click on the auth button, PeerTube will forward the request in this function
+  onAuthRequest: (req, res) => {
+    res.redirect('https://external-auth.example.com/auth')
+  },
+
+  // Same than registerIdAndPassAuth option
+  // onLogout: ...
+
+  // Same than registerIdAndPassAuth option
+  // hookTokenValidity: ...
+})
+
+router.use('/external-auth-callback', (req, res) => {
+  // Forward the request to PeerTube
+  result.userAuthenticated({
+    req,
+    res,
+    username: 'user'
+    email: 'user@example.com'
+    role: 2
+    displayName: 'User display name'
+  })
+})
+
+// Unregister this external auth method
+unregisterExternalAuth('my-auth-method)
+```
+
+#### Add new transcoding profiles
+
+Adding transcoding profiles allow admins to change ffmpeg encoding parameters and/or encoders.
+A transcoding profile has to be chosen by the admin of the instance using the admin configuration.
+
+```js
+async function register ({
+  transcodingManager
+}) {
+
+  // Adapt bitrate when using libx264 encoder
+  {
+    const builder = (options) => {
+      const { input, resolution, fps, streamNum } = options
+
+      const streamString = streamNum ? ':' + streamNum : ''
+
+      // You can also return a promise
+      return {
+        outputOptions: [
+        // Use a custom bitrate
+          '-b' + streamString + ' 10K'
+        ]
+      }
+    }
+
+    const encoder = 'libx264'
+    const profileName = 'low-quality'
+
+    // Support this profile for VOD transcoding
+    transcodingManager.addVODProfile(encoder, profileName, builder)
+
+    // And/Or support this profile for live transcoding
+    transcodingManager.addLiveProfile(encoder, profileName, builder)
+  }
+
+  {
+    const builder = (options) => {
+      const { streamNum } = options
+
+      const streamString = streamNum ? ':' + streamNum : ''
+
+      // Always copy stream when PeerTube use libfdk_aac or aac encoders
+      return {
+        copy: true
+      }
+    }
+
+    const profileName = 'copy-audio'
+
+    for (const encoder of [ 'libfdk_aac', 'aac' ]) {
+      transcodingManager.addVODProfile(encoder, profileName, builder)
+    }
+  }
+```
+
+PeerTube will try different encoders depending on their priority.
+If the encoder is not available in the current transcoding profile or in ffmpeg, it tries the next one.
+Plugins can change the order of these encoders and add their custom encoders:
+
+```js
+async function register ({
+  transcodingManager
+}) {
+
+  // Adapt bitrate when using libx264 encoder
+  {
+    const builder = () => {
+      return {
+        outputOptions: []
+      }
+    }
+
+    // Support libopus and libvpx-vp9 encoders (these codecs could be incompatible with the player)
+    transcodingManager.addVODProfile('libopus', 'test-vod-profile', builder)
+
+    // Default priorities are ~100
+    // Lowest priority = 1
+    transcodingManager.addVODEncoderPriority('audio', 'libopus', 1000)
+
+    transcodingManager.addVODProfile('libvpx-vp9', 'test-vod-profile', builder)
+    transcodingManager.addVODEncoderPriority('video', 'libvpx-vp9', 1000)
+
+    transcodingManager.addLiveProfile('libopus', 'test-live-profile', builder)
+    transcodingManager.addLiveEncoderPriority('audio', 'libopus', 1000)
+  }
+```
+
 ### Client helpers (themes & plugins)
 
 #### Plugin static route
@@ -278,12 +466,80 @@ peertubeHelpers.getSettings()
       console.error('Matomo settings are not set.')
       return
     }
-    
+
     // ...
   })
-``` 
+```
+
+#### Get server config
+
+```js
+peertubeHelpers.getServerConfig()
+  .then(config => {
+    console.log('Fetched server config.', config)
+  })
+```
+
+#### Add custom fields to video form
+
+To add custom fields in the video form (in *Plugin settings* tab):
+
+```js
+async function register ({ registerVideoField, peertubeHelpers }) {
+  const descriptionHTML = await peertubeHelpers.translate(descriptionSource)
+  const commonOptions = {
+    name: 'my-field-name,
+    label: 'My added field',
+    descriptionHTML: 'Optional description',
+    type: 'input-textarea',
+    default: ''
+  }
+
+  for (const type of [ 'upload', 'import-url', 'import-torrent', 'update' ]) {
+    registerVideoField(commonOptions, { type })
+  }
+}
+```
+
+PeerTube will send this field value in `body.pluginData['my-field-name']` and fetch it from `video.pluginData['my-field-name']`.
+
+So for example, if you want to store an additional metadata for videos, register the following hooks in **server**:
+
+```js
+async function register ({
+  registerHook,
+  storageManager
+}) {
+  const fieldName = 'my-field-name'
+
+  // Store data associated to this video
+  registerHook({
+    target: 'action:api.video.updated',
+    handler: ({ video, body }) => {
+      if (!body.pluginData) return
+
+      const value = body.pluginData[fieldName]
+      if (!value) return
+
+      storageManager.storeData(fieldName + '-' + video.id, value)
+    }
+  })
+
+  // Add your custom value to the video, so the client autofill your field using the previously stored value
+  registerHook({
+    target: 'filter:api.video.get.result',
+    handler: async (video) => {
+      if (!video) return video
+      if (!video.pluginData) video.pluginData = {}
 
+      const result = await storageManager.getData(fieldName + '-' + video.id)
+      video.pluginData[fieldName] = result
 
+      return video
+    }
+  })
+}
+```
 ### Publishing
 
 PeerTube plugins and themes should be published on [NPM](https://www.npmjs.com/) so that PeerTube indexes
@@ -344,9 +600,9 @@ Update the `package.json` fields:
    * `author`
    * `bugs`
    * `engine.peertube` (the PeerTube version compatibility, must be `>=x.y.z` and nothing else)
-   
+
 **Caution:** Don't update or remove other keys, or PeerTube will not be able to index/install your plugin.
-If you don't need static directories, use an empty `object`: 
+If you don't need static directories, use an empty `object`:
 
 ```json
 {
@@ -371,7 +627,7 @@ And if you don't need CSS or client script files, use an empty `array`:
 
 Now you can register hooks or settings, write CSS and add static directories to your plugin or your theme :)
 
-**Caution:** It's up to you to check the code you write will be compatible with the PeerTube NodeJS version, 
+**Caution:** It's up to you to check the code you write will be compatible with the PeerTube NodeJS version,
 and will be supported by web browsers.
 If you want to write modern JavaScript, please use a transpiler like [Babel](https://babeljs.io/).
 
@@ -402,30 +658,55 @@ Translation files are just objects, with the english sentence as the key and the
 }
 ```
 
+### Build your plugin
+
+If you added client scripts, you'll need to build them using webpack.
+
+Install webpack:
+
+```
+$ npm install
+```
+
+Add/update your files in the `clientFiles` array of `webpack.config.js`:
+
+```
+$ $EDITOR ./webpack.config.js
+```
+
+Build your client files:
+
+```
+$ npm run build
+```
+
+You built files are in the `dist/` directory. Check `package.json` to correctly point to them.
+
+
 ### Test your plugin/theme
 
 You'll need to have a local PeerTube instance:
- * Follow the [dev prerequisites](https://github.com/Chocobozzz/PeerTube/blob/develop/.github/CONTRIBUTING.md#prerequisites) 
+ * Follow the [dev prerequisites](https://github.com/Chocobozzz/PeerTube/blob/develop/.github/CONTRIBUTING.md#prerequisites)
  (to clone the repository, install dependencies and prepare the database)
- * Build PeerTube (`--light` to only build the english language): 
+ * Build PeerTube (`--light` to only build the english language):
 
 ```
 $ npm run build -- --light
 ```
 
  * Build the CLI:
+
 ```
 $ npm run setup:cli
 ```
- * Run PeerTube (you can access to your instance on http://localhost:9000): 
+
+ * Run PeerTube (you can access to your instance on http://localhost:9000):
 
 ```
 $ NODE_ENV=test npm start
 ```
 
- * Register the instance via the CLI: 
+ * Register the instance via the CLI:
 
 ```
 $ node ./dist/server/tools/peertube.js auth add -u 'http://localhost:9000' -U 'root' --password 'test'
@@ -451,7 +732,7 @@ and republish it on NPM. Remember that the PeerTube index will take into account
 
 ## Plugin & Theme hooks/helpers API
 
-See the dedicated documentation: https://docs.joinpeertube.org/#/api-plugins
+See the dedicated documentation: https://docs.joinpeertube.org/api-plugins
 
 
 ## Tips
@@ -473,11 +754,11 @@ registerHook({
   }
 })
 ```
-  * 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)
-  * Don't use PeerTube dependencies. Use your own :) 
+  * 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/choose)
+  * Don't use PeerTube dependencies. Use your own :)
 
 If your plugin is broken with a new PeerTube release, update your code and the `peertubeEngine` field of your `package.json` field.
-This way, older PeerTube versions will still use your old plugin, and new PeerTube versions will use your updated plugin. 
+This way, older PeerTube versions will still use your old plugin, and new PeerTube versions will use your updated plugin.
 
 ### Spam/moderation plugin
 
@@ -488,7 +769,7 @@ If you want to create an antispam/moderation plugin, you could use the following
  * `filter:api.video-threads.list.result`: to change/hide the text of threads
  * `filter:api.video-thread-comments.list.result`: to change/hide the text of replies
  * `filter:video.auto-blacklist.result`: to automatically blacklist local or remote videos
+
 ### Other plugin examples
 
 You can take a look to "official" PeerTube plugins if you want to take inspiration from them: https://framagit.org/framasoft/peertube/official-plugins