mirror of https://github.com/Chocobozzz/PeerTube
Fallback HLS to webtorrent
parent
0920929696
commit
6ec0b75beb
|
@ -23,7 +23,13 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { VideoCaptionService } from '@app/shared/video-caption'
|
import { VideoCaptionService } from '@app/shared/video-caption'
|
||||||
import { MarkdownService } from '@app/shared/renderer'
|
import { MarkdownService } from '@app/shared/renderer'
|
||||||
import { P2PMediaLoaderOptions, PeertubePlayerManager, PlayerMode, WebtorrentOptions } from '../../../assets/player/peertube-player-manager'
|
import {
|
||||||
|
P2PMediaLoaderOptions,
|
||||||
|
PeertubePlayerManager,
|
||||||
|
PeertubePlayerManagerOptions,
|
||||||
|
PlayerMode,
|
||||||
|
WebtorrentOptions
|
||||||
|
} from '../../../assets/player/peertube-player-manager'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-watch',
|
selector: 'my-video-watch',
|
||||||
|
@ -395,10 +401,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
src: environment.apiUrl + c.captionPath
|
src: environment.apiUrl + c.captionPath
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const options = {
|
const options: PeertubePlayerManagerOptions = {
|
||||||
common: {
|
common: {
|
||||||
autoplay: this.isAutoplay(),
|
autoplay: this.isAutoplay(),
|
||||||
|
|
||||||
playerElement: this.playerElement,
|
playerElement: this.playerElement,
|
||||||
|
onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
|
||||||
|
|
||||||
videoDuration: this.video.duration,
|
videoDuration: this.video.duration,
|
||||||
enableHotkeys: true,
|
enableHotkeys: true,
|
||||||
inactivityTimeout: 2500,
|
inactivityTimeout: 2500,
|
||||||
|
@ -424,6 +433,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
serverUrl: environment.apiUrl,
|
serverUrl: environment.apiUrl,
|
||||||
|
|
||||||
videoCaptions: playerCaptions
|
videoCaptions: playerCaptions
|
||||||
|
},
|
||||||
|
|
||||||
|
webtorrent: {
|
||||||
|
videoFiles: this.video.files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,6 +444,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
const hlsPlaylist = this.video.getHlsPlaylist()
|
const hlsPlaylist = this.video.getHlsPlaylist()
|
||||||
if (hlsPlaylist) {
|
if (hlsPlaylist) {
|
||||||
mode = 'p2p-media-loader'
|
mode = 'p2p-media-loader'
|
||||||
|
|
||||||
const p2pMediaLoader = {
|
const p2pMediaLoader = {
|
||||||
playlistUrl: hlsPlaylist.playlistUrl,
|
playlistUrl: hlsPlaylist.playlistUrl,
|
||||||
segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
|
segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
|
||||||
|
@ -442,11 +456,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
Object.assign(options, { p2pMediaLoader })
|
Object.assign(options, { p2pMediaLoader })
|
||||||
} else {
|
} else {
|
||||||
mode = 'webtorrent'
|
mode = 'webtorrent'
|
||||||
const webtorrent = {
|
|
||||||
videoFiles: this.video.files
|
|
||||||
} as WebtorrentOptions
|
|
||||||
|
|
||||||
Object.assign(options, { webtorrent })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.zone.runOutsideAngular(async () => {
|
this.zone.runOutsideAngular(async () => {
|
||||||
|
|
|
@ -51,17 +51,25 @@ class P2pMediaLoaderPlugin extends Plugin {
|
||||||
src: options.src
|
src: options.src
|
||||||
})
|
})
|
||||||
|
|
||||||
|
player.on('play', () => {
|
||||||
|
player.addClass('vjs-has-big-play-button-clicked')
|
||||||
|
})
|
||||||
|
|
||||||
player.ready(() => this.initialize())
|
player.ready(() => this.initialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
|
if (this.hlsjs) this.hlsjs.destroy()
|
||||||
|
if (this.p2pEngine) this.p2pEngine.destroy()
|
||||||
|
|
||||||
clearInterval(this.networkInfoInterval)
|
clearInterval(this.networkInfoInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
private initialize () {
|
private initialize () {
|
||||||
initHlsJsPlayer(this.hlsjs)
|
initHlsJsPlayer(this.hlsjs)
|
||||||
|
|
||||||
this.p2pEngine = this.player.tech_.options_.hlsjsConfig.loader.getEngine()
|
const tech = this.player.tech_
|
||||||
|
this.p2pEngine = tech.options_.hlsjsConfig.loader.getEngine()
|
||||||
|
|
||||||
// Avoid using constants to not import hls.hs
|
// Avoid using constants to not import hls.hs
|
||||||
// https://github.com/video-dev/hls.js/blob/master/src/events.js#L37
|
// https://github.com/video-dev/hls.js/blob/master/src/events.js#L37
|
||||||
|
|
|
@ -41,6 +41,7 @@ export type P2PMediaLoaderOptions = {
|
||||||
|
|
||||||
export type CommonOptions = {
|
export type CommonOptions = {
|
||||||
playerElement: HTMLVideoElement
|
playerElement: HTMLVideoElement
|
||||||
|
onPlayerElementChange: (element: HTMLVideoElement) => void
|
||||||
|
|
||||||
autoplay: boolean
|
autoplay: boolean
|
||||||
videoDuration: number
|
videoDuration: number
|
||||||
|
@ -71,13 +72,14 @@ export type CommonOptions = {
|
||||||
|
|
||||||
export type PeertubePlayerManagerOptions = {
|
export type PeertubePlayerManagerOptions = {
|
||||||
common: CommonOptions,
|
common: CommonOptions,
|
||||||
webtorrent?: WebtorrentOptions,
|
webtorrent: WebtorrentOptions,
|
||||||
p2pMediaLoader?: P2PMediaLoaderOptions
|
p2pMediaLoader?: P2PMediaLoaderOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PeertubePlayerManager {
|
export class PeertubePlayerManager {
|
||||||
|
|
||||||
private static videojsLocaleCache: { [ path: string ]: any } = {}
|
private static videojsLocaleCache: { [ path: string ]: any } = {}
|
||||||
|
private static playerElementClassName: string
|
||||||
|
|
||||||
static getServerTranslations (serverUrl: string, locale: string) {
|
static getServerTranslations (serverUrl: string, locale: string) {
|
||||||
const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
|
const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
|
||||||
|
@ -95,6 +97,8 @@ export class PeertubePlayerManager {
|
||||||
static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions) {
|
static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions) {
|
||||||
let p2pMediaLoader: any
|
let p2pMediaLoader: any
|
||||||
|
|
||||||
|
this.playerElementClassName = options.common.playerElement.className
|
||||||
|
|
||||||
if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin')
|
if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin')
|
||||||
if (mode === 'p2p-media-loader') {
|
if (mode === 'p2p-media-loader') {
|
||||||
[ p2pMediaLoader ] = await Promise.all([
|
[ p2pMediaLoader ] = await Promise.all([
|
||||||
|
@ -112,6 +116,13 @@ export class PeertubePlayerManager {
|
||||||
videojs(options.common.playerElement, videojsOptions, function (this: any) {
|
videojs(options.common.playerElement, videojsOptions, function (this: any) {
|
||||||
const player = this
|
const player = this
|
||||||
|
|
||||||
|
player.tech_.on('error', () => {
|
||||||
|
// Fallback to webtorrent?
|
||||||
|
if (mode === 'p2p-media-loader') {
|
||||||
|
self.fallbackToWebTorrent(player, options)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
self.addContextMenu(mode, player, options.common.embedUrl)
|
self.addContextMenu(mode, player, options.common.embedUrl)
|
||||||
|
|
||||||
return res(player)
|
return res(player)
|
||||||
|
@ -119,6 +130,32 @@ export class PeertubePlayerManager {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async fallbackToWebTorrent (player: any, options: PeertubePlayerManagerOptions) {
|
||||||
|
const newVideoElement = document.createElement('video')
|
||||||
|
newVideoElement.className = this.playerElementClassName
|
||||||
|
|
||||||
|
// VideoJS wraps our video element inside a div
|
||||||
|
const currentParentPlayerElement = options.common.playerElement.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) {
|
private static loadLocaleInVideoJS (serverUrl: string, locale: string) {
|
||||||
const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
|
const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
|
||||||
// It is the default locale, nothing to translate
|
// It is the default locale, nothing to translate
|
||||||
|
@ -166,7 +203,7 @@ export class PeertubePlayerManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p2pMediaLoaderOptions) {
|
if (mode === 'p2p-media-loader') {
|
||||||
const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
|
const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
|
||||||
redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls,
|
redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls,
|
||||||
type: 'application/x-mpegURL',
|
type: 'application/x-mpegURL',
|
||||||
|
@ -209,7 +246,7 @@ export class PeertubePlayerManager {
|
||||||
html5 = streamrootHls.html5
|
html5 = streamrootHls.html5
|
||||||
}
|
}
|
||||||
|
|
||||||
if (webtorrentOptions) {
|
if (mode === 'webtorrent') {
|
||||||
const webtorrent = {
|
const webtorrent = {
|
||||||
autoplay,
|
autoplay,
|
||||||
videoDuration: commonOptions.videoDuration,
|
videoDuration: commonOptions.videoDuration,
|
||||||
|
@ -235,7 +272,7 @@ export class PeertubePlayerManager {
|
||||||
: undefined, // Undefined so the player knows it has to check the local storage
|
: undefined, // Undefined so the player knows it has to check the local storage
|
||||||
|
|
||||||
poster: commonOptions.poster,
|
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,
|
inactivityTimeout: commonOptions.inactivityTimeout,
|
||||||
playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ],
|
playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ],
|
||||||
plugins,
|
plugins,
|
||||||
|
|
|
@ -47,7 +47,11 @@ class PeerTubePlugin extends Plugin {
|
||||||
this.videoDuration = options.videoDuration
|
this.videoDuration = options.videoDuration
|
||||||
this.videoCaptions = options.videoCaptions
|
this.videoCaptions = options.videoCaptions
|
||||||
|
|
||||||
if (this.autoplay === true) this.player.addClass('vjs-has-autoplay')
|
if (options.autoplay === true) this.player.addClass('vjs-has-autoplay')
|
||||||
|
|
||||||
|
this.player.on('autoplay-failure', () => {
|
||||||
|
this.player.removeClass('vjs-has-autoplay')
|
||||||
|
})
|
||||||
|
|
||||||
this.player.ready(() => {
|
this.player.ready(() => {
|
||||||
const playerOptions = this.player.options_
|
const playerOptions = this.player.options_
|
||||||
|
|
|
@ -311,7 +311,10 @@ class PeerTubeEmbed {
|
||||||
videoCaptions,
|
videoCaptions,
|
||||||
inactivityTimeout: 1500,
|
inactivityTimeout: 1500,
|
||||||
videoViewUrl: this.getVideoUrl(videoId) + '/views',
|
videoViewUrl: this.getVideoUrl(videoId) + '/views',
|
||||||
|
|
||||||
playerElement: this.videoElement,
|
playerElement: this.videoElement,
|
||||||
|
onPlayerElementChange: (element: HTMLVideoElement) => this.videoElement = element,
|
||||||
|
|
||||||
videoDuration: videoInfo.duration,
|
videoDuration: videoInfo.duration,
|
||||||
enableHotkeys: true,
|
enableHotkeys: true,
|
||||||
peertubeLink: true,
|
peertubeLink: true,
|
||||||
|
@ -321,6 +324,10 @@ class PeerTubeEmbed {
|
||||||
serverUrl: window.location.origin,
|
serverUrl: window.location.origin,
|
||||||
language: navigator.language,
|
language: navigator.language,
|
||||||
embedUrl: window.location.origin + videoInfo.embedPath
|
embedUrl: window.location.origin + videoInfo.embedPath
|
||||||
|
},
|
||||||
|
|
||||||
|
webtorrent: {
|
||||||
|
videoFiles: videoInfo.files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,12 +343,6 @@ class PeerTubeEmbed {
|
||||||
videoFiles: videoInfo.files
|
videoFiles: videoInfo.files
|
||||||
} as P2PMediaLoaderOptions
|
} as P2PMediaLoaderOptions
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
Object.assign(options, {
|
|
||||||
webtorrent: {
|
|
||||||
videoFiles: videoInfo.files
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.player = await PeertubePlayerManager.initialize(this.mode, options)
|
this.player = await PeertubePlayerManager.initialize(this.mode, options)
|
||||||
|
|
Loading…
Reference in New Issue