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 params/result used to list replies of a specific comment
  // PeerTube >= 8.3
  'filter:api.video-comment-replies.list.params': true,
  'filter:api.video-comment-replies.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 signup requires approval on the instance
  // PeerTube >= 8.2
  'filter:api.user.signup.requires-approval.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 >= 8.2
  'filter:feed.podcast.video-playlist.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,

  // Peertube >= 7.1
  'filter:oauth.password-grant.get-user.params': true,
  'filter:api.email-verification.ask-send-verify-email.body': true,
  'filter:api.users.ask-reset-password.body': true,

  // Peertube >= 7.2
  'filter:email.subject.result': true,
  'filter:email.template-path.result': true,

  // Filter notifications enabled state
  // Peertube >= 8.2
  'filter:notifier.notification.enabled.result': true,

  // PeerTube >= 8.1
  'filter:feed.videos.list.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 browse videos page
  'filter:api.browse-videos.videos.list.params': true,
  'filter:api.browse-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 the replies of a comment nested in a thread of the video-watch page
  // PeerTube >= 8.3
  'filter:api.video-watch.video-comment-replies.list.params': true,
  'filter:api.video-watch.video-comment-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,

  // Filter bulk actions in user list
  'filter:admin-users-list.bulk-actions.create.result': true,

  // Filter actions in comment list
  'filter:admin-video-comments-list.actions.create.result': true,

  // Filter bulk actions in comment list
  'filter:admin-video-comments-list.bulk-actions.create.result': true,

  // Filter user moderation actions
  'filter:user-moderation.actions.create.result': true,

  // Filter actions in abuse list
  'filter:admin-abuse-list.actions.create.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' |
  'admin-users' |
  'admin-comments' |
  'moderation'

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 {
  PluginPlaylistPrivacyManager,
  PluginSettingsManager,
  PluginStorageManager,
  PluginTranscodingManager,
  PluginVideoCategoryManager,
  PluginVideoLanguageManager,
  PluginVideoLicenceManager,
  PluginVideoPrivacyManager,
  RegisterServerHookOptions,
  RegisterServerSettingOptions,
  ServerConfig,
  VideoBlacklistCreate,
  VideoCommentPolicyType,
  VideoPrivacyType
} from '@peertube/peertube-models'
import { ActorModel } from '@server/models/actor/actor.js'
import { Response, Router } from 'express'
import { Server } from 'http'
import { Logger } from 'winston'
import { MAutomaticTag, MUserDefault, MVideo, MVideoThumbnails, MVideoWithAllFiles, UserNotificationModelForApi } from '../models/index.js'
import { RegisterCommentAutoTaggerOptions, RegisterVideoAutoTaggerOptions } from './register-auto-tagger.model.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<MVideoThumbnails>
    loadByIdOrUUIDWithFiles: (id: number | string) => Promise<MVideoWithAllFiles>
    loadByIdOrUUID: (id: number | string) => Promise<MVideoThumbnails>

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

    // PeerTube >= 8.3
    // Update video metadata (does not handle thumbnail/preview files or the video channel)
    updateVideo: (options: {
      videoId: number | string
      attributes: {
        name?: string
        category?: number
        licence?: number
        language?: string
        description?: string
        support?: string
        privacy?: VideoPrivacyType
        tags?: string[]

        commentsPolicy?: VideoCommentPolicyType

        downloadEnabled?: boolean

        nsfw?: boolean
        nsfwSummary?: string
        nsfwFlags?: number

        waitTranscoding?: boolean

        originallyPublishedAt?: Date | string
        videoPasswords?: string[]
      }
    }) => Promise<void>

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

    // PeerTube >= 8.3
    // Make a video file available on disk (downloading it from remote storage if needed) so it can be analyzed
    // The video files of the video are locked until the promise returned by `cb` is resolved/rejected
    withFile: <T>(
      options: {
        videoId: number | string
        videoFileId: number
      },
      cb: (path: string) => Promise<T> | T
    ) => Promise<T>

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

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

      thumbnails: {
        // 1 = Thumbnail, 2 = Preview (deprecated, use width/height instead)
        type: 1 | 2

        height: number
        width: number
        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>
  }

  automaticTags: {
    // PeerTube >= 8.3
    getServerCommentAutomaticTags: (options: { commentId: number }) => Promise<MAutomaticTag[]>
    // PeerTube >= 8.3
    getAccountCommentAutomaticTags: (options: { accountId: number, commentId: number }) => Promise<MAutomaticTag[]>

    // PeerTube >= 8.3
    getServerVideoAutomaticTags: (options: { videoId: number }) => Promise<MAutomaticTag[]>
  }

  email: {
    // PeerTube >= 8.3
    createJob: (payload: {
      // Language of the email to send. If not provided, the default language of the instance will be used
      to: { email: string, language?: string }

      // Email subject
      subject: string

      // Optional title, injected in HTML email
      title?: string

      // Text inside the HTML email
      text: string

      // Button that can be added at the end of the email
      action?: {
        url: string
        text: string
      }
    }) => Promise<void>
  }
}

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

  registerCommentAutoTagger: (options: RegisterCommentAutoTaggerOptions) => void
  registerVideoAutoTagger: (options: RegisterVideoAutoTaggerOptions) => void
  unregisterCommentAutoTagger: (options: RegisterCommentAutoTaggerOptions) => void
  unregisterVideoAutoTagger: (options: RegisterVideoAutoTaggerOptions) => 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: () => any
  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: <T = unknown>(key: string) => Promise<T | undefined>

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

  deleteData: (key: string) => Promise<any>
}

Register auth methods API ​

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

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

export type RegisterServerAuthOptions = RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions

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

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

  // Stable identifier of this user at the identity provider (OIDC `sub` claim, SAML `NameID`, LDAP `uid`/`entryUUID`...)
  // When provided, PeerTube links/looks up the local account using this id instead of relying only on the email
  // address, which is more robust across identity provider email changes and avoids account confusion from
  // email collisions. Optional for backward compatibility.
  // PeerTube >= 8.3
  externalId?: string

  // PeerTube >= 5.1
  adminFlags?: UserAdminFlagType

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

  // Interface/email language of the user (must be one of PeerTube's available locales)
  // PeerTube >= 8.3
  language?: string
}

export interface RegisterServerExternalAuthenticatedResult extends RegisterServerAuthenticatedResult {
  req: express.Request
  res: express.Response
  // Redirect the user to this external URI after the external auth has been verified.
  externalRedirectUri?: string
}

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: MOAuthTokenLight
    user: MUser
    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
  }

  // Additional video filters to apply (e.g., fps)
  videoFilters?: {
    name: string
    rawOptions?: 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 {
  ClientDoAction,
  ClientRouter,
  MyUser,
  RegisterClientFormFieldOptions,
  RegisterClientHookOptions,
  RegisterClientRouteOptions,
  RegisterClientSettingsScriptOptions,
  RegisterClientVideoFieldOptions,
  ServerConfig, SettingEntries
} from '@peertube/peertube-models'

export type RegisterClientOptions = {
  router: ClientRouter

  doAction: ClientDoAction

  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-network-statistics' |
  'about-instance-moderation' |
  'about-instance-other-information' |
  'about-menu-instance' |
  'about-menu-peertube' |
  'about-menu-network'

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'

Client actions ​

ts
export const clientDoActionObject = {
  'admin-abuse-list:load-data': true,
  'application:increment-loader': true,
  'application:decrement-loader': true,
  'admin-users-list:load-data': true,
  'admin-video-comment-list:load-data': true,
  'video-watch-comment-list:load-data': true
}

export type ClientDoActionName = keyof typeof clientDoActionObject

export type ClientDoActionCallback = () => Promise<any>
export type ClientDoAction = (actionName: ClientDoActionName) => Promise<any>