Adapt client with video channels
[oweals/peertube.git] / client / src / app / videos / +video-watch / video-watch.component.ts
1 import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'
2 import { ActivatedRoute, Router } from '@angular/router'
3 import { Observable } from 'rxjs/Observable'
4 import { Subscription } from 'rxjs/Subscription'
5
6 import videojs from 'video.js'
7 import '../../../assets/player/peertube-videojs-plugin'
8
9 import { MetaService } from '@ngx-meta/core'
10 import { NotificationsService } from 'angular2-notifications'
11
12 import { AuthService, ConfirmService } from '../../core'
13 import { VideoDownloadComponent } from './video-download.component'
14 import { VideoShareComponent } from './video-share.component'
15 import { VideoReportComponent } from './video-report.component'
16 import { VideoDetails, VideoService } from '../shared'
17 import { VideoBlacklistService } from '../../shared'
18 import { UserVideoRateType, VideoRateType } from '../../../../../shared'
19
20 @Component({
21   selector: 'my-video-watch',
22   templateUrl: './video-watch.component.html',
23   styleUrls: [ './video-watch.component.scss' ]
24 })
25 export class VideoWatchComponent implements OnInit, OnDestroy {
26   @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
27   @ViewChild('videoShareModal') videoShareModal: VideoShareComponent
28   @ViewChild('videoReportModal') videoReportModal: VideoReportComponent
29
30   downloadSpeed: number
31   error = false
32   loading = false
33   numPeers: number
34   player: videojs.Player
35   playerElement: HTMLMediaElement
36   uploadSpeed: number
37   userRating: UserVideoRateType = null
38   video: VideoDetails = null
39   videoPlayerLoaded = false
40   videoNotFound = false
41
42   private paramsSub: Subscription
43
44   constructor (
45     private elementRef: ElementRef,
46     private route: ActivatedRoute,
47     private router: Router,
48     private videoService: VideoService,
49     private videoBlacklistService: VideoBlacklistService,
50     private confirmService: ConfirmService,
51     private metaService: MetaService,
52     private authService: AuthService,
53     private notificationsService: NotificationsService
54   ) {}
55
56   ngOnInit () {
57     this.paramsSub = this.route.params.subscribe(routeParams => {
58       let uuid = routeParams['uuid']
59       this.videoService.getVideo(uuid).subscribe(
60         video => this.onVideoFetched(video),
61
62         error => {
63           this.videoNotFound = true
64           console.error(error)
65         }
66       )
67     })
68   }
69
70   ngOnDestroy () {
71     // Remove player if it exists
72     if (this.videoPlayerLoaded === true) {
73       videojs(this.playerElement).dispose()
74     }
75
76     // Unsubscribe subscriptions
77     this.paramsSub.unsubscribe()
78   }
79
80   setLike () {
81     if (this.isUserLoggedIn() === false) return
82     // Already liked this video
83     if (this.userRating === 'like') return
84
85     this.videoService.setVideoLike(this.video.id)
86                      .subscribe(
87                       () => {
88                         // Update the video like attribute
89                         this.updateVideoRating(this.userRating, 'like')
90                         this.userRating = 'like'
91                       },
92
93                       err => this.notificationsService.error('Error', err.message)
94                      )
95   }
96
97   setDislike () {
98     if (this.isUserLoggedIn() === false) return
99     // Already disliked this video
100     if (this.userRating === 'dislike') return
101
102     this.videoService.setVideoDislike(this.video.id)
103                      .subscribe(
104                       () => {
105                         // Update the video dislike attribute
106                         this.updateVideoRating(this.userRating, 'dislike')
107                         this.userRating = 'dislike'
108                       },
109
110                       err => this.notificationsService.error('Error', err.message)
111                      )
112   }
113
114   removeVideo (event: Event) {
115     event.preventDefault()
116
117     this.confirmService.confirm('Do you really want to delete this video?', 'Delete').subscribe(
118       res => {
119         if (res === false) return
120
121         this.videoService.removeVideo(this.video.id)
122                          .subscribe(
123                            status => {
124                              this.notificationsService.success('Success', `Video ${this.video.name} deleted.`)
125                              // Go back to the video-list.
126                              this.router.navigate(['/videos/list'])
127                            },
128
129                            error => this.notificationsService.error('Error', error.text)
130                           )
131       }
132     )
133   }
134
135   blacklistVideo (event: Event) {
136     event.preventDefault()
137
138     this.confirmService.confirm('Do you really want to blacklist this video ?', 'Blacklist').subscribe(
139       res => {
140         if (res === false) return
141
142         this.videoBlacklistService.blacklistVideo(this.video.id)
143                                   .subscribe(
144                                     status => {
145                                       this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`)
146                                       this.router.navigate(['/videos/list'])
147                                     },
148
149                                     error => this.notificationsService.error('Error', error.text)
150                                   )
151       }
152     )
153   }
154
155   showReportModal (event: Event) {
156     event.preventDefault()
157     this.videoReportModal.show()
158   }
159
160   showShareModal () {
161     this.videoShareModal.show()
162   }
163
164   showDownloadModal (event: Event) {
165     event.preventDefault()
166     this.videoDownloadModal.show()
167   }
168
169   isUserLoggedIn () {
170     return this.authService.isLoggedIn()
171   }
172
173   canUserUpdateVideo () {
174     return this.video.isUpdatableBy(this.authService.getUser())
175   }
176
177   isVideoRemovable () {
178     return this.video.isRemovableBy(this.authService.getUser())
179   }
180
181   isVideoBlacklistable () {
182     return this.video.isBlackistableBy(this.authService.getUser())
183   }
184
185   private handleError (err: any) {
186     const errorMessage: string = typeof err === 'string' ? err : err.message
187     let message = ''
188
189     if (errorMessage.indexOf('http error') !== -1) {
190       message = 'Cannot fetch video from server, maybe down.'
191     } else {
192       message = errorMessage
193     }
194
195     this.notificationsService.error('Error', message)
196   }
197
198   private checkUserRating () {
199     // Unlogged users do not have ratings
200     if (this.isUserLoggedIn() === false) return
201
202     this.videoService.getUserVideoRating(this.video.id)
203                      .subscribe(
204                        ratingObject => {
205                          if (ratingObject) {
206                            this.userRating = ratingObject.rating
207                          }
208                        },
209
210                        err => this.notificationsService.error('Error', err.message)
211                       )
212   }
213
214   private onVideoFetched (video: VideoDetails) {
215     this.video = video
216
217     let observable
218     if (this.video.isVideoNSFWForUser(this.authService.getUser())) {
219       observable = this.confirmService.confirm('This video is not safe for work. Are you sure you want to watch it?', 'NSFW')
220     } else {
221       observable = Observable.of(true)
222     }
223
224     observable.subscribe(
225       res => {
226         if (res === false) {
227
228           return this.router.navigate([ '/videos/list' ])
229         }
230
231         this.playerElement = this.elementRef.nativeElement.querySelector('#video-container')
232
233         const videojsOptions = {
234           controls: true,
235           autoplay: true,
236           plugins: {
237             peertube: {
238               videoFiles: this.video.files,
239               playerElement: this.playerElement,
240               autoplay: true,
241               peerTubeLink: false
242             }
243           }
244         }
245
246         this.videoPlayerLoaded = true
247
248         const self = this
249         videojs(this.playerElement, videojsOptions, function () {
250           self.player = this
251           this.on('customError', (event, data) => {
252             self.handleError(data.err)
253           })
254
255           this.on('torrentInfo', (event, data) => {
256             self.downloadSpeed = data.downloadSpeed
257             self.numPeers = data.numPeers
258             self.uploadSpeed = data.uploadSpeed
259           })
260         })
261
262         this.setOpenGraphTags()
263         this.checkUserRating()
264       }
265     )
266   }
267
268   private updateVideoRating (oldRating: UserVideoRateType, newRating: VideoRateType) {
269     let likesToIncrement = 0
270     let dislikesToIncrement = 0
271
272     if (oldRating) {
273       if (oldRating === 'like') likesToIncrement--
274       if (oldRating === 'dislike') dislikesToIncrement--
275     }
276
277     if (newRating === 'like') likesToIncrement++
278     if (newRating === 'dislike') dislikesToIncrement++
279
280     this.video.likes += likesToIncrement
281     this.video.dislikes += dislikesToIncrement
282   }
283
284   private setOpenGraphTags () {
285     this.metaService.setTitle(this.video.name)
286
287     this.metaService.setTag('og:type', 'video')
288
289     this.metaService.setTag('og:title', this.video.name)
290     this.metaService.setTag('name', this.video.name)
291
292     this.metaService.setTag('og:description', this.video.description)
293     this.metaService.setTag('description', this.video.description)
294
295     this.metaService.setTag('og:image', this.video.previewPath)
296
297     this.metaService.setTag('og:duration', this.video.duration.toString())
298
299     this.metaService.setTag('og:site_name', 'PeerTube')
300
301     this.metaService.setTag('og:url', window.location.href)
302     this.metaService.setTag('url', window.location.href)
303   }
304 }