diff options
author | Chocobozzz <me@florianbigard.com> | 2023-06-29 15:55:00 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-07-10 16:08:28 +0200 |
commit | a1bd2b77d99cec5c27d38501f5f12f9dc339de17 (patch) | |
tree | 58b4666297e0b832a52f962541498af61319110a /client/src/standalone/videos | |
parent | 8ef866071f8109719e68647141d4c9e138438585 (diff) | |
download | PeerTube-a1bd2b77d99cec5c27d38501f5f12f9dc339de17.tar.gz PeerTube-a1bd2b77d99cec5c27d38501f5f12f9dc339de17.tar.zst PeerTube-a1bd2b77d99cec5c27d38501f5f12f9dc339de17.zip |
Remove webtorrent support from client
Diffstat (limited to 'client/src/standalone/videos')
-rw-r--r-- | client/src/standalone/videos/embed-api.ts | 17 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.html | 8 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 225 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/index.ts | 2 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/player-html.ts | 19 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/player-options-builder.ts (renamed from client/src/standalone/videos/shared/player-manager-options.ts) | 261 |
6 files changed, 251 insertions, 281 deletions
diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts index a99f1edae..cdda122b2 100644 --- a/client/src/standalone/videos/embed-api.ts +++ b/client/src/standalone/videos/embed-api.ts | |||
@@ -72,15 +72,12 @@ export class PeerTubeEmbedApi { | |||
72 | private setResolution (resolutionId: number) { | 72 | private setResolution (resolutionId: number) { |
73 | logger.info(`Set resolution ${resolutionId}`) | 73 | logger.info(`Set resolution ${resolutionId}`) |
74 | 74 | ||
75 | if (this.isWebtorrent()) { | 75 | if (this.isWebVideo() && resolutionId === -1) { |
76 | if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionPossible() === false) return | 76 | logger.error('Auto resolution cannot be set in web video player mode') |
77 | |||
78 | this.embed.player.webtorrent().changeQuality(resolutionId) | ||
79 | |||
80 | return | 77 | return |
81 | } | 78 | } |
82 | 79 | ||
83 | this.embed.player.p2pMediaLoader().getHLSJS().currentLevel = resolutionId | 80 | this.embed.player.peertubeResolutions().select({ id: resolutionId, fireCallback: true }) |
84 | } | 81 | } |
85 | 82 | ||
86 | private getCaptions (): PeerTubeTextTrack[] { | 83 | private getCaptions (): PeerTubeTextTrack[] { |
@@ -152,8 +149,8 @@ export class PeerTubeEmbedApi { | |||
152 | // --------------------------------------------------------------------------- | 149 | // --------------------------------------------------------------------------- |
153 | 150 | ||
154 | // PeerTube specific capabilities | 151 | // PeerTube specific capabilities |
155 | this.embed.player.peertubeResolutions().on('resolutionsAdded', () => this.loadResolutions()) | 152 | this.embed.player.peertubeResolutions().on('resolutions-added', () => this.loadResolutions()) |
156 | this.embed.player.peertubeResolutions().on('resolutionChanged', () => this.loadResolutions()) | 153 | this.embed.player.peertubeResolutions().on('resolutions-changed', () => this.loadResolutions()) |
157 | 154 | ||
158 | this.loadResolutions() | 155 | this.loadResolutions() |
159 | 156 | ||
@@ -193,7 +190,7 @@ export class PeerTubeEmbedApi { | |||
193 | }) | 190 | }) |
194 | } | 191 | } |
195 | 192 | ||
196 | private isWebtorrent () { | 193 | private isWebVideo () { |
197 | return !!this.embed.player.webtorrent | 194 | return !!this.embed.player.webVideo |
198 | } | 195 | } |
199 | } | 196 | } |
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html index a74bb4cee..e2dc02b60 100644 --- a/client/src/standalone/videos/embed.html +++ b/client/src/standalone/videos/embed.html | |||
@@ -44,11 +44,11 @@ | |||
44 | <div id="video-password-block"> | 44 | <div id="video-password-block"> |
45 | <!-- eslint-disable-next-line @angular-eslint/template/elements-content --> | 45 | <!-- eslint-disable-next-line @angular-eslint/template/elements-content --> |
46 | <h1 id="video-password-title"></h1> | 46 | <h1 id="video-password-title"></h1> |
47 | 47 | ||
48 | <div id="video-password-content"></div> | 48 | <div id="video-password-content"></div> |
49 | 49 | ||
50 | <form id="video-password-form"> | 50 | <form id="video-password-form"> |
51 | <input type="password" id="video-password-input" name="video-password" required> | 51 | <input type="password" id="video-password-input" name="video-password" autocomplete="user-password" required> |
52 | <button type="submit" id="video-password-submit"> </button> | 52 | <button type="submit" id="video-password-submit"> </button> |
53 | </form> | 53 | </form> |
54 | 54 | ||
@@ -60,8 +60,6 @@ | |||
60 | 60 | ||
61 | <div id="video-wrapper"></div> | 61 | <div id="video-wrapper"></div> |
62 | 62 | ||
63 | <div id="placeholder-preview"></div> | ||
64 | |||
65 | <script type="text/javascript"> | 63 | <script type="text/javascript"> |
66 | // Can be called in embed.ts | 64 | // Can be called in embed.ts |
67 | window.displayIncompatibleBrowser = function () { | 65 | window.displayIncompatibleBrowser = function () { |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 6e37ce193..5c68aaaf2 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -3,7 +3,6 @@ import '../../assets/player/shared/dock/peertube-dock-component' | |||
3 | import '../../assets/player/shared/dock/peertube-dock-plugin' | 3 | import '../../assets/player/shared/dock/peertube-dock-plugin' |
4 | import { PeerTubeServerError } from 'src/types' | 4 | import { PeerTubeServerError } from 'src/types' |
5 | import videojs from 'video.js' | 5 | import videojs from 'video.js' |
6 | import { peertubeTranslate } from '../../../../shared/core-utils/i18n' | ||
7 | import { | 6 | import { |
8 | HTMLServerConfig, | 7 | HTMLServerConfig, |
9 | ResultList, | 8 | ResultList, |
@@ -13,7 +12,7 @@ import { | |||
13 | VideoPlaylistElement, | 12 | VideoPlaylistElement, |
14 | VideoState | 13 | VideoState |
15 | } from '../../../../shared/models' | 14 | } from '../../../../shared/models' |
16 | import { PeertubePlayerManager } from '../../assets/player' | 15 | import { PeerTubePlayer } from '../../assets/player/peertube-player' |
17 | import { TranslationsManager } from '../../assets/player/translations-manager' | 16 | import { TranslationsManager } from '../../assets/player/translations-manager' |
18 | import { getParamString, logger, videoRequiresFileToken } from '../../root-helpers' | 17 | import { getParamString, logger, videoRequiresFileToken } from '../../root-helpers' |
19 | import { PeerTubeEmbedApi } from './embed-api' | 18 | import { PeerTubeEmbedApi } from './embed-api' |
@@ -21,7 +20,7 @@ import { | |||
21 | AuthHTTP, | 20 | AuthHTTP, |
22 | LiveManager, | 21 | LiveManager, |
23 | PeerTubePlugin, | 22 | PeerTubePlugin, |
24 | PlayerManagerOptions, | 23 | PlayerOptionsBuilder, |
25 | PlaylistFetcher, | 24 | PlaylistFetcher, |
26 | PlaylistTracker, | 25 | PlaylistTracker, |
27 | Translations, | 26 | Translations, |
@@ -36,17 +35,23 @@ export class PeerTubeEmbed { | |||
36 | config: HTMLServerConfig | 35 | config: HTMLServerConfig |
37 | 36 | ||
38 | private translationsPromise: Promise<{ [id: string]: string }> | 37 | private translationsPromise: Promise<{ [id: string]: string }> |
39 | private PeertubePlayerManagerModulePromise: Promise<any> | 38 | private PeerTubePlayerManagerModulePromise: Promise<any> |
40 | 39 | ||
41 | private readonly http: AuthHTTP | 40 | private readonly http: AuthHTTP |
42 | private readonly videoFetcher: VideoFetcher | 41 | private readonly videoFetcher: VideoFetcher |
43 | private readonly playlistFetcher: PlaylistFetcher | 42 | private readonly playlistFetcher: PlaylistFetcher |
44 | private readonly peertubePlugin: PeerTubePlugin | 43 | private readonly peertubePlugin: PeerTubePlugin |
45 | private readonly playerHTML: PlayerHTML | 44 | private readonly playerHTML: PlayerHTML |
46 | private readonly playerManagerOptions: PlayerManagerOptions | 45 | private readonly playerOptionsBuilder: PlayerOptionsBuilder |
47 | private readonly liveManager: LiveManager | 46 | private readonly liveManager: LiveManager |
48 | 47 | ||
48 | private peertubePlayer: PeerTubePlayer | ||
49 | |||
49 | private playlistTracker: PlaylistTracker | 50 | private playlistTracker: PlaylistTracker |
51 | |||
52 | private alreadyInitialized = false | ||
53 | private alreadyPlayed = false | ||
54 | |||
50 | private videoPassword: string | 55 | private videoPassword: string |
51 | private requiresPassword: boolean | 56 | private requiresPassword: boolean |
52 | 57 | ||
@@ -59,7 +64,7 @@ export class PeerTubeEmbed { | |||
59 | this.playlistFetcher = new PlaylistFetcher(this.http) | 64 | this.playlistFetcher = new PlaylistFetcher(this.http) |
60 | this.peertubePlugin = new PeerTubePlugin(this.http) | 65 | this.peertubePlugin = new PeerTubePlugin(this.http) |
61 | this.playerHTML = new PlayerHTML(videoWrapperId) | 66 | this.playerHTML = new PlayerHTML(videoWrapperId) |
62 | this.playerManagerOptions = new PlayerManagerOptions(this.playerHTML, this.videoFetcher, this.peertubePlugin) | 67 | this.playerOptionsBuilder = new PlayerOptionsBuilder(this.playerHTML, this.videoFetcher, this.peertubePlugin) |
63 | this.liveManager = new LiveManager(this.playerHTML) | 68 | this.liveManager = new LiveManager(this.playerHTML) |
64 | this.requiresPassword = false | 69 | this.requiresPassword = false |
65 | 70 | ||
@@ -81,14 +86,14 @@ export class PeerTubeEmbed { | |||
81 | } | 86 | } |
82 | 87 | ||
83 | getScope () { | 88 | getScope () { |
84 | return this.playerManagerOptions.getScope() | 89 | return this.playerOptionsBuilder.getScope() |
85 | } | 90 | } |
86 | 91 | ||
87 | // --------------------------------------------------------------------------- | 92 | // --------------------------------------------------------------------------- |
88 | 93 | ||
89 | async init () { | 94 | async init () { |
90 | this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language) | 95 | this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language) |
91 | this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager') | 96 | this.PeerTubePlayerManagerModulePromise = import('../../assets/player/peertube-player') |
92 | 97 | ||
93 | // Issue when we parsed config from HTML, fallback to API | 98 | // Issue when we parsed config from HTML, fallback to API |
94 | if (!this.config) { | 99 | if (!this.config) { |
@@ -102,7 +107,7 @@ export class PeerTubeEmbed { | |||
102 | 107 | ||
103 | if (!videoId) return | 108 | if (!videoId) return |
104 | 109 | ||
105 | return this.loadVideoAndBuildPlayer({ uuid: videoId, autoplayFromPreviousVideo: false, forceAutoplay: false }) | 110 | return this.loadVideoAndBuildPlayer({ uuid: videoId, forceAutoplay: false }) |
106 | } | 111 | } |
107 | 112 | ||
108 | private async initPlaylist () { | 113 | private async initPlaylist () { |
@@ -137,7 +142,7 @@ export class PeerTubeEmbed { | |||
137 | } | 142 | } |
138 | 143 | ||
139 | private initializeApi () { | 144 | private initializeApi () { |
140 | if (this.playerManagerOptions.hasAPIEnabled()) { | 145 | if (this.playerOptionsBuilder.hasAPIEnabled()) { |
141 | if (this.api) { | 146 | if (this.api) { |
142 | this.api.reInit() | 147 | this.api.reInit() |
143 | return | 148 | return |
@@ -159,7 +164,7 @@ export class PeerTubeEmbed { | |||
159 | 164 | ||
160 | this.playlistTracker.setCurrentElement(next) | 165 | this.playlistTracker.setCurrentElement(next) |
161 | 166 | ||
162 | return this.loadVideoAndBuildPlayer({ uuid: next.video.uuid, autoplayFromPreviousVideo: true, forceAutoplay: false }) | 167 | return this.loadVideoAndBuildPlayer({ uuid: next.video.uuid, forceAutoplay: false }) |
163 | } | 168 | } |
164 | 169 | ||
165 | async playPreviousPlaylistVideo () { | 170 | async playPreviousPlaylistVideo () { |
@@ -171,7 +176,7 @@ export class PeerTubeEmbed { | |||
171 | 176 | ||
172 | this.playlistTracker.setCurrentElement(previous) | 177 | this.playlistTracker.setCurrentElement(previous) |
173 | 178 | ||
174 | await this.loadVideoAndBuildPlayer({ uuid: previous.video.uuid, autoplayFromPreviousVideo: true, forceAutoplay: false }) | 179 | await this.loadVideoAndBuildPlayer({ uuid: previous.video.uuid, forceAutoplay: false }) |
175 | } | 180 | } |
176 | 181 | ||
177 | getCurrentPlaylistPosition () { | 182 | getCurrentPlaylistPosition () { |
@@ -182,10 +187,9 @@ export class PeerTubeEmbed { | |||
182 | 187 | ||
183 | private async loadVideoAndBuildPlayer (options: { | 188 | private async loadVideoAndBuildPlayer (options: { |
184 | uuid: string | 189 | uuid: string |
185 | autoplayFromPreviousVideo: boolean | ||
186 | forceAutoplay: boolean | 190 | forceAutoplay: boolean |
187 | }) { | 191 | }) { |
188 | const { uuid, autoplayFromPreviousVideo, forceAutoplay } = options | 192 | const { uuid, forceAutoplay } = options |
189 | 193 | ||
190 | try { | 194 | try { |
191 | const { | 195 | const { |
@@ -194,7 +198,7 @@ export class PeerTubeEmbed { | |||
194 | storyboardsPromise | 198 | storyboardsPromise |
195 | } = await this.videoFetcher.loadVideo({ videoId: uuid, videoPassword: this.videoPassword }) | 199 | } = await this.videoFetcher.loadVideo({ videoId: uuid, videoPassword: this.videoPassword }) |
196 | 200 | ||
197 | return this.buildVideoPlayer({ videoResponse, captionsPromise, storyboardsPromise, autoplayFromPreviousVideo, forceAutoplay }) | 201 | return this.buildVideoPlayer({ videoResponse, captionsPromise, storyboardsPromise, forceAutoplay }) |
198 | } catch (err) { | 202 | } catch (err) { |
199 | 203 | ||
200 | if (await this.handlePasswordError(err)) this.loadVideoAndBuildPlayer({ ...options }) | 204 | if (await this.handlePasswordError(err)) this.loadVideoAndBuildPlayer({ ...options }) |
@@ -206,20 +210,14 @@ export class PeerTubeEmbed { | |||
206 | videoResponse: Response | 210 | videoResponse: Response |
207 | storyboardsPromise: Promise<Response> | 211 | storyboardsPromise: Promise<Response> |
208 | captionsPromise: Promise<Response> | 212 | captionsPromise: Promise<Response> |
209 | autoplayFromPreviousVideo: boolean | ||
210 | forceAutoplay: boolean | 213 | forceAutoplay: boolean |
211 | }) { | 214 | }) { |
212 | const { videoResponse, captionsPromise, storyboardsPromise, autoplayFromPreviousVideo, forceAutoplay } = options | 215 | const { videoResponse, captionsPromise, storyboardsPromise, forceAutoplay } = options |
213 | |||
214 | this.resetPlayerElement() | ||
215 | 216 | ||
216 | const videoInfoPromise = videoResponse.json() | 217 | const videoInfoPromise = videoResponse.json() |
217 | .then(async (videoInfo: VideoDetails) => { | 218 | .then(async (videoInfo: VideoDetails) => { |
218 | this.playerManagerOptions.loadParams(this.config, videoInfo) | 219 | this.playerOptionsBuilder.loadParams(this.config, videoInfo) |
219 | 220 | ||
220 | if (!autoplayFromPreviousVideo && !this.playerManagerOptions.hasAutoplay()) { | ||
221 | this.playerHTML.buildPlaceholder(videoInfo) | ||
222 | } | ||
223 | const live = videoInfo.isLive | 221 | const live = videoInfo.isLive |
224 | ? await this.videoFetcher.loadLive(videoInfo) | 222 | ? await this.videoFetcher.loadLive(videoInfo) |
225 | : undefined | 223 | : undefined |
@@ -235,89 +233,75 @@ export class PeerTubeEmbed { | |||
235 | { video, live, videoFileToken }, | 233 | { video, live, videoFileToken }, |
236 | translations, | 234 | translations, |
237 | captionsResponse, | 235 | captionsResponse, |
238 | storyboardsResponse, | 236 | storyboardsResponse |
239 | PeertubePlayerManagerModule | ||
240 | ] = await Promise.all([ | 237 | ] = await Promise.all([ |
241 | videoInfoPromise, | 238 | videoInfoPromise, |
242 | this.translationsPromise, | 239 | this.translationsPromise, |
243 | captionsPromise, | 240 | captionsPromise, |
244 | storyboardsPromise, | 241 | storyboardsPromise, |
245 | this.PeertubePlayerManagerModulePromise | 242 | this.buildPlayerIfNeeded() |
246 | ]) | 243 | ]) |
247 | 244 | ||
248 | await this.peertubePlugin.loadPlugins(this.config, translations) | 245 | this.peertubePlayer.setPoster(window.location.origin + video.previewPath) |
246 | |||
247 | const playlist = this.playlistTracker | ||
248 | ? { | ||
249 | onVideoUpdate: (uuid: string) => this.loadVideoAndBuildPlayer({ uuid, forceAutoplay: false }), | ||
249 | 250 | ||
250 | const PlayerManager: typeof PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager | 251 | playlistTracker: this.playlistTracker, |
252 | playNext: () => this.playNextPlaylistVideo(), | ||
253 | playPrevious: () => this.playPreviousPlaylistVideo() | ||
254 | } | ||
255 | : undefined | ||
251 | 256 | ||
252 | const playerOptions = await this.playerManagerOptions.getPlayerOptions({ | 257 | const loadOptions = await this.playerOptionsBuilder.getPlayerLoadOptions({ |
253 | video, | 258 | video, |
254 | captionsResponse, | 259 | captionsResponse, |
255 | autoplayFromPreviousVideo, | ||
256 | translations, | 260 | translations, |
257 | serverConfig: this.config, | ||
258 | 261 | ||
259 | storyboardsResponse, | 262 | storyboardsResponse, |
260 | 263 | ||
261 | authorizationHeader: () => this.http.getHeaderTokenValue(), | ||
262 | videoFileToken: () => videoFileToken, | 264 | videoFileToken: () => videoFileToken, |
263 | videoPassword: () => this.videoPassword, | 265 | videoPassword: () => this.videoPassword, |
264 | requiresPassword: this.requiresPassword, | 266 | requiresPassword: this.requiresPassword, |
265 | 267 | ||
266 | onVideoUpdate: (uuid: string) => this.loadVideoAndBuildPlayer({ uuid, autoplayFromPreviousVideo: true, forceAutoplay: false }), | 268 | playlist, |
267 | |||
268 | playlistTracker: this.playlistTracker, | ||
269 | playNextPlaylistVideo: () => this.playNextPlaylistVideo(), | ||
270 | playPreviousPlaylistVideo: () => this.playPreviousPlaylistVideo(), | ||
271 | 269 | ||
272 | live, | 270 | live, |
273 | forceAutoplay | 271 | forceAutoplay, |
272 | alreadyPlayed: this.alreadyPlayed | ||
274 | }) | 273 | }) |
274 | await this.peertubePlayer.load(loadOptions) | ||
275 | 275 | ||
276 | this.player = await PlayerManager.initialize(this.playerManagerOptions.getMode(), playerOptions, (player: videojs.Player) => { | 276 | if (!this.alreadyInitialized) { |
277 | this.player = player | 277 | this.player = this.peertubePlayer.getPlayer(); |
278 | }) | ||
279 | 278 | ||
280 | this.player.on('customError', (event: any, data: any) => { | 279 | (window as any)['videojsPlayer'] = this.player |
281 | const message = data?.err?.message || '' | ||
282 | if (!message.includes('from xs param')) return | ||
283 | 280 | ||
284 | this.player.dispose() | 281 | this.buildCSS() |
285 | this.playerHTML.removePlayerElement() | 282 | this.initializeApi() |
286 | this.playerHTML.displayError('This video is not available because the remote instance is not responding.', translations) | 283 | } |
287 | }); | ||
288 | 284 | ||
289 | (window as any)['videojsPlayer'] = this.player | 285 | this.alreadyInitialized = true |
290 | 286 | ||
291 | this.buildCSS() | 287 | this.player.one('play', () => { |
292 | this.buildPlayerDock(video) | 288 | this.alreadyPlayed = true |
293 | this.initializeApi() | 289 | }) |
294 | 290 | ||
295 | this.playerHTML.removePlaceholder() | ||
296 | if (this.videoPassword) this.playerHTML.removeVideoPasswordBlock() | 291 | if (this.videoPassword) this.playerHTML.removeVideoPasswordBlock() |
297 | 292 | ||
298 | if (this.isPlaylistEmbed()) { | ||
299 | await this.buildPlayerPlaylistUpnext() | ||
300 | |||
301 | this.player.playlist().updateSelected() | ||
302 | |||
303 | this.player.on('stopped', () => { | ||
304 | this.playNextPlaylistVideo() | ||
305 | }) | ||
306 | } | ||
307 | |||
308 | if (video.isLive) { | 293 | if (video.isLive) { |
309 | this.liveManager.listenForChanges({ | 294 | this.liveManager.listenForChanges({ |
310 | video, | 295 | video, |
311 | onPublishedVideo: () => { | 296 | onPublishedVideo: () => { |
312 | this.liveManager.stopListeningForChanges(video) | 297 | this.liveManager.stopListeningForChanges(video) |
313 | this.loadVideoAndBuildPlayer({ uuid: video.uuid, autoplayFromPreviousVideo: false, forceAutoplay: true }) | 298 | this.loadVideoAndBuildPlayer({ uuid: video.uuid, forceAutoplay: true }) |
314 | } | 299 | } |
315 | }) | 300 | }) |
316 | 301 | ||
317 | if (video.state.id === VideoState.WAITING_FOR_LIVE || video.state.id === VideoState.LIVE_ENDED) { | 302 | if (video.state.id === VideoState.WAITING_FOR_LIVE || video.state.id === VideoState.LIVE_ENDED) { |
318 | this.liveManager.displayInfo({ state: video.state.id, translations }) | 303 | this.liveManager.displayInfo({ state: video.state.id, translations }) |
319 | 304 | this.peertubePlayer.disable() | |
320 | this.disablePlayer() | ||
321 | } else { | 305 | } else { |
322 | this.correctlyHandleLiveEnding(translations) | 306 | this.correctlyHandleLiveEnding(translations) |
323 | } | 307 | } |
@@ -326,74 +310,15 @@ export class PeerTubeEmbed { | |||
326 | this.peertubePlugin.getPluginsManager().runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video }) | 310 | this.peertubePlugin.getPluginsManager().runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video }) |
327 | } | 311 | } |
328 | 312 | ||
329 | private resetPlayerElement () { | ||
330 | if (this.player) { | ||
331 | this.player.dispose() | ||
332 | this.player = undefined | ||
333 | } | ||
334 | |||
335 | const playerElement = document.createElement('video') | ||
336 | playerElement.className = 'video-js vjs-peertube-skin' | ||
337 | playerElement.setAttribute('playsinline', 'true') | ||
338 | |||
339 | this.playerHTML.setPlayerElement(playerElement) | ||
340 | this.playerHTML.addPlayerElementToDOM() | ||
341 | } | ||
342 | |||
343 | private async buildPlayerPlaylistUpnext () { | ||
344 | const translations = await this.translationsPromise | ||
345 | |||
346 | this.player.upnext({ | ||
347 | timeout: 10000, // 10s | ||
348 | headText: peertubeTranslate('Up Next', translations), | ||
349 | cancelText: peertubeTranslate('Cancel', translations), | ||
350 | suspendedText: peertubeTranslate('Autoplay is suspended', translations), | ||
351 | getTitle: () => this.playlistTracker.nextVideoTitle(), | ||
352 | next: () => this.playNextPlaylistVideo(), | ||
353 | condition: () => !!this.playlistTracker.getNextPlaylistElement(), | ||
354 | suspended: () => false | ||
355 | }) | ||
356 | } | ||
357 | |||
358 | private buildPlayerDock (videoInfo: VideoDetails) { | ||
359 | if (!this.playerManagerOptions.hasControls()) return | ||
360 | |||
361 | // On webtorrent fallback, player may have been disposed | ||
362 | if (!this.player.player_) return | ||
363 | |||
364 | const title = this.playerManagerOptions.hasTitle() | ||
365 | ? videoInfo.name | ||
366 | : undefined | ||
367 | |||
368 | const description = this.playerManagerOptions.hasWarningTitle() && this.playerManagerOptions.hasP2PEnabled() | ||
369 | ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>' | ||
370 | : undefined | ||
371 | |||
372 | if (!title && !description) return | ||
373 | |||
374 | const availableAvatars = videoInfo.channel.avatars.filter(a => a.width < 50) | ||
375 | const avatar = availableAvatars.length !== 0 | ||
376 | ? availableAvatars[0] | ||
377 | : undefined | ||
378 | |||
379 | this.player.peertubeDock({ | ||
380 | title, | ||
381 | description, | ||
382 | avatarUrl: title && avatar | ||
383 | ? avatar.path | ||
384 | : undefined | ||
385 | }) | ||
386 | } | ||
387 | |||
388 | private buildCSS () { | 313 | private buildCSS () { |
389 | const body = document.getElementById('custom-css') | 314 | const body = document.getElementById('custom-css') |
390 | 315 | ||
391 | if (this.playerManagerOptions.hasBigPlayBackgroundColor()) { | 316 | if (this.playerOptionsBuilder.hasBigPlayBackgroundColor()) { |
392 | body.style.setProperty('--embedBigPlayBackgroundColor', this.playerManagerOptions.getBigPlayBackgroundColor()) | 317 | body.style.setProperty('--embedBigPlayBackgroundColor', this.playerOptionsBuilder.getBigPlayBackgroundColor()) |
393 | } | 318 | } |
394 | 319 | ||
395 | if (this.playerManagerOptions.hasForegroundColor()) { | 320 | if (this.playerOptionsBuilder.hasForegroundColor()) { |
396 | body.style.setProperty('--embedForegroundColor', this.playerManagerOptions.getForegroundColor()) | 321 | body.style.setProperty('--embedForegroundColor', this.playerOptionsBuilder.getForegroundColor()) |
397 | } | 322 | } |
398 | } | 323 | } |
399 | 324 | ||
@@ -415,23 +340,10 @@ export class PeerTubeEmbed { | |||
415 | // Display the live ended information | 340 | // Display the live ended information |
416 | this.liveManager.displayInfo({ state: VideoState.LIVE_ENDED, translations }) | 341 | this.liveManager.displayInfo({ state: VideoState.LIVE_ENDED, translations }) |
417 | 342 | ||
418 | this.disablePlayer() | 343 | this.peertubePlayer.disable() |
419 | }) | 344 | }) |
420 | } | 345 | } |
421 | 346 | ||
422 | private disablePlayer () { | ||
423 | if (this.player.isFullscreen()) { | ||
424 | this.player.exitFullscreen() | ||
425 | } | ||
426 | |||
427 | // Disable player | ||
428 | this.player.hasStarted(false) | ||
429 | this.player.removeClass('vjs-has-autoplay') | ||
430 | this.player.bigPlayButton.hide(); | ||
431 | |||
432 | (this.player.el() as HTMLElement).style.pointerEvents = 'none' | ||
433 | } | ||
434 | |||
435 | private async handlePasswordError (err: PeerTubeServerError) { | 347 | private async handlePasswordError (err: PeerTubeServerError) { |
436 | let incorrectPassword: boolean = null | 348 | let incorrectPassword: boolean = null |
437 | if (err.serverCode === ServerErrorCode.VIDEO_REQUIRES_PASSWORD) incorrectPassword = false | 349 | if (err.serverCode === ServerErrorCode.VIDEO_REQUIRES_PASSWORD) incorrectPassword = false |
@@ -447,6 +359,33 @@ export class PeerTubeEmbed { | |||
447 | return true | 359 | return true |
448 | } | 360 | } |
449 | 361 | ||
362 | private async buildPlayerIfNeeded () { | ||
363 | if (this.peertubePlayer) { | ||
364 | this.peertubePlayer.enable() | ||
365 | |||
366 | return | ||
367 | } | ||
368 | |||
369 | const playerElement = document.createElement('video') | ||
370 | playerElement.className = 'video-js vjs-peertube-skin' | ||
371 | playerElement.setAttribute('playsinline', 'true') | ||
372 | |||
373 | this.playerHTML.setPlayerElement(playerElement) | ||
374 | this.playerHTML.addPlayerElementToDOM() | ||
375 | |||
376 | const [ { PeerTubePlayer } ] = await Promise.all([ | ||
377 | this.PeerTubePlayerManagerModulePromise, | ||
378 | this.peertubePlugin.loadPlugins(this.config, await this.translationsPromise) | ||
379 | ]) | ||
380 | |||
381 | const constructorOptions = this.playerOptionsBuilder.getPlayerConstructorOptions({ | ||
382 | serverConfig: this.config, | ||
383 | authorizationHeader: () => this.http.getHeaderTokenValue() | ||
384 | }) | ||
385 | this.peertubePlayer = new PeerTubePlayer(constructorOptions) | ||
386 | |||
387 | this.player = this.peertubePlayer.getPlayer() | ||
388 | } | ||
450 | } | 389 | } |
451 | 390 | ||
452 | PeerTubeEmbed.main() | 391 | PeerTubeEmbed.main() |
diff --git a/client/src/standalone/videos/shared/index.ts b/client/src/standalone/videos/shared/index.ts index 928b8e270..dcc522ac6 100644 --- a/client/src/standalone/videos/shared/index.ts +++ b/client/src/standalone/videos/shared/index.ts | |||
@@ -2,7 +2,7 @@ export * from './auth-http' | |||
2 | export * from './peertube-plugin' | 2 | export * from './peertube-plugin' |
3 | export * from './live-manager' | 3 | export * from './live-manager' |
4 | export * from './player-html' | 4 | export * from './player-html' |
5 | export * from './player-manager-options' | 5 | export * from './player-options-builder' |
6 | export * from './playlist-fetcher' | 6 | export * from './playlist-fetcher' |
7 | export * from './playlist-tracker' | 7 | export * from './playlist-tracker' |
8 | export * from './translations' | 8 | export * from './translations' |
diff --git a/client/src/standalone/videos/shared/player-html.ts b/client/src/standalone/videos/shared/player-html.ts index a0846d9d7..0defa0d70 100644 --- a/client/src/standalone/videos/shared/player-html.ts +++ b/client/src/standalone/videos/shared/player-html.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import { peertubeTranslate } from '../../../../../shared/core-utils/i18n' | 1 | import { peertubeTranslate } from '../../../../../shared/core-utils/i18n' |
2 | import { VideoDetails } from '../../../../../shared/models' | ||
3 | import { logger } from '../../../root-helpers' | 2 | import { logger } from '../../../root-helpers' |
4 | import { Translations } from './translations' | 3 | import { Translations } from './translations' |
5 | 4 | ||
@@ -59,7 +58,6 @@ export class PlayerHTML { | |||
59 | const { incorrectPassword, translations } = options | 58 | const { incorrectPassword, translations } = options |
60 | return new Promise((resolve) => { | 59 | return new Promise((resolve) => { |
61 | 60 | ||
62 | this.removePlaceholder() | ||
63 | this.wrapperElement.style.display = 'none' | 61 | this.wrapperElement.style.display = 'none' |
64 | 62 | ||
65 | const translatedTitle = peertubeTranslate('This video is password protected', translations) | 63 | const translatedTitle = peertubeTranslate('This video is password protected', translations) |
@@ -107,19 +105,6 @@ export class PlayerHTML { | |||
107 | this.wrapperElement.style.display = 'block' | 105 | this.wrapperElement.style.display = 'block' |
108 | } | 106 | } |
109 | 107 | ||
110 | buildPlaceholder (video: VideoDetails) { | ||
111 | const placeholder = this.getPlaceholderElement() | ||
112 | |||
113 | const url = window.location.origin + video.previewPath | ||
114 | placeholder.style.backgroundImage = `url("${url}")` | ||
115 | placeholder.style.display = 'block' | ||
116 | } | ||
117 | |||
118 | removePlaceholder () { | ||
119 | const placeholder = this.getPlaceholderElement() | ||
120 | placeholder.style.display = 'none' | ||
121 | } | ||
122 | |||
123 | displayInformation (text: string, translations: Translations) { | 108 | displayInformation (text: string, translations: Translations) { |
124 | if (this.informationElement) this.removeInformation() | 109 | if (this.informationElement) this.removeInformation() |
125 | 110 | ||
@@ -137,10 +122,6 @@ export class PlayerHTML { | |||
137 | this.informationElement = undefined | 122 | this.informationElement = undefined |
138 | } | 123 | } |
139 | 124 | ||
140 | private getPlaceholderElement () { | ||
141 | return document.getElementById('placeholder-preview') | ||
142 | } | ||
143 | |||
144 | private removeElement (element: HTMLElement) { | 125 | private removeElement (element: HTMLElement) { |
145 | element.parentElement.removeChild(element) | 126 | element.parentElement.removeChild(element) |
146 | } | 127 | } |
diff --git a/client/src/standalone/videos/shared/player-manager-options.ts b/client/src/standalone/videos/shared/player-options-builder.ts index 3c7521bc2..8a4e32444 100644 --- a/client/src/standalone/videos/shared/player-manager-options.ts +++ b/client/src/standalone/videos/shared/player-options-builder.ts | |||
@@ -10,7 +10,7 @@ import { | |||
10 | VideoState, | 10 | VideoState, |
11 | VideoStreamingPlaylistType | 11 | VideoStreamingPlaylistType |
12 | } from '../../../../../shared/models' | 12 | } from '../../../../../shared/models' |
13 | import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode, VideoJSCaption } from '../../../assets/player' | 13 | import { HLSOptions, PeerTubePlayerContructorOptions, PeerTubePlayerLoadOptions, PlayerMode, VideoJSCaption } from '../../../assets/player' |
14 | import { | 14 | import { |
15 | getBoolOrDefault, | 15 | getBoolOrDefault, |
16 | getParamString, | 16 | getParamString, |
@@ -27,7 +27,7 @@ import { PlaylistTracker } from './playlist-tracker' | |||
27 | import { Translations } from './translations' | 27 | import { Translations } from './translations' |
28 | import { VideoFetcher } from './video-fetcher' | 28 | import { VideoFetcher } from './video-fetcher' |
29 | 29 | ||
30 | export class PlayerManagerOptions { | 30 | export class PlayerOptionsBuilder { |
31 | private autoplay: boolean | 31 | private autoplay: boolean |
32 | 32 | ||
33 | private controls: boolean | 33 | private controls: boolean |
@@ -141,10 +141,10 @@ export class PlayerManagerOptions { | |||
141 | 141 | ||
142 | if (modeParam) { | 142 | if (modeParam) { |
143 | if (modeParam === 'p2p-media-loader') this.mode = 'p2p-media-loader' | 143 | if (modeParam === 'p2p-media-loader') this.mode = 'p2p-media-loader' |
144 | else this.mode = 'webtorrent' | 144 | else this.mode = 'web-video' |
145 | } else { | 145 | } else { |
146 | if (Array.isArray(video.streamingPlaylists) && video.streamingPlaylists.length !== 0) this.mode = 'p2p-media-loader' | 146 | if (Array.isArray(video.streamingPlaylists) && video.streamingPlaylists.length !== 0) this.mode = 'p2p-media-loader' |
147 | else this.mode = 'webtorrent' | 147 | else this.mode = 'web-video' |
148 | } | 148 | } |
149 | } catch (err) { | 149 | } catch (err) { |
150 | logger.error('Cannot get params from URL.', err) | 150 | logger.error('Cannot get params from URL.', err) |
@@ -153,7 +153,47 @@ export class PlayerManagerOptions { | |||
153 | 153 | ||
154 | // --------------------------------------------------------------------------- | 154 | // --------------------------------------------------------------------------- |
155 | 155 | ||
156 | async getPlayerOptions (options: { | 156 | getPlayerConstructorOptions (options: { |
157 | serverConfig: HTMLServerConfig | ||
158 | authorizationHeader: () => string | ||
159 | }): PeerTubePlayerContructorOptions { | ||
160 | const { serverConfig, authorizationHeader } = options | ||
161 | |||
162 | return { | ||
163 | controls: this.controls, | ||
164 | controlBar: this.controlBar, | ||
165 | |||
166 | muted: this.muted, | ||
167 | loop: this.loop, | ||
168 | |||
169 | playbackRate: this.playbackRate, | ||
170 | |||
171 | inactivityTimeout: 2500, | ||
172 | videoViewIntervalMs: 5000, | ||
173 | metricsUrl: window.location.origin + '/api/v1/metrics/playback', | ||
174 | |||
175 | authorizationHeader, | ||
176 | |||
177 | playerElement: () => this.playerHTML.getPlayerElement(), | ||
178 | enableHotkeys: true, | ||
179 | |||
180 | peertubeLink: () => this.peertubeLink, | ||
181 | instanceName: serverConfig.instance.name, | ||
182 | |||
183 | theaterButton: false, | ||
184 | |||
185 | serverUrl: window.location.origin, | ||
186 | language: navigator.language, | ||
187 | |||
188 | pluginsManager: this.peertubePlugin.getPluginsManager(), | ||
189 | |||
190 | errorNotifier: () => { | ||
191 | // Empty, we don't have a notifier in the embed | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | |||
196 | async getPlayerLoadOptions (options: { | ||
157 | video: VideoDetails | 197 | video: VideoDetails |
158 | captionsResponse: Response | 198 | captionsResponse: Response |
159 | 199 | ||
@@ -161,39 +201,35 @@ export class PlayerManagerOptions { | |||
161 | 201 | ||
162 | live?: LiveVideo | 202 | live?: LiveVideo |
163 | 203 | ||
204 | alreadyPlayed: boolean | ||
164 | forceAutoplay: boolean | 205 | forceAutoplay: boolean |
165 | 206 | ||
166 | authorizationHeader: () => string | ||
167 | videoFileToken: () => string | 207 | videoFileToken: () => string |
168 | 208 | ||
169 | videoPassword: () => string | 209 | videoPassword: () => string |
170 | requiresPassword: boolean | 210 | requiresPassword: boolean |
171 | 211 | ||
172 | serverConfig: HTMLServerConfig | ||
173 | |||
174 | autoplayFromPreviousVideo: boolean | ||
175 | |||
176 | translations: Translations | 212 | translations: Translations |
177 | 213 | ||
178 | playlistTracker?: PlaylistTracker | 214 | playlist?: { |
179 | playNextPlaylistVideo?: () => any | 215 | playlistTracker: PlaylistTracker |
180 | playPreviousPlaylistVideo?: () => any | 216 | playNext: () => any |
181 | onVideoUpdate?: (uuid: string) => any | 217 | playPrevious: () => any |
182 | }) { | 218 | onVideoUpdate: (uuid: string) => any |
219 | } | ||
220 | }): Promise<PeerTubePlayerLoadOptions> { | ||
183 | const { | 221 | const { |
184 | video, | 222 | video, |
185 | captionsResponse, | 223 | captionsResponse, |
186 | autoplayFromPreviousVideo, | ||
187 | videoFileToken, | 224 | videoFileToken, |
188 | videoPassword, | 225 | videoPassword, |
189 | requiresPassword, | 226 | requiresPassword, |
190 | translations, | 227 | translations, |
228 | alreadyPlayed, | ||
191 | forceAutoplay, | 229 | forceAutoplay, |
192 | playlistTracker, | 230 | playlist, |
193 | live, | 231 | live, |
194 | storyboardsResponse, | 232 | storyboardsResponse |
195 | authorizationHeader, | ||
196 | serverConfig | ||
197 | } = options | 233 | } = options |
198 | 234 | ||
199 | const [ videoCaptions, storyboard ] = await Promise.all([ | 235 | const [ videoCaptions, storyboard ] = await Promise.all([ |
@@ -201,88 +237,56 @@ export class PlayerManagerOptions { | |||
201 | this.buildStoryboard(storyboardsResponse) | 237 | this.buildStoryboard(storyboardsResponse) |
202 | ]) | 238 | ]) |
203 | 239 | ||
204 | const playerOptions: PeertubePlayerManagerOptions = { | 240 | return { |
205 | common: { | 241 | mode: this.mode, |
206 | // Autoplay in playlist mode | ||
207 | autoplay: autoplayFromPreviousVideo ? true : this.autoplay, | ||
208 | forceAutoplay, | ||
209 | |||
210 | controls: this.controls, | ||
211 | controlBar: this.controlBar, | ||
212 | |||
213 | muted: this.muted, | ||
214 | loop: this.loop, | ||
215 | |||
216 | p2pEnabled: this.p2pEnabled, | ||
217 | |||
218 | captions: videoCaptions.length !== 0, | ||
219 | subtitle: this.subtitle, | ||
220 | 242 | ||
221 | storyboard, | 243 | autoplay: forceAutoplay || alreadyPlayed || this.autoplay, |
244 | forceAutoplay, | ||
222 | 245 | ||
223 | startTime: playlistTracker | 246 | p2pEnabled: this.p2pEnabled, |
224 | ? playlistTracker.getCurrentElement().startTimestamp | ||
225 | : this.startTime, | ||
226 | stopTime: playlistTracker | ||
227 | ? playlistTracker.getCurrentElement().stopTimestamp | ||
228 | : this.stopTime, | ||
229 | 247 | ||
230 | playbackRate: this.playbackRate, | 248 | subtitle: this.subtitle, |
231 | 249 | ||
232 | videoCaptions, | 250 | storyboard, |
233 | inactivityTimeout: 2500, | ||
234 | videoViewUrl: this.videoFetcher.getVideoViewsUrl(video.uuid), | ||
235 | videoViewIntervalMs: 5000, | ||
236 | metricsUrl: window.location.origin + '/api/v1/metrics/playback', | ||
237 | 251 | ||
238 | videoShortUUID: video.shortUUID, | 252 | startTime: playlist |
239 | videoUUID: video.uuid, | 253 | ? playlist.playlistTracker.getCurrentElement().startTimestamp |
254 | : this.startTime, | ||
255 | stopTime: playlist | ||
256 | ? playlist.playlistTracker.getCurrentElement().stopTimestamp | ||
257 | : this.stopTime, | ||
240 | 258 | ||
241 | playerElement: this.playerHTML.getPlayerElement(), | 259 | videoCaptions, |
242 | onPlayerElementChange: (element: HTMLVideoElement) => { | 260 | videoViewUrl: this.videoFetcher.getVideoViewsUrl(video.uuid), |
243 | this.playerHTML.setPlayerElement(element) | ||
244 | }, | ||
245 | 261 | ||
246 | videoDuration: video.duration, | 262 | videoShortUUID: video.shortUUID, |
247 | enableHotkeys: true, | 263 | videoUUID: video.uuid, |
248 | 264 | ||
249 | peertubeLink: this.peertubeLink, | 265 | duration: video.duration, |
250 | instanceName: serverConfig.instance.name, | ||
251 | 266 | ||
252 | poster: window.location.origin + video.previewPath, | 267 | poster: window.location.origin + video.previewPath, |
253 | theaterButton: false, | ||
254 | 268 | ||
255 | serverUrl: window.location.origin, | 269 | embedUrl: window.location.origin + video.embedPath, |
256 | language: navigator.language, | 270 | embedTitle: video.name, |
257 | embedUrl: window.location.origin + video.embedPath, | ||
258 | embedTitle: video.name, | ||
259 | 271 | ||
260 | requiresUserAuth: videoRequiresUserAuth(video), | 272 | requiresUserAuth: videoRequiresUserAuth(video), |
261 | authorizationHeader, | 273 | videoFileToken, |
262 | videoFileToken, | ||
263 | 274 | ||
264 | requiresPassword, | 275 | requiresPassword, |
265 | videoPassword, | 276 | videoPassword, |
266 | 277 | ||
267 | errorNotifier: () => { | 278 | ...this.buildLiveOptions(video, live), |
268 | // Empty, we don't have a notifier in the embed | ||
269 | }, | ||
270 | 279 | ||
271 | ...this.buildLiveOptions(video, live), | 280 | ...this.buildPlaylistOptions(playlist), |
272 | 281 | ||
273 | ...this.buildPlaylistOptions(options) | 282 | dock: this.buildDockOptions(video), |
274 | }, | ||
275 | 283 | ||
276 | webtorrent: { | 284 | webVideo: { |
277 | videoFiles: video.files | 285 | videoFiles: video.files |
278 | }, | 286 | }, |
279 | 287 | ||
280 | ...this.buildP2PMediaLoaderOptions(video), | 288 | hls: this.buildHLSOptions(video) |
281 | |||
282 | pluginsManager: this.peertubePlugin.getPluginsManager() | ||
283 | } | 289 | } |
284 | |||
285 | return playerOptions | ||
286 | } | 290 | } |
287 | 291 | ||
288 | private buildLiveOptions (video: VideoDetails, live: LiveVideo) { | 292 | private buildLiveOptions (video: VideoDetails, live: LiveVideo) { |
@@ -308,15 +312,27 @@ export class PlayerManagerOptions { | |||
308 | } | 312 | } |
309 | } | 313 | } |
310 | 314 | ||
311 | private buildPlaylistOptions (options: { | 315 | private buildPlaylistOptions (options?: { |
312 | playlistTracker?: PlaylistTracker | 316 | playlistTracker: PlaylistTracker |
313 | playNextPlaylistVideo?: () => any | 317 | playNext: () => any |
314 | playPreviousPlaylistVideo?: () => any | 318 | playPrevious: () => any |
315 | onVideoUpdate?: (uuid: string) => any | 319 | onVideoUpdate: (uuid: string) => any |
316 | }) { | 320 | }) { |
317 | const { playlistTracker, playNextPlaylistVideo, playPreviousPlaylistVideo, onVideoUpdate } = options | 321 | if (!options) { |
322 | return { | ||
323 | nextVideo: { | ||
324 | enabled: false, | ||
325 | displayControlBarButton: false, | ||
326 | getVideoTitle: () => '' | ||
327 | }, | ||
328 | previousVideo: { | ||
329 | enabled: false, | ||
330 | displayControlBarButton: false | ||
331 | } | ||
332 | } | ||
333 | } | ||
318 | 334 | ||
319 | if (!playlistTracker) return {} | 335 | const { playlistTracker, playNext, playPrevious, onVideoUpdate } = options |
320 | 336 | ||
321 | return { | 337 | return { |
322 | playlist: { | 338 | playlist: { |
@@ -332,27 +348,37 @@ export class PlayerManagerOptions { | |||
332 | } | 348 | } |
333 | }, | 349 | }, |
334 | 350 | ||
335 | nextVideo: () => playNextPlaylistVideo(), | 351 | previousVideo: { |
336 | hasNextVideo: () => playlistTracker.hasNextPlaylistElement(), | 352 | enabled: playlistTracker.hasPreviousPlaylistElement(), |
353 | handler: () => playPrevious(), | ||
354 | displayControlBarButton: true | ||
355 | }, | ||
356 | |||
357 | nextVideo: { | ||
358 | enabled: playlistTracker.hasNextPlaylistElement(), | ||
359 | handler: () => playNext(), | ||
360 | getVideoTitle: () => playlistTracker.getNextPlaylistElement()?.video?.name, | ||
361 | displayControlBarButton: true | ||
362 | }, | ||
337 | 363 | ||
338 | previousVideo: () => playPreviousPlaylistVideo(), | 364 | upnext: { |
339 | hasPreviousVideo: () => playlistTracker.hasPreviousPlaylistElement() | 365 | isEnabled: () => true, |
366 | isSuspended: () => false, | ||
367 | timeout: 0 | ||
368 | } | ||
340 | } | 369 | } |
341 | } | 370 | } |
342 | 371 | ||
343 | private buildP2PMediaLoaderOptions (video: VideoDetails) { | 372 | private buildHLSOptions (video: VideoDetails): HLSOptions { |
344 | if (this.mode !== 'p2p-media-loader') return {} | ||
345 | |||
346 | const hlsPlaylist = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) | 373 | const hlsPlaylist = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) |
374 | if (!hlsPlaylist) return undefined | ||
347 | 375 | ||
348 | return { | 376 | return { |
349 | p2pMediaLoader: { | 377 | playlistUrl: hlsPlaylist.playlistUrl, |
350 | playlistUrl: hlsPlaylist.playlistUrl, | 378 | segmentsSha256Url: hlsPlaylist.segmentsSha256Url, |
351 | segmentsSha256Url: hlsPlaylist.segmentsSha256Url, | 379 | redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl), |
352 | redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl), | 380 | trackerAnnounce: video.trackerUrls, |
353 | trackerAnnounce: video.trackerUrls, | 381 | videoFiles: hlsPlaylist.files |
354 | videoFiles: hlsPlaylist.files | ||
355 | } as P2PMediaLoaderOptions | ||
356 | } | 382 | } |
357 | } | 383 | } |
358 | 384 | ||
@@ -374,6 +400,35 @@ export class PlayerManagerOptions { | |||
374 | 400 | ||
375 | // --------------------------------------------------------------------------- | 401 | // --------------------------------------------------------------------------- |
376 | 402 | ||
403 | private buildDockOptions (videoInfo: VideoDetails) { | ||
404 | if (!this.hasControls()) return undefined | ||
405 | |||
406 | const title = this.hasTitle() | ||
407 | ? videoInfo.name | ||
408 | : undefined | ||
409 | |||
410 | const description = this.hasWarningTitle() && this.hasP2PEnabled() | ||
411 | ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>' | ||
412 | : undefined | ||
413 | |||
414 | if (!title && !description) return | ||
415 | |||
416 | const availableAvatars = videoInfo.channel.avatars.filter(a => a.width < 50) | ||
417 | const avatar = availableAvatars.length !== 0 | ||
418 | ? availableAvatars[0] | ||
419 | : undefined | ||
420 | |||
421 | return { | ||
422 | title, | ||
423 | description, | ||
424 | avatarUrl: title && avatar | ||
425 | ? avatar.path | ||
426 | : undefined | ||
427 | } | ||
428 | } | ||
429 | |||
430 | // --------------------------------------------------------------------------- | ||
431 | |||
377 | private isP2PEnabled (config: HTMLServerConfig, video: Video) { | 432 | private isP2PEnabled (config: HTMLServerConfig, video: Video) { |
378 | const userP2PEnabled = getBoolOrDefault( | 433 | const userP2PEnabled = getBoolOrDefault( |
379 | peertubeLocalStorage.getItem(UserLocalStorageKeys.P2P_ENABLED), | 434 | peertubeLocalStorage.getItem(UserLocalStorageKeys.P2P_ENABLED), |