MetaService,
Notifier,
PeerTubeSocket,
+ PluginService,
RestExtractor,
ScreenService,
ServerService,
private videoCaptionService: VideoCaptionService,
private hotkeysService: HotkeysService,
private hooks: HooksService,
+ private pluginService: PluginService,
private peertubeSocket: PeerTubeSocket,
private screenService: ScreenService,
private location: PlatformLocation,
webtorrent: {
videoFiles: video.files
- }
+ },
+
+ pluginsManager: this.pluginService.getPluginsManager()
}
// Only set this if we're in a playlist
-import * as debug from 'debug'
-import { Observable, of, ReplaySubject } from 'rxjs'
-import { catchError, first, map, shareReplay } from 'rxjs/operators'
+import { Observable, of } from 'rxjs'
+import { catchError, map, shareReplay } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http'
import { Inject, Injectable, LOCALE_ID, NgZone } from '@angular/core'
import { VideoEditType } from '@app/+videos/+video-edit/shared/video-edit.type'
import { ServerService } from '@app/core/server/server.service'
import { getDevLocale, isOnDevLocale } from '@app/helpers'
import { CustomModalComponent } from '@app/modal/custom-modal.component'
-import { FormFields, Hooks, loadPlugin, PluginInfo, runHook } from '@root-helpers/plugins'
+import { PluginInfo, PluginsManager } from '@root-helpers/plugins-manager'
import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
import {
ClientHook,
PluginTranslation,
PluginType,
PublicServerSetting,
+ RegisterClientFormFieldOptions,
RegisterClientSettingsScript,
+ RegisterClientVideoFieldOptions,
ServerConfigPlugin
} from '@shared/models'
import { environment } from '../../../environments/environment'
import { RegisterClientHelpers } from '../../../types/register-client-option.model'
-const logger = debug('peertube:plugins')
+type FormFields = {
+ video: {
+ commonOptions: RegisterClientFormFieldOptions
+ videoFormOptions: RegisterClientVideoFieldOptions
+ }[]
+}
@Injectable()
export class PluginService implements ClientHook {
private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins'
private static BASE_PLUGIN_URL = environment.apiUrl + '/plugins'
- pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
- common: new ReplaySubject<boolean>(1),
- 'admin-plugin': new ReplaySubject<boolean>(1),
- search: new ReplaySubject<boolean>(1),
- 'video-watch': new ReplaySubject<boolean>(1),
- signup: new ReplaySubject<boolean>(1),
- login: new ReplaySubject<boolean>(1),
- 'video-edit': new ReplaySubject<boolean>(1),
- embed: new ReplaySubject<boolean>(1)
- }
-
translationsObservable: Observable<PluginTranslation>
customModal: CustomModalComponent
- private plugins: ServerConfigPlugin[] = []
private helpers: { [ npmName: string ]: RegisterClientHelpers } = {}
- private scopes: { [ scopeName: string ]: PluginInfo[] } = {}
-
- private loadedScripts: { [ script: string ]: boolean } = {}
- private loadedScopes: PluginClientScope[] = []
- private loadingScopes: { [id in PluginClientScope]?: boolean } = {}
-
- private hooks: Hooks = {}
private formFields: FormFields = {
video: []
}
private settingsScripts: { [ npmName: string ]: RegisterClientSettingsScript } = {}
+ private pluginsManager: PluginsManager
+
constructor (
private authService: AuthService,
private notifier: Notifier,
@Inject(LOCALE_ID) private localeId: string
) {
this.loadTranslations()
+
+ this.pluginsManager = new PluginsManager({
+ peertubeHelpersFactory: this.buildPeerTubeHelpers.bind(this),
+ onFormFields: this.onFormFields.bind(this),
+ onSettingsScripts: this.onSettingsScripts.bind(this)
+ })
}
initializePlugins () {
- const config = this.server.getHTMLConfig()
- this.plugins = config.plugin.registered
-
- this.buildScopeStruct()
+ this.pluginsManager.loadPluginsList(this.server.getHTMLConfig())
- this.ensurePluginsAreLoaded('common')
+ this.pluginsManager.ensurePluginsAreLoaded('common')
}
initializeCustomModal (customModal: CustomModalComponent) {
this.customModal = customModal
}
- ensurePluginsAreLoaded (scope: PluginClientScope) {
- this.loadPluginsByScope(scope)
-
- return this.pluginsLoaded[scope].asObservable()
- .pipe(first(), shareReplay())
- .toPromise()
+ runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
+ return this.zone.runOutsideAngular(() => {
+ return this.pluginsManager.runHook(hookName, result, params)
+ })
}
- addPlugin (plugin: ServerConfigPlugin, isTheme = false) {
- const pathPrefix = this.getPluginPathPrefix(isTheme)
-
- for (const key of Object.keys(plugin.clientScripts)) {
- const clientScript = plugin.clientScripts[key]
-
- for (const scope of clientScript.scopes) {
- if (!this.scopes[scope]) this.scopes[scope] = []
-
- this.scopes[scope].push({
- plugin,
- clientScript: {
- script: `${pathPrefix}/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`,
- scopes: clientScript.scopes
- },
- pluginType: isTheme ? PluginType.THEME : PluginType.PLUGIN,
- isTheme
- })
-
- this.loadedScripts[clientScript.script] = false
- }
- }
+ ensurePluginsAreLoaded (scope: PluginClientScope) {
+ return this.pluginsManager.ensurePluginsAreLoaded(scope)
}
- removePlugin (plugin: ServerConfigPlugin) {
- for (const key of Object.keys(this.scopes)) {
- this.scopes[key] = this.scopes[key].filter(o => o.plugin.name !== plugin.name)
- }
+ reloadLoadedScopes () {
+ return this.pluginsManager.reloadLoadedScopes()
}
- async reloadLoadedScopes () {
- for (const scope of this.loadedScopes) {
- await this.loadPluginsByScope(scope, true)
- }
+ getPluginsManager () {
+ return this.pluginsManager
}
- async loadPluginsByScope (scope: PluginClientScope, isReload = false) {
- if (this.loadingScopes[scope]) return
- if (!isReload && this.loadedScopes.includes(scope)) return
-
- this.loadingScopes[scope] = true
-
- logger('Loading scope %s', scope)
-
- try {
- if (!isReload) this.loadedScopes.push(scope)
-
- const toLoad = this.scopes[ scope ]
- if (!Array.isArray(toLoad)) {
- this.loadingScopes[scope] = false
- this.pluginsLoaded[scope].next(true)
-
- logger('Nothing to load for scope %s', scope)
- return
- }
-
- const promises: Promise<any>[] = []
- for (const pluginInfo of toLoad) {
- const clientScript = pluginInfo.clientScript
-
- if (this.loadedScripts[ clientScript.script ]) continue
-
- promises.push(this.loadPlugin(pluginInfo))
-
- this.loadedScripts[ clientScript.script ] = true
- }
-
- await Promise.all(promises)
-
- this.pluginsLoaded[scope].next(true)
- this.loadingScopes[scope] = false
-
- logger('Scope %s loaded', scope)
- } catch (err) {
- console.error('Cannot load plugins by scope %s.', scope, err)
- }
+ addPlugin (plugin: ServerConfigPlugin, isTheme = false) {
+ return this.pluginsManager.addPlugin(plugin, isTheme)
}
- runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
- return this.zone.runOutsideAngular(() => {
- return runHook(this.hooks, hookName, result, params)
- })
+ removePlugin (plugin: ServerConfigPlugin) {
+ return this.pluginsManager.removePlugin(plugin)
}
nameToNpmName (name: string, type: PluginType) {
return prefix + name
}
- pluginTypeFromNpmName (npmName: string) {
- return npmName.startsWith('peertube-plugin-')
- ? PluginType.PLUGIN
- : PluginType.THEME
- }
-
getRegisteredVideoFormFields (type: VideoEditType) {
return this.formFields.video.filter(f => f.videoFormOptions.type === type)
}
return helpers.translate(toTranslate)
}
- private loadPlugin (pluginInfo: PluginInfo) {
- return this.zone.runOutsideAngular(() => {
- const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType)
-
- const helpers = this.buildPeerTubeHelpers(pluginInfo)
- this.helpers[npmName] = helpers
-
- return loadPlugin({
- hooks: this.hooks,
- formFields: this.formFields,
- onSettingsScripts: options => this.settingsScripts[npmName] = options,
- pluginInfo,
- peertubeHelpersFactory: () => helpers
- })
+ private onFormFields (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) {
+ this.formFields.video.push({
+ commonOptions,
+ videoFormOptions
})
}
- private buildScopeStruct () {
- for (const plugin of this.plugins) {
- this.addPlugin(plugin)
- }
+ private onSettingsScripts (pluginInfo: PluginInfo, options: RegisterClientSettingsScript) {
+ const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType)
+
+ this.settingsScripts[npmName] = options
}
private buildPeerTubeHelpers (pluginInfo: PluginInfo): RegisterClientHelpers {
return {
getBaseStaticRoute: () => {
- const pathPrefix = this.getPluginPathPrefix(pluginInfo.isTheme)
+ const pathPrefix = PluginsManager.getPluginPathPrefix(pluginInfo.isTheme)
return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/static`
},
getBaseRouterRoute: () => {
- const pathPrefix = this.getPluginPathPrefix(pluginInfo.isTheme)
+ const pathPrefix = PluginsManager.getPluginPathPrefix(pluginInfo.isTheme)
return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/router`
},
.get<PluginTranslation>(PluginService.BASE_PLUGIN_URL + '/translations/' + completeLocale + '.json')
.pipe(shareReplay())
}
-
- private getPluginPathPrefix (isTheme: boolean) {
- return isTheme ? '/themes' : '/plugins'
- }
}
const theme = this.getTheme(currentTheme)
if (theme) {
console.log('Adding scripts of theme %s.', currentTheme)
+
this.pluginService.addPlugin(theme, true)
this.pluginService.reloadLoadedScopes()
import './videojs-components/theater-button'
import './playlist/playlist-plugin'
import videojs from 'video.js'
+import { PluginsManager } from '@root-helpers/plugins-manager'
import { isDefaultLocale } from '@shared/core-utils/i18n'
import { VideoFile } from '@shared/models'
+import { copyToClipboard } from '../../root-helpers/utils'
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder'
import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
VideoJSPluginOptions
} from './peertube-videojs-typings'
import { TranslationsManager } from './translations-manager'
-import { buildVideoOrPlaylistEmbed, buildVideoLink, getRtcConfig, isSafari, isIOS } from './utils'
-import { copyToClipboard } from '../../root-helpers/utils'
+import { buildVideoLink, buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
}
export type PeertubePlayerManagerOptions = {
- common: CommonOptions,
- webtorrent: WebtorrentOptions,
+ common: CommonOptions
+ webtorrent: WebtorrentOptions
p2pMediaLoader?: P2PMediaLoaderOptions
+
+ pluginsManager: PluginsManager
}
export class PeertubePlayerManager {
private static playerElementClassName: string
private static onPlayerChange: (player: videojs.Player) => void
private static alreadyPlayed = false
+ private static pluginsManager: PluginsManager
static initState () {
PeertubePlayerManager.alreadyPlayed = false
}
static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: videojs.Player) => void) {
+ this.pluginsManager = options.pluginsManager
+
let p2pMediaLoader: any
this.onPlayerChange = onPlayerChange
])
}
- const videojsOptions = this.getVideojsOptions(mode, options, p2pMediaLoader)
+ const videojsOptions = await this.getVideojsOptions(mode, options, p2pMediaLoader)
await TranslationsManager.loadLocaleInVideoJS(options.common.serverUrl, options.common.language, videojs)
await import('./webtorrent/webtorrent-plugin')
const mode = 'webtorrent'
- const videojsOptions = this.getVideojsOptions(mode, options)
+ const videojsOptions = await this.getVideojsOptions(mode, options)
const self = this
videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
})
}
- private static getVideojsOptions (
+ private static async getVideojsOptions (
mode: PlayerMode,
options: PeertubePlayerManagerOptions,
p2pMediaLoaderModule?: any
- ): videojs.PlayerOptions {
+ ): Promise<videojs.PlayerOptions> {
const commonOptions = options.common
const isHLS = mode === 'p2p-media-loader'
let autoplay = this.getAutoPlayValue(commonOptions.autoplay)
- let html5 = {
+ const html5 = {
preloadTextTracks: false
}
Object.assign(videojsOptions, { language: commonOptions.language })
}
- return videojsOptions
+ return this.pluginsManager.runHook('filter:internal.player.videojs.options.result', videojsOptions)
}
private static addP2PMediaLoaderOptions (
export * from './bytes'
export * from './peertube-web-storage'
export * from './utils'
+export * from './plugins-manager'
--- /dev/null
+import * as debug from 'debug'
+import { ReplaySubject } from 'rxjs'
+import { first, shareReplay } from 'rxjs/operators'
+import { RegisterClientHelpers } from 'src/types/register-client-option.model'
+import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks'
+import {
+ ClientHookName,
+ clientHookObject,
+ ClientScript,
+ HTMLServerConfig,
+ PluginClientScope,
+ PluginType,
+ RegisterClientFormFieldOptions,
+ RegisterClientHookOptions,
+ RegisterClientSettingsScript,
+ RegisterClientVideoFieldOptions,
+ ServerConfigPlugin
+} from '../../../shared/models'
+import { environment } from '../environments/environment'
+import { ClientScript as ClientScriptModule } from '../types/client-script.model'
+
+interface HookStructValue extends RegisterClientHookOptions {
+ plugin: ServerConfigPlugin
+ clientScript: ClientScript
+}
+
+type Hooks = { [ name: string ]: HookStructValue[] }
+
+type PluginInfo = {
+ plugin: ServerConfigPlugin
+ clientScript: ClientScript
+ pluginType: PluginType
+ isTheme: boolean
+}
+
+type PeertubeHelpersFactory = (pluginInfo: PluginInfo) => RegisterClientHelpers
+type OnFormFields = (options: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => void
+type OnSettingsScripts = (pluginInfo: PluginInfo, options: RegisterClientSettingsScript) => void
+
+const logger = debug('peertube:plugins')
+
+class PluginsManager {
+ private hooks: Hooks = {}
+
+ private scopes: { [ scopeName: string ]: PluginInfo[] } = {}
+
+ private loadedScripts: { [ script: string ]: boolean } = {}
+ private loadedScopes: PluginClientScope[] = []
+ private loadingScopes: { [id in PluginClientScope]?: boolean } = {}
+
+ private pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
+ common: new ReplaySubject<boolean>(1),
+ 'admin-plugin': new ReplaySubject<boolean>(1),
+ search: new ReplaySubject<boolean>(1),
+ 'video-watch': new ReplaySubject<boolean>(1),
+ signup: new ReplaySubject<boolean>(1),
+ login: new ReplaySubject<boolean>(1),
+ 'video-edit': new ReplaySubject<boolean>(1),
+ embed: new ReplaySubject<boolean>(1)
+ }
+
+ private readonly peertubeHelpersFactory: PeertubeHelpersFactory
+ private readonly onFormFields: OnFormFields
+ private readonly onSettingsScripts: OnSettingsScripts
+
+ constructor (options: {
+ peertubeHelpersFactory: PeertubeHelpersFactory
+ onFormFields?: OnFormFields
+ onSettingsScripts?: OnSettingsScripts
+ }) {
+ this.peertubeHelpersFactory = options.peertubeHelpersFactory
+ this.onFormFields = options.onFormFields
+ this.onSettingsScripts = options.onSettingsScripts
+ }
+
+ static getPluginPathPrefix (isTheme: boolean) {
+ return isTheme ? '/themes' : '/plugins'
+ }
+
+ loadPluginsList (config: HTMLServerConfig) {
+ for (const plugin of config.plugin.registered) {
+ this.addPlugin(plugin)
+ }
+ }
+
+ async runHook<T> (hookName: ClientHookName, result?: T, params?: any) {
+ if (!this.hooks[hookName]) return result
+
+ const hookType = getHookType(hookName)
+
+ for (const hook of this.hooks[hookName]) {
+ console.log('Running hook %s of plugin %s.', hookName, hook.plugin.name)
+
+ result = await internalRunHook(hook.handler, hookType, result, params, err => {
+ console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.clientScript.script, hook.plugin.name, err)
+ })
+ }
+
+ return result
+ }
+
+ ensurePluginsAreLoaded (scope: PluginClientScope) {
+ this.loadPluginsByScope(scope)
+
+ return this.pluginsLoaded[scope].asObservable()
+ .pipe(first(), shareReplay())
+ .toPromise()
+ }
+
+ async reloadLoadedScopes () {
+ for (const scope of this.loadedScopes) {
+ await this.loadPluginsByScope(scope, true)
+ }
+ }
+
+ addPlugin (plugin: ServerConfigPlugin, isTheme = false) {
+ const pathPrefix = PluginsManager.getPluginPathPrefix(isTheme)
+
+ for (const key of Object.keys(plugin.clientScripts)) {
+ const clientScript = plugin.clientScripts[key]
+
+ for (const scope of clientScript.scopes) {
+ if (!this.scopes[scope]) this.scopes[scope] = []
+
+ this.scopes[scope].push({
+ plugin,
+ clientScript: {
+ script: `${pathPrefix}/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`,
+ scopes: clientScript.scopes
+ },
+ pluginType: isTheme ? PluginType.THEME : PluginType.PLUGIN,
+ isTheme
+ })
+
+ this.loadedScripts[clientScript.script] = false
+ }
+ }
+ }
+
+ removePlugin (plugin: ServerConfigPlugin) {
+ for (const key of Object.keys(this.scopes)) {
+ this.scopes[key] = this.scopes[key].filter(o => o.plugin.name !== plugin.name)
+ }
+ }
+
+ async loadPluginsByScope (scope: PluginClientScope, isReload = false) {
+ if (this.loadingScopes[scope]) return
+ if (!isReload && this.loadedScopes.includes(scope)) return
+
+ this.loadingScopes[scope] = true
+
+ logger('Loading scope %s', scope)
+
+ try {
+ if (!isReload) this.loadedScopes.push(scope)
+
+ const toLoad = this.scopes[ scope ]
+ if (!Array.isArray(toLoad)) {
+ this.loadingScopes[scope] = false
+ this.pluginsLoaded[scope].next(true)
+
+ logger('Nothing to load for scope %s', scope)
+ return
+ }
+
+ const promises: Promise<any>[] = []
+ for (const pluginInfo of toLoad) {
+ const clientScript = pluginInfo.clientScript
+
+ if (this.loadedScripts[ clientScript.script ]) continue
+
+ promises.push(this.loadPlugin(pluginInfo))
+
+ this.loadedScripts[ clientScript.script ] = true
+ }
+
+ await Promise.all(promises)
+
+ this.pluginsLoaded[scope].next(true)
+ this.loadingScopes[scope] = false
+
+ logger('Scope %s loaded', scope)
+ } catch (err) {
+ console.error('Cannot load plugins by scope %s.', scope, err)
+ }
+ }
+
+ private loadPlugin (pluginInfo: PluginInfo) {
+ const { plugin, clientScript } = pluginInfo
+
+ const registerHook = (options: RegisterClientHookOptions) => {
+ if (clientHookObject[options.target] !== true) {
+ console.error('Unknown hook %s of plugin %s. Skipping.', options.target, plugin.name)
+ return
+ }
+
+ if (!this.hooks[options.target]) this.hooks[options.target] = []
+
+ this.hooks[options.target].push({
+ plugin,
+ clientScript,
+ target: options.target,
+ handler: options.handler,
+ priority: options.priority || 0
+ })
+ }
+
+ const registerVideoField = (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => {
+ if (!this.onFormFields) {
+ throw new Error('Video field registration is not supported')
+ }
+
+ return this.onFormFields(commonOptions, videoFormOptions)
+ }
+
+ const registerSettingsScript = (options: RegisterClientSettingsScript) => {
+ if (!this.onSettingsScripts) {
+ throw new Error('Registering settings script is not supported')
+ }
+
+ return this.onSettingsScripts(pluginInfo, options)
+ }
+
+ const peertubeHelpers = this.peertubeHelpersFactory(pluginInfo)
+
+ console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name)
+
+ const absURL = (environment.apiUrl || window.location.origin) + clientScript.script
+ return import(/* webpackIgnore: true */ absURL)
+ .then((script: ClientScriptModule) => script.register({ registerHook, registerVideoField, registerSettingsScript, peertubeHelpers }))
+ .then(() => this.sortHooksByPriority())
+ .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err))
+ }
+
+ private sortHooksByPriority () {
+ for (const hookName of Object.keys(this.hooks)) {
+ this.hooks[hookName].sort((a, b) => {
+ return b.priority - a.priority
+ })
+ }
+ }
+}
+
+export {
+ PluginsManager,
+
+ PluginInfo,
+ PeertubeHelpersFactory,
+ OnFormFields,
+ OnSettingsScripts
+}
+++ /dev/null
-import { RegisterClientHelpers } from 'src/types/register-client-option.model'
-import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks'
-import {
- ClientHookName,
- clientHookObject,
- ClientScript,
- PluginType,
- RegisterClientFormFieldOptions,
- RegisterClientHookOptions,
- RegisterClientSettingsScript,
- RegisterClientVideoFieldOptions,
- ServerConfigPlugin
-} from '../../../shared/models'
-import { environment } from '../environments/environment'
-import { ClientScript as ClientScriptModule } from '../types/client-script.model'
-
-interface HookStructValue extends RegisterClientHookOptions {
- plugin: ServerConfigPlugin
- clientScript: ClientScript
-}
-
-type Hooks = { [ name: string ]: HookStructValue[] }
-
-type PluginInfo = {
- plugin: ServerConfigPlugin
- clientScript: ClientScript
- pluginType: PluginType
- isTheme: boolean
-}
-
-type FormFields = {
- video: {
- commonOptions: RegisterClientFormFieldOptions
- videoFormOptions: RegisterClientVideoFieldOptions
- }[]
-}
-
-async function runHook<T> (hooks: Hooks, hookName: ClientHookName, result?: T, params?: any) {
- if (!hooks[hookName]) return result
-
- const hookType = getHookType(hookName)
-
- for (const hook of hooks[hookName]) {
- console.log('Running hook %s of plugin %s.', hookName, hook.plugin.name)
-
- result = await internalRunHook(hook.handler, hookType, result, params, err => {
- console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.clientScript.script, hook.plugin.name, err)
- })
- }
-
- return result
-}
-
-function loadPlugin (options: {
- hooks: Hooks
- pluginInfo: PluginInfo
- peertubeHelpersFactory: (pluginInfo: PluginInfo) => RegisterClientHelpers
- formFields?: FormFields
- onSettingsScripts?: (options: RegisterClientSettingsScript) => void
-}) {
- const { hooks, pluginInfo, peertubeHelpersFactory, formFields, onSettingsScripts } = options
- const { plugin, clientScript } = pluginInfo
-
- const registerHook = (options: RegisterClientHookOptions) => {
- if (clientHookObject[options.target] !== true) {
- console.error('Unknown hook %s of plugin %s. Skipping.', options.target, plugin.name)
- return
- }
-
- if (!hooks[options.target]) hooks[options.target] = []
-
- hooks[options.target].push({
- plugin,
- clientScript,
- target: options.target,
- handler: options.handler,
- priority: options.priority || 0
- })
- }
-
- const registerVideoField = (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => {
- if (!formFields) {
- throw new Error('Video field registration is not supported')
- }
-
- formFields.video.push({
- commonOptions,
- videoFormOptions
- })
- }
-
- const registerSettingsScript = (options: RegisterClientSettingsScript) => {
- if (!onSettingsScripts) {
- throw new Error('Registering settings script is not supported')
- }
-
- return onSettingsScripts(options)
- }
-
- const peertubeHelpers = peertubeHelpersFactory(pluginInfo)
-
- console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name)
-
- const absURL = (environment.apiUrl || window.location.origin) + clientScript.script
- return import(/* webpackIgnore: true */ absURL)
- .then((script: ClientScriptModule) => script.register({ registerHook, registerVideoField, registerSettingsScript, peertubeHelpers }))
- .then(() => sortHooksByPriority(hooks))
- .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err))
-}
-
-export {
- HookStructValue,
- Hooks,
- PluginInfo,
- FormFields,
- loadPlugin,
- runHook
-}
-
-function sortHooksByPriority (hooks: Hooks) {
- for (const hookName of Object.keys(hooks)) {
- hooks[hookName].sort((a, b) => {
- return b.priority - a.priority
- })
- }
-}
import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import {
- ClientHookName,
HTMLServerConfig,
OAuth2ErrorCode,
PluginType,
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
import { TranslationsManager } from '../../assets/player/translations-manager'
import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
-import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins'
+import { PluginsManager } from '../../root-helpers/plugins-manager'
import { Tokens } from '../../root-helpers/users'
import { objectToUrlEncoded } from '../../root-helpers/utils'
import { RegisterClientHelpers } from '../../types/register-client-option.model'
private wrapperElement: HTMLElement
- private peertubeHooks: Hooks = {}
- private loadedScripts = new Set<string>()
+ private pluginsManager: PluginsManager
static async main () {
const videoContainerId = 'video-wrapper'
this.PeertubePlayerManagerModulePromise
])
- await this.ensurePluginsAreLoaded(serverTranslations)
+ await this.loadPlugins(serverTranslations)
const videoInfo: VideoDetails = videoInfoTmp
webtorrent: {
videoFiles: videoInfo.files
- }
+ },
+
+ pluginsManager: this.pluginsManager
}
if (this.mode === 'p2p-media-loader') {
})
}
- this.runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video: videoInfo })
+ this.pluginsManager.runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video: videoInfo })
}
private async initCore () {
return window.location.pathname.split('/')[1] === 'video-playlists'
}
- private async ensurePluginsAreLoaded (translations?: { [ id: string ]: string }) {
- if (this.config.plugin.registered.length === 0) return
-
- for (const plugin of this.config.plugin.registered) {
- for (const key of Object.keys(plugin.clientScripts)) {
- const clientScript = plugin.clientScripts[key]
-
- if (clientScript.scopes.includes('embed') === false) continue
-
- const script = `/plugins/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`
-
- if (this.loadedScripts.has(script)) continue
+ private loadPlugins (translations?: { [ id: string ]: string }) {
+ this.pluginsManager = new PluginsManager({
+ peertubeHelpersFactory: _ => this.buildPeerTubeHelpers(translations)
+ })
- const pluginInfo = {
- plugin,
- clientScript: {
- script,
- scopes: clientScript.scopes
- },
- pluginType: PluginType.PLUGIN,
- isTheme: false
- }
+ this.pluginsManager.loadPluginsList(this.config)
- await loadPlugin({
- hooks: this.peertubeHooks,
- pluginInfo,
- onSettingsScripts: () => undefined,
- peertubeHelpersFactory: _ => this.buildPeerTubeHelpers(translations)
- })
- }
- }
+ return this.pluginsManager.ensurePluginsAreLoaded('embed')
}
private buildPeerTubeHelpers (translations?: { [ id: string ]: string }): RegisterClientHelpers {
}
}
}
-
- private runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
- return runHook(this.peertubeHooks, hookName, result, params)
- }
}
PeerTubeEmbed.main()
'filter:internal.common.svg-icons.get-content.result': true,
// Filter left menu links
- 'filter:left-menu.links.create.result': true
+ 'filter:left-menu.links.create.result': true,
+
+ // Filter videojs options built for PeerTube player
+ 'filter:internal.player.videojs.options.result': true
}
export type ClientFilterHookName = keyof typeof clientFilterHookObject