7cc98c77a1764f207187f2ea87262bc36f921d3a
[oweals/peertube.git] / client / src / app / shared / video / video.service.ts
1 import { catchError, map, switchMap } from 'rxjs/operators'
2 import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'
3 import { Injectable } from '@angular/core'
4 import { Observable } from 'rxjs'
5 import { Video as VideoServerModel, VideoDetails as VideoDetailsServerModel } from '../../../../../shared'
6 import { ResultList } from '../../../../../shared/models/result-list.model'
7 import { UserVideoRate, UserVideoRateUpdate, VideoFilter, VideoRateType, VideoUpdate } from '../../../../../shared/models/videos'
8 import { FeedFormat } from '../../../../../shared/models/feeds/feed-format.enum'
9 import { environment } from '../../../environments/environment'
10 import { ComponentPagination } from '../rest/component-pagination.model'
11 import { RestExtractor } from '../rest/rest-extractor.service'
12 import { RestService } from '../rest/rest.service'
13 import { UserService } from '../users/user.service'
14 import { VideoSortField } from './sort-field.type'
15 import { VideoDetails } from './video-details.model'
16 import { VideoEdit } from './video-edit.model'
17 import { Video } from './video.model'
18 import { objectToFormData } from '@app/shared/misc/utils'
19 import { Account } from '@app/shared/account/account.model'
20 import { AccountService } from '@app/shared/account/account.service'
21 import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
22 import { ServerService } from '@app/core'
23 import { UserSubscriptionService } from '@app/shared/user-subscription'
24 import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
25
26 @Injectable()
27 export class VideoService {
28   static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
29   static BASE_FEEDS_URL = environment.apiUrl + '/feeds/videos.'
30
31   constructor (
32     private authHttp: HttpClient,
33     private restExtractor: RestExtractor,
34     private restService: RestService,
35     private serverService: ServerService
36   ) {}
37
38   getVideoViewUrl (uuid: string) {
39     return VideoService.BASE_VIDEO_URL + uuid + '/views'
40   }
41
42   getVideo (uuid: string): Observable<VideoDetails> {
43     return this.serverService.localeObservable
44                .pipe(
45                  switchMap(translations => {
46                    return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
47                               .pipe(map(videoHash => ({ videoHash, translations })))
48                  }),
49                  map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)),
50                  catchError(err => this.restExtractor.handleError(err))
51                )
52   }
53
54   updateVideo (video: VideoEdit) {
55     const language = video.language || null
56     const licence = video.licence || null
57     const category = video.category || null
58     const description = video.description || null
59     const support = video.support || null
60     const scheduleUpdate = video.scheduleUpdate || null
61
62     const body: VideoUpdate = {
63       name: video.name,
64       category,
65       licence,
66       language,
67       support,
68       description,
69       channelId: video.channelId,
70       privacy: video.privacy,
71       tags: video.tags,
72       nsfw: video.nsfw,
73       waitTranscoding: video.waitTranscoding,
74       commentsEnabled: video.commentsEnabled,
75       thumbnailfile: video.thumbnailfile,
76       previewfile: video.previewfile,
77       scheduleUpdate
78     }
79
80     const data = objectToFormData(body)
81
82     return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, data)
83                .pipe(
84                  map(this.restExtractor.extractDataBool),
85                  catchError(err => this.restExtractor.handleError(err))
86                )
87   }
88
89   uploadVideo (video: FormData) {
90     const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true })
91
92     return this.authHttp
93                .request<{ video: { id: number, uuid: string } }>(req)
94                .pipe(catchError(err => this.restExtractor.handleError(err)))
95   }
96
97   getMyVideos (videoPagination: ComponentPagination, sort: VideoSortField): Observable<{ videos: Video[], totalVideos: number }> {
98     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
99
100     let params = new HttpParams()
101     params = this.restService.addRestGetParams(params, pagination, sort)
102
103     return this.authHttp
104                .get<ResultList<Video>>(UserService.BASE_USERS_URL + '/me/videos', { params })
105                .pipe(
106                  switchMap(res => this.extractVideos(res)),
107                  catchError(err => this.restExtractor.handleError(err))
108                )
109   }
110
111   getAccountVideos (
112     account: Account,
113     videoPagination: ComponentPagination,
114     sort: VideoSortField
115   ): Observable<{ videos: Video[], totalVideos: number }> {
116     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
117
118     let params = new HttpParams()
119     params = this.restService.addRestGetParams(params, pagination, sort)
120
121     return this.authHttp
122                .get<ResultList<Video>>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params })
123                .pipe(
124                  switchMap(res => this.extractVideos(res)),
125                  catchError(err => this.restExtractor.handleError(err))
126                )
127   }
128
129   getVideoChannelVideos (
130     videoChannel: VideoChannel,
131     videoPagination: ComponentPagination,
132     sort: VideoSortField
133   ): Observable<{ videos: Video[], totalVideos: number }> {
134     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
135
136     let params = new HttpParams()
137     params = this.restService.addRestGetParams(params, pagination, sort)
138
139     return this.authHttp
140                .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/videos', { params })
141                .pipe(
142                  switchMap(res => this.extractVideos(res)),
143                  catchError(err => this.restExtractor.handleError(err))
144                )
145   }
146
147   getUserSubscriptionVideos (
148     videoPagination: ComponentPagination,
149     sort: VideoSortField
150   ): Observable<{ videos: Video[], totalVideos: number }> {
151     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
152
153     let params = new HttpParams()
154     params = this.restService.addRestGetParams(params, pagination, sort)
155
156     return this.authHttp
157                .get<ResultList<Video>>(UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/videos', { params })
158                .pipe(
159                  switchMap(res => this.extractVideos(res)),
160                  catchError(err => this.restExtractor.handleError(err))
161                )
162   }
163
164   getVideos (
165     videoPagination: ComponentPagination,
166     sort: VideoSortField,
167     filter?: VideoFilter,
168     categoryOneOf?: number
169   ): Observable<{ videos: Video[], totalVideos: number }> {
170     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
171
172     let params = new HttpParams()
173     params = this.restService.addRestGetParams(params, pagination, sort)
174
175     if (filter) {
176       params = params.set('filter', filter)
177     }
178
179     if (categoryOneOf) {
180       params = params.set('categoryOneOf', categoryOneOf + '')
181     }
182
183     return this.authHttp
184                .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
185                .pipe(
186                  switchMap(res => this.extractVideos(res)),
187                  catchError(err => this.restExtractor.handleError(err))
188                )
189   }
190
191   buildBaseFeedUrls (params: HttpParams) {
192     const feeds = [
193       {
194         label: 'rss 2.0',
195         url: VideoService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase()
196       },
197       {
198         label: 'atom 1.0',
199         url: VideoService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase()
200       },
201       {
202         label: 'json 1.0',
203         url: VideoService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase()
204       }
205     ]
206
207     if (params && params.keys().length !== 0) {
208       for (const feed of feeds) {
209         feed.url += '?' + params.toString()
210       }
211     }
212
213     return feeds
214   }
215
216   getVideoFeedUrls (sort: VideoSortField, filter?: VideoFilter, categoryOneOf?: number) {
217     let params = this.restService.addRestGetParams(new HttpParams(), undefined, sort)
218
219     if (filter) params = params.set('filter', filter)
220
221     if (categoryOneOf) params = params.set('categoryOneOf', categoryOneOf + '')
222
223     return this.buildBaseFeedUrls(params)
224   }
225
226   getAccountFeedUrls (accountId: number) {
227     let params = this.restService.addRestGetParams(new HttpParams())
228     params = params.set('accountId', accountId.toString())
229
230     return this.buildBaseFeedUrls(params)
231   }
232
233   getVideoChannelFeedUrls (videoChannelId: number) {
234     let params = this.restService.addRestGetParams(new HttpParams())
235     params = params.set('videoChannelId', videoChannelId.toString())
236
237     return this.buildBaseFeedUrls(params)
238   }
239
240   removeVideo (id: number) {
241     return this.authHttp
242                .delete(VideoService.BASE_VIDEO_URL + id)
243                .pipe(
244                  map(this.restExtractor.extractDataBool),
245                  catchError(err => this.restExtractor.handleError(err))
246                )
247   }
248
249   loadCompleteDescription (descriptionPath: string) {
250     return this.authHttp
251                .get(environment.apiUrl + descriptionPath)
252                .pipe(
253                  map(res => res[ 'description' ]),
254                  catchError(err => this.restExtractor.handleError(err))
255                )
256   }
257
258   setVideoLike (id: number) {
259     return this.setVideoRate(id, 'like')
260   }
261
262   setVideoDislike (id: number) {
263     return this.setVideoRate(id, 'dislike')
264   }
265
266   unsetVideoLike (id: number) {
267     return this.setVideoRate(id, 'none')
268   }
269
270   getUserVideoRating (id: number) {
271     const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating'
272
273     return this.authHttp.get<UserVideoRate>(url)
274                .pipe(catchError(err => this.restExtractor.handleError(err)))
275   }
276
277   extractVideos (result: ResultList<VideoServerModel>) {
278     return this.serverService.localeObservable
279                .pipe(
280                  map(translations => {
281                    const videosJson = result.data
282                    const totalVideos = result.total
283                    const videos: Video[] = []
284
285                    for (const videoJson of videosJson) {
286                      videos.push(new Video(videoJson, translations))
287                    }
288
289                    return { videos, totalVideos }
290                  })
291                )
292   }
293
294   private setVideoRate (id: number, rateType: VideoRateType) {
295     const url = VideoService.BASE_VIDEO_URL + id + '/rate'
296     const body: UserVideoRateUpdate = {
297       rating: rateType
298     }
299
300     return this.authHttp
301                .put(url, body)
302                .pipe(
303                  map(this.restExtractor.extractDataBool),
304                  catchError(err => this.restExtractor.handleError(err))
305                )
306   }
307 }