X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=client%2Fsrc%2Fstandalone%2Fvideos%2Fembed.ts;h=38ff398904942b7d4bcbb494398915537f5914b6;hb=01dd04cd5ab7b55d2a9af7d0ebf405bee9579b09;hp=fc61d37303bc4b6cc94e27fce1d52f4c7fe1068d;hpb=5ec3cbdf22fc88ebe57f370fc0bc0e3df7453458;p=github%2FChocobozzz%2FPeerTube.git diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index fc61d3730..38ff39890 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -1,25 +1,29 @@ import './embed.scss' +import '../../assets/player/dock/peertube-dock-component' +import '../../assets/player/dock/peertube-dock-plugin' import videojs from 'video.js' import { peertubeTranslate } from '../../../../shared/core-utils/i18n' -import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { - ClientHookName, HTMLServerConfig, - PluginType, + HttpStatusCode, + OAuth2ErrorCode, ResultList, UserRefreshToken, + Video, VideoCaption, VideoDetails, VideoPlaylist, VideoPlaylistElement, VideoStreamingPlaylistType } from '../../../../shared/models' -import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager' +import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player' import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' import { TranslationsManager } from '../../assets/player/translations-manager' +import { isP2PEnabled } from '../../assets/player/utils' +import { getBoolOrDefault } from '../../root-helpers/local-storage-utils' import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage' -import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins' -import { Tokens } from '../../root-helpers/users' +import { PluginsManager } from '../../root-helpers/plugins-manager' +import { UserLocalStorageKeys, UserTokens } from '../../root-helpers/users' import { objectToUrlEncoded } from '../../root-helpers/utils' import { RegisterClientHelpers } from '../../types/register-client-option.model' import { PeerTubeEmbedApi } from './embed-api' @@ -43,13 +47,14 @@ export class PeerTubeEmbed { title: boolean warningTitle: boolean peertubeLink: boolean + p2pEnabled: boolean bigPlayBackgroundColor: string foregroundColor: string mode: PlayerMode scope = 'peertube' - userTokens: Tokens + userTokens: UserTokens headers = new Headers() LOCAL_STORAGE_OAUTH_CLIENT_KEYS = { CLIENT_ID: 'client_id', @@ -65,18 +70,11 @@ export class PeerTubeEmbed { private playlistElements: VideoPlaylistElement[] private currentPlaylistElement: VideoPlaylistElement - private wrapperElement: HTMLElement + private readonly wrapperElement: HTMLElement - private peertubeHooks: Hooks = {} - private loadedScripts = new Set() + private pluginsManager: PluginsManager - static async main () { - const videoContainerId = 'video-wrapper' - const embed = new PeerTubeEmbed(videoContainerId) - await embed.init() - } - - constructor (private videoWrapperId: string) { + constructor (private readonly videoWrapperId: string) { this.wrapperElement = document.getElementById(this.videoWrapperId) try { @@ -86,6 +84,12 @@ export class PeerTubeEmbed { } } + static async main () { + const videoContainerId = 'video-wrapper' + const embed = new PeerTubeEmbed(videoContainerId) + await embed.init() + } + getVideoUrl (id: string) { return window.location.origin + '/api/v1/videos/' + id } @@ -118,9 +122,9 @@ export class PeerTubeEmbed { if (res.status === HttpStatusCode.UNAUTHORIZED_401) return undefined return res.json() - }).then((obj: UserRefreshToken & { code: 'invalid_grant'}) => { - if (!obj || obj.code === 'invalid_grant') { - Tokens.flush() + }).then((obj: UserRefreshToken & { code?: OAuth2ErrorCode }) => { + if (!obj || obj.code === OAuth2ErrorCode.INVALID_GRANT) { + UserTokens.flushLocalStorage(peertubeLocalStorage) this.removeTokensFromHeaders() return resolve() @@ -128,7 +132,7 @@ export class PeerTubeEmbed { this.userTokens.accessToken = obj.access_token this.userTokens.refreshToken = obj.refresh_token - this.userTokens.save() + UserTokens.saveToLocalStorage(peertubeLocalStorage, this.userTokens) this.setHeadersFromTokens() @@ -140,7 +144,7 @@ export class PeerTubeEmbed { return refreshingTokenPromise .catch(() => { - Tokens.flush() + UserTokens.flushLocalStorage(peertubeLocalStorage) this.removeTokensFromHeaders() }).then(() => fetch(url, { @@ -260,12 +264,8 @@ export class PeerTubeEmbed { } async init () { - try { - this.userTokens = Tokens.load() - await this.initCore() - } catch (e) { - console.error(e) - } + this.userTokens = UserTokens.getUserTokens(peertubeLocalStorage) + await this.initCore() } private initializeApi () { @@ -287,6 +287,7 @@ export class PeerTubeEmbed { this.enableApi = this.getParamToggle(params, 'api', this.enableApi) this.warningTitle = this.getParamToggle(params, 'warningTitle', true) this.peertubeLink = this.getParamToggle(params, 'peertubeLink', true) + this.p2pEnabled = this.getParamToggle(params, 'p2p', this.isP2PEnabled(video)) this.scope = this.getParamString(params, 'scope', this.scope) this.subtitle = this.getParamString(params, 'subtitle') @@ -318,7 +319,7 @@ export class PeerTubeEmbed { while (total > elements.length && i < 10) { const result = await this.loadPlaylistElements(playlistId, elements.length) - const json = await result.json() as ResultList + const json = await result.json() total = json.total elements = elements.concat(json.data) @@ -471,12 +472,14 @@ export class PeerTubeEmbed { // Issue when we parsed config from HTML, fallback to API if (!this.config) { this.config = await this.refreshFetch('/api/v1/config') - .then(res => res.json()) + .then(res => res.json()) } const videoInfoPromise = videoResponse.json() .then((videoInfo: VideoDetails) => { - if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo) + this.loadParams(videoInfo) + + if (!alreadyHadPlayer && !this.autoplay) this.loadPlaceholder(videoInfo) return videoInfo }) @@ -488,15 +491,13 @@ export class PeerTubeEmbed { this.PeertubePlayerManagerModulePromise ]) - await this.ensurePluginsAreLoaded(serverTranslations) + await this.loadPlugins(serverTranslations) const videoInfo: VideoDetails = videoInfoTmp const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse) - this.loadParams(videoInfo) - const playlistPlugin = this.currentPlaylistElement ? { elements: this.playlistElements, @@ -508,7 +509,7 @@ export class PeerTubeEmbed { this.currentPlaylistElement = videoPlaylistElement this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid) - .catch(err => console.error(err)) + .catch(err => console.error(err)) } } : undefined @@ -521,6 +522,8 @@ export class PeerTubeEmbed { muted: this.muted, loop: this.loop, + p2pEnabled: this.p2pEnabled, + captions: videoCaptions.length !== 0, subtitle: this.subtitle, @@ -538,12 +541,15 @@ export class PeerTubeEmbed { videoCaptions, inactivityTimeout: 2500, videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views', + videoShortUUID: videoInfo.shortUUID, videoUUID: videoInfo.uuid, isLive: videoInfo.isLive, playerElement: this.playerElement, - onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element, + onPlayerElementChange: (element: HTMLVideoElement) => { + this.playerElement = element + }, videoDuration: videoInfo.duration, enableHotkeys: true, @@ -554,12 +560,18 @@ export class PeerTubeEmbed { serverUrl: window.location.origin, language: navigator.language, embedUrl: window.location.origin + videoInfo.embedPath, - embedTitle: videoInfo.name + embedTitle: videoInfo.name, + + errorNotifier: () => { + // Empty, we don't have a notifier in the embed + } }, webtorrent: { videoFiles: videoInfo.files - } + }, + + pluginsManager: this.pluginsManager } if (this.mode === 'p2p-media-loader') { @@ -576,14 +588,17 @@ export class PeerTubeEmbed { }) } - this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: videojs.Player) => this.player = player) + this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: videojs.Player) => { + this.player = player + }) + this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) - window[ 'videojsPlayer' ] = this.player + window['videojsPlayer'] = this.player this.buildCSS() - await this.buildDock(videoInfo) + this.buildDock(videoInfo) this.initializeApi() @@ -599,7 +614,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 () { @@ -651,30 +666,36 @@ export class PeerTubeEmbed { } private handleError (err: Error, translations?: { [ id: string ]: string }) { - if (err.message.indexOf('from xs param') !== -1) { + if (err.message.includes('from xs param')) { this.player.dispose() this.playerElement = null this.displayError('This video is not available because the remote instance is not responding.', translations) - return } } - private async buildDock (videoInfo: VideoDetails) { + private buildDock (videoInfo: VideoDetails) { if (!this.controls) return // On webtorrent fallback, player may have been disposed if (!this.player.player_) return const title = this.title ? videoInfo.name : undefined - - const description = this.config.tracker.enabled && this.warningTitle + const description = this.warningTitle && this.p2pEnabled ? '' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '' : undefined + const availableAvatars = videoInfo.channel.avatars.filter(a => a.width < 50) + const avatar = availableAvatars.length !== 0 + ? availableAvatars[0] + : undefined + if (title || description) { - this.player.dock({ + this.player.peertubeDock({ title, - description + description, + avatarUrl: title && avatar + ? avatar.path + : undefined }) } } @@ -693,9 +714,9 @@ export class PeerTubeEmbed { private async buildCaptions (serverTranslations: any, captionsResponse: Response): Promise { if (captionsResponse.ok) { - const { data } = (await captionsResponse.json()) as ResultList + const { data } = await captionsResponse.json() - return data.map(c => ({ + return data.map((c: VideoCaption) => ({ label: peertubeTranslate(c.language.label, serverTranslations), language: c.language.id, src: window.location.origin + c.captionPath @@ -732,53 +753,32 @@ export class PeerTubeEmbed { private getResourceId () { const urlParts = window.location.pathname.split('/') - return urlParts[ urlParts.length - 1 ] + return urlParts[urlParts.length - 1] } private isPlaylistEmbed () { 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}` + private loadPlugins (translations?: { [ id: string ]: string }) { + this.pluginsManager = new PluginsManager({ + peertubeHelpersFactory: _ => this.buildPeerTubeHelpers(translations) + }) - if (this.loadedScripts.has(script)) continue + this.pluginsManager.loadPluginsList(this.config) - 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) - }) - } - } + return this.pluginsManager.ensurePluginsAreLoaded('embed') } private buildPeerTubeHelpers (translations?: { [ id: string ]: string }): RegisterClientHelpers { - function unimplemented (): any { + const unimplemented = () => { throw new Error('This helper is not implemented in embed.') } return { getBaseStaticRoute: unimplemented, + getBaseRouterRoute: unimplemented, + getBasePluginClientPath: unimplemented, getSettings: unimplemented, @@ -800,16 +800,23 @@ export class PeerTubeEmbed { enhancedMarkdownToHTML: unimplemented }, - translate: (value: string) => { - return Promise.resolve(peertubeTranslate(value, translations)) - } + translate: (value: string) => Promise.resolve(peertubeTranslate(value, translations)) } } - private runHook (hookName: ClientHookName, result?: T, params?: any): Promise { - return runHook(this.peertubeHooks, hookName, result, params) + private isP2PEnabled (video: Video) { + const userP2PEnabled = getBoolOrDefault( + peertubeLocalStorage.getItem(UserLocalStorageKeys.P2P_ENABLED), + this.config.defaults.p2p.embed.enabled + ) + + return isP2PEnabled(video, this.config, userP2PEnabled) } } PeerTubeEmbed.main() - .catch(err => console.error('Cannot init embed.', err)) + .catch(err => { + (window as any).displayIncompatibleBrowser() + + console.error('Cannot init embed.', err) + })