diff options
Diffstat (limited to 'client')
9 files changed, 70 insertions, 42 deletions
diff --git a/client/e2e/src/suites-all/private-videos.e2e-spec.ts b/client/e2e/src/suites-all/private-videos.e2e-spec.ts index db3554659..a25208bb3 100644 --- a/client/e2e/src/suites-all/private-videos.e2e-spec.ts +++ b/client/e2e/src/suites-all/private-videos.e2e-spec.ts | |||
@@ -15,6 +15,7 @@ describe('Private videos all workflow', () => { | |||
15 | let playerPage: PlayerPage | 15 | let playerPage: PlayerPage |
16 | 16 | ||
17 | const internalVideoName = 'Internal E2E test' | 17 | const internalVideoName = 'Internal E2E test' |
18 | const internalHLSOnlyVideoName = 'Internal E2E test - HLS only' | ||
18 | 19 | ||
19 | beforeEach(async () => { | 20 | beforeEach(async () => { |
20 | videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari()) | 21 | videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari()) |
@@ -44,6 +45,13 @@ describe('Private videos all workflow', () => { | |||
44 | await checkCorrectlyPlay(playerPage) | 45 | await checkCorrectlyPlay(playerPage) |
45 | }) | 46 | }) |
46 | 47 | ||
48 | it('Should play an internal HLS only video', async () => { | ||
49 | await go(FIXTURE_URLS.INTERNAL_HLS_ONLY_VIDEO) | ||
50 | |||
51 | await videoWatchPage.waitWatchVideoName(internalHLSOnlyVideoName) | ||
52 | await checkCorrectlyPlay(playerPage) | ||
53 | }) | ||
54 | |||
47 | it('Should play an internal WebTorrent video in embed', async () => { | 55 | it('Should play an internal WebTorrent video in embed', async () => { |
48 | await go(FIXTURE_URLS.INTERNAL_EMBED_WEBTORRENT_VIDEO) | 56 | await go(FIXTURE_URLS.INTERNAL_EMBED_WEBTORRENT_VIDEO) |
49 | 57 | ||
@@ -57,4 +65,11 @@ describe('Private videos all workflow', () => { | |||
57 | await videoWatchPage.waitEmbedForDisplayed() | 65 | await videoWatchPage.waitEmbedForDisplayed() |
58 | await checkCorrectlyPlay(playerPage) | 66 | await checkCorrectlyPlay(playerPage) |
59 | }) | 67 | }) |
68 | |||
69 | it('Should play an internal HLS only video in embed', async () => { | ||
70 | await go(FIXTURE_URLS.INTERNAL_EMBED_HLS_ONLY_VIDEO) | ||
71 | |||
72 | await videoWatchPage.waitEmbedForDisplayed() | ||
73 | await checkCorrectlyPlay(playerPage) | ||
74 | }) | ||
60 | }) | 75 | }) |
diff --git a/client/e2e/src/utils/urls.ts b/client/e2e/src/utils/urls.ts index f91d9a048..cc0bdfbff 100644 --- a/client/e2e/src/utils/urls.ts +++ b/client/e2e/src/utils/urls.ts | |||
@@ -1,9 +1,13 @@ | |||
1 | const FIXTURE_URLS = { | 1 | const FIXTURE_URLS = { |
2 | INTERNAL_WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?mode=webtorrent&start=0', | 2 | INTERNAL_WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?mode=webtorrent&start=0', |
3 | INTERNAL_HLS_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?start=0', | 3 | INTERNAL_HLS_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?start=0', |
4 | |||
4 | INTERNAL_EMBED_WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?mode=webtorrent&start=0', | 5 | INTERNAL_EMBED_WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?mode=webtorrent&start=0', |
5 | INTERNAL_EMBED_HLS_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?start=0', | 6 | INTERNAL_EMBED_HLS_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?start=0', |
6 | 7 | ||
8 | INTERNAL_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/w/tKQmHcqdYZRdCszLUiWM3V?start=0', | ||
9 | INTERNAL_EMBED_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/videos/embed/tKQmHcqdYZRdCszLUiWM3V?start=0', | ||
10 | |||
7 | WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/w/122d093a-1ede-43bd-bd34-59d2931ffc5e', | 11 | WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/w/122d093a-1ede-43bd-bd34-59d2931ffc5e', |
8 | 12 | ||
9 | HLS_EMBED: 'https://peertube2.cpy.re/videos/embed/969bf103-7818-43b5-94a0-de159e13de50', | 13 | HLS_EMBED: 'https://peertube2.cpy.re/videos/embed/969bf103-7818-43b5-94a0-de159e13de50', |
diff --git a/client/src/app/+stats/video/video-stats.component.ts b/client/src/app/+stats/video/video-stats.component.ts index bfad4f823..18312ec33 100644 --- a/client/src/app/+stats/video/video-stats.component.ts +++ b/client/src/app/+stats/video/video-stats.component.ts | |||
@@ -175,7 +175,7 @@ export class VideoStatsComponent implements OnInit { | |||
175 | this.statsService.getOverallStats({ videoId: this.video.uuid, startDate: this.statsStartDate, endDate: this.statsEndDate }) | 175 | this.statsService.getOverallStats({ videoId: this.video.uuid, startDate: this.statsStartDate, endDate: this.statsEndDate }) |
176 | .subscribe({ | 176 | .subscribe({ |
177 | next: res => { | 177 | next: res => { |
178 | this.countries = res.countries.slice(0, 10).map(c => ({ | 178 | this.countries = res.countries.map(c => ({ |
179 | name: this.countryCodeToName(c.isoCode), | 179 | name: this.countryCodeToName(c.isoCode), |
180 | viewers: c.viewers | 180 | viewers: c.viewers |
181 | })) | 181 | })) |
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 497a97436..63e9fa8c8 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 | |||
@@ -32,6 +32,7 @@ export class HLSOptionsBuilder { | |||
32 | 32 | ||
33 | const p2pMediaLoader: P2PMediaLoaderPluginOptions = { | 33 | const p2pMediaLoader: P2PMediaLoaderPluginOptions = { |
34 | requiresAuth: commonOptions.requiresAuth, | 34 | requiresAuth: commonOptions.requiresAuth, |
35 | videoFileToken: commonOptions.videoFileToken, | ||
35 | 36 | ||
36 | redundancyUrlManager, | 37 | redundancyUrlManager, |
37 | type: 'application/x-mpegURL', | 38 | type: 'application/x-mpegURL', |
diff --git a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts index b608ee3e2..e6f525fea 100644 --- a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts +++ b/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts | |||
@@ -3,7 +3,7 @@ import videojs from 'video.js' | |||
3 | import { Events, Segment } from '@peertube/p2p-media-loader-core' | 3 | import { Events, Segment } from '@peertube/p2p-media-loader-core' |
4 | import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from '@peertube/p2p-media-loader-hlsjs' | 4 | import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from '@peertube/p2p-media-loader-hlsjs' |
5 | import { logger } from '@root-helpers/logger' | 5 | import { logger } from '@root-helpers/logger' |
6 | import { timeToInt } from '@shared/core-utils' | 6 | import { addQueryParams, timeToInt } from '@shared/core-utils' |
7 | import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types' | 7 | import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types' |
8 | import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' | 8 | import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' |
9 | 9 | ||
@@ -39,46 +39,37 @@ class P2pMediaLoaderPlugin extends Plugin { | |||
39 | super(player) | 39 | super(player) |
40 | 40 | ||
41 | this.options = options | 41 | this.options = options |
42 | this.startTime = timeToInt(options.startTime) | ||
42 | 43 | ||
43 | // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080 | 44 | // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080 |
44 | if (!(videojs as any).Html5Hlsjs) { | 45 | if (!(videojs as any).Html5Hlsjs) { |
45 | logger.warn('HLS.js does not seem to be supported. Try to fallback to built in HLS.') | 46 | if (player.canPlayType('application/vnd.apple.mpegurl')) { |
46 | 47 | this.fallbackToBuiltInIOS() | |
47 | let message: string | 48 | return |
48 | if (!player.canPlayType('application/vnd.apple.mpegurl')) { | ||
49 | message = 'Cannot fallback to built-in HLS' | ||
50 | } else if (options.requiresAuth) { | ||
51 | message = 'Video requires auth which is not compatible to build-in HLS player' | ||
52 | } | 49 | } |
53 | 50 | ||
54 | if (message) { | 51 | const message = 'HLS.js does not seem to be supported. Cannot fallback to built-in HLS' |
55 | logger.warn(message) | 52 | logger.warn(message) |
56 | 53 | ||
57 | const error: MediaError = { | 54 | const error: MediaError = { |
58 | code: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, | 55 | code: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, |
59 | message, | 56 | message, |
60 | MEDIA_ERR_ABORTED: MediaError.MEDIA_ERR_ABORTED, | 57 | MEDIA_ERR_ABORTED: MediaError.MEDIA_ERR_ABORTED, |
61 | MEDIA_ERR_DECODE: MediaError.MEDIA_ERR_DECODE, | 58 | MEDIA_ERR_DECODE: MediaError.MEDIA_ERR_DECODE, |
62 | MEDIA_ERR_NETWORK: MediaError.MEDIA_ERR_NETWORK, | 59 | MEDIA_ERR_NETWORK: MediaError.MEDIA_ERR_NETWORK, |
63 | MEDIA_ERR_SRC_NOT_SUPPORTED: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED | 60 | MEDIA_ERR_SRC_NOT_SUPPORTED: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED |
64 | } | ||
65 | |||
66 | player.ready(() => player.error(error)) | ||
67 | return | ||
68 | } | 61 | } |
69 | 62 | ||
70 | // Workaround to force video.js to not re create a video element | 63 | player.ready(() => player.error(error)) |
71 | (this.player as any).playerElIngest_ = this.player.el().parentNode | 64 | return |
72 | } else { | ||
73 | // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080 | ||
74 | (videojs as any).Html5Hlsjs.addHook('beforeinitialize', (videojsPlayer: any, hlsjs: any) => { | ||
75 | this.hlsjs = hlsjs | ||
76 | }) | ||
77 | |||
78 | initVideoJsContribHlsJsPlayer(player) | ||
79 | } | 65 | } |
80 | 66 | ||
81 | this.startTime = timeToInt(options.startTime) | 67 | // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080 |
68 | (videojs as any).Html5Hlsjs.addHook('beforeinitialize', (_videojsPlayer: any, hlsjs: any) => { | ||
69 | this.hlsjs = hlsjs | ||
70 | }) | ||
71 | |||
72 | initVideoJsContribHlsJsPlayer(player) | ||
82 | 73 | ||
83 | player.src({ | 74 | player.src({ |
84 | type: options.type, | 75 | type: options.type, |
@@ -88,9 +79,7 @@ class P2pMediaLoaderPlugin extends Plugin { | |||
88 | player.ready(() => { | 79 | player.ready(() => { |
89 | this.initializeCore() | 80 | this.initializeCore() |
90 | 81 | ||
91 | if ((videojs as any).Html5Hlsjs) { | 82 | this.initializePlugin() |
92 | this.initializePlugin() | ||
93 | } | ||
94 | }) | 83 | }) |
95 | } | 84 | } |
96 | 85 | ||
@@ -199,6 +188,25 @@ class P2pMediaLoaderPlugin extends Plugin { | |||
199 | private arraySum (data: number[]) { | 188 | private arraySum (data: number[]) { |
200 | return data.reduce((a: number, b: number) => a + b, 0) | 189 | return data.reduce((a: number, b: number) => a + b, 0) |
201 | } | 190 | } |
191 | |||
192 | private fallbackToBuiltInIOS () { | ||
193 | logger.info('HLS.js does not seem to be supported. Fallback to built-in HLS.'); | ||
194 | |||
195 | // Workaround to force video.js to not re create a video element | ||
196 | (this.player as any).playerElIngest_ = this.player.el().parentNode | ||
197 | |||
198 | this.player.src({ | ||
199 | type: this.options.type, | ||
200 | src: addQueryParams(this.options.src, { | ||
201 | videoFileToken: this.options.videoFileToken(), | ||
202 | reinjectVideoFileToken: 'true' | ||
203 | }) | ||
204 | }) | ||
205 | |||
206 | this.player.ready(() => { | ||
207 | this.initializeCore() | ||
208 | }) | ||
209 | } | ||
202 | } | 210 | } |
203 | 211 | ||
204 | videojs.registerPlugin('p2pMediaLoader', P2pMediaLoaderPlugin) | 212 | videojs.registerPlugin('p2pMediaLoader', P2pMediaLoaderPlugin) |
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 a7ee91950..3c76d63f7 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,6 +2,7 @@ import { basename } from 'path' | |||
2 | import { Segment } from '@peertube/p2p-media-loader-core' | 2 | import { Segment } from '@peertube/p2p-media-loader-core' |
3 | import { logger } from '@root-helpers/logger' | 3 | import { logger } from '@root-helpers/logger' |
4 | import { wait } from '@root-helpers/utils' | 4 | import { wait } from '@root-helpers/utils' |
5 | import { removeQueryParams } from '@shared/core-utils' | ||
5 | import { isSameOrigin } from '../common' | 6 | import { isSameOrigin } from '../common' |
6 | 7 | ||
7 | type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } } | 8 | type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } } |
@@ -24,7 +25,7 @@ function segmentValidatorFactory (options: { | |||
24 | // Wait for hash generation from the server | 25 | // Wait for hash generation from the server |
25 | if (isLive) await wait(1000) | 26 | if (isLive) await wait(1000) |
26 | 27 | ||
27 | const filename = basename(segment.url) | 28 | const filename = basename(removeQueryParams(segment.url)) |
28 | 29 | ||
29 | const segmentValue = (await segmentsJSON)[filename] | 30 | const segmentValue = (await segmentsJSON)[filename] |
30 | 31 | ||
diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/assets/player/types/peertube-videojs-typings.ts index 3d9d5270e..c60154f3b 100644 --- a/client/src/assets/player/types/peertube-videojs-typings.ts +++ b/client/src/assets/player/types/peertube-videojs-typings.ts | |||
@@ -168,6 +168,7 @@ type P2PMediaLoaderPluginOptions = { | |||
168 | loader: P2PMediaLoader | 168 | loader: P2PMediaLoader |
169 | 169 | ||
170 | requiresAuth: boolean | 170 | requiresAuth: boolean |
171 | videoFileToken: () => string | ||
171 | } | 172 | } |
172 | 173 | ||
173 | export type P2PMediaLoader = { | 174 | export type P2PMediaLoader = { |
diff --git a/client/src/sass/player/offline-notification.scss b/client/src/sass/player/offline-notification.scss index 2108c2e30..450c95bbc 100644 --- a/client/src/sass/player/offline-notification.scss +++ b/client/src/sass/player/offline-notification.scss | |||
@@ -14,9 +14,3 @@ $height: 40px; | |||
14 | justify-content: center; | 14 | justify-content: center; |
15 | align-items: center; | 15 | align-items: center; |
16 | } | 16 | } |
17 | |||
18 | .vjs-modal-dialog | ||
19 | .vjs-modal-dialog-content, | ||
20 | .video-js .vjs-modal-dialog { | ||
21 | top: $height; | ||
22 | } | ||
diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss index d4c43ff68..4df8dbaf0 100644 --- a/client/src/sass/player/peertube-skin.scss +++ b/client/src/sass/player/peertube-skin.scss | |||
@@ -202,6 +202,10 @@ body { | |||
202 | } | 202 | } |
203 | } | 203 | } |
204 | 204 | ||
205 | .vjs-modal-dialog-content { | ||
206 | padding-top: 40px !important; | ||
207 | } | ||
208 | |||
205 | // Error display disabled | 209 | // Error display disabled |
206 | .vjs-error:not(.vjs-error-display-enabled) { | 210 | .vjs-error:not(.vjs-error-display-enabled) { |
207 | .vjs-custom-error-display { | 211 | .vjs-custom-error-display { |