X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=client%2Fsrc%2Fassets%2Fplayer%2Fpeertube-player-manager.ts;h=083c621d21cc376e9003ae996d4b8c47981754c4;hb=85394ba22a07bde1dfccebf3f591a5d6dbe9df56;hp=91ca6a2aafdd1d8b11859d14cda6fa6cb4a37511;hpb=4348a27d252a3349bafa7ef4859c0e2cf060c255;p=github%2FChocobozzz%2FPeerTube.git diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 91ca6a2aa..083c621d2 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts @@ -13,8 +13,10 @@ import './videojs-components/p2p-info-button' import './videojs-components/peertube-load-progress-bar' import './videojs-components/theater-button' import { P2PMediaLoaderPluginOptions, UserWatching, VideoJSCaption, VideoJSPluginOptions, videojsUntyped } from './peertube-videojs-typings' -import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils' +import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from './utils' import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n' +import { segmentValidatorFactory } from './p2p-media-loader/segment-validator' +import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder' // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' @@ -31,31 +33,41 @@ export type WebtorrentOptions = { export type P2PMediaLoaderOptions = { playlistUrl: string + segmentsSha256Url: string trackerAnnounce: string[] + redundancyBaseUrls: string[] + videoFiles: VideoFile[] +} + +export interface CustomizationOptions { + startTime: number | string + stopTime: number | string + + controls?: boolean + muted?: boolean + loop?: boolean + subtitle?: string + + peertubeLink: boolean } -export type CommonOptions = { +export interface CommonOptions extends CustomizationOptions { playerElement: HTMLVideoElement + onPlayerElementChange: (element: HTMLVideoElement) => void autoplay: boolean videoDuration: number enableHotkeys: boolean inactivityTimeout: number poster: string - startTime: number | string theaterMode: boolean captions: boolean - peertubeLink: boolean videoViewUrl: string embedUrl: string language?: string - controls?: boolean - muted?: boolean - loop?: boolean - subtitle?: string videoCaptions: VideoJSCaption[] @@ -66,13 +78,14 @@ export type CommonOptions = { export type PeertubePlayerManagerOptions = { common: CommonOptions, - webtorrent?: WebtorrentOptions, + webtorrent: WebtorrentOptions, p2pMediaLoader?: P2PMediaLoaderOptions } export class PeertubePlayerManager { private static videojsLocaleCache: { [ path: string ]: any } = {} + private static playerElementClassName: string static getServerTranslations (serverUrl: string, locale: string) { const path = PeertubePlayerManager.getLocalePath(serverUrl, locale) @@ -90,11 +103,13 @@ export class PeertubePlayerManager { static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions) { let p2pMediaLoader: any - if (mode === 'webtorrent') await import('./webtorrent-plugin') + this.playerElementClassName = options.common.playerElement.className + + if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin') if (mode === 'p2p-media-loader') { [ p2pMediaLoader ] = await Promise.all([ import('p2p-media-loader-hlsjs'), - import('./p2p-media-loader-plugin') + import('./p2p-media-loader/p2p-media-loader-plugin') ]) } @@ -107,6 +122,18 @@ export class PeertubePlayerManager { videojs(options.common.playerElement, videojsOptions, function (this: any) { const player = this + let alreadyFallback = false + + player.tech_.one('error', () => { + if (!alreadyFallback) self.maybeFallbackToWebTorrent(mode, player, options) + alreadyFallback = true + }) + + player.one('error', () => { + if (!alreadyFallback) self.maybeFallbackToWebTorrent(mode, player, options) + alreadyFallback = true + }) + self.addContextMenu(mode, player, options.common.embedUrl) return res(player) @@ -114,6 +141,39 @@ export class PeertubePlayerManager { }) } + private static async maybeFallbackToWebTorrent (currentMode: PlayerMode, player: any, options: PeertubePlayerManagerOptions) { + if (currentMode === 'webtorrent') return + + console.log('Fallback to webtorrent.') + + const newVideoElement = document.createElement('video') + newVideoElement.className = this.playerElementClassName + + // VideoJS wraps our video element inside a div + let currentParentPlayerElement = options.common.playerElement.parentNode + // Fix on IOS, don't ask me why + if (!currentParentPlayerElement) currentParentPlayerElement = document.getElementById(options.common.playerElement.id).parentNode + + currentParentPlayerElement.parentNode.insertBefore(newVideoElement, currentParentPlayerElement) + + options.common.playerElement = newVideoElement + options.common.onPlayerElementChange(newVideoElement) + + player.dispose() + + await import('./webtorrent/webtorrent-plugin') + + const mode = 'webtorrent' + const videojsOptions = this.getVideojsOptions(mode, options) + + const self = this + videojs(newVideoElement, videojsOptions, function (this: any) { + const player = this + + self.addContextMenu(mode, player, options.common.embedUrl) + }) + } + private static loadLocaleInVideoJS (serverUrl: string, locale: string) { const path = PeertubePlayerManager.getLocalePath(serverUrl, locale) // It is the default locale, nothing to translate @@ -144,35 +204,55 @@ export class PeertubePlayerManager { const commonOptions = options.common const webtorrentOptions = options.webtorrent const p2pMediaLoaderOptions = options.p2pMediaLoader + + let autoplay = options.common.autoplay let html5 = {} const plugins: VideoJSPluginOptions = { peertube: { - autoplay: commonOptions.autoplay, // Use peertube plugin autoplay because we get the file by webtorrent + mode, + autoplay, // Use peertube plugin autoplay because we get the file by webtorrent videoViewUrl: commonOptions.videoViewUrl, videoDuration: commonOptions.videoDuration, - startTime: commonOptions.startTime, userWatching: commonOptions.userWatching, subtitle: commonOptions.subtitle, - videoCaptions: commonOptions.videoCaptions + videoCaptions: commonOptions.videoCaptions, + stopTime: commonOptions.stopTime } } - if (p2pMediaLoaderOptions) { + if (mode === 'p2p-media-loader') { const p2pMediaLoader: P2PMediaLoaderPluginOptions = { + redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls, type: 'application/x-mpegURL', + startTime: commonOptions.startTime, src: p2pMediaLoaderOptions.playlistUrl } + const trackerAnnounce = p2pMediaLoaderOptions.trackerAnnounce + .filter(t => t.startsWith('ws')) + const p2pMediaLoaderConfig = { - // loader: { - // trackerAnnounce: p2pMediaLoaderOptions.trackerAnnounce - // }, + loader: { + trackerAnnounce, + segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url), + rtcConfig: getRtcConfig(), + requiredSegmentsPriority: 5, + segmentUrlBuilder: segmentUrlBuilderFactory(options.p2pMediaLoader.redundancyBaseUrls) + }, segments: { swarmId: p2pMediaLoaderOptions.playlistUrl } } const streamrootHls = { + levelLabelHandler: (level: { height: number, width: number }) => { + const file = p2pMediaLoaderOptions.videoFiles.find(f => f.resolution.id === level.height) + + let label = file.resolution.label + if (file.fps >= 50) label += file.fps + + return label + }, html5: { hlsjsConfig: { liveSyncDurationCount: 7, @@ -185,14 +265,18 @@ export class PeertubePlayerManager { html5 = streamrootHls.html5 } - if (webtorrentOptions) { + if (mode === 'webtorrent') { const webtorrent = { - autoplay: commonOptions.autoplay, + autoplay, videoDuration: commonOptions.videoDuration, playerElement: commonOptions.playerElement, - videoFiles: webtorrentOptions.videoFiles + videoFiles: webtorrentOptions.videoFiles, + startTime: commonOptions.startTime } Object.assign(plugins, { webtorrent }) + + // WebTorrent plugin handles autoplay, because we do some hackish stuff in there + autoplay = false } const videojsOptions = { @@ -208,7 +292,7 @@ export class PeertubePlayerManager { : undefined, // Undefined so the player knows it has to check the local storage poster: commonOptions.poster, - autoplay: false, + autoplay: autoplay === true ? 'any' : autoplay, // Use 'any' instead of true to get notifier by videojs if autoplay fails inactivityTimeout: commonOptions.inactivityTimeout, playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ], plugins, @@ -362,7 +446,7 @@ export class PeertubePlayerManager { label: player.localize('Copy the video URL at the current time'), listener: function () { const player = this as videojs.Player - copyToClipboard(buildVideoLink(player.currentTime())) + copyToClipboard(buildVideoLink({ startTime: player.currentTime() })) } }, {