]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/videos/+video-watch/video-watch.component.ts
Merge branch 'release/1.4.0' into develop
[github/Chocobozzz/PeerTube.git] / client / src / app / videos / +video-watch / video-watch.component.ts
CommitLineData
e972e046 1import { catchError } from 'rxjs/operators'
3b492bff 2import { ChangeDetectorRef, Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
df98563e 3import { ActivatedRoute, Router } from '@angular/router'
901637bb 4import { RedirectService } from '@app/core/routing/redirect.service'
0bd78bf3 5import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
07fa4c97 6import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
1f3e9fec 7import { MetaService } from '@ngx-meta/core'
f8b2c1b4 8import { Notifier, ServerService } from '@app/core'
4c72c1cd 9import { forkJoin, Observable, Subscription } from 'rxjs'
20d21199 10import { Hotkey, HotkeysService } from 'angular2-hotkeys'
72675ebe 11import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared'
df98563e 12import { AuthService, ConfirmService } from '../../core'
a51bad1a 13import { RestExtractor, VideoBlacklistService } from '../../shared'
ff249f49 14import { VideoDetails } from '../../shared/video/video-details.model'
63c4db6d 15import { VideoService } from '../../shared/video/video.service'
4635f59d 16import { VideoShareComponent } from './modal/video-share.component'
20d21199 17import { SubscribeButtonComponent } from '@app/shared/user-subscription/subscribe-button.component'
989e526a 18import { I18n } from '@ngx-translate/i18n-polyfill'
e945b184 19import { environment } from '../../../environments/environment'
16f7022b 20import { VideoCaptionService } from '@app/shared/video-caption'
1506307f 21import { MarkdownService } from '@app/shared/renderer'
6ec0b75b 22import {
5efab546 23 CustomizationOptions,
6ec0b75b
C
24 P2PMediaLoaderOptions,
25 PeertubePlayerManager,
26 PeertubePlayerManagerOptions,
597a9266 27 PlayerMode
6ec0b75b 28} from '../../../assets/player/peertube-player-manager'
e2f01c47
C
29import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
30import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
e2f01c47 31import { Video } from '@app/shared/video/video.model'
5efab546 32import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils'
72675ebe 33import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component'
011e1e6b 34import { getStoredTheater } from '../../../assets/player/peertube-player-local-storage'
18a6f04c 35import { PluginService } from '@app/core/plugins/plugin.service'
93cae479 36import { HooksService } from '@app/core/plugins/hooks.service'
60c2bc80 37import { PlatformLocation } from '@angular/common'
dc8bc31b 38
dc8bc31b
C
39@Component({
40 selector: 'my-video-watch',
ec8d8440
C
41 templateUrl: './video-watch.component.html',
42 styleUrls: [ './video-watch.component.scss' ]
dc8bc31b 43})
0629423c 44export class VideoWatchComponent implements OnInit, OnDestroy {
22b59e80
C
45 private static LOCAL_STORAGE_PRIVACY_CONCERN_KEY = 'video-watch-privacy-concern'
46
f36da21e
C
47 @ViewChild('videoWatchPlaylist', { static: true }) videoWatchPlaylist: VideoWatchPlaylistComponent
48 @ViewChild('videoShareModal', { static: false }) videoShareModal: VideoShareComponent
49 @ViewChild('videoSupportModal', { static: false }) videoSupportModal: VideoSupportComponent
50 @ViewChild('subscribeButton', { static: false }) subscribeButton: SubscribeButtonComponent
df98563e 51
2adfc7ea 52 player: any
0826c92d 53 playerElement: HTMLVideoElement
9a18a625 54 theaterEnabled = false
154898b0 55 userRating: UserVideoRateType = null
80958c78 56 descriptionLoading = false
2de96f4d 57
2f4c784a
C
58 video: VideoDetails = null
59 videoCaptions: VideoCaption[] = []
60
e2f01c47 61 playlist: VideoPlaylist = null
e2f01c47 62
2de96f4d
C
63 completeDescriptionShown = false
64 completeVideoDescription: string
65 shortVideoDescription: string
9d9597df 66 videoHTMLDescription = ''
e9189001 67 likesBarTooltipText = ''
73e09f27 68 hasAlreadyAcceptedPrivacyConcern = false
6d88de72 69 remoteServerDown = false
20d21199 70 hotkeys: Hotkey[]
df98563e 71
f0a39880 72 private currentTime: number
df98563e 73 private paramsSub: Subscription
e2f01c47 74 private queryParamsSub: Subscription
31b6ddf8 75 private configSub: Subscription
df98563e
C
76
77 constructor (
4fd8aa32 78 private elementRef: ElementRef,
3b492bff 79 private changeDetector: ChangeDetectorRef,
0629423c 80 private route: ActivatedRoute,
92fb909c 81 private router: Router,
d3ef341a 82 private videoService: VideoService,
e2f01c47 83 private playlistService: VideoPlaylistService,
35bf0c83 84 private videoBlacklistService: VideoBlacklistService,
92fb909c 85 private confirmService: ConfirmService,
3ec343a4 86 private metaService: MetaService,
7ddd02c9 87 private authService: AuthService,
0883b324 88 private serverService: ServerService,
a51bad1a 89 private restExtractor: RestExtractor,
f8b2c1b4 90 private notifier: Notifier,
18a6f04c 91 private pluginService: PluginService,
7ae71355 92 private markdownService: MarkdownService,
901637bb 93 private zone: NgZone,
989e526a 94 private redirectService: RedirectService,
16f7022b 95 private videoCaptionService: VideoCaptionService,
e945b184 96 private i18n: I18n,
20d21199 97 private hotkeysService: HotkeysService,
93cae479 98 private hooks: HooksService,
60c2bc80 99 private location: PlatformLocation,
e945b184 100 @Inject(LOCALE_ID) private localeId: string
d3ef341a 101 ) {}
dc8bc31b 102
b2731bff
C
103 get user () {
104 return this.authService.getUser()
105 }
106
18a6f04c 107 async ngOnInit () {
31b6ddf8
C
108 this.configSub = this.serverService.configLoaded
109 .subscribe(() => {
110 if (
111 isWebRTCDisabled() ||
112 this.serverService.getConfig().tracker.enabled === false ||
113 peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
114 ) {
115 this.hasAlreadyAcceptedPrivacyConcern = true
116 }
117 })
2b3b76ab 118
13fc89f4 119 this.paramsSub = this.route.params.subscribe(routeParams => {
e2f01c47
C
120 const videoId = routeParams[ 'videoId' ]
121 if (videoId) this.loadVideo(videoId)
a51bad1a 122
e2f01c47
C
123 const playlistId = routeParams[ 'playlistId' ]
124 if (playlistId) this.loadPlaylist(playlistId)
125 })
bf079b7b 126
e2f01c47
C
127 this.queryParamsSub = this.route.queryParams.subscribe(queryParams => {
128 const videoId = queryParams[ 'videoId' ]
129 if (videoId) this.loadVideo(videoId)
df98563e 130 })
20d21199 131
1c8ddbfa 132 this.initHotkeys()
011e1e6b
C
133
134 this.theaterEnabled = getStoredTheater()
18a6f04c 135
c9e3eeed 136 this.hooks.runAction('action:video-watch.init', 'video-watch')
d1992b93
C
137 }
138
df98563e 139 ngOnDestroy () {
09edde40 140 this.flushPlayer()
067e3f84 141
13fc89f4 142 // Unsubscribe subscriptions
e2f01c47
C
143 if (this.paramsSub) this.paramsSub.unsubscribe()
144 if (this.queryParamsSub) this.queryParamsSub.unsubscribe()
20d21199
RK
145
146 // Unbind hotkeys
147 if (this.isUserLoggedIn()) this.hotkeysService.remove(this.hotkeys)
dc8bc31b 148 }
98b01bac 149
df98563e
C
150 setLike () {
151 if (this.isUserLoggedIn() === false) return
4c72c1cd
C
152
153 // Already liked this video
154 if (this.userRating === 'like') this.setRating('none')
155 else this.setRating('like')
d38b8281
C
156 }
157
df98563e
C
158 setDislike () {
159 if (this.isUserLoggedIn() === false) return
4c72c1cd
C
160
161 // Already disliked this video
162 if (this.userRating === 'dislike') this.setRating('none')
163 else this.setRating('dislike')
d38b8281
C
164 }
165
2de96f4d 166 showMoreDescription () {
2de96f4d
C
167 if (this.completeVideoDescription === undefined) {
168 return this.loadCompleteDescription()
169 }
170
171 this.updateVideoDescription(this.completeVideoDescription)
80958c78 172 this.completeDescriptionShown = true
2de96f4d
C
173 }
174
175 showLessDescription () {
2de96f4d 176 this.updateVideoDescription(this.shortVideoDescription)
80958c78 177 this.completeDescriptionShown = false
2de96f4d
C
178 }
179
180 loadCompleteDescription () {
80958c78
C
181 this.descriptionLoading = true
182
2de96f4d 183 this.videoService.loadCompleteDescription(this.video.descriptionPath)
2186386c
C
184 .subscribe(
185 description => {
186 this.completeDescriptionShown = true
187 this.descriptionLoading = false
188
189 this.shortVideoDescription = this.video.description
190 this.completeVideoDescription = description
191
192 this.updateVideoDescription(this.completeVideoDescription)
193 },
194
195 error => {
196 this.descriptionLoading = false
f8b2c1b4 197 this.notifier.error(error.message)
2186386c
C
198 }
199 )
2de96f4d
C
200 }
201
07fa4c97
C
202 showSupportModal () {
203 this.videoSupportModal.show()
204 }
205
df98563e 206 showShareModal () {
f0a39880 207 this.videoShareModal.show(this.currentTime)
99cc4f49
C
208 }
209
df98563e
C
210 isUserLoggedIn () {
211 return this.authService.isLoggedIn()
4f8c0eb0
C
212 }
213
b1fa3eba
C
214 getVideoTags () {
215 if (!this.video || Array.isArray(this.video.tags) === false) return []
216
4278710d 217 return this.video.tags
b1fa3eba
C
218 }
219
3a0fb65c
C
220 onVideoRemoved () {
221 this.redirectService.redirectToHomepage()
6725d05c
C
222 }
223
73e09f27 224 acceptedPrivacyConcern () {
0bd78bf3 225 peertubeLocalStorage.setItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY, 'true')
73e09f27
C
226 this.hasAlreadyAcceptedPrivacyConcern = true
227 }
228
2186386c
C
229 isVideoToTranscode () {
230 return this.video && this.video.state.id === VideoState.TO_TRANSCODE
231 }
232
516df59b
C
233 isVideoToImport () {
234 return this.video && this.video.state.id === VideoState.TO_IMPORT
235 }
236
bbe0f064
C
237 hasVideoScheduledPublication () {
238 return this.video && this.video.scheduledUpdate !== undefined
239 }
240
e2f01c47
C
241 isVideoBlur (video: Video) {
242 return video.isVideoNSFWForUser(this.user, this.serverService.getConfig())
243 }
244
e2f01c47
C
245 private loadVideo (videoId: string) {
246 // Video did not change
247 if (this.video && this.video.uuid === videoId) return
248
249 if (this.player) this.player.pause()
250
93cae479
C
251 const videoObs = this.hooks.wrapObsFun(
252 this.videoService.getVideo.bind(this.videoService),
253 { videoId },
254 'video-watch',
255 'filter:api.video-watch.video.get.params',
256 'filter:api.video-watch.video.get.result'
257 )
258
e2f01c47 259 // Video did change
c8861d5d 260 forkJoin([
93cae479 261 videoObs,
e2f01c47 262 this.videoCaptionService.listCaptions(videoId)
c8861d5d 263 ])
e2f01c47
C
264 .pipe(
265 // If 401, the video is private or blacklisted so redirect to 404
266 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]))
267 )
268 .subscribe(([ video, captionsResult ]) => {
269 const queryParams = this.route.snapshot.queryParams
e2f01c47 270
4c72c1cd
C
271 const urlOptions = {
272 startTime: queryParams.start,
273 stopTime: queryParams.stop,
5efab546
C
274
275 muted: queryParams.muted,
276 loop: queryParams.loop,
4c72c1cd 277 subtitle: queryParams.subtitle,
5efab546
C
278
279 playerMode: queryParams.mode,
280 peertubeLink: false
4c72c1cd
C
281 }
282
283 this.onVideoFetched(video, captionsResult.data, urlOptions)
e2f01c47
C
284 .catch(err => this.handleError(err))
285 })
286 }
287
288 private loadPlaylist (playlistId: string) {
289 // Playlist did not change
290 if (this.playlist && this.playlist.uuid === playlistId) return
291
292 this.playlistService.getVideoPlaylist(playlistId)
293 .pipe(
294 // If 401, the video is private or blacklisted so redirect to 404
295 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]))
296 )
297 .subscribe(playlist => {
298 this.playlist = playlist
299
300 const videoId = this.route.snapshot.queryParams['videoId']
72675ebe 301 this.videoWatchPlaylist.loadPlaylistElements(playlist, !videoId)
e2f01c47
C
302 })
303 }
304
2de96f4d
C
305 private updateVideoDescription (description: string) {
306 this.video.description = description
307 this.setVideoDescriptionHTML()
4c72c1cd 308 .catch(err => console.error(err))
2de96f4d
C
309 }
310
41d71344
C
311 private async setVideoDescriptionHTML () {
312 this.videoHTMLDescription = await this.markdownService.textMarkdownToHTML(this.video.description)
2de96f4d
C
313 }
314
e9189001 315 private setVideoLikesBarTooltipText () {
2186386c
C
316 this.likesBarTooltipText = this.i18n('{{likesNumber}} likes / {{dislikesNumber}} dislikes', {
317 likesNumber: this.video.likes,
318 dislikesNumber: this.video.dislikes
319 })
e9189001
C
320 }
321
0c31c33d
C
322 private handleError (err: any) {
323 const errorMessage: string = typeof err === 'string' ? err : err.message
bf5685f0
C
324 if (!errorMessage) return
325
6d88de72 326 // Display a message in the video player instead of a notification
0f7fedc3 327 if (errorMessage.indexOf('from xs param') !== -1) {
6d88de72
C
328 this.flushPlayer()
329 this.remoteServerDown = true
3b492bff
C
330 this.changeDetector.detectChanges()
331
6d88de72 332 return
0c31c33d
C
333 }
334
f8b2c1b4 335 this.notifier.error(errorMessage)
0c31c33d
C
336 }
337
df98563e 338 private checkUserRating () {
d38b8281 339 // Unlogged users do not have ratings
df98563e 340 if (this.isUserLoggedIn() === false) return
d38b8281
C
341
342 this.videoService.getUserVideoRating(this.video.id)
2186386c
C
343 .subscribe(
344 ratingObject => {
345 if (ratingObject) {
346 this.userRating = ratingObject.rating
347 }
348 },
349
f8b2c1b4 350 err => this.notifier.error(err.message)
2186386c 351 )
d38b8281
C
352 }
353
597a9266
C
354 private async onVideoFetched (
355 video: VideoDetails,
356 videoCaptions: VideoCaption[],
5efab546 357 urlOptions: CustomizationOptions & { playerMode: PlayerMode }
597a9266 358 ) {
df98563e 359 this.video = video
2f4c784a 360 this.videoCaptions = videoCaptions
92fb909c 361
c448d412
C
362 // Re init attributes
363 this.descriptionLoading = false
364 this.completeDescriptionShown = false
6d88de72 365 this.remoteServerDown = false
f0a39880 366 this.currentTime = undefined
c448d412 367
72675ebe 368 this.videoWatchPlaylist.updatePlaylistIndex(video)
e2f01c47 369
5efab546 370 let startTime = timeToInt(urlOptions.startTime) || (this.video.userHistory ? this.video.userHistory.currentTime : 0)
43483d12 371 // If we are at the end of the video, reset the timer
6e46de09
C
372 if (this.video.duration - startTime <= 1) startTime = 0
373
e2f01c47 374 if (this.isVideoBlur(this.video)) {
22b59e80 375 const res = await this.confirmService.confirm(
989e526a
C
376 this.i18n('This video contains mature or explicit content. Are you sure you want to watch it?'),
377 this.i18n('Mature or explicit content')
d6e32a2e 378 )
60c2bc80 379 if (res === false) return this.location.back()
92fb909c
C
380 }
381
09edde40
C
382 // Flush old player if needed
383 this.flushPlayer()
b891f9bc 384
60c2bc80 385 // Build video element, because videojs removes it on dispose
e2f01c47 386 const playerElementWrapper = this.elementRef.nativeElement.querySelector('#videojs-wrapper')
b891f9bc
C
387 this.playerElement = document.createElement('video')
388 this.playerElement.className = 'video-js vjs-peertube-skin'
e7eb5b39 389 this.playerElement.setAttribute('playsinline', 'true')
b891f9bc
C
390 playerElementWrapper.appendChild(this.playerElement)
391
16f7022b
C
392 const playerCaptions = videoCaptions.map(c => ({
393 label: c.language.label,
394 language: c.language.id,
395 src: environment.apiUrl + c.captionPath
396 }))
397
6ec0b75b 398 const options: PeertubePlayerManagerOptions = {
2adfc7ea
C
399 common: {
400 autoplay: this.isAutoplay(),
6ec0b75b 401
2adfc7ea 402 playerElement: this.playerElement,
6ec0b75b
C
403 onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
404
2adfc7ea
C
405 videoDuration: this.video.duration,
406 enableHotkeys: true,
407 inactivityTimeout: 2500,
408 poster: this.video.previewUrl,
5efab546 409
2adfc7ea 410 startTime,
f0a39880 411 stopTime: urlOptions.stopTime,
5efab546
C
412 controls: urlOptions.controls,
413 muted: urlOptions.muted,
414 loop: urlOptions.loop,
415 subtitle: urlOptions.subtitle,
416
417 peertubeLink: urlOptions.peertubeLink,
2adfc7ea
C
418
419 theaterMode: true,
420 captions: videoCaptions.length !== 0,
2adfc7ea 421
4c72c1cd
C
422 videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE
423 ? this.videoService.getVideoViewUrl(this.video.uuid)
424 : null,
2adfc7ea
C
425 embedUrl: this.video.embedUrl,
426
427 language: this.localeId,
428
2adfc7ea
C
429 userWatching: this.user && this.user.videosHistoryEnabled === true ? {
430 url: this.videoService.getUserWatchingVideoUrl(this.video.uuid),
431 authorizationHeader: this.authService.getRequestHeaderValue()
432 } : undefined,
aa8b6df4 433
2adfc7ea
C
434 serverUrl: environment.apiUrl,
435
436 videoCaptions: playerCaptions
6ec0b75b
C
437 },
438
439 webtorrent: {
440 videoFiles: this.video.files
09209296 441 }
e945b184
C
442 }
443
65659166
C
444 let mode: PlayerMode
445
446 if (urlOptions.playerMode) {
447 if (urlOptions.playerMode === 'p2p-media-loader') mode = 'p2p-media-loader'
448 else mode = 'webtorrent'
449 } else {
450 if (this.video.hasHlsPlaylist()) mode = 'p2p-media-loader'
451 else mode = 'webtorrent'
452 }
597a9266
C
453
454 if (mode === 'p2p-media-loader') {
455 const hlsPlaylist = this.video.getHlsPlaylist()
e945b184 456
09209296
C
457 const p2pMediaLoader = {
458 playlistUrl: hlsPlaylist.playlistUrl,
459 segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
460 redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl),
461 trackerAnnounce: this.video.trackerUrls,
2adfc7ea 462 videoFiles: this.video.files
09209296
C
463 } as P2PMediaLoaderOptions
464
465 Object.assign(options, { p2pMediaLoader })
e945b184
C
466 }
467
e945b184 468 this.zone.runOutsideAngular(async () => {
bfbd9128 469 this.player = await PeertubePlayerManager.initialize(mode, options, player => this.player = player)
9a18a625 470
2adfc7ea 471 this.player.on('customError', ({ err }: { err: any }) => this.handleError(err))
f0a39880
C
472
473 this.player.on('timeupdate', () => {
474 this.currentTime = Math.floor(this.player.currentTime())
475 })
e2f01c47
C
476
477 this.player.one('ended', () => {
478 if (this.playlist) {
72675ebe 479 this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
e2f01c47
C
480 }
481 })
482
483 this.player.one('stopped', () => {
484 if (this.playlist) {
72675ebe 485 this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
e2f01c47
C
486 }
487 })
9a18a625
C
488
489 this.player.on('theaterChange', (_: any, enabled: boolean) => {
490 this.zone.run(() => this.theaterEnabled = enabled)
491 })
b891f9bc 492 })
22b59e80
C
493
494 this.setVideoDescriptionHTML()
495 this.setVideoLikesBarTooltipText()
496
497 this.setOpenGraphTags()
498 this.checkUserRating()
93cae479 499
c9e3eeed 500 this.hooks.runAction('action:video-watch.video.loaded', 'video-watch')
92fb909c
C
501 }
502
5c6d985f 503 private setRating (nextRating: UserVideoRateType) {
4c72c1cd
C
504 const ratingMethods: { [id in UserVideoRateType]: (id: number) => Observable<any> } = {
505 like: this.videoService.setVideoLike,
506 dislike: this.videoService.setVideoDislike,
507 none: this.videoService.unsetVideoLike
57a49263
BB
508 }
509
4c72c1cd 510 ratingMethods[nextRating].call(this.videoService, this.video.id)
2186386c
C
511 .subscribe(
512 () => {
513 // Update the video like attribute
514 this.updateVideoRating(this.userRating, nextRating)
515 this.userRating = nextRating
516 },
517
f8b2c1b4 518 (err: { message: string }) => this.notifier.error(err.message)
2186386c 519 )
57a49263
BB
520 }
521
5c6d985f 522 private updateVideoRating (oldRating: UserVideoRateType, newRating: UserVideoRateType) {
df98563e
C
523 let likesToIncrement = 0
524 let dislikesToIncrement = 0
d38b8281
C
525
526 if (oldRating) {
df98563e
C
527 if (oldRating === 'like') likesToIncrement--
528 if (oldRating === 'dislike') dislikesToIncrement--
d38b8281
C
529 }
530
df98563e
C
531 if (newRating === 'like') likesToIncrement++
532 if (newRating === 'dislike') dislikesToIncrement++
d38b8281 533
df98563e
C
534 this.video.likes += likesToIncrement
535 this.video.dislikes += dislikesToIncrement
20b40b19 536
22b59e80 537 this.video.buildLikeAndDislikePercents()
20b40b19 538 this.setVideoLikesBarTooltipText()
d38b8281
C
539 }
540
df98563e
C
541 private setOpenGraphTags () {
542 this.metaService.setTitle(this.video.name)
758b996d 543
df98563e 544 this.metaService.setTag('og:type', 'video')
3ec343a4 545
df98563e
C
546 this.metaService.setTag('og:title', this.video.name)
547 this.metaService.setTag('name', this.video.name)
3ec343a4 548
df98563e
C
549 this.metaService.setTag('og:description', this.video.description)
550 this.metaService.setTag('description', this.video.description)
3ec343a4 551
d38309c3 552 this.metaService.setTag('og:image', this.video.previewPath)
3ec343a4 553
df98563e 554 this.metaService.setTag('og:duration', this.video.duration.toString())
3ec343a4 555
df98563e 556 this.metaService.setTag('og:site_name', 'PeerTube')
3ec343a4 557
df98563e
C
558 this.metaService.setTag('og:url', window.location.href)
559 this.metaService.setTag('url', window.location.href)
3ec343a4 560 }
1f3e9fec 561
d4c6a3b9 562 private isAutoplay () {
bf079b7b
C
563 // We'll jump to the thread id, so do not play the video
564 if (this.route.snapshot.params['threadId']) return false
565
566 // Otherwise true by default
d4c6a3b9
C
567 if (!this.user) return true
568
569 // Be sure the autoPlay is set to false
570 return this.user.autoPlayVideo !== false
571 }
09edde40
C
572
573 private flushPlayer () {
574 // Remove player if it exists
575 if (this.player) {
536598cf
C
576 try {
577 this.player.dispose()
578 this.player = undefined
579 } catch (err) {
580 console.error('Cannot dispose player.', err)
581 }
09edde40
C
582 }
583 }
1c8ddbfa
C
584
585 private initHotkeys () {
586 this.hotkeys = [
4c72c1cd 587 new Hotkey('shift+l', () => {
1c8ddbfa
C
588 this.setLike()
589 return false
590 }, undefined, this.i18n('Like the video')),
4c72c1cd
C
591
592 new Hotkey('shift+d', () => {
1c8ddbfa
C
593 this.setDislike()
594 return false
595 }, undefined, this.i18n('Dislike the video')),
4c72c1cd
C
596
597 new Hotkey('shift+s', () => {
598 this.subscribeButton.subscribed ? this.subscribeButton.unsubscribe() : this.subscribeButton.subscribe()
1c8ddbfa
C
599 return false
600 }, undefined, this.i18n('Subscribe to the account'))
601 ]
602 if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys)
603 }
dc8bc31b 604}