parent
520bf885c5
commit
72f611ca15
|
@ -12,6 +12,7 @@ import {
|
||||||
MetaService,
|
MetaService,
|
||||||
Notifier,
|
Notifier,
|
||||||
PeerTubeSocket,
|
PeerTubeSocket,
|
||||||
|
PluginService,
|
||||||
RestExtractor,
|
RestExtractor,
|
||||||
ScreenService,
|
ScreenService,
|
||||||
ServerService,
|
ServerService,
|
||||||
|
@ -146,6 +147,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private videoCaptionService: VideoCaptionService,
|
private videoCaptionService: VideoCaptionService,
|
||||||
private hotkeysService: HotkeysService,
|
private hotkeysService: HotkeysService,
|
||||||
private hooks: HooksService,
|
private hooks: HooksService,
|
||||||
|
private pluginService: PluginService,
|
||||||
private peertubeSocket: PeerTubeSocket,
|
private peertubeSocket: PeerTubeSocket,
|
||||||
private screenService: ScreenService,
|
private screenService: ScreenService,
|
||||||
private location: PlatformLocation,
|
private location: PlatformLocation,
|
||||||
|
@ -859,7 +861,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
webtorrent: {
|
webtorrent: {
|
||||||
videoFiles: video.files
|
videoFiles: video.files
|
||||||
}
|
},
|
||||||
|
|
||||||
|
pluginsManager: this.pluginService.getPluginsManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only set this if we're in a playlist
|
// Only set this if we're in a playlist
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as debug from 'debug'
|
import { Observable, of } from 'rxjs'
|
||||||
import { Observable, of, ReplaySubject } from 'rxjs'
|
import { catchError, map, shareReplay } from 'rxjs/operators'
|
||||||
import { catchError, first, map, shareReplay } from 'rxjs/operators'
|
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Inject, Injectable, LOCALE_ID, NgZone } from '@angular/core'
|
import { Inject, Injectable, LOCALE_ID, NgZone } from '@angular/core'
|
||||||
import { VideoEditType } from '@app/+videos/+video-edit/shared/video-edit.type'
|
import { VideoEditType } from '@app/+videos/+video-edit/shared/video-edit.type'
|
||||||
|
@ -11,7 +10,7 @@ import { RestExtractor } from '@app/core/rest'
|
||||||
import { ServerService } from '@app/core/server/server.service'
|
import { ServerService } from '@app/core/server/server.service'
|
||||||
import { getDevLocale, isOnDevLocale } from '@app/helpers'
|
import { getDevLocale, isOnDevLocale } from '@app/helpers'
|
||||||
import { CustomModalComponent } from '@app/modal/custom-modal.component'
|
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 { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
|
||||||
import {
|
import {
|
||||||
ClientHook,
|
ClientHook,
|
||||||
|
@ -20,49 +19,39 @@ import {
|
||||||
PluginTranslation,
|
PluginTranslation,
|
||||||
PluginType,
|
PluginType,
|
||||||
PublicServerSetting,
|
PublicServerSetting,
|
||||||
|
RegisterClientFormFieldOptions,
|
||||||
RegisterClientSettingsScript,
|
RegisterClientSettingsScript,
|
||||||
|
RegisterClientVideoFieldOptions,
|
||||||
ServerConfigPlugin
|
ServerConfigPlugin
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { RegisterClientHelpers } from '../../../types/register-client-option.model'
|
import { RegisterClientHelpers } from '../../../types/register-client-option.model'
|
||||||
|
|
||||||
const logger = debug('peertube:plugins')
|
type FormFields = {
|
||||||
|
video: {
|
||||||
|
commonOptions: RegisterClientFormFieldOptions
|
||||||
|
videoFormOptions: RegisterClientVideoFieldOptions
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PluginService implements ClientHook {
|
export class PluginService implements ClientHook {
|
||||||
private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins'
|
private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins'
|
||||||
private static BASE_PLUGIN_URL = environment.apiUrl + '/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>
|
translationsObservable: Observable<PluginTranslation>
|
||||||
|
|
||||||
customModal: CustomModalComponent
|
customModal: CustomModalComponent
|
||||||
|
|
||||||
private plugins: ServerConfigPlugin[] = []
|
|
||||||
private helpers: { [ npmName: string ]: RegisterClientHelpers } = {}
|
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 = {
|
private formFields: FormFields = {
|
||||||
video: []
|
video: []
|
||||||
}
|
}
|
||||||
private settingsScripts: { [ npmName: string ]: RegisterClientSettingsScript } = {}
|
private settingsScripts: { [ npmName: string ]: RegisterClientSettingsScript } = {}
|
||||||
|
|
||||||
|
private pluginsManager: PluginsManager
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
|
@ -74,111 +63,48 @@ export class PluginService implements ClientHook {
|
||||||
@Inject(LOCALE_ID) private localeId: string
|
@Inject(LOCALE_ID) private localeId: string
|
||||||
) {
|
) {
|
||||||
this.loadTranslations()
|
this.loadTranslations()
|
||||||
|
|
||||||
|
this.pluginsManager = new PluginsManager({
|
||||||
|
peertubeHelpersFactory: this.buildPeerTubeHelpers.bind(this),
|
||||||
|
onFormFields: this.onFormFields.bind(this),
|
||||||
|
onSettingsScripts: this.onSettingsScripts.bind(this)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
initializePlugins () {
|
initializePlugins () {
|
||||||
const config = this.server.getHTMLConfig()
|
this.pluginsManager.loadPluginsList(this.server.getHTMLConfig())
|
||||||
this.plugins = config.plugin.registered
|
|
||||||
|
|
||||||
this.buildScopeStruct()
|
this.pluginsManager.ensurePluginsAreLoaded('common')
|
||||||
|
|
||||||
this.ensurePluginsAreLoaded('common')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeCustomModal (customModal: CustomModalComponent) {
|
initializeCustomModal (customModal: CustomModalComponent) {
|
||||||
this.customModal = customModal
|
this.customModal = customModal
|
||||||
}
|
}
|
||||||
|
|
||||||
ensurePluginsAreLoaded (scope: PluginClientScope) {
|
runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
|
||||||
this.loadPluginsByScope(scope)
|
return this.zone.runOutsideAngular(() => {
|
||||||
|
return this.pluginsManager.runHook(hookName, result, params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return this.pluginsLoaded[scope].asObservable()
|
ensurePluginsAreLoaded (scope: PluginClientScope) {
|
||||||
.pipe(first(), shareReplay())
|
return this.pluginsManager.ensurePluginsAreLoaded(scope)
|
||||||
.toPromise()
|
}
|
||||||
|
|
||||||
|
reloadLoadedScopes () {
|
||||||
|
return this.pluginsManager.reloadLoadedScopes()
|
||||||
|
}
|
||||||
|
|
||||||
|
getPluginsManager () {
|
||||||
|
return this.pluginsManager
|
||||||
}
|
}
|
||||||
|
|
||||||
addPlugin (plugin: ServerConfigPlugin, isTheme = false) {
|
addPlugin (plugin: ServerConfigPlugin, isTheme = false) {
|
||||||
const pathPrefix = this.getPluginPathPrefix(isTheme)
|
return this.pluginsManager.addPlugin(plugin, 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) {
|
removePlugin (plugin: ServerConfigPlugin) {
|
||||||
for (const key of Object.keys(this.scopes)) {
|
return this.pluginsManager.removePlugin(plugin)
|
||||||
this.scopes[key] = this.scopes[key].filter(o => o.plugin.name !== plugin.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async reloadLoadedScopes () {
|
|
||||||
for (const scope of this.loadedScopes) {
|
|
||||||
await this.loadPluginsByScope(scope, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
|
|
||||||
return this.zone.runOutsideAngular(() => {
|
|
||||||
return runHook(this.hooks, hookName, result, params)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nameToNpmName (name: string, type: PluginType) {
|
nameToNpmName (name: string, type: PluginType) {
|
||||||
|
@ -189,12 +115,6 @@ export class PluginService implements ClientHook {
|
||||||
return prefix + name
|
return prefix + name
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginTypeFromNpmName (npmName: string) {
|
|
||||||
return npmName.startsWith('peertube-plugin-')
|
|
||||||
? PluginType.PLUGIN
|
|
||||||
: PluginType.THEME
|
|
||||||
}
|
|
||||||
|
|
||||||
getRegisteredVideoFormFields (type: VideoEditType) {
|
getRegisteredVideoFormFields (type: VideoEditType) {
|
||||||
return this.formFields.video.filter(f => f.videoFormOptions.type === type)
|
return this.formFields.video.filter(f => f.videoFormOptions.type === type)
|
||||||
}
|
}
|
||||||
|
@ -213,27 +133,17 @@ export class PluginService implements ClientHook {
|
||||||
return helpers.translate(toTranslate)
|
return helpers.translate(toTranslate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadPlugin (pluginInfo: PluginInfo) {
|
private onFormFields (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) {
|
||||||
return this.zone.runOutsideAngular(() => {
|
this.formFields.video.push({
|
||||||
const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType)
|
commonOptions,
|
||||||
|
videoFormOptions
|
||||||
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 buildScopeStruct () {
|
private onSettingsScripts (pluginInfo: PluginInfo, options: RegisterClientSettingsScript) {
|
||||||
for (const plugin of this.plugins) {
|
const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType)
|
||||||
this.addPlugin(plugin)
|
|
||||||
}
|
this.settingsScripts[npmName] = options
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildPeerTubeHelpers (pluginInfo: PluginInfo): RegisterClientHelpers {
|
private buildPeerTubeHelpers (pluginInfo: PluginInfo): RegisterClientHelpers {
|
||||||
|
@ -242,12 +152,12 @@ export class PluginService implements ClientHook {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getBaseStaticRoute: () => {
|
getBaseStaticRoute: () => {
|
||||||
const pathPrefix = this.getPluginPathPrefix(pluginInfo.isTheme)
|
const pathPrefix = PluginsManager.getPluginPathPrefix(pluginInfo.isTheme)
|
||||||
return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/static`
|
return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/static`
|
||||||
},
|
},
|
||||||
|
|
||||||
getBaseRouterRoute: () => {
|
getBaseRouterRoute: () => {
|
||||||
const pathPrefix = this.getPluginPathPrefix(pluginInfo.isTheme)
|
const pathPrefix = PluginsManager.getPluginPathPrefix(pluginInfo.isTheme)
|
||||||
return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/router`
|
return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/router`
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -324,8 +234,4 @@ export class PluginService implements ClientHook {
|
||||||
.get<PluginTranslation>(PluginService.BASE_PLUGIN_URL + '/translations/' + completeLocale + '.json')
|
.get<PluginTranslation>(PluginService.BASE_PLUGIN_URL + '/translations/' + completeLocale + '.json')
|
||||||
.pipe(shareReplay())
|
.pipe(shareReplay())
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPluginPathPrefix (isTheme: boolean) {
|
|
||||||
return isTheme ? '/themes' : '/plugins'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@ export class ThemeService {
|
||||||
const theme = this.getTheme(currentTheme)
|
const theme = this.getTheme(currentTheme)
|
||||||
if (theme) {
|
if (theme) {
|
||||||
console.log('Adding scripts of theme %s.', currentTheme)
|
console.log('Adding scripts of theme %s.', currentTheme)
|
||||||
|
|
||||||
this.pluginService.addPlugin(theme, true)
|
this.pluginService.addPlugin(theme, true)
|
||||||
|
|
||||||
this.pluginService.reloadLoadedScopes()
|
this.pluginService.reloadLoadedScopes()
|
||||||
|
|
|
@ -22,8 +22,10 @@ import './videojs-components/settings-panel-child'
|
||||||
import './videojs-components/theater-button'
|
import './videojs-components/theater-button'
|
||||||
import './playlist/playlist-plugin'
|
import './playlist/playlist-plugin'
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
|
import { PluginsManager } from '@root-helpers/plugins-manager'
|
||||||
import { isDefaultLocale } from '@shared/core-utils/i18n'
|
import { isDefaultLocale } from '@shared/core-utils/i18n'
|
||||||
import { VideoFile } from '@shared/models'
|
import { VideoFile } from '@shared/models'
|
||||||
|
import { copyToClipboard } from '../../root-helpers/utils'
|
||||||
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
|
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
|
||||||
import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder'
|
import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder'
|
||||||
import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
|
import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
|
||||||
|
@ -37,8 +39,7 @@ import {
|
||||||
VideoJSPluginOptions
|
VideoJSPluginOptions
|
||||||
} from './peertube-videojs-typings'
|
} from './peertube-videojs-typings'
|
||||||
import { TranslationsManager } from './translations-manager'
|
import { TranslationsManager } from './translations-manager'
|
||||||
import { buildVideoOrPlaylistEmbed, buildVideoLink, getRtcConfig, isSafari, isIOS } from './utils'
|
import { buildVideoLink, buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
|
||||||
import { copyToClipboard } from '../../root-helpers/utils'
|
|
||||||
|
|
||||||
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
|
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
|
||||||
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
|
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
|
||||||
|
@ -116,21 +117,26 @@ export interface CommonOptions extends CustomizationOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PeertubePlayerManagerOptions = {
|
export type PeertubePlayerManagerOptions = {
|
||||||
common: CommonOptions,
|
common: CommonOptions
|
||||||
webtorrent: WebtorrentOptions,
|
webtorrent: WebtorrentOptions
|
||||||
p2pMediaLoader?: P2PMediaLoaderOptions
|
p2pMediaLoader?: P2PMediaLoaderOptions
|
||||||
|
|
||||||
|
pluginsManager: PluginsManager
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PeertubePlayerManager {
|
export class PeertubePlayerManager {
|
||||||
private static playerElementClassName: string
|
private static playerElementClassName: string
|
||||||
private static onPlayerChange: (player: videojs.Player) => void
|
private static onPlayerChange: (player: videojs.Player) => void
|
||||||
private static alreadyPlayed = false
|
private static alreadyPlayed = false
|
||||||
|
private static pluginsManager: PluginsManager
|
||||||
|
|
||||||
static initState () {
|
static initState () {
|
||||||
PeertubePlayerManager.alreadyPlayed = false
|
PeertubePlayerManager.alreadyPlayed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: videojs.Player) => void) {
|
static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: videojs.Player) => void) {
|
||||||
|
this.pluginsManager = options.pluginsManager
|
||||||
|
|
||||||
let p2pMediaLoader: any
|
let p2pMediaLoader: any
|
||||||
|
|
||||||
this.onPlayerChange = onPlayerChange
|
this.onPlayerChange = onPlayerChange
|
||||||
|
@ -144,7 +150,7 @@ export class PeertubePlayerManager {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
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 TranslationsManager.loadLocaleInVideoJS(options.common.serverUrl, options.common.language, videojs)
|
||||||
|
|
||||||
|
@ -206,7 +212,7 @@ export class PeertubePlayerManager {
|
||||||
await import('./webtorrent/webtorrent-plugin')
|
await import('./webtorrent/webtorrent-plugin')
|
||||||
|
|
||||||
const mode = 'webtorrent'
|
const mode = 'webtorrent'
|
||||||
const videojsOptions = this.getVideojsOptions(mode, options)
|
const videojsOptions = await this.getVideojsOptions(mode, options)
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
|
videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
|
||||||
|
@ -218,16 +224,16 @@ export class PeertubePlayerManager {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getVideojsOptions (
|
private static async getVideojsOptions (
|
||||||
mode: PlayerMode,
|
mode: PlayerMode,
|
||||||
options: PeertubePlayerManagerOptions,
|
options: PeertubePlayerManagerOptions,
|
||||||
p2pMediaLoaderModule?: any
|
p2pMediaLoaderModule?: any
|
||||||
): videojs.PlayerOptions {
|
): Promise<videojs.PlayerOptions> {
|
||||||
const commonOptions = options.common
|
const commonOptions = options.common
|
||||||
const isHLS = mode === 'p2p-media-loader'
|
const isHLS = mode === 'p2p-media-loader'
|
||||||
|
|
||||||
let autoplay = this.getAutoPlayValue(commonOptions.autoplay)
|
let autoplay = this.getAutoPlayValue(commonOptions.autoplay)
|
||||||
let html5 = {
|
const html5 = {
|
||||||
preloadTextTracks: false
|
preloadTextTracks: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +312,7 @@ export class PeertubePlayerManager {
|
||||||
Object.assign(videojsOptions, { language: commonOptions.language })
|
Object.assign(videojsOptions, { language: commonOptions.language })
|
||||||
}
|
}
|
||||||
|
|
||||||
return videojsOptions
|
return this.pluginsManager.runHook('filter:internal.player.videojs.options.result', videojsOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static addP2PMediaLoaderOptions (
|
private static addP2PMediaLoaderOptions (
|
||||||
|
|
|
@ -2,3 +2,4 @@ export * from './users'
|
||||||
export * from './bytes'
|
export * from './bytes'
|
||||||
export * from './peertube-web-storage'
|
export * from './peertube-web-storage'
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
export * from './plugins-manager'
|
||||||
|
|
251
client/src/root-helpers/plugins-manager.ts
Normal file
251
client/src/root-helpers/plugins-manager.ts
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,126 +0,0 @@
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ import videojs from 'video.js'
|
||||||
import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
|
import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
|
||||||
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
|
||||||
import {
|
import {
|
||||||
ClientHookName,
|
|
||||||
HTMLServerConfig,
|
HTMLServerConfig,
|
||||||
OAuth2ErrorCode,
|
OAuth2ErrorCode,
|
||||||
PluginType,
|
PluginType,
|
||||||
|
@ -19,7 +18,7 @@ import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from
|
||||||
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
||||||
import { TranslationsManager } from '../../assets/player/translations-manager'
|
import { TranslationsManager } from '../../assets/player/translations-manager'
|
||||||
import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
|
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 { Tokens } from '../../root-helpers/users'
|
||||||
import { objectToUrlEncoded } from '../../root-helpers/utils'
|
import { objectToUrlEncoded } from '../../root-helpers/utils'
|
||||||
import { RegisterClientHelpers } from '../../types/register-client-option.model'
|
import { RegisterClientHelpers } from '../../types/register-client-option.model'
|
||||||
|
@ -68,8 +67,7 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
private wrapperElement: HTMLElement
|
private wrapperElement: HTMLElement
|
||||||
|
|
||||||
private peertubeHooks: Hooks = {}
|
private pluginsManager: PluginsManager
|
||||||
private loadedScripts = new Set<string>()
|
|
||||||
|
|
||||||
static async main () {
|
static async main () {
|
||||||
const videoContainerId = 'video-wrapper'
|
const videoContainerId = 'video-wrapper'
|
||||||
|
@ -489,7 +487,7 @@ export class PeerTubeEmbed {
|
||||||
this.PeertubePlayerManagerModulePromise
|
this.PeertubePlayerManagerModulePromise
|
||||||
])
|
])
|
||||||
|
|
||||||
await this.ensurePluginsAreLoaded(serverTranslations)
|
await this.loadPlugins(serverTranslations)
|
||||||
|
|
||||||
const videoInfo: VideoDetails = videoInfoTmp
|
const videoInfo: VideoDetails = videoInfoTmp
|
||||||
|
|
||||||
|
@ -560,7 +558,9 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
webtorrent: {
|
webtorrent: {
|
||||||
videoFiles: videoInfo.files
|
videoFiles: videoInfo.files
|
||||||
}
|
},
|
||||||
|
|
||||||
|
pluginsManager: this.pluginsManager
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mode === 'p2p-media-loader') {
|
if (this.mode === 'p2p-media-loader') {
|
||||||
|
@ -600,7 +600,7 @@ export class PeerTubeEmbed {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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 () {
|
private async initCore () {
|
||||||
|
@ -740,37 +740,14 @@ export class PeerTubeEmbed {
|
||||||
return window.location.pathname.split('/')[1] === 'video-playlists'
|
return window.location.pathname.split('/')[1] === 'video-playlists'
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ensurePluginsAreLoaded (translations?: { [ id: string ]: string }) {
|
private loadPlugins (translations?: { [ id: string ]: string }) {
|
||||||
if (this.config.plugin.registered.length === 0) return
|
this.pluginsManager = new PluginsManager({
|
||||||
|
peertubeHelpersFactory: _ => this.buildPeerTubeHelpers(translations)
|
||||||
|
})
|
||||||
|
|
||||||
for (const plugin of this.config.plugin.registered) {
|
this.pluginsManager.loadPluginsList(this.config)
|
||||||
for (const key of Object.keys(plugin.clientScripts)) {
|
|
||||||
const clientScript = plugin.clientScripts[key]
|
|
||||||
|
|
||||||
if (clientScript.scopes.includes('embed') === false) continue
|
return this.pluginsManager.ensurePluginsAreLoaded('embed')
|
||||||
|
|
||||||
const script = `/plugins/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`
|
|
||||||
|
|
||||||
if (this.loadedScripts.has(script)) continue
|
|
||||||
|
|
||||||
const pluginInfo = {
|
|
||||||
plugin,
|
|
||||||
clientScript: {
|
|
||||||
script,
|
|
||||||
scopes: clientScript.scopes
|
|
||||||
},
|
|
||||||
pluginType: PluginType.PLUGIN,
|
|
||||||
isTheme: false
|
|
||||||
}
|
|
||||||
|
|
||||||
await loadPlugin({
|
|
||||||
hooks: this.peertubeHooks,
|
|
||||||
pluginInfo,
|
|
||||||
onSettingsScripts: () => undefined,
|
|
||||||
peertubeHelpersFactory: _ => this.buildPeerTubeHelpers(translations)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildPeerTubeHelpers (translations?: { [ id: string ]: string }): RegisterClientHelpers {
|
private buildPeerTubeHelpers (translations?: { [ id: string ]: string }): RegisterClientHelpers {
|
||||||
|
@ -808,10 +785,6 @@ export class PeerTubeEmbed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
|
|
||||||
return runHook(this.peertubeHooks, hookName, result, params)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerTubeEmbed.main()
|
PeerTubeEmbed.main()
|
||||||
|
|
|
@ -53,7 +53,10 @@ export const clientFilterHookObject = {
|
||||||
'filter:internal.common.svg-icons.get-content.result': true,
|
'filter:internal.common.svg-icons.get-content.result': true,
|
||||||
|
|
||||||
// Filter left menu links
|
// 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
|
export type ClientFilterHookName = keyof typeof clientFilterHookObject
|
||||||
|
|
Loading…
Reference in New Issue
Block a user