Fallback HLS to webtorrent

pull/1625/head
Chocobozzz 2019-02-06 10:39:50 +01:00 committed by Chocobozzz
parent 0920929696
commit 6ec0b75beb
5 changed files with 78 additions and 19 deletions

View File

@ -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 () => {

View File

@ -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

View File

@ -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,

View File

@ -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_

View File

@ -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)