aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/assets/player
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/assets/player')
-rw-r--r--client/src/assets/player/shared/common/utils.ts8
-rw-r--r--client/src/assets/player/shared/manager-options/hls-options-builder.ts18
-rw-r--r--client/src/assets/player/shared/manager-options/webtorrent-options-builder.ts15
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/segment-validator.ts30
-rw-r--r--client/src/assets/player/shared/peertube/peertube-plugin.ts4
-rw-r--r--client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts22
-rw-r--r--client/src/assets/player/types/manager-options.ts4
-rw-r--r--client/src/assets/player/types/peertube-videojs-typings.ts7
8 files changed, 91 insertions, 17 deletions
diff --git a/client/src/assets/player/shared/common/utils.ts b/client/src/assets/player/shared/common/utils.ts
index a010d9184..609240626 100644
--- a/client/src/assets/player/shared/common/utils.ts
+++ b/client/src/assets/player/shared/common/utils.ts
@@ -52,6 +52,10 @@ function getRtcConfig () {
52 } 52 }
53} 53}
54 54
55function isSameOrigin (current: string, target: string) {
56 return new URL(current).origin === new URL(target).origin
57}
58
55// --------------------------------------------------------------------------- 59// ---------------------------------------------------------------------------
56 60
57export { 61export {
@@ -60,5 +64,7 @@ export {
60 64
61 videoFileMaxByResolution, 65 videoFileMaxByResolution,
62 videoFileMinByResolution, 66 videoFileMinByResolution,
63 bytes 67 bytes,
68
69 isSameOrigin
64} 70}
diff --git a/client/src/assets/player/shared/manager-options/hls-options-builder.ts b/client/src/assets/player/shared/manager-options/hls-options-builder.ts
index 361c76f4b..933c0d595 100644
--- a/client/src/assets/player/shared/manager-options/hls-options-builder.ts
+++ b/client/src/assets/player/shared/manager-options/hls-options-builder.ts
@@ -5,7 +5,7 @@ import { LiveVideoLatencyMode } from '@shared/models'
5import { getAverageBandwidthInStore } from '../../peertube-player-local-storage' 5import { getAverageBandwidthInStore } from '../../peertube-player-local-storage'
6import { P2PMediaLoader, P2PMediaLoaderPluginOptions } from '../../types' 6import { P2PMediaLoader, P2PMediaLoaderPluginOptions } from '../../types'
7import { PeertubePlayerManagerOptions } from '../../types/manager-options' 7import { PeertubePlayerManagerOptions } from '../../types/manager-options'
8import { getRtcConfig } from '../common' 8import { getRtcConfig, isSameOrigin } from '../common'
9import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager' 9import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager'
10import { segmentUrlBuilderFactory } from '../p2p-media-loader/segment-url-builder' 10import { segmentUrlBuilderFactory } from '../p2p-media-loader/segment-url-builder'
11import { segmentValidatorFactory } from '../p2p-media-loader/segment-validator' 11import { segmentValidatorFactory } from '../p2p-media-loader/segment-validator'
@@ -84,7 +84,21 @@ export class HLSOptionsBuilder {
84 simultaneousHttpDownloads: 1, 84 simultaneousHttpDownloads: 1,
85 httpFailedSegmentTimeout: 1000, 85 httpFailedSegmentTimeout: 1000,
86 86
87 segmentValidator: segmentValidatorFactory(this.options.p2pMediaLoader.segmentsSha256Url, this.options.common.isLive), 87 xhrSetup: (xhr, url) => {
88 if (!this.options.common.requiresAuth) return
89 if (!isSameOrigin(this.options.common.serverUrl, url)) return
90
91 xhr.setRequestHeader('Authorization', this.options.common.authorizationHeader())
92 },
93
94 segmentValidator: segmentValidatorFactory({
95 segmentsSha256Url: this.options.p2pMediaLoader.segmentsSha256Url,
96 isLive: this.options.common.isLive,
97 authorizationHeader: this.options.common.authorizationHeader,
98 requiresAuth: this.options.common.requiresAuth,
99 serverUrl: this.options.common.serverUrl
100 }),
101
88 segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager), 102 segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager),
89 103
90 useP2P: this.options.common.p2pEnabled, 104 useP2P: this.options.common.p2pEnabled,
diff --git a/client/src/assets/player/shared/manager-options/webtorrent-options-builder.ts b/client/src/assets/player/shared/manager-options/webtorrent-options-builder.ts
index 257cf1e05..b5bdcd4e6 100644
--- a/client/src/assets/player/shared/manager-options/webtorrent-options-builder.ts
+++ b/client/src/assets/player/shared/manager-options/webtorrent-options-builder.ts
@@ -1,4 +1,5 @@
1import { PeertubePlayerManagerOptions } from '../../types' 1import { addQueryParams } from '../../../../../../shared/core-utils'
2import { PeertubePlayerManagerOptions, WebtorrentPluginOptions } from '../../types'
2 3
3export class WebTorrentOptionsBuilder { 4export class WebTorrentOptionsBuilder {
4 5
@@ -16,13 +17,23 @@ export class WebTorrentOptionsBuilder {
16 17
17 const autoplay = this.autoPlayValue === 'play' 18 const autoplay = this.autoPlayValue === 'play'
18 19
19 const webtorrent = { 20 const webtorrent: WebtorrentPluginOptions = {
20 autoplay, 21 autoplay,
21 22
22 playerRefusedP2P: commonOptions.p2pEnabled === false, 23 playerRefusedP2P: commonOptions.p2pEnabled === false,
23 videoDuration: commonOptions.videoDuration, 24 videoDuration: commonOptions.videoDuration,
24 playerElement: commonOptions.playerElement, 25 playerElement: commonOptions.playerElement,
25 26
27 videoFileToken: commonOptions.videoFileToken,
28
29 requiresAuth: commonOptions.requiresAuth,
30
31 buildWebSeedUrls: file => {
32 if (!commonOptions.requiresAuth) return []
33
34 return [ addQueryParams(file.fileUrl, { videoFileToken: commonOptions.videoFileToken() }) ]
35 },
36
26 videoFiles: webtorrentOptions.videoFiles.length !== 0 37 videoFiles: webtorrentOptions.videoFiles.length !== 0
27 ? webtorrentOptions.videoFiles 38 ? webtorrentOptions.videoFiles
28 // The WebTorrent plugin won't be able to play these files, but it will fallback to HTTP mode 39 // The WebTorrent plugin won't be able to play these files, but it will fallback to HTTP mode
diff --git a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts b/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
index 18cb6750f..a7ee91950 100644
--- a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
+++ b/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
@@ -2,13 +2,22 @@ import { basename } from 'path'
2import { Segment } from '@peertube/p2p-media-loader-core' 2import { Segment } from '@peertube/p2p-media-loader-core'
3import { logger } from '@root-helpers/logger' 3import { logger } from '@root-helpers/logger'
4import { wait } from '@root-helpers/utils' 4import { wait } from '@root-helpers/utils'
5import { isSameOrigin } from '../common'
5 6
6type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } } 7type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } }
7 8
8const maxRetries = 3 9const maxRetries = 3
9 10
10function segmentValidatorFactory (segmentsSha256Url: string, isLive: boolean) { 11function segmentValidatorFactory (options: {
11 let segmentsJSON = fetchSha256Segments(segmentsSha256Url) 12 serverUrl: string
13 segmentsSha256Url: string
14 isLive: boolean
15 authorizationHeader: () => string
16 requiresAuth: boolean
17}) {
18 const { serverUrl, segmentsSha256Url, isLive, authorizationHeader, requiresAuth } = options
19
20 let segmentsJSON = fetchSha256Segments({ serverUrl, segmentsSha256Url, authorizationHeader, requiresAuth })
12 const regex = /bytes=(\d+)-(\d+)/ 21 const regex = /bytes=(\d+)-(\d+)/
13 22
14 return async function segmentValidator (segment: Segment, _method: string, _peerId: string, retry = 1) { 23 return async function segmentValidator (segment: Segment, _method: string, _peerId: string, retry = 1) {
@@ -28,7 +37,7 @@ function segmentValidatorFactory (segmentsSha256Url: string, isLive: boolean) {
28 37
29 await wait(1000) 38 await wait(1000)
30 39
31 segmentsJSON = fetchSha256Segments(segmentsSha256Url) 40 segmentsJSON = fetchSha256Segments({ serverUrl, segmentsSha256Url, authorizationHeader, requiresAuth })
32 await segmentValidator(segment, _method, _peerId, retry + 1) 41 await segmentValidator(segment, _method, _peerId, retry + 1)
33 42
34 return 43 return
@@ -68,8 +77,19 @@ export {
68 77
69// --------------------------------------------------------------------------- 78// ---------------------------------------------------------------------------
70 79
71function fetchSha256Segments (url: string) { 80function fetchSha256Segments (options: {
72 return fetch(url) 81 serverUrl: string
82 segmentsSha256Url: string
83 authorizationHeader: () => string
84 requiresAuth: boolean
85}) {
86 const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options
87
88 const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url)
89 ? { Authorization: authorizationHeader() }
90 : {}
91
92 return fetch(segmentsSha256Url, { headers })
73 .then(res => res.json() as Promise<SegmentsJSON>) 93 .then(res => res.json() as Promise<SegmentsJSON>)
74 .catch(err => { 94 .catch(err => {
75 logger.error('Cannot get sha256 segments', err) 95 logger.error('Cannot get sha256 segments', err)
diff --git a/client/src/assets/player/shared/peertube/peertube-plugin.ts b/client/src/assets/player/shared/peertube/peertube-plugin.ts
index a5d712d70..4bd038bb1 100644
--- a/client/src/assets/player/shared/peertube/peertube-plugin.ts
+++ b/client/src/assets/player/shared/peertube/peertube-plugin.ts
@@ -22,7 +22,7 @@ const Plugin = videojs.getPlugin('plugin')
22 22
23class PeerTubePlugin extends Plugin { 23class PeerTubePlugin extends Plugin {
24 private readonly videoViewUrl: string 24 private readonly videoViewUrl: string
25 private readonly authorizationHeader: string 25 private readonly authorizationHeader: () => string
26 26
27 private readonly videoUUID: string 27 private readonly videoUUID: string
28 private readonly startTime: number 28 private readonly startTime: number
@@ -228,7 +228,7 @@ class PeerTubePlugin extends Plugin {
228 'Content-type': 'application/json; charset=UTF-8' 228 'Content-type': 'application/json; charset=UTF-8'
229 }) 229 })
230 230
231 if (this.authorizationHeader) headers.set('Authorization', this.authorizationHeader) 231 if (this.authorizationHeader) headers.set('Authorization', this.authorizationHeader())
232 232
233 return fetch(this.videoViewUrl, { method: 'POST', body: JSON.stringify(body), headers }) 233 return fetch(this.videoViewUrl, { method: 'POST', body: JSON.stringify(body), headers })
234 } 234 }
diff --git a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
index fa3f48a9a..658b7c867 100644
--- a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
@@ -2,7 +2,7 @@ import videojs from 'video.js'
2import * as WebTorrent from 'webtorrent' 2import * as WebTorrent from 'webtorrent'
3import { logger } from '@root-helpers/logger' 3import { logger } from '@root-helpers/logger'
4import { isIOS } from '@root-helpers/web-browser' 4import { isIOS } from '@root-helpers/web-browser'
5import { timeToInt } from '@shared/core-utils' 5import { addQueryParams, timeToInt } from '@shared/core-utils'
6import { VideoFile } from '@shared/models' 6import { VideoFile } from '@shared/models'
7import { getAverageBandwidthInStore, getStoredMute, getStoredVolume, saveAverageBandwidth } from '../../peertube-player-local-storage' 7import { getAverageBandwidthInStore, getStoredMute, getStoredVolume, saveAverageBandwidth } from '../../peertube-player-local-storage'
8import { PeerTubeResolution, PlayerNetworkInfo, WebtorrentPluginOptions } from '../../types' 8import { PeerTubeResolution, PlayerNetworkInfo, WebtorrentPluginOptions } from '../../types'
@@ -38,6 +38,8 @@ class WebTorrentPlugin extends Plugin {
38 BANDWIDTH_AVERAGE_NUMBER_OF_VALUES: 5 // Last 5 seconds to build average bandwidth 38 BANDWIDTH_AVERAGE_NUMBER_OF_VALUES: 5 // Last 5 seconds to build average bandwidth
39 } 39 }
40 40
41 private readonly buildWebSeedUrls: (file: VideoFile) => string[]
42
41 private readonly webtorrent = new WebTorrent({ 43 private readonly webtorrent = new WebTorrent({
42 tracker: { 44 tracker: {
43 rtcConfig: getRtcConfig() 45 rtcConfig: getRtcConfig()
@@ -57,6 +59,9 @@ class WebTorrentPlugin extends Plugin {
57 private isAutoResolutionObservation = false 59 private isAutoResolutionObservation = false
58 private playerRefusedP2P = false 60 private playerRefusedP2P = false
59 61
62 private requiresAuth: boolean
63 private videoFileToken: () => string
64
60 private torrentInfoInterval: any 65 private torrentInfoInterval: any
61 private autoQualityInterval: any 66 private autoQualityInterval: any
62 private addTorrentDelay: any 67 private addTorrentDelay: any
@@ -81,6 +86,11 @@ class WebTorrentPlugin extends Plugin {
81 this.savePlayerSrcFunction = this.player.src 86 this.savePlayerSrcFunction = this.player.src
82 this.playerElement = options.playerElement 87 this.playerElement = options.playerElement
83 88
89 this.requiresAuth = options.requiresAuth
90 this.videoFileToken = options.videoFileToken
91
92 this.buildWebSeedUrls = options.buildWebSeedUrls
93
84 this.player.ready(() => { 94 this.player.ready(() => {
85 const playerOptions = this.player.options_ 95 const playerOptions = this.player.options_
86 96
@@ -268,7 +278,8 @@ class WebTorrentPlugin extends Plugin {
268 return new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), { 278 return new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), {
269 max: 100 279 max: 100
270 }) 280 })
271 } 281 },
282 urlList: this.buildWebSeedUrls(this.currentVideoFile)
272 } 283 }
273 284
274 this.torrent = this.webtorrent.add(magnetOrTorrentUrl, torrentOptions, torrent => { 285 this.torrent = this.webtorrent.add(magnetOrTorrentUrl, torrentOptions, torrent => {
@@ -533,7 +544,12 @@ class WebTorrentPlugin extends Plugin {
533 // Enable error display now this is our last fallback 544 // Enable error display now this is our last fallback
534 this.player.one('error', () => this.player.peertube().displayFatalError()) 545 this.player.one('error', () => this.player.peertube().displayFatalError())
535 546
536 const httpUrl = this.currentVideoFile.fileUrl 547 let httpUrl = this.currentVideoFile.fileUrl
548
549 if (this.requiresAuth && this.videoFileToken) {
550 httpUrl = addQueryParams(httpUrl, { videoFileToken: this.videoFileToken() })
551 }
552
537 this.player.src = this.savePlayerSrcFunction 553 this.player.src = this.savePlayerSrcFunction
538 this.player.src(httpUrl) 554 this.player.src(httpUrl)
539 555
diff --git a/client/src/assets/player/types/manager-options.ts b/client/src/assets/player/types/manager-options.ts
index b4d9374c3..9da8fedf8 100644
--- a/client/src/assets/player/types/manager-options.ts
+++ b/client/src/assets/player/types/manager-options.ts
@@ -57,7 +57,7 @@ export interface CommonOptions extends CustomizationOptions {
57 captions: boolean 57 captions: boolean
58 58
59 videoViewUrl: string 59 videoViewUrl: string
60 authorizationHeader?: string 60 authorizationHeader?: () => string
61 61
62 metricsUrl: string 62 metricsUrl: string
63 63
@@ -77,6 +77,8 @@ export interface CommonOptions extends CustomizationOptions {
77 videoShortUUID: string 77 videoShortUUID: string
78 78
79 serverUrl: string 79 serverUrl: string
80 requiresAuth: boolean
81 videoFileToken: () => string
80 82
81 errorNotifier: (message: string) => void 83 errorNotifier: (message: string) => void
82} 84}
diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/assets/player/types/peertube-videojs-typings.ts
index 6df94992c..037c4b74b 100644
--- a/client/src/assets/player/types/peertube-videojs-typings.ts
+++ b/client/src/assets/player/types/peertube-videojs-typings.ts
@@ -95,7 +95,7 @@ type PeerTubePluginOptions = {
95 videoDuration: number 95 videoDuration: number
96 96
97 videoViewUrl: string 97 videoViewUrl: string
98 authorizationHeader?: string 98 authorizationHeader?: () => string
99 99
100 subtitle?: string 100 subtitle?: string
101 101
@@ -151,6 +151,11 @@ type WebtorrentPluginOptions = {
151 startTime: number | string 151 startTime: number | string
152 152
153 playerRefusedP2P: boolean 153 playerRefusedP2P: boolean
154
155 requiresAuth: boolean
156 videoFileToken: () => string
157
158 buildWebSeedUrls: (file: VideoFile) => string[]
154} 159}
155 160
156type P2PMediaLoaderPluginOptions = { 161type P2PMediaLoaderPluginOptions = {