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'
+import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
videoFiles: VideoFile[]
}
-export type CommonOptions = {
+export interface CustomizationOptions {
+ startTime: number | string
+ stopTime: number | string
+
+ controls?: boolean
+ muted?: boolean
+ loop?: boolean
+ subtitle?: string
+
+ peertubeLink: boolean
+}
+
+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[]
export type PeertubePlayerManagerOptions = {
common: CommonOptions,
- webtorrent?: WebtorrentOptions,
+ webtorrent: WebtorrentOptions,
p2pMediaLoader?: P2PMediaLoaderOptions
}
export class PeertubePlayerManager {
private static videojsLocaleCache: { [ path: string ]: any } = {}
+ private static playerElementClassName: string
+ private static onPlayerChange: (player: any) => void
static getServerTranslations (serverUrl: string, locale: string) {
const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
})
}
- static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions) {
+ static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: any) => void) {
let p2pMediaLoader: any
+ this.onPlayerChange = onPlayerChange
+ this.playerElementClassName = options.common.playerElement.className
+
if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin')
if (mode === 'p2p-media-loader') {
[ p2pMediaLoader ] = await Promise.all([
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)
})
}
+ 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)
+
+ PeertubePlayerManager.onPlayerChange(player)
+ })
+ }
+
private static loadLocaleInVideoJS (serverUrl: string, locale: string) {
const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
// It is the default locale, nothing to translate
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 redundancyUrlManager = new RedundancyUrlManager(options.p2pMediaLoader.redundancyBaseUrls)
+
const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
- redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls,
+ redundancyUrlManager,
type: 'application/x-mpegURL',
+ startTime: commonOptions.startTime,
src: p2pMediaLoaderOptions.playlistUrl
}
segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url),
rtcConfig: getRtcConfig(),
requiredSegmentsPriority: 5,
- segmentUrlBuilder: segmentUrlBuilderFactory(options.p2pMediaLoader.redundancyBaseUrls)
+ segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager)
},
segments: {
swarmId: p2pMediaLoaderOptions.playlistUrl
},
html5: {
hlsjsConfig: {
+ autoStartLoad: false,
liveSyncDurationCount: 7,
loader: new p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass()
}
html5 = streamrootHls.html5
}
- if (webtorrentOptions) {
+ if (mode === 'webtorrent') {
const webtorrent = {
autoplay,
videoDuration: commonOptions.videoDuration,
playerElement: commonOptions.playerElement,
- videoFiles: webtorrentOptions.videoFiles
+ videoFiles: webtorrentOptions.videoFiles,
+ startTime: commonOptions.startTime
}
Object.assign(plugins, { webtorrent })
: undefined, // Undefined so the player knows it has to check the local storage
poster: commonOptions.poster,
- autoplay,
+ 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,
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() }))
}
},
{