Skip to content

Plugins & Themes API

This is the API reference for plugins and themes. An introduction and quickstart into its use is provided in the corresponding Contribute guide.

Hooks

Server hooks (only plugins)

ts
// {hookType}:{root}.{location}.{subLocation?}.{actionType}.{target}

export const serverFilterHookObject = {
  // Filter params/result used to list videos for the REST API
  // (used by the trending page, recently-added page, local page etc)
  'filter:api.videos.list.params': true,
  'filter:api.videos.list.result': true,

  // Filter params/result used to list a video playlists videos
  // for the REST API
  'filter:api.video-playlist.videos.list.params': true,
  'filter:api.video-playlist.videos.list.result': true,

  // Filter params/result used to list account videos for the REST API
  'filter:api.accounts.videos.list.params': true,
  'filter:api.accounts.videos.list.result': true,

  // Filter params/result used to list channel videos for the REST API
  'filter:api.video-channels.videos.list.params': true,
  'filter:api.video-channels.videos.list.result': true,

  // Filter params/result used to list my user videos for the REST API
  'filter:api.user.me.videos.list.params': true,
  'filter:api.user.me.videos.list.result': true,

  // Filter params/result used to list overview videos for the REST API
  'filter:api.overviews.videos.list.params': true,
  'filter:api.overviews.videos.list.result': true,

  // Filter params/result used to list subscription videos for the REST API
  'filter:api.user.me.subscription-videos.list.params': true,
  'filter:api.user.me.subscription-videos.list.result': true,

  // Filter params/results to search videos/channels in the DB or on the remote index
  'filter:api.search.videos.local.list.params': true,
  'filter:api.search.videos.local.list.result': true,
  'filter:api.search.videos.index.list.params': true,
  'filter:api.search.videos.index.list.result': true,
  'filter:api.search.video-channels.local.list.params': true,
  'filter:api.search.video-channels.local.list.result': true,
  'filter:api.search.video-channels.index.list.params': true,
  'filter:api.search.video-channels.index.list.result': true,
  'filter:api.search.video-playlists.local.list.params': true,
  'filter:api.search.video-playlists.local.list.result': true,
  'filter:api.search.video-playlists.index.list.params': true,
  'filter:api.search.video-playlists.index.list.result': true,

  // Filter the result of the get function
  // Used to get detailed video information (video watch page for example)
  'filter:api.video.get.result': true,

  // Filter params/results when listing video channels
  'filter:api.video-channels.list.params': true,
  'filter:api.video-channels.list.result': true,

  // Filter the result when getting a video channel
  'filter:api.video-channel.get.result': true,

  // Filter the result of the accept upload/live, import via torrent/url functions
  // If this function returns false then the upload is aborted with an error
  'filter:api.video.upload.accept.result': true,
  'filter:api.live-video.create.accept.result': true,
  'filter:api.video.pre-import-url.accept.result': true,
  'filter:api.video.pre-import-torrent.accept.result': true,
  'filter:api.video.post-import-url.accept.result': true,
  'filter:api.video.post-import-torrent.accept.result': true,
  'filter:api.video.update-file.accept.result': true,
  // PeerTube >= 6.1
  'filter:api.video.user-import.accept.result': true,
  // Filter the result of the accept comment (thread or reply) functions
  // If the functions return false then the user cannot post its comment
  'filter:api.video-thread.create.accept.result': true,
  'filter:api.video-comment-reply.create.accept.result': true,

  // Filter attributes when creating video object
  'filter:api.video.upload.video-attribute.result': true,
  'filter:api.video.import-url.video-attribute.result': true,
  'filter:api.video.import-torrent.video-attribute.result': true,
  'filter:api.video.live.video-attribute.result': true,
  // PeerTube >= 6.1
  'filter:api.video.user-import.video-attribute.result': true,

  // Filter params/result used to list threads of a specific video
  // (used by the video watch page)
  'filter:api.video-threads.list.params': true,
  'filter:api.video-threads.list.result': true,

  // Filter params/result used to list replies of a specific thread
  // (used by the video watch page when we click on the "View replies" button)
  'filter:api.video-thread-comments.list.params': true,
  'filter:api.video-thread-comments.list.result': true,

  // Filter get stats result
  'filter:api.server.stats.get.result': true,

  // Filter result used to check if we need to auto blacklist a video
  // (fired when a local or remote video is created or updated)
  'filter:video.auto-blacklist.result': true,

  // Filter result used to check if a user can register on the instance
  'filter:api.user.signup.allowed.result': true,

  // Filter result used to check if a user can send a registration request on the instance
  // PeerTube >= 5.1
  'filter:api.user.request-signup.allowed.result': true,

  // Filter result used to check if video/torrent download is allowed
  'filter:api.download.video.allowed.result': true,
  'filter:api.download.generated-video.allowed.result': true,
  'filter:api.download.torrent.allowed.result': true,

  // Filter result to check if the embed is allowed for a particular request
  'filter:html.embed.video.allowed.result': true,
  'filter:html.embed.video-playlist.allowed.result': true,

  // Peertube >= 5.2
  'filter:html.client.json-ld.result': true,

  'filter:job-queue.process.params': true,
  'filter:job-queue.process.result': true,

  'filter:transcoding.manual.resolutions-to-transcode.result': true,
  'filter:transcoding.auto.resolutions-to-transcode.result': true,

  'filter:activity-pub.remote-video-comment.create.accept.result': true,

  'filter:activity-pub.activity.context.build.result': true,

  // Filter the result of video JSON LD builder
  // You may also need to use filter:activity-pub.activity.context.build.result to also update JSON LD context
  'filter:activity-pub.video.json-ld.build.result': true,

  // Filter result to allow custom XMLNS definitions in podcast RSS feeds
  // Peertube >= 5.2
  'filter:feed.podcast.rss.create-custom-xmlns.result': true,

  // Filter result to allow custom tags in podcast RSS feeds
  // Peertube >= 5.2
  'filter:feed.podcast.channel.create-custom-tags.result': true,
  // Peertube >= 5.2
  'filter:feed.podcast.video.create-custom-tags.result': true,
  // Peertube >= 6.1
  'filter:api.user.me.get.result': true
}

export type ServerFilterHookName = keyof typeof serverFilterHookObject

export const serverActionHookObject = {
  // Fired when the application has been loaded and is listening HTTP requests
  'action:application.listening': true,

  // Fired when a new notification is created
  'action:notifier.notification.created': true,

  // API actions hooks give access to the original express `req` and `res` parameters

  // Fired when a local video is updated
  'action:api.video.updated': true,
  // Fired when a local video is deleted
  'action:api.video.deleted': true,
  // Fired when a local video is uploaded
  'action:api.video.uploaded': true,
  // Fired when a local video is viewed
  'action:api.video.viewed': true,

  // Fired when a local video file has been replaced by a new one
  'action:api.video.file-updated': true,

  // Fired when a video channel is created
  'action:api.video-channel.created': true,
  // Fired when a video channel is updated
  'action:api.video-channel.updated': true,
  // Fired when a video channel is deleted
  'action:api.video-channel.deleted': true,

  // Fired when a live video is created
  'action:api.live-video.created': true,
  // Fired when a live video starts or ends
  // Peertube >= 5.2
  'action:live.video.state.updated': true,

  // Fired when a thread is created
  'action:api.video-thread.created': true,
  // Fired when a reply to a thread is created
  'action:api.video-comment-reply.created': true,
  // Fired when a comment (thread or reply) is deleted
  'action:api.video-comment.deleted': true,

  // Fired when a caption is created
  'action:api.video-caption.created': true,
  // Fired when a caption is deleted
  'action:api.video-caption.deleted': true,

  // Fired when a user is blocked (banned)
  'action:api.user.blocked': true,
  // Fired when a user is unblocked (unbanned)
  'action:api.user.unblocked': true,
  // Fired when a user registered on the instance
  'action:api.user.registered': true,
  // Fired when a user requested registration on the instance
  // PeerTube >= 5.1
  'action:api.user.requested-registration': true,
  // Fired when an admin/moderator created a user
  'action:api.user.created': true,
  // Fired when a user is removed by an admin/moderator
  'action:api.user.deleted': true,
  // Fired when a user is updated by an admin/moderator
  'action:api.user.updated': true,

  // Fired when a user got a new oauth2 token
  'action:api.user.oauth2-got-token': true,

  // Fired when a video is added to a playlist
  'action:api.video-playlist-element.created': true,

  // Fired when a remote video has been created/updated
  'action:activity-pub.remote-video.created': true,
  'action:activity-pub.remote-video.updated': true
}

export type ServerActionHookName = keyof typeof serverActionHookObject

export const serverHookObject = Object.assign({}, serverFilterHookObject, serverActionHookObject)
export type ServerHookName = keyof typeof serverHookObject

export interface ServerHook {
  runHook <T> (hookName: ServerHookName, result?: T, params?: any): Promise<T>
}

Client hooks

ts
// Data from API hooks: {hookType}:api.{location}.{elementType}.{actionType}.{target}
// Data in internal functions: {hookType}:{location}.{elementType}.{actionType}.{target}

export const clientFilterHookObject = {
  // Filter params/result of the function that fetch videos of the trending page
  'filter:api.trending-videos.videos.list.params': true,
  'filter:api.trending-videos.videos.list.result': true,

  // Filter params/result of the function that fetch videos of the trending page
  'filter:api.most-liked-videos.videos.list.params': true,
  'filter:api.most-liked-videos.videos.list.result': true,

  // Filter params/result of the function that fetch videos of the local page
  'filter:api.local-videos.videos.list.params': true,
  'filter:api.local-videos.videos.list.result': true,

  // Filter params/result of the function that fetch videos of the recently-added page
  'filter:api.recently-added-videos.videos.list.params': true,
  'filter:api.recently-added-videos.videos.list.result': true,

  // Filter params/result of the function that fetch videos of the user subscription page
  'filter:api.user-subscriptions-videos.videos.list.params': true,
  'filter:api.user-subscriptions-videos.videos.list.result': true,

  // Filter params/result of the function that fetch the video of the video-watch page
  'filter:api.video-watch.video.get.params': true,
  'filter:api.video-watch.video.get.result': true,

  // Filter params/result of the function that fetch video playlist elements of the video-watch page
  'filter:api.video-watch.video-playlist-elements.get.params': true,
  'filter:api.video-watch.video-playlist-elements.get.result': true,

  // Filter params/result of the function that fetch the threads of the video-watch page
  'filter:api.video-watch.video-threads.list.params': true,
  'filter:api.video-watch.video-threads.list.result': true,

  // Filter params/result of the function that fetch the replies of a thread in the video-watch page
  'filter:api.video-watch.video-thread-replies.list.params': true,
  'filter:api.video-watch.video-thread-replies.list.result': true,

  // Filter params/result of the function that fetch videos according to the user search
  'filter:api.search.videos.list.params': true,
  'filter:api.search.videos.list.result': true,
  // Filter params/result of the function that fetch video channels according to the user search
  'filter:api.search.video-channels.list.params': true,
  'filter:api.search.video-channels.list.result': true,
  // Filter params/result of the function that fetch video playlists according to the user search
  'filter:api.search.video-playlists.list.params': true,
  'filter:api.search.video-playlists.list.result': true,

  // Filter form
  'filter:api.signup.registration.create.params': true,

  // Filter params/result of the function that fetch video playlist elements of the my-library page
  'filter:api.my-library.video-playlist-elements.list.params': true,
  'filter:api.my-library.video-playlist-elements.list.result': true,

  // Filter the options to create our player
  'filter:internal.video-watch.player.build-options.params': true,
  'filter:internal.video-watch.player.build-options.result': true,

  // Filter the options to load a new video in our player
  'filter:internal.video-watch.player.load-options.params': true,
  'filter:internal.video-watch.player.load-options.result': true,

  // Filter our SVG icons content
  'filter:internal.common.svg-icons.get-content.params': true,
  'filter:internal.common.svg-icons.get-content.result': true,

  // Filter left menu links
  'filter:left-menu.links.create.result': true,

  // Filter upload page alert messages
  'filter:upload.messages.create.result': true,

  'filter:login.instance-about-plugin-panels.create.result': true,
  'filter:signup.instance-about-plugin-panels.create.result': true,

  'filter:share.video-embed-code.build.params': true,
  'filter:share.video-embed-code.build.result': true,
  'filter:share.video-playlist-embed-code.build.params': true,
  'filter:share.video-playlist-embed-code.build.result': true,

  'filter:share.video-embed-url.build.params': true,
  'filter:share.video-embed-url.build.result': true,
  'filter:share.video-playlist-embed-url.build.params': true,
  'filter:share.video-playlist-embed-url.build.result': true,

  'filter:share.video-url.build.params': true,
  'filter:share.video-url.build.result': true,
  'filter:share.video-playlist-url.build.params': true,
  'filter:share.video-playlist-url.build.result': true,

  'filter:video-watch.video-plugin-metadata.result': true,

  // Filter videojs options built for PeerTube player
  'filter:internal.player.videojs.options.result': true,

  // Filter p2p media loader options built for PeerTube player
  'filter:internal.player.p2p-media-loader.options.result': true
}

export type ClientFilterHookName = keyof typeof clientFilterHookObject

export const clientActionHookObject = {
  // Fired when the application is being initialized
  'action:application.init': true,

  // Fired when the video watch page is being initialized
  'action:video-watch.init': true,
  // Fired when the video watch page loaded the video
  'action:video-watch.video.loaded': true,
  // Fired when the player finished loading
  'action:video-watch.player.loaded': true,
  // Fired when the video watch page comments(threads) are loaded and load more comments on scroll
  'action:video-watch.video-threads.loaded': true,
  // Fired when a user click on 'View x replies' and they're loaded
  'action:video-watch.video-thread-replies.loaded': true,

  // Fired when the video channel creation page is being initialized
  'action:video-channel-create.init': true,

  // Fired when the video channel update page is being initialized
  'action:video-channel-update.init': true,
  'action:video-channel-update.video-channel.loaded': true,

  // Fired when the page that list video channel videos is being initialized
  'action:video-channel-videos.init': true,
  'action:video-channel-videos.video-channel.loaded': true,
  'action:video-channel-videos.videos.loaded': true,

  // Fired when the page that list video channel playlists is being initialized
  'action:video-channel-playlists.init': true,
  'action:video-channel-playlists.video-channel.loaded': true,
  'action:video-channel-playlists.playlists.loaded': true,

  // Fired when the video edit page (upload, URL/torrent import, update) is being initialized
  // Contains a `type` and `updateForm` object attributes
  'action:video-edit.init': true,

  // Fired when values of the video edit form changed
  'action:video-edit.form.updated': true,

  // Fired when the login page is being initialized
  'action:login.init': true,

  // Fired when the search page is being initialized
  'action:search.init': true,

  // Fired every time Angular URL changes
  'action:router.navigation-end': true,

  // Fired when the registration page is being initialized
  'action:signup.register.init': true,

  // PeerTube >= 3.2
  // Fired when the admin plugin settings page is being initialized
  'action:admin-plugin-settings.init': true,

  // Fired when the video upload page is being initialized
  'action:video-upload.init': true,
  // Fired when the video import by URL page is being initialized
  'action:video-url-import.init': true,
  // Fired when the video import by torrent/magnet URI page is being initialized
  'action:video-torrent-import.init': true,
  // Fired when the "Go Live" page is being initialized
  'action:go-live.init': true,

  // Fired when the user explicitly logged in/logged out
  'action:auth-user.logged-in': true,
  'action:auth-user.logged-out': true,
  // Fired when the application loaded user information (using tokens from the local storage or after a successful login)
  'action:auth-user.information-loaded': true,

  // Fired when the modal to download a video/caption is shown
  'action:modal.video-download.shown': true,
  // Fired when the modal to share a video/playlist is shown
  'action:modal.share.shown': true,

  // ####### Embed hooks #######
  // /!\ In embed scope, peertube helpers are not available
  // ###########################

  // Fired when the embed loaded the player
  'action:embed.player.loaded': true
}

export type ClientActionHookName = keyof typeof clientActionHookObject

export const clientHookObject = Object.assign({}, clientFilterHookObject, clientActionHookObject)
export type ClientHookName = keyof typeof clientHookObject

export interface ClientHook {
  runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T>
}
ts
export type PluginClientScope =
  'common' |
  'video-watch' |
  'search' |
  'signup' |
  'login' |
  'embed' |
  'video-edit' |
  'admin-plugin' |
  'my-library' |
  'video-channel' |
  'my-account'

Server register/unregister (only plugins)

Your library file should export a register and unregister functions:

ts
import { RegisterServerOptions } from './register-server-option.model.js'

export interface PluginLibrary {
  register: (options: RegisterServerOptions) => Promise<any>

  unregister: () => Promise<any>
}

PeerTube provides different helpers to the register function:

ts
import { Response, Router } from 'express'
import { Server } from 'http'
import { Logger } from 'winston'
import {
  PluginPlaylistPrivacyManager,
  PluginSettingsManager,
  PluginStorageManager,
  PluginTranscodingManager,
  PluginVideoCategoryManager,
  PluginVideoLanguageManager,
  PluginVideoLicenceManager,
  PluginVideoPrivacyManager,
  RegisterServerHookOptions,
  RegisterServerSettingOptions,
  ServerConfig,
  ThumbnailType_Type,
  VideoBlacklistCreate
} from '@peertube/peertube-models'
import { ActorModel } from '@server/models/actor/actor.js'
import { MUserDefault, MVideo, MVideoThumbnail, MVideoWithAllFiles, UserNotificationModelForApi } from '../models/index.js'
import {
  RegisterServerAuthExternalOptions,
  RegisterServerAuthExternalResult,
  RegisterServerAuthPassOptions
} from './register-server-auth.model.js'
import { RegisterServerWebSocketRouteOptions } from './register-server-websocket-route.model.js'

export type PeerTubeHelpers = {
  logger: Logger

  database: {
    query: Function
  }

  videos: {
    loadByUrl: (url: string) => Promise<MVideoThumbnail>
    loadByIdOrUUIDWithFiles: (id: number | string) => Promise<MVideoWithAllFiles>
    loadByIdOrUUID: (id: number | string) => Promise<MVideoThumbnail>

    removeVideo: (videoId: number) => Promise<void>

    ffprobe: (path: string) => Promise<any>

    getFiles: (id: number | string) => Promise<{
      webtorrent: { // TODO: remove in v7
        videoFiles: {
          path: string // Could be null if using remote storage
          url: string
          resolution: number
          size: number
          fps: number
        }[]
      }

      webVideo: {
        videoFiles: {
          path: string // Could be null if using remote storage
          url: string
          resolution: number
          size: number
          fps: number
        }[]
      }

      hls: {
        videoFiles: {
          path: string // Could be null if using remote storage
          url: string
          resolution: number
          size: number
          fps: number
        }[]
      }

      thumbnails: {
        type: ThumbnailType_Type
        path: string
      }[]
    }>
  }

  config: {
    getWebserverUrl: () => string

    // PeerTube >= 5.1
    getServerListeningConfig: () => { hostname: string, port: number }

    getServerConfig: () => Promise<ServerConfig>
  }

  moderation: {
    blockServer: (options: { byAccountId: number, hostToBlock: string }) => Promise<void>
    unblockServer: (options: { byAccountId: number, hostToUnblock: string }) => Promise<void>
    blockAccount: (options: { byAccountId: number, handleToBlock: string }) => Promise<void>
    unblockAccount: (options: { byAccountId: number, handleToUnblock: string }) => Promise<void>

    blacklistVideo: (options: { videoIdOrUUID: number | string, createOptions: VideoBlacklistCreate }) => Promise<void>
    unblacklistVideo: (options: { videoIdOrUUID: number | string }) => Promise<void>
  }

  server: {
    // PeerTube >= 5.0
    getHTTPServer: () => Server

    getServerActor: () => Promise<ActorModel>
  }

  socket: {
    sendNotification: (userId: number, notification: UserNotificationModelForApi) => void
    sendVideoLiveNewState: (video: MVideo) => void
  }

  plugin: {
    // PeerTube >= 3.2
    getBaseStaticRoute: () => string

    // PeerTube >= 3.2
    getBaseRouterRoute: () => string
    // PeerTube >= 5.0
    getBaseWebSocketRoute: () => string

    // PeerTube >= 3.2
    getDataDirectoryPath: () => string
  }

  user: {
    // PeerTube >= 3.2
    getAuthUser: (response: Response) => Promise<MUserDefault>

    // PeerTube >= 4.3
    loadById: (id: number) => Promise<MUserDefault>
  }
}

export type RegisterServerOptions = {
  registerHook: (options: RegisterServerHookOptions) => void

  registerSetting: (options: RegisterServerSettingOptions) => void

  settingsManager: PluginSettingsManager

  storageManager: PluginStorageManager

  videoCategoryManager: PluginVideoCategoryManager
  videoLanguageManager: PluginVideoLanguageManager
  videoLicenceManager: PluginVideoLicenceManager

  videoPrivacyManager: PluginVideoPrivacyManager
  playlistPrivacyManager: PluginPlaylistPrivacyManager

  transcodingManager: PluginTranscodingManager

  registerIdAndPassAuth: (options: RegisterServerAuthPassOptions) => void
  registerExternalAuth: (options: RegisterServerAuthExternalOptions) => RegisterServerAuthExternalResult
  unregisterIdAndPassAuth: (authName: string) => void
  unregisterExternalAuth: (authName: string) => void

  // Get plugin router to create custom routes
  // Base routes of this router are
  //  * /plugins/:pluginName/:pluginVersion/router/...
  //  * /plugins/:pluginName/router/...
  getRouter(): Router

  // PeerTube >= 5.0
  // Register WebSocket route
  // Base routes of the WebSocket router are
  //  * /plugins/:pluginName/:pluginVersion/ws/...
  //  * /plugins/:pluginName/ws/...
  registerWebSocketRoute: (options: RegisterServerWebSocketRouteOptions) => void

  peertubeHelpers: PeerTubeHelpers
}

Register hook options

To register hook listeners:

ts
import { ServerHookName } from './server-hook.model.js'

export interface RegisterServerHookOptions {
  target: ServerHookName
  handler: Function
  priority?: number
}

Register settings options

To register settings:

ts
import { RegisterClientFormFieldOptions } from '../../client/index.js'

export type RegisterServerSettingOptions = RegisterClientFormFieldOptions & {
  // If the setting is not private, anyone can view its value (client code included)
  // If the setting is private, only server-side hooks can access it
  // Mainly used by the PeerTube client to get admin config
  private: boolean
}

export interface RegisteredServerSettings {
  registeredSettings: RegisterServerSettingOptions[]
}
ts
export type RegisterClientFormFieldOptions = {
  name?: string
  label?: string
  type: 'input' | 'input-checkbox' | 'input-password' | 'input-textarea' | 'markdown-text' | 'markdown-enhanced' | 'select' | 'html'

  // For select type
  options?: { value: string, label: string }[]

  // For html type
  html?: string

  descriptionHTML?: string

  // Default setting value
  default?: string | boolean

  // Not supported by plugin setting registration, use registerSettingsScript instead
  hidden?: (options: any) => boolean

  // Return undefined | null if there is no error or return a string with the detailed error
  // Not supported by plugin setting registration
  error?: (options: any) => Promise<{ error: boolean, text?: string }>
}

export interface RegisterClientVideoFieldOptions {
  type: 'update' | 'upload' | 'import-url' | 'import-torrent' | 'go-live'

  // Default to 'plugin-settings'
  tab?: 'main' | 'plugin-settings'
}
ts
export type SettingValue = string | boolean

export interface SettingEntries {
  [settingName: string]: SettingValue
}

export type SettingsChangeCallback = (settings: SettingEntries) => Promise<any>

export interface PluginSettingsManager {
  getSetting: (name: string) => Promise<SettingValue>

  getSettings: (names: string[]) => Promise<SettingEntries>

  setSetting: (name: string, value: SettingValue) => Promise<any>

  onSettingsChange: (cb: SettingsChangeCallback) => void
}

Storage manager API

To save/load JSON (please don't put too much data in there because we store it in the PeerTube database):

ts
export interface PluginStorageManager {
  getData: (key: string) => Promise<string>

  storeData: (key: string, data: any) => Promise<any>
}

Register auth methods API

To register id and pass auth methods (LDAP etc), or external auth (OpenID, SAML2 etc) methods:

ts
import express from 'express'
import { UserAdminFlagType, UserRoleType } from '@peertube/peertube-models'
import { MOAuthToken, MUser } from '../models/index.js'

export type RegisterServerAuthOptions = RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions

export type AuthenticatedResultUpdaterFieldName = 'displayName' | 'role' | 'adminFlags' | 'videoQuota' | 'videoQuotaDaily'

export interface RegisterServerAuthenticatedResult {
  // Update the user profile if it already exists
  // Default behaviour is no update
  // Introduced in PeerTube >= 5.1
  userUpdater?: <T> (options: {
    fieldName: AuthenticatedResultUpdaterFieldName
    currentValue: T
    newValue: T
  }) => T

  username: string
  email: string
  role?: UserRoleType
  displayName?: string

  // PeerTube >= 5.1
  adminFlags?: UserAdminFlagType

  // PeerTube >= 5.1
  videoQuota?: number
  // PeerTube >= 5.1
  videoQuotaDaily?: number
}

export interface RegisterServerExternalAuthenticatedResult extends RegisterServerAuthenticatedResult {
  req: express.Request
  res: express.Response
}

interface RegisterServerAuthBase {
  // Authentication name (a plugin can register multiple auth strategies)
  authName: string

  // Called by PeerTube when a user from your plugin logged out
  // Returns a redirectUrl sent to the client or nothing
  onLogout?(user: MUser, req: express.Request): Promise<string>

  // Your plugin can hook PeerTube access/refresh token validity
  // So you can control for your plugin the user session lifetime
  hookTokenValidity?(options: { token: MOAuthToken, type: 'access' | 'refresh' }): Promise<{ valid: boolean }>
}

export interface RegisterServerAuthPassOptions extends RegisterServerAuthBase {
  // Weight of this authentication so PeerTube tries the auth methods in DESC weight order
  getWeight(): number

  // Used by PeerTube to login a user
  // Returns null if the login failed, or { username, email } on success
  login(body: {
    id: string
    password: string
  }): Promise<RegisterServerAuthenticatedResult | null>
}

export interface RegisterServerAuthExternalOptions extends RegisterServerAuthBase {
  // Will be displayed in a block next to the login form
  authDisplayName: () => string

  onAuthRequest: (req: express.Request, res: express.Response) => void
}

export interface RegisterServerAuthExternalResult {
  userAuthenticated (options: RegisterServerExternalAuthenticatedResult): void
}

Video categories manager API

ts
import { ConstantManager } from '../plugin-constant-manager.model.js'

export interface PluginVideoCategoryManager extends ConstantManager<number> {
  /**
   * @deprecated use `addConstant` instead
   */
  addCategory: (categoryKey: number, categoryLabel: string) => boolean

  /**
   * @deprecated use `deleteConstant` instead
   */
  deleteCategory: (categoryKey: number) => boolean
}

Video languages manager API

ts
import { ConstantManager } from '../plugin-constant-manager.model.js'

export interface PluginVideoLanguageManager extends ConstantManager<string> {
  /**
   * @deprecated use `addConstant` instead
   */
  addLanguage: (languageKey: string, languageLabel: string) => boolean

  /**
   * @deprecated use `deleteConstant` instead
   */
  deleteLanguage: (languageKey: string) => boolean
}

Video licences manager API

ts
import { ConstantManager } from '../plugin-constant-manager.model.js'

export interface PluginVideoLicenceManager extends ConstantManager<number> {
  /**
   * @deprecated use `addConstant` instead
   */
  addLicence: (licenceKey: number, licenceLabel: string) => boolean

  /**
   * @deprecated use `deleteConstant` instead
   */
  deleteLicence: (licenceKey: number) => boolean
}

Video privacy manager API

ts
import { VideoPrivacyType } from '../../../videos/video-privacy.enum.js'
import { ConstantManager } from '../plugin-constant-manager.model.js'

export interface PluginVideoPrivacyManager extends ConstantManager<VideoPrivacyType> {
  /**
   * PUBLIC = 1,
   * UNLISTED = 2,
   * PRIVATE = 3
   * INTERNAL = 4
   * @deprecated use `deleteConstant` instead
   */
  deletePrivacy: (privacyKey: VideoPrivacyType) => boolean
}

Video playlist privacy manager API

ts
import { VideoPlaylistPrivacyType } from '../../../videos/playlist/video-playlist-privacy.model.js'
import { ConstantManager } from '../plugin-constant-manager.model.js'

export interface PluginPlaylistPrivacyManager extends ConstantManager<VideoPlaylistPrivacyType> {
  /**
   * PUBLIC = 1,
   * UNLISTED = 2,
   * PRIVATE = 3
   * @deprecated use `deleteConstant` instead
   */
  deletePlaylistPrivacy: (privacyKey: VideoPlaylistPrivacyType) => boolean
}

Video transcoding manager API

To add profile and encoders priority to ffmpeg transcoding jobs (profile needs to be selected by the admin in the PeerTube configuration):

ts
import { EncoderOptionsBuilder } from '../../../videos/transcoding/index.js'

export interface PluginTranscodingManager {
  addLiveProfile (encoder: string, profile: string, builder: EncoderOptionsBuilder): boolean

  addVODProfile (encoder: string, profile: string, builder: EncoderOptionsBuilder): boolean

  addLiveEncoderPriority (streamType: 'audio' | 'video', encoder: string, priority: number): void

  addVODEncoderPriority (streamType: 'audio' | 'video', encoder: string, priority: number): void

  removeAllProfilesAndEncoderPriorities(): void
}
ts
// Types used by plugins and ffmpeg-utils

import { FfprobeData } from 'fluent-ffmpeg'

export type EncoderOptionsBuilderParams = {
  input: string

  resolution: number

  // If PeerTube applies a filter, transcoding profile must not copy input stream
  canCopyAudio: boolean
  canCopyVideo: boolean

  fps: number

  // Could be undefined if we could not get input bitrate (some RTMP streams for example)
  inputBitrate: number
  inputRatio: number
  inputProbe: FfprobeData

  // For lives
  streamNum?: number
}

export type EncoderOptionsBuilder = (params: EncoderOptionsBuilderParams) => Promise<EncoderOptions> | EncoderOptions

export interface EncoderOptions {
  copy?: boolean // Copy stream? Default to false

  scaleFilter?: {
    name: string
  }

  inputOptions?: string[]
  outputOptions?: string[]
}

// All our encoders

export interface EncoderProfile <T> {
  [ profile: string ]: T

  default: T
}

export type AvailableEncoders = {
  available: {
    live: {
      [ encoder: string ]: EncoderProfile<EncoderOptionsBuilder>
    }

    vod: {
      [ encoder: string ]: EncoderProfile<EncoderOptionsBuilder>
    }
  }

  encodersToTry: {
    vod: {
      video: string[]
      audio: string[]
    }

    live: {
      video: string[]
      audio: string[]
    }
  }
}

Client register

Your client script should export a register function:

ts
import { RegisterClientOptions } from './register-client-option.model'

export interface ClientScript {
  register: (options: RegisterClientOptions) => Promise<any>
}

PeerTube provides different helpers to the register function:

ts
import {
  MyUser,
  RegisterClientFormFieldOptions,
  RegisterClientHookOptions,
  RegisterClientRouteOptions,
  RegisterClientSettingsScriptOptions,
  RegisterClientVideoFieldOptions,
  ServerConfig, SettingEntries
} from '@peertube/peertube-models'

export type RegisterClientOptions = {
  registerHook: (options: RegisterClientHookOptions) => void

  registerVideoField: (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => void

  registerSettingsScript: (options: RegisterClientSettingsScriptOptions) => void

  registerClientRoute: (options: RegisterClientRouteOptions) => void

  peertubeHelpers: RegisterClientHelpers
}

export type RegisterClientHelpers = {
  getBaseStaticRoute: () => string

  getBaseRouterRoute: () => string

  // PeerTube >= 5.0
  getBaseWebSocketRoute: () => string

  getBasePluginClientPath: () => string

  isLoggedIn: () => boolean

  getAuthHeader: () => { 'Authorization': string } | undefined

  getSettings: () => Promise<SettingEntries>

  getUser: () => MyUser

  getServerConfig: () => Promise<ServerConfig>

  notifier: {
    info: (text: string, title?: string, timeout?: number) => void
    error: (text: string, title?: string, timeout?: number) => void
    success: (text: string, title?: string, timeout?: number) => void
  }

  showModal: (input: {
    title: string
    content: string
    close?: boolean
    cancel?: { value: string, action?: () => void }
    confirm?: { value: string, action?: () => void }
  }) => void

  markdownRenderer: {
    textMarkdownToHTML: (textMarkdown: string) => Promise<string>
    enhancedMarkdownToHTML: (enhancedMarkdown: string) => Promise<string>
  }

  translate: (toTranslate: string) => Promise<string>
}

Register hook options

To register hook listeners:

ts
import { ClientHookName } from './client-hook.model.js'

export interface RegisterClientHookOptions {
  target: ClientHookName
  handler: Function
  priority?: number
}

Register video form field options

ts
import { RegisterServerSettingOptions } from '../server/index.js'

export interface RegisterClientSettingsScriptOptions {
  isSettingHidden (options: {
    setting: RegisterServerSettingOptions
    formValues: { [name: string]: any }
  }): boolean
}

Client plugin selectors

Selector ids are prefixed by plugin-selector-. For example: plugin-selector-login-form

ts
export type PluginSelectorId =
  'login-form' |
  'menu-user-dropdown-language-item' |
  'about-instance-features' |
  'about-instance-statistics' |
  'about-instance-moderation' |
  'about-menu-instance' |
  'about-menu-peertube' |
  'about-menu-network' |
  'about-instance-other-information'

Client placeholder elements

Element ids are prefixed by plugin-placeholder-. For example: plugin-placeholder-player-next

ts
export type PluginElementPlaceholder =
  'player-next' |
  'share-modal-playlist-settings' |
  'share-modal-video-settings'