]>
Commit | Line | Data |
---|---|---|
f1a0f3b7 C |
1 | import { peertubeTranslate } from '../../../../../shared/core-utils/i18n' |
2 | import { | |
3 | HTMLServerConfig, | |
4 | LiveVideo, | |
5 | Video, | |
6 | VideoCaption, | |
7 | VideoDetails, | |
8 | VideoPlaylistElement, | |
aeb112ed | 9 | VideoState, |
f1a0f3b7 C |
10 | VideoStreamingPlaylistType |
11 | } from '../../../../../shared/models' | |
12 | import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode, VideoJSCaption } from '../../../assets/player' | |
13 | import { | |
14 | getBoolOrDefault, | |
15 | getParamString, | |
16 | getParamToggle, | |
17 | isP2PEnabled, | |
42b40636 | 18 | logger, |
f1a0f3b7 | 19 | peertubeLocalStorage, |
3545e72c C |
20 | UserLocalStorageKeys, |
21 | videoRequiresAuth | |
f1a0f3b7 C |
22 | } from '../../../root-helpers' |
23 | import { PeerTubePlugin } from './peertube-plugin' | |
24 | import { PlayerHTML } from './player-html' | |
25 | import { PlaylistTracker } from './playlist-tracker' | |
26 | import { Translations } from './translations' | |
27 | import { VideoFetcher } from './video-fetcher' | |
28 | ||
29 | export class PlayerManagerOptions { | |
30 | private autoplay: boolean | |
31 | ||
32 | private controls: boolean | |
33 | private controlBar: boolean | |
34 | ||
35 | private muted: boolean | |
36 | private loop: boolean | |
37 | private subtitle: string | |
38 | private enableApi = false | |
39 | private startTime: number | string = 0 | |
40 | private stopTime: number | string | |
4fe7cde2 | 41 | private playbackRate: number | string |
f1a0f3b7 C |
42 | |
43 | private title: boolean | |
44 | private warningTitle: boolean | |
45 | private peertubeLink: boolean | |
46 | private p2pEnabled: boolean | |
47 | private bigPlayBackgroundColor: string | |
48 | private foregroundColor: string | |
49 | ||
50 | private mode: PlayerMode | |
51 | private scope = 'peertube' | |
52 | ||
53 | constructor ( | |
54 | private readonly playerHTML: PlayerHTML, | |
55 | private readonly videoFetcher: VideoFetcher, | |
56 | private readonly peertubePlugin: PeerTubePlugin | |
57 | ) {} | |
58 | ||
59 | hasAPIEnabled () { | |
60 | return this.enableApi | |
61 | } | |
62 | ||
63 | hasAutoplay () { | |
64 | return this.autoplay | |
65 | } | |
66 | ||
67 | hasControls () { | |
68 | return this.controls | |
69 | } | |
70 | ||
71 | hasTitle () { | |
72 | return this.title | |
73 | } | |
74 | ||
75 | hasWarningTitle () { | |
76 | return this.warningTitle | |
77 | } | |
78 | ||
79 | hasP2PEnabled () { | |
80 | return !!this.p2pEnabled | |
81 | } | |
82 | ||
83 | hasBigPlayBackgroundColor () { | |
84 | return !!this.bigPlayBackgroundColor | |
85 | } | |
86 | ||
87 | getBigPlayBackgroundColor () { | |
88 | return this.bigPlayBackgroundColor | |
89 | } | |
90 | ||
91 | hasForegroundColor () { | |
92 | return !!this.foregroundColor | |
93 | } | |
94 | ||
95 | getForegroundColor () { | |
96 | return this.foregroundColor | |
97 | } | |
98 | ||
99 | getMode () { | |
100 | return this.mode | |
101 | } | |
102 | ||
103 | getScope () { | |
104 | return this.scope | |
105 | } | |
106 | ||
107 | // --------------------------------------------------------------------------- | |
108 | ||
109 | loadParams (config: HTMLServerConfig, video: VideoDetails) { | |
110 | try { | |
111 | const params = new URL(window.location.toString()).searchParams | |
112 | ||
113 | this.autoplay = getParamToggle(params, 'autoplay', false) | |
aeb112ed C |
114 | // Disable auto play on live videos that are not streamed |
115 | if (video.state.id === VideoState.LIVE_ENDED || video.state.id === VideoState.WAITING_FOR_LIVE) { | |
116 | this.autoplay = false | |
117 | } | |
f1a0f3b7 C |
118 | |
119 | this.controls = getParamToggle(params, 'controls', true) | |
120 | this.controlBar = getParamToggle(params, 'controlBar', true) | |
121 | ||
122 | this.muted = getParamToggle(params, 'muted', undefined) | |
123 | this.loop = getParamToggle(params, 'loop', false) | |
124 | this.title = getParamToggle(params, 'title', true) | |
125 | this.enableApi = getParamToggle(params, 'api', this.enableApi) | |
126 | this.warningTitle = getParamToggle(params, 'warningTitle', true) | |
127 | this.peertubeLink = getParamToggle(params, 'peertubeLink', true) | |
128 | this.p2pEnabled = getParamToggle(params, 'p2p', this.isP2PEnabled(config, video)) | |
129 | ||
130 | this.scope = getParamString(params, 'scope', this.scope) | |
131 | this.subtitle = getParamString(params, 'subtitle') | |
132 | this.startTime = getParamString(params, 'start') | |
133 | this.stopTime = getParamString(params, 'stop') | |
4fe7cde2 | 134 | this.playbackRate = getParamString(params, 'playbackRate') |
f1a0f3b7 C |
135 | |
136 | this.bigPlayBackgroundColor = getParamString(params, 'bigPlayBackgroundColor') | |
137 | this.foregroundColor = getParamString(params, 'foregroundColor') | |
138 | ||
139 | const modeParam = getParamString(params, 'mode') | |
140 | ||
141 | if (modeParam) { | |
142 | if (modeParam === 'p2p-media-loader') this.mode = 'p2p-media-loader' | |
143 | else this.mode = 'webtorrent' | |
144 | } else { | |
145 | if (Array.isArray(video.streamingPlaylists) && video.streamingPlaylists.length !== 0) this.mode = 'p2p-media-loader' | |
146 | else this.mode = 'webtorrent' | |
147 | } | |
148 | } catch (err) { | |
42b40636 | 149 | logger.error('Cannot get params from URL.', err) |
f1a0f3b7 C |
150 | } |
151 | } | |
152 | ||
153 | // --------------------------------------------------------------------------- | |
154 | ||
155 | async getPlayerOptions (options: { | |
156 | video: VideoDetails | |
157 | captionsResponse: Response | |
158 | live?: LiveVideo | |
159 | ||
59a643aa C |
160 | forceAutoplay: boolean |
161 | ||
3545e72c C |
162 | authorizationHeader: () => string |
163 | videoFileToken: () => string | |
164 | ||
bd2b51be C |
165 | serverConfig: HTMLServerConfig |
166 | ||
7dcd45a9 | 167 | autoplayFromPreviousVideo: boolean |
f1a0f3b7 C |
168 | |
169 | translations: Translations | |
170 | ||
171 | playlistTracker?: PlaylistTracker | |
172 | playNextPlaylistVideo?: () => any | |
173 | playPreviousPlaylistVideo?: () => any | |
174 | onVideoUpdate?: (uuid: string) => any | |
175 | }) { | |
176 | const { | |
177 | video, | |
178 | captionsResponse, | |
7dcd45a9 | 179 | autoplayFromPreviousVideo, |
3545e72c | 180 | videoFileToken, |
f1a0f3b7 | 181 | translations, |
59a643aa | 182 | forceAutoplay, |
f1a0f3b7 | 183 | playlistTracker, |
bd2b51be | 184 | live, |
3545e72c | 185 | authorizationHeader, |
bd2b51be | 186 | serverConfig |
f1a0f3b7 C |
187 | } = options |
188 | ||
189 | const videoCaptions = await this.buildCaptions(captionsResponse, translations) | |
190 | ||
191 | const playerOptions: PeertubePlayerManagerOptions = { | |
192 | common: { | |
193 | // Autoplay in playlist mode | |
7dcd45a9 | 194 | autoplay: autoplayFromPreviousVideo ? true : this.autoplay, |
59a643aa | 195 | forceAutoplay, |
f1a0f3b7 C |
196 | |
197 | controls: this.controls, | |
198 | controlBar: this.controlBar, | |
199 | ||
200 | muted: this.muted, | |
201 | loop: this.loop, | |
202 | ||
203 | p2pEnabled: this.p2pEnabled, | |
204 | ||
205 | captions: videoCaptions.length !== 0, | |
206 | subtitle: this.subtitle, | |
207 | ||
208 | startTime: playlistTracker | |
209 | ? playlistTracker.getCurrentElement().startTimestamp | |
210 | : this.startTime, | |
211 | stopTime: playlistTracker | |
212 | ? playlistTracker.getCurrentElement().stopTimestamp | |
213 | : this.stopTime, | |
214 | ||
4fe7cde2 C |
215 | playbackRate: this.playbackRate, |
216 | ||
f1a0f3b7 C |
217 | videoCaptions, |
218 | inactivityTimeout: 2500, | |
219 | videoViewUrl: this.videoFetcher.getVideoViewsUrl(video.uuid), | |
6de07622 | 220 | videoViewIntervalMs: 5000, |
fd3c2e87 | 221 | metricsUrl: window.location.origin + '/api/v1/metrics/playback', |
f1a0f3b7 C |
222 | |
223 | videoShortUUID: video.shortUUID, | |
224 | videoUUID: video.uuid, | |
225 | ||
226 | playerElement: this.playerHTML.getPlayerElement(), | |
227 | onPlayerElementChange: (element: HTMLVideoElement) => { | |
228 | this.playerHTML.setPlayerElement(element) | |
229 | }, | |
230 | ||
231 | videoDuration: video.duration, | |
232 | enableHotkeys: true, | |
bd2b51be | 233 | |
f1a0f3b7 | 234 | peertubeLink: this.peertubeLink, |
bd2b51be C |
235 | instanceName: serverConfig.instance.name, |
236 | ||
f1a0f3b7 C |
237 | poster: window.location.origin + video.previewPath, |
238 | theaterButton: false, | |
239 | ||
240 | serverUrl: window.location.origin, | |
241 | language: navigator.language, | |
242 | embedUrl: window.location.origin + video.embedPath, | |
243 | embedTitle: video.name, | |
244 | ||
3545e72c C |
245 | requiresAuth: videoRequiresAuth(video), |
246 | authorizationHeader, | |
247 | videoFileToken, | |
248 | ||
f1a0f3b7 C |
249 | errorNotifier: () => { |
250 | // Empty, we don't have a notifier in the embed | |
251 | }, | |
252 | ||
253 | ...this.buildLiveOptions(video, live), | |
254 | ||
255 | ...this.buildPlaylistOptions(options) | |
256 | }, | |
257 | ||
258 | webtorrent: { | |
259 | videoFiles: video.files | |
260 | }, | |
261 | ||
262 | ...this.buildP2PMediaLoaderOptions(video), | |
263 | ||
264 | pluginsManager: this.peertubePlugin.getPluginsManager() | |
265 | } | |
266 | ||
267 | return playerOptions | |
268 | } | |
269 | ||
270 | private buildLiveOptions (video: VideoDetails, live: LiveVideo) { | |
271 | if (!video.isLive) return { isLive: false } | |
272 | ||
273 | return { | |
274 | isLive: true, | |
275 | liveOptions: { | |
276 | latencyMode: live.latencyMode | |
277 | } | |
278 | } | |
279 | } | |
280 | ||
281 | private buildPlaylistOptions (options: { | |
282 | playlistTracker?: PlaylistTracker | |
283 | playNextPlaylistVideo?: () => any | |
284 | playPreviousPlaylistVideo?: () => any | |
285 | onVideoUpdate?: (uuid: string) => any | |
286 | }) { | |
287 | const { playlistTracker, playNextPlaylistVideo, playPreviousPlaylistVideo, onVideoUpdate } = options | |
288 | ||
289 | if (!playlistTracker) return {} | |
290 | ||
291 | return { | |
292 | playlist: { | |
293 | elements: playlistTracker.getPlaylistElements(), | |
294 | playlist: playlistTracker.getPlaylist(), | |
295 | ||
296 | getCurrentPosition: () => playlistTracker.getCurrentPosition(), | |
297 | ||
298 | onItemClicked: (videoPlaylistElement: VideoPlaylistElement) => { | |
299 | playlistTracker.setCurrentElement(videoPlaylistElement) | |
300 | ||
301 | onVideoUpdate(videoPlaylistElement.video.uuid) | |
302 | } | |
303 | }, | |
304 | ||
305 | nextVideo: () => playNextPlaylistVideo(), | |
306 | hasNextVideo: () => playlistTracker.hasNextPlaylistElement(), | |
307 | ||
308 | previousVideo: () => playPreviousPlaylistVideo(), | |
309 | hasPreviousVideo: () => playlistTracker.hasPreviousPlaylistElement() | |
310 | } | |
311 | } | |
312 | ||
313 | private buildP2PMediaLoaderOptions (video: VideoDetails) { | |
314 | if (this.mode !== 'p2p-media-loader') return {} | |
315 | ||
316 | const hlsPlaylist = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) | |
317 | ||
318 | return { | |
319 | p2pMediaLoader: { | |
320 | playlistUrl: hlsPlaylist.playlistUrl, | |
321 | segmentsSha256Url: hlsPlaylist.segmentsSha256Url, | |
322 | redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl), | |
323 | trackerAnnounce: video.trackerUrls, | |
324 | videoFiles: hlsPlaylist.files | |
325 | } as P2PMediaLoaderOptions | |
326 | } | |
327 | } | |
328 | ||
329 | // --------------------------------------------------------------------------- | |
330 | ||
331 | private async buildCaptions (captionsResponse: Response, translations: Translations): Promise<VideoJSCaption[]> { | |
332 | if (captionsResponse.ok) { | |
333 | const { data } = await captionsResponse.json() | |
334 | ||
335 | return data.map((c: VideoCaption) => ({ | |
336 | label: peertubeTranslate(c.language.label, translations), | |
337 | language: c.language.id, | |
338 | src: window.location.origin + c.captionPath | |
339 | })) | |
340 | } | |
341 | ||
342 | return [] | |
343 | } | |
344 | ||
345 | // --------------------------------------------------------------------------- | |
346 | ||
347 | private isP2PEnabled (config: HTMLServerConfig, video: Video) { | |
348 | const userP2PEnabled = getBoolOrDefault( | |
349 | peertubeLocalStorage.getItem(UserLocalStorageKeys.P2P_ENABLED), | |
350 | config.defaults.p2p.embed.enabled | |
351 | ) | |
352 | ||
353 | return isP2PEnabled(video, config, userP2PEnabled) | |
354 | } | |
355 | } |