diff --git a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts index 8fb7ba2ea..0c8c612ee 100644 --- a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts +++ b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts @@ -3,7 +3,7 @@ import * as videojs from 'video.js' import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideoJSComponentInterface } from '../peertube-videojs-typings' import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs' -import { Events } from 'p2p-media-loader-core' +import { Events, Segment } from 'p2p-media-loader-core' import { timeToInt } from '../utils' // videojs-hlsjs-plugin needs videojs in window @@ -57,7 +57,6 @@ class P2pMediaLoaderPlugin extends Plugin { initVideoJsContribHlsJsPlayer(player) this.startTime = timeToInt(options.startTime) - console.log(this.startTime) player.src({ type: options.type, @@ -90,11 +89,13 @@ class P2pMediaLoaderPlugin extends Plugin { this.trigger('resolutionChange', { auto: this.hlsjs.autoLevelEnabled, resolutionId: data.height }) }) - this.p2pEngine.on(Events.SegmentError, (segment, err) => { + this.p2pEngine.on(Events.SegmentError, (segment: Segment, err) => { console.error('Segment error.', segment, err) + + this.options.redundancyUrlManager.removeByOriginUrl(segment.url) }) - this.statsP2PBytes.numPeers = 1 + this.options.redundancyBaseUrls.length + this.statsP2PBytes.numPeers = 1 + this.options.redundancyUrlManager.countBaseUrls() this.runStats() diff --git a/client/src/assets/player/p2p-media-loader/redundancy-url-manager.ts b/client/src/assets/player/p2p-media-loader/redundancy-url-manager.ts new file mode 100644 index 000000000..7fc2b6ab1 --- /dev/null +++ b/client/src/assets/player/p2p-media-loader/redundancy-url-manager.ts @@ -0,0 +1,57 @@ +import { basename, dirname } from 'path' + +class RedundancyUrlManager { + + // Remember by what new URL we replaced an origin URL + private replacedSegmentUrls: { [originUrl: string]: string } = {} + + constructor (private baseUrls: string[] = []) { + // empty + } + + removeBySegmentUrl (segmentUrl: string) { + console.log('Removing redundancy of segment URL %s.', segmentUrl) + + const baseUrl = dirname(segmentUrl) + + this.baseUrls = this.baseUrls.filter(u => u !== baseUrl && u !== baseUrl + '/') + } + + removeByOriginUrl (originUrl: string) { + const replaced = this.replacedSegmentUrls[originUrl] + if (!replaced) return + + return this.removeBySegmentUrl(replaced) + } + + buildUrl (url: string) { + delete this.replacedSegmentUrls[url] + + const max = this.baseUrls.length + 1 + const i = this.getRandomInt(max) + + if (i === max - 1) return url + + const newBaseUrl = this.baseUrls[i] + const slashPart = newBaseUrl.endsWith('/') ? '' : '/' + + const newUrl = newBaseUrl + slashPart + basename(url) + this.replacedSegmentUrls[url] = newUrl + + return newUrl + } + + countBaseUrls () { + return this.baseUrls.length + } + + private getRandomInt (max: number) { + return Math.floor(Math.random() * Math.floor(max)) + } +} + +// --------------------------------------------------------------------------- + +export { + RedundancyUrlManager +} diff --git a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts index fb990a19d..039777cea 100644 --- a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts +++ b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts @@ -1,17 +1,9 @@ -import { basename } from 'path' import { Segment } from 'p2p-media-loader-core' +import { RedundancyUrlManager } from './redundancy-url-manager' -function segmentUrlBuilderFactory (baseUrls: string[]) { +function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager) { return function segmentBuilder (segment: Segment) { - const max = baseUrls.length + 1 - const i = getRandomInt(max) - - if (i === max - 1) return segment.url - - const newBaseUrl = baseUrls[i] - const middlePart = newBaseUrl.endsWith('/') ? '' : '/' - - return newBaseUrl + middlePart + basename(segment.url) + return redundancyUrlManager.buildUrl(segment.url) } } @@ -20,9 +12,3 @@ function segmentUrlBuilderFactory (baseUrls: string[]) { export { segmentUrlBuilderFactory } - -// --------------------------------------------------------------------------- - -function getRandomInt (max: number) { - return Math.floor(Math.random() * Math.floor(max)) -} diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 6c8b13087..7be9f8719 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts @@ -17,6 +17,7 @@ import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from ' 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' @@ -226,8 +227,10 @@ export class PeertubePlayerManager { } 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 @@ -242,7 +245,7 @@ export class PeertubePlayerManager { segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url), rtcConfig: getRtcConfig(), requiredSegmentsPriority: 5, - segmentUrlBuilder: segmentUrlBuilderFactory(options.p2pMediaLoader.redundancyBaseUrls) + segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager) }, segments: { swarmId: p2pMediaLoaderOptions.playlistUrl diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts index a96b0bc8c..b7f2eec94 100644 --- a/client/src/assets/player/peertube-videojs-typings.ts +++ b/client/src/assets/player/peertube-videojs-typings.ts @@ -7,6 +7,7 @@ import { PeerTubePlugin } from './peertube-plugin' import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin' import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin' import { PlayerMode } from './peertube-player-manager' +import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager' declare namespace videojs { interface Player { @@ -62,7 +63,7 @@ type WebtorrentPluginOptions = { } type P2PMediaLoaderPluginOptions = { - redundancyBaseUrls: string[] + redundancyUrlManager: RedundancyUrlManager type: string src: string