aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+videos/+video-watch/video-watch.component.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-06-30 09:49:45 +0200
committerChocobozzz <me@florianbigard.com>2021-06-30 09:49:45 +0200
commitc894a1ea72fd1f16c9f1fc0dae14213b2937152d (patch)
treee99f5f064eecf82356637d92ec72b3debf1e9335 /client/src/app/+videos/+video-watch/video-watch.component.ts
parent2453589a286e1f65843af582512387b2fa17b502 (diff)
downloadPeerTube-c894a1ea72fd1f16c9f1fc0dae14213b2937152d.tar.gz
PeerTube-c894a1ea72fd1f16c9f1fc0dae14213b2937152d.tar.zst
PeerTube-c894a1ea72fd1f16c9f1fc0dae14213b2937152d.zip
Move watch attributes in a dedicated component
Diffstat (limited to 'client/src/app/+videos/+video-watch/video-watch.component.ts')
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts400
1 files changed, 180 insertions, 220 deletions
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index 7af37ef03..85b882225 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -1,8 +1,7 @@
1import { Hotkey, HotkeysService } from 'angular2-hotkeys' 1import { Hotkey, HotkeysService } from 'angular2-hotkeys'
2import { forkJoin, Subscription } from 'rxjs' 2import { forkJoin, Subscription } from 'rxjs'
3import { catchError } from 'rxjs/operators'
4import { PlatformLocation } from '@angular/common' 3import { PlatformLocation } from '@angular/common'
5import { ChangeDetectorRef, Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' 4import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
6import { ActivatedRoute, Router } from '@angular/router' 5import { ActivatedRoute, Router } from '@angular/router'
7import { 6import {
8 AuthService, 7 AuthService,
@@ -21,7 +20,6 @@ import { HooksService } from '@app/core/plugins/hooks.service'
21import { isXPercentInViewport, scrollToTop } from '@app/helpers' 20import { isXPercentInViewport, scrollToTop } from '@app/helpers'
22import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
23import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
24import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature'
25import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
26import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 24import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
27import { HTMLServerConfig, PeerTubeProblemDocument, ServerErrorCode, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' 25import { HTMLServerConfig, PeerTubeProblemDocument, ServerErrorCode, VideoCaption, VideoPrivacy, VideoState } from '@shared/models'
@@ -51,10 +49,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
51 49
52 player: any 50 player: any
53 playerElement: HTMLVideoElement 51 playerElement: HTMLVideoElement
54
55 theaterEnabled = false
56
57 playerPlaceholderImgSrc: string 52 playerPlaceholderImgSrc: string
53 theaterEnabled = false
58 54
59 video: VideoDetails = null 55 video: VideoDetails = null
60 videoCaptions: VideoCaption[] = [] 56 videoCaptions: VideoCaption[] = []
@@ -62,28 +58,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
62 playlistPosition: number 58 playlistPosition: number
63 playlist: VideoPlaylist = null 59 playlist: VideoPlaylist = null
64 60
65 likesBarTooltipText = ''
66
67 remoteServerDown = false 61 remoteServerDown = false
68 62
69 tooltipSupport = '' 63 private nextVideoUUID = ''
70 tooltipSaveToPlaylist = ''
71
72 videoActionsOptions: VideoActionsDisplayType = {
73 playlist: false,
74 download: true,
75 update: true,
76 blacklist: true,
77 delete: true,
78 report: true,
79 duplicate: true,
80 mute: true,
81 liveInfo: true
82 }
83
84 private nextVideoUuid = ''
85 private nextVideoTitle = '' 64 private nextVideoTitle = ''
65
86 private currentTime: number 66 private currentTime: number
67
87 private paramsSub: Subscription 68 private paramsSub: Subscription
88 private queryParamsSub: Subscription 69 private queryParamsSub: Subscription
89 private configSub: Subscription 70 private configSub: Subscription
@@ -95,7 +76,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
95 76
96 constructor ( 77 constructor (
97 private elementRef: ElementRef, 78 private elementRef: ElementRef,
98 private changeDetector: ChangeDetectorRef,
99 private route: ActivatedRoute, 79 private route: ActivatedRoute,
100 private router: Router, 80 private router: Router,
101 private videoService: VideoService, 81 private videoService: VideoService,
@@ -129,40 +109,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
129 async ngOnInit () { 109 async ngOnInit () {
130 this.serverConfig = this.serverService.getHTMLConfig() 110 this.serverConfig = this.serverService.getHTMLConfig()
131 111
132 // Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover
133 if (this.user || !this.screenService.isInMobileView()) {
134 this.tooltipSupport = $localize`Support options for this video`
135 this.tooltipSaveToPlaylist = $localize`Save to playlist`
136 }
137
138 PeertubePlayerManager.initState() 112 PeertubePlayerManager.initState()
139 113
140 this.paramsSub = this.route.params.subscribe(routeParams => { 114 this.loadRouteParams()
141 const videoId = routeParams[ 'videoId' ] 115 this.loadRouteQuery()
142 if (videoId) this.loadVideo(videoId)
143
144 const playlistId = routeParams[ 'playlistId' ]
145 if (playlistId) this.loadPlaylist(playlistId)
146 })
147
148 this.queryParamsSub = this.route.queryParams.subscribe(queryParams => {
149 // Handle the ?playlistPosition
150 const positionParam = queryParams[ 'playlistPosition' ] ?? 1
151
152 this.playlistPosition = positionParam === 'last'
153 ? -1 // Handle the "last" index
154 : parseInt(positionParam + '', 10)
155
156 if (isNaN(this.playlistPosition)) {
157 console.error(`playlistPosition query param '${positionParam}' was parsed as NaN, defaulting to 1.`)
158 this.playlistPosition = 1
159 }
160
161 this.videoWatchPlaylist.updatePlaylistIndex(this.playlistPosition)
162
163 const start = queryParams[ 'start' ]
164 if (this.player && start) this.player.currentTime(parseInt(start, 10))
165 })
166 116
167 this.initHotkeys() 117 this.initHotkeys()
168 118
@@ -194,41 +144,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
194 return this.videoWatchPlaylist.currentPlaylistPosition 144 return this.videoWatchPlaylist.currentPlaylistPosition
195 } 145 }
196 146
197 isUserLoggedIn () {
198 return this.authService.isLoggedIn()
199 }
200
201 getVideoUrl () {
202 if (!this.video.url) {
203 return this.video.originInstanceUrl + VideoDetails.buildWatchUrl(this.video)
204 }
205 return this.video.url
206 }
207
208 getVideoTags () {
209 if (!this.video || Array.isArray(this.video.tags) === false) return []
210
211 return this.video.tags
212 }
213
214 onRecommendations (videos: Video[]) { 147 onRecommendations (videos: Video[]) {
215 if (videos.length > 0) { 148 if (videos.length === 0) return
216 // The recommended videos's first element should be the next video
217 const video = videos[0]
218 this.nextVideoUuid = video.uuid
219 this.nextVideoTitle = video.name
220 }
221 }
222 149
223 isVideoBlur (video: Video) { 150 // The recommended videos's first element should be the next video
224 return video.isVideoNSFWForUser(this.user, this.serverConfig) 151 const video = videos[0]
225 } 152 this.nextVideoUUID = video.uuid
226 153 this.nextVideoTitle = video.name
227 isAutoPlayEnabled () {
228 return (
229 (this.user && this.user.autoPlayNextVideo) ||
230 this.anonymousUser.autoPlayNextVideo
231 )
232 } 154 }
233 155
234 handleTimestampClicked (timestamp: number) { 156 handleTimestampClicked (timestamp: number) {
@@ -238,11 +160,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
238 scrollToTop() 160 scrollToTop()
239 } 161 }
240 162
241 isPlaylistAutoPlayEnabled () { 163 onPlaylistVideoFound (videoId: string) {
242 return ( 164 this.loadVideo(videoId)
243 (this.user && this.user.autoPlayNextVideoPlaylist) || 165 }
244 this.anonymousUser.autoPlayNextVideoPlaylist 166
245 ) 167 isUserLoggedIn () {
168 return this.authService.isLoggedIn()
169 }
170
171 isVideoBlur (video: Video) {
172 return video.isVideoNSFWForUser(this.user, this.serverConfig)
246 } 173 }
247 174
248 isChannelDisplayNameGeneric () { 175 isChannelDisplayNameGeneric () {
@@ -254,21 +181,44 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
254 return genericChannelDisplayName.includes(this.video.channel.displayName) 181 return genericChannelDisplayName.includes(this.video.channel.displayName)
255 } 182 }
256 183
257 onPlaylistVideoFound (videoId: string) {
258 this.loadVideo(videoId)
259 }
260
261 displayOtherVideosAsRow () { 184 displayOtherVideosAsRow () {
262 // Use the same value as in the SASS file 185 // Use the same value as in the SASS file
263 return this.screenService.getWindowInnerWidth() <= 1100 186 return this.screenService.getWindowInnerWidth() <= 1100
264 } 187 }
265 188
189 private loadRouteParams () {
190 this.paramsSub = this.route.params.subscribe(routeParams => {
191 const videoId = routeParams[ 'videoId' ]
192 if (videoId) return this.loadVideo(videoId)
193
194 const playlistId = routeParams[ 'playlistId' ]
195 if (playlistId) return this.loadPlaylist(playlistId)
196 })
197 }
198
199 private loadRouteQuery () {
200 this.queryParamsSub = this.route.queryParams.subscribe(queryParams => {
201 // Handle the ?playlistPosition
202 const positionParam = queryParams[ 'playlistPosition' ] ?? 1
203
204 this.playlistPosition = positionParam === 'last'
205 ? -1 // Handle the "last" index
206 : parseInt(positionParam + '', 10)
207
208 if (isNaN(this.playlistPosition)) {
209 console.error(`playlistPosition query param '${positionParam}' was parsed as NaN, defaulting to 1.`)
210 this.playlistPosition = 1
211 }
212
213 this.videoWatchPlaylist.updatePlaylistIndex(this.playlistPosition)
214
215 const start = queryParams[ 'start' ]
216 if (this.player && start) this.player.currentTime(parseInt(start, 10))
217 })
218 }
219
266 private loadVideo (videoId: string) { 220 private loadVideo (videoId: string) {
267 // Video did not change 221 if (this.isSameElement(this.video, videoId)) return
268 if (
269 this.video &&
270 (this.video.uuid === videoId || this.video.shortUUID === videoId)
271 ) return
272 222
273 if (this.player) this.player.pause() 223 if (this.player) this.player.pause()
274 224
@@ -280,90 +230,77 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
280 'filter:api.video-watch.video.get.result' 230 'filter:api.video-watch.video.get.result'
281 ) 231 )
282 232
283 // Video did change 233 forkJoin([ videoObs, this.videoCaptionService.listCaptions(videoId)])
284 forkJoin([ 234 .subscribe(
285 videoObs, 235 ([ video, captionsResult ]) => {
286 this.videoCaptionService.listCaptions(videoId) 236 const queryParams = this.route.snapshot.queryParams
287 ])
288 .pipe(
289 // If 400, 403 or 404, the video is private or blocked so redirect to 404
290 catchError(err => {
291 const errorBody = err.body as PeerTubeProblemDocument
292
293 if (errorBody.code === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && errorBody.originUrl) {
294 const search = window.location.search
295 let originUrl = errorBody.originUrl
296 if (search) originUrl += search
297
298 this.confirmService.confirm(
299 $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`,
300 $localize`Redirection`
301 ).then(res => {
302 if (res === false) {
303 return this.restExtractor.redirectTo404IfNotFound(err, 'video', [
304 HttpStatusCode.BAD_REQUEST_400,
305 HttpStatusCode.FORBIDDEN_403,
306 HttpStatusCode.NOT_FOUND_404
307 ])
308 }
309
310 return window.location.href = originUrl
311 })
312 }
313 237
314 return this.restExtractor.redirectTo404IfNotFound(err, 'video', [ 238 const urlOptions = {
315 HttpStatusCode.BAD_REQUEST_400, 239 resume: queryParams.resume,
316 HttpStatusCode.FORBIDDEN_403,
317 HttpStatusCode.NOT_FOUND_404
318 ])
319 })
320 )
321 .subscribe(([ video, captionsResult ]) => {
322 const queryParams = this.route.snapshot.queryParams
323 240
324 const urlOptions = { 241 startTime: queryParams.start,
325 resume: queryParams.resume, 242 stopTime: queryParams.stop,
326 243
327 startTime: queryParams.start, 244 muted: queryParams.muted,
328 stopTime: queryParams.stop, 245 loop: queryParams.loop,
246 subtitle: queryParams.subtitle,
329 247
330 muted: queryParams.muted, 248 playerMode: queryParams.mode,
331 loop: queryParams.loop, 249 peertubeLink: false
332 subtitle: queryParams.subtitle, 250 }
333 251
334 playerMode: queryParams.mode, 252 this.onVideoFetched(video, captionsResult.data, urlOptions)
335 peertubeLink: false 253 .catch(err => this.handleGlobalError(err))
336 } 254 },
337 255
338 this.onVideoFetched(video, captionsResult.data, urlOptions) 256 err => this.handleRequestError(err)
339 .catch(err => this.handleError(err)) 257 )
340 })
341 } 258 }
342 259
343 private loadPlaylist (playlistId: string) { 260 private loadPlaylist (playlistId: string) {
344 // Playlist did not change 261 if (this.isSameElement(this.playlist, playlistId)) return
345 if (
346 this.playlist &&
347 (this.playlist.uuid === playlistId || this.playlist.shortUUID === playlistId)
348 ) return
349 262
350 this.playlistService.getVideoPlaylist(playlistId) 263 this.playlistService.getVideoPlaylist(playlistId)
351 .pipe( 264 .subscribe(
352 // If 400 or 403, the video is private or blocked so redirect to 404 265 playlist => {
353 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'video', [ 266 this.playlist = playlist
354 HttpStatusCode.BAD_REQUEST_400, 267
355 HttpStatusCode.FORBIDDEN_403, 268 this.videoWatchPlaylist.loadPlaylistElements(playlist, !this.playlistPosition, this.playlistPosition)
356 HttpStatusCode.NOT_FOUND_404 269 },
357 ])) 270
271 err => this.handleRequestError(err)
358 ) 272 )
359 .subscribe(playlist => { 273 }
360 this.playlist = playlist
361 274
362 this.videoWatchPlaylist.loadPlaylistElements(playlist, !this.playlistPosition, this.playlistPosition) 275 private isSameElement (element: VideoDetails | VideoPlaylist, newId: string) {
363 }) 276 if (!element) return false
277
278 return (element.id + '') === newId || element.uuid === newId || element.shortUUID === newId
279 }
280
281 private async handleRequestError (err: any) {
282 const errorBody = err.body as PeerTubeProblemDocument
283
284 if (errorBody.code === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && errorBody.originUrl) {
285 const originUrl = errorBody.originUrl + (window.location.search ?? '')
286
287 const res = await this.confirmService.confirm(
288 $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`,
289 $localize`Redirection`
290 )
291
292 if (res === true) return window.location.href = originUrl
293 }
294
295 // If 400, 403 or 404, the video is private or blocked so redirect to 404
296 return this.restExtractor.redirectTo404IfNotFound(err, 'video', [
297 HttpStatusCode.BAD_REQUEST_400,
298 HttpStatusCode.FORBIDDEN_403,
299 HttpStatusCode.NOT_FOUND_404
300 ])
364 } 301 }
365 302
366 private handleError (err: any) { 303 private handleGlobalError (err: any) {
367 const errorMessage: string = typeof err === 'string' ? err : err.message 304 const errorMessage: string = typeof err === 'string' ? err : err.message
368 if (!errorMessage) return 305 if (!errorMessage) return
369 306
@@ -371,7 +308,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
371 if (errorMessage.indexOf('from xs param') !== -1) { 308 if (errorMessage.indexOf('from xs param') !== -1) {
372 this.flushPlayer() 309 this.flushPlayer()
373 this.remoteServerDown = true 310 this.remoteServerDown = true
374 this.changeDetector.detectChanges()
375 311
376 return 312 return
377 } 313 }
@@ -449,39 +385,44 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
449 this.zone.runOutsideAngular(async () => { 385 this.zone.runOutsideAngular(async () => {
450 this.player = await PeertubePlayerManager.initialize(playerMode, playerOptions, player => this.player = player) 386 this.player = await PeertubePlayerManager.initialize(playerMode, playerOptions, player => this.player = player)
451 387
452 this.player.on('customError', ({ err }: { err: any }) => this.handleError(err)) 388 this.player.on('customError', ({ err }: { err: any }) => {
389 this.zone.run(() => this.handleGlobalError(err))
390 })
453 391
454 this.player.on('timeupdate', () => { 392 this.player.on('timeupdate', () => {
393 // Don't need to trigger angular change for this variable, that is sent to children components on click
455 this.currentTime = Math.floor(this.player.currentTime()) 394 this.currentTime = Math.floor(this.player.currentTime())
456 }) 395 })
457 396
458 /** 397 /**
459 * replaces this.player.one('ended') 398 * condition: true to make the upnext functionality trigger, false to disable the upnext functionality
460 * 'condition()': true to make the upnext functionality trigger, 399 * go to the next video in 'condition()' if you don't want of the timer.
461 * false to disable the upnext functionality 400 * next: function triggered at the end of the timer.
462 * go to the next video in 'condition()' if you don't want of the timer. 401 * suspended: function used at each click of the timer checking if we need to reset progress
463 * 'next': function triggered at the end of the timer. 402 * and wait until suspended becomes truthy again.
464 * 'suspended': function used at each clic of the timer checking if we need
465 * to reset progress and wait until 'suspended' becomes truthy again.
466 */ 403 */
467 this.player.upnext({ 404 this.player.upnext({
468 timeout: 10000, // 10s 405 timeout: 5000, // 5s
406
469 headText: $localize`Up Next`, 407 headText: $localize`Up Next`,
470 cancelText: $localize`Cancel`, 408 cancelText: $localize`Cancel`,
471 suspendedText: $localize`Autoplay is suspended`, 409 suspendedText: $localize`Autoplay is suspended`,
410
472 getTitle: () => this.nextVideoTitle, 411 getTitle: () => this.nextVideoTitle,
473 next: () => this.zone.run(() => this.autoplayNext()), 412
413 next: () => this.zone.run(() => this.playNextVideoInAngularZone()),
474 condition: () => { 414 condition: () => {
475 if (this.playlist) { 415 if (!this.playlist) return this.isAutoPlayNext()
476 if (this.isPlaylistAutoPlayEnabled()) { 416
477 // upnext will not trigger, and instead the next video will play immediately 417 // Don't wait timeout to play the next playlist video
478 this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo()) 418 if (this.isPlaylistAutoPlayNext()) {
479 } 419 this.playNextVideoInAngularZone()
480 } else if (this.isAutoPlayEnabled()) { 420 return undefined
481 return true // upnext will trigger
482 } 421 }
483 return false // upnext will not trigger, and instead leave the video stopping 422
423 return false
484 }, 424 },
425
485 suspended: () => { 426 suspended: () => {
486 return ( 427 return (
487 !isXPercentInViewport(this.player.el(), 80) || 428 !isXPercentInViewport(this.player.el(), 80) ||
@@ -491,8 +432,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
491 }) 432 })
492 433
493 this.player.one('stopped', () => { 434 this.player.one('stopped', () => {
494 if (this.playlist) { 435 if (this.playlist && this.isPlaylistAutoPlayNext()) {
495 if (this.isPlaylistAutoPlayEnabled()) this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo()) 436 this.playNextVideoInAngularZone()
496 } 437 }
497 }) 438 })
498 439
@@ -510,33 +451,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
510 }) 451 })
511 } 452 }
512 453
513 private autoplayNext () { 454 private playNextVideoInAngularZone () {
514 if (this.playlist) { 455 if (this.playlist) {
515 this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo()) 456 this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
516 } else if (this.nextVideoUuid) { 457 return
517 this.router.navigate([ '/w', this.nextVideoUuid ])
518 } 458 }
519 }
520
521 private setOpenGraphTags () {
522 this.metaService.setTitle(this.video.name)
523
524 this.metaService.setTag('og:type', 'video')
525
526 this.metaService.setTag('og:title', this.video.name)
527 this.metaService.setTag('name', this.video.name)
528 459
529 this.metaService.setTag('og:description', this.video.description) 460 if (this.nextVideoUUID) {
530 this.metaService.setTag('description', this.video.description) 461 this.router.navigate([ '/w', this.nextVideoUUID ])
531 462 return
532 this.metaService.setTag('og:image', this.video.previewPath) 463 }
533
534 this.metaService.setTag('og:duration', this.video.duration.toString())
535
536 this.metaService.setTag('og:site_name', 'PeerTube')
537
538 this.metaService.setTag('og:url', window.location.href)
539 this.metaService.setTag('url', window.location.href)
540 } 464 }
541 465
542 private isAutoplay () { 466 private isAutoplay () {
@@ -550,6 +474,20 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
550 return this.user.autoPlayVideo !== false 474 return this.user.autoPlayVideo !== false
551 } 475 }
552 476
477 private isAutoPlayNext () {
478 return (
479 (this.user && this.user.autoPlayNextVideo) ||
480 this.anonymousUser.autoPlayNextVideo
481 )
482 }
483
484 private isPlaylistAutoPlayNext () {
485 return (
486 (this.user && this.user.autoPlayNextVideoPlaylist) ||
487 this.anonymousUser.autoPlayNextVideoPlaylist
488 )
489 }
490
553 private flushPlayer () { 491 private flushPlayer () {
554 // Remove player if it exists 492 // Remove player if it exists
555 if (!this.player) return 493 if (!this.player) return
@@ -569,6 +507,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
569 user?: AuthUser 507 user?: AuthUser
570 }) { 508 }) {
571 const { video, videoCaptions, urlOptions, user } = params 509 const { video, videoCaptions, urlOptions, user } = params
510
572 const getStartTime = () => { 511 const getStartTime = () => {
573 const byUrl = urlOptions.startTime !== undefined 512 const byUrl = urlOptions.startTime !== undefined
574 const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined) 513 const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined)
@@ -595,7 +534,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
595 const options: PeertubePlayerManagerOptions = { 534 const options: PeertubePlayerManagerOptions = {
596 common: { 535 common: {
597 autoplay: this.isAutoplay(), 536 autoplay: this.isAutoplay(),
598 nextVideo: () => this.zone.run(() => this.autoplayNext()), 537 nextVideo: () => this.playNextVideoInAngularZone(),
599 538
600 playerElement: this.playerElement, 539 playerElement: this.playerElement,
601 onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element, 540 onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
@@ -663,7 +602,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
663 else mode = 'webtorrent' 602 else mode = 'webtorrent'
664 } 603 }
665 604
666 // p2p-media-loader needs TextEncoder, try to fallback on WebTorrent 605 // p2p-media-loader needs TextEncoder, fallback on WebTorrent if not available
667 if (typeof TextEncoder === 'undefined') { 606 if (typeof TextEncoder === 'undefined') {
668 mode = 'webtorrent' 607 mode = 'webtorrent'
669 } 608 }
@@ -717,7 +656,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
717 656
718 const videoUUID = this.video.uuid 657 const videoUUID = this.video.uuid
719 658
720 // Reset to refetch the video 659 // Reset to force refresh the video
721 this.video = undefined 660 this.video = undefined
722 this.loadVideo(videoUUID) 661 this.loadVideo(videoUUID)
723 } 662 }
@@ -765,4 +704,25 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
765 704
766 this.hotkeysService.add(this.hotkeys) 705 this.hotkeysService.add(this.hotkeys)
767 } 706 }
707
708 private setOpenGraphTags () {
709 this.metaService.setTitle(this.video.name)
710
711 this.metaService.setTag('og:type', 'video')
712
713 this.metaService.setTag('og:title', this.video.name)
714 this.metaService.setTag('name', this.video.name)
715
716 this.metaService.setTag('og:description', this.video.description)
717 this.metaService.setTag('description', this.video.description)
718
719 this.metaService.setTag('og:image', this.video.previewPath)
720
721 this.metaService.setTag('og:duration', this.video.duration.toString())
722
723 this.metaService.setTag('og:site_name', 'PeerTube')
724
725 this.metaService.setTag('og:url', window.location.href)
726 this.metaService.setTag('url', window.location.href)
727 }
768} 728}