Add concept of video state, and add ability to wait transcoding before
[oweals/peertube.git] / client / src / app / +my-account / my-account-videos / my-account-videos.component.ts
1 import { from as observableFrom, Observable } from 'rxjs'
2 import { concatAll, tap } from 'rxjs/operators'
3 import { Component, OnDestroy, OnInit } from '@angular/core'
4 import { ActivatedRoute, Router } from '@angular/router'
5 import { Location } from '@angular/common'
6 import { immutableAssign } from '@app/shared/misc/utils'
7 import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
8 import { NotificationsService } from 'angular2-notifications'
9 import { AuthService } from '../../core/auth'
10 import { ConfirmService } from '../../core/confirm'
11 import { AbstractVideoList } from '../../shared/video/abstract-video-list'
12 import { Video } from '../../shared/video/video.model'
13 import { VideoService } from '../../shared/video/video.service'
14 import { I18n } from '@ngx-translate/i18n-polyfill'
15 import { VideoState } from '../../../../../shared/models/videos'
16
17 @Component({
18   selector: 'my-account-videos',
19   templateUrl: './my-account-videos.component.html',
20   styleUrls: [ './my-account-videos.component.scss' ]
21 })
22 export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
23   titlePage: string
24   currentRoute = '/my-account/videos'
25   checkedVideos: { [ id: number ]: boolean } = {}
26   pagination: ComponentPagination = {
27     currentPage: 1,
28     itemsPerPage: 5,
29     totalItems: null
30   }
31
32   protected baseVideoWidth = -1
33   protected baseVideoHeight = 155
34
35   constructor (
36     protected router: Router,
37     protected route: ActivatedRoute,
38     protected authService: AuthService,
39     protected notificationsService: NotificationsService,
40     protected confirmService: ConfirmService,
41     protected location: Location,
42     protected i18n: I18n,
43     private videoService: VideoService
44   ) {
45     super()
46
47     this.titlePage = this.i18n('My videos')
48   }
49
50   ngOnInit () {
51     super.ngOnInit()
52   }
53
54   ngOnDestroy () {
55     super.ngOnDestroy()
56   }
57
58   abortSelectionMode () {
59     this.checkedVideos = {}
60   }
61
62   isInSelectionMode () {
63     return Object.keys(this.checkedVideos).some(k => this.checkedVideos[ k ] === true)
64   }
65
66   getVideosObservable (page: number) {
67     const newPagination = immutableAssign(this.pagination, { currentPage: page })
68
69     return this.videoService.getMyVideos(newPagination, this.sort)
70   }
71
72   generateSyndicationList () {
73     throw new Error('Method not implemented.')
74   }
75
76   async deleteSelectedVideos () {
77     const toDeleteVideosIds = Object.keys(this.checkedVideos)
78                                     .filter(k => this.checkedVideos[ k ] === true)
79                                     .map(k => parseInt(k, 10))
80
81     const res = await this.confirmService.confirm(
82       this.i18n('Do you really want to delete {{deleteLength}} videos?', { deleteLength: toDeleteVideosIds.length }),
83       this.i18n('Delete')
84     )
85     if (res === false) return
86
87     const observables: Observable<any>[] = []
88     for (const videoId of toDeleteVideosIds) {
89       const o = this.videoService.removeVideo(videoId)
90                     .pipe(tap(() => this.spliceVideosById(videoId)))
91
92       observables.push(o)
93     }
94
95     observableFrom(observables)
96       .pipe(concatAll())
97       .subscribe(
98         res => {
99           this.notificationsService.success(
100             this.i18n('Success'),
101             this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length })
102           )
103
104           this.abortSelectionMode()
105           this.reloadVideos()
106         },
107
108         err => this.notificationsService.error(this.i18n('Error'), err.message)
109       )
110   }
111
112   async deleteVideo (video: Video) {
113     const res = await this.confirmService.confirm(
114       this.i18n('Do you really want to delete {{videoName}}?', { videoName: video.name }),
115       this.i18n('Delete')
116     )
117     if (res === false) return
118
119     this.videoService.removeVideo(video.id)
120         .subscribe(
121           status => {
122             this.notificationsService.success(
123               this.i18n('Success'),
124               this.i18n('Video {{videoName}} deleted.', { videoName: video.name })
125             )
126             this.reloadVideos()
127           },
128
129           error => this.notificationsService.error(this.i18n('Error'), error.message)
130         )
131   }
132
133   getStateLabel (video: Video) {
134     if (video.state.id === VideoState.PUBLISHED) return this.i18n('Published')
135
136     if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) return this.i18n('Waiting transcoding')
137     if (video.state.id === VideoState.TO_TRANSCODE) return this.i18n('To transcode')
138
139     return this.i18n('Unknown state')
140   }
141
142   protected buildVideoHeight () {
143     // In account videos, the video height is fixed
144     return this.baseVideoHeight
145   }
146
147   private spliceVideosById (id: number) {
148     for (const key of Object.keys(this.loadedPages)) {
149       const videos = this.loadedPages[ key ]
150       const index = videos.findIndex(v => v.id === id)
151
152       if (index !== -1) {
153         videos.splice(index, 1)
154         return
155       }
156     }
157   }
158 }