558db95431119c5e4a69daf5242bbeec07d31b47
[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   viewVideo (uuid: string): Observable<boolean> {
55     return this.authHttp.post(this.getVideoViewUrl(uuid), {})
56                .pipe(
57                  map(this.restExtractor.extractDataBool),
58                  catchError(err => this.restExtractor.handleError(err))
59                )
60   }
61
62   updateVideo (video: VideoEdit) {
63     const language = video.language || null
64     const licence = video.licence || null
65     const category = video.category || null
66     const description = video.description || null
67     const support = video.support || null
68     const scheduleUpdate = video.scheduleUpdate || null
69
70     const body: VideoUpdate = {
71       name: video.name,
72       category,
73       licence,
74       language,
75       support,
76       description,
77       channelId: video.channelId,
78       privacy: video.privacy,
79       tags: video.tags,
80       nsfw: video.nsfw,
81       waitTranscoding: video.waitTranscoding,
82       commentsEnabled: video.commentsEnabled,
83       thumbnailfile: video.thumbnailfile,
84       previewfile: video.previewfile,
85       scheduleUpdate
86     }
87
88     const data = objectToFormData(body)
89
90     return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, data)
91                .pipe(
92                  map(this.restExtractor.extractDataBool),
93                  catchError(err => this.restExtractor.handleError(err))
94                )
95   }
96
97   uploadVideo (video: FormData) {
98     const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true })
99
100     return this.authHttp
101                .request<{ video: { id: number, uuid: string } }>(req)
102                .pipe(catchError(err => this.restExtractor.handleError(err)))
103   }
104
105   getMyVideos (videoPagination: ComponentPagination, sort: VideoSortField): Observable<{ videos: Video[], totalVideos: number }> {
106     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
107
108     let params = new HttpParams()
109     params = this.restService.addRestGetParams(params, pagination, sort)
110
111     return this.authHttp
112                .get<ResultList<Video>>(UserService.BASE_USERS_URL + '/me/videos', { params })
113                .pipe(
114                  switchMap(res => this.extractVideos(res)),
115                  catchError(err => this.restExtractor.handleError(err))
116                )
117   }
118
119   getAccountVideos (
120     account: Account,
121     videoPagination: ComponentPagination,
122     sort: VideoSortField
123   ): Observable<{ videos: Video[], totalVideos: number }> {
124     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
125
126     let params = new HttpParams()
127     params = this.restService.addRestGetParams(params, pagination, sort)
128
129     return this.authHttp
130                .get<ResultList<Video>>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params })
131                .pipe(
132                  switchMap(res => this.extractVideos(res)),
133                  catchError(err => this.restExtractor.handleError(err))
134                )
135   }
136
137   getVideoChannelVideos (
138     videoChannel: VideoChannel,
139     videoPagination: ComponentPagination,
140     sort: VideoSortField
141   ): Observable<{ videos: Video[], totalVideos: number }> {
142     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
143
144     let params = new HttpParams()
145     params = this.restService.addRestGetParams(params, pagination, sort)
146
147     return this.authHttp
148                .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/videos', { params })
149                .pipe(
150                  switchMap(res => this.extractVideos(res)),
151                  catchError(err => this.restExtractor.handleError(err))
152                )
153   }
154
155   getUserSubscriptionVideos (
156     videoPagination: ComponentPagination,
157     sort: VideoSortField
158   ): Observable<{ videos: Video[], totalVideos: number }> {
159     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
160
161     let params = new HttpParams()
162     params = this.restService.addRestGetParams(params, pagination, sort)
163
164     return this.authHttp
165                .get<ResultList<Video>>(UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/videos', { params })
166                .pipe(
167                  switchMap(res => this.extractVideos(res)),
168                  catchError(err => this.restExtractor.handleError(err))
169                )
170   }
171
172   getVideos (
173     videoPagination: ComponentPagination,
174     sort: VideoSortField,
175     filter?: VideoFilter,
176     categoryOneOf?: number
177   ): Observable<{ videos: Video[], totalVideos: number }> {
178     const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
179
180     let params = new HttpParams()
181     params = this.restService.addRestGetParams(params, pagination, sort)
182
183     if (filter) {
184       params = params.set('filter', filter)
185     }
186
187     if (categoryOneOf) {
188       params = params.set('categoryOneOf', categoryOneOf + '')
189     }
190
191     return this.authHttp
192                .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
193                .pipe(
194                  switchMap(res => this.extractVideos(res)),
195                  catchError(err => this.restExtractor.handleError(err))
196                )
197   }
198
199   buildBaseFeedUrls (params: HttpParams) {
200     const feeds = [
201       {
202         label: 'rss 2.0',
203         url: VideoService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase()
204       },
205       {
206         label: 'atom 1.0',
207         url: VideoService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase()
208       },
209       {
210         label: 'json 1.0',
211         url: VideoService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase()
212       }
213     ]
214
215     if (params && params.keys().length !== 0) {
216       for (const feed of feeds) {
217         feed.url += '?' + params.toString()
218       }
219     }
220
221     return feeds
222   }
223
224   getVideoFeedUrls (sort: VideoSortField, filter?: VideoFilter, categoryOneOf?: number) {
225     let params = this.restService.addRestGetParams(new HttpParams(), undefined, sort)
226
227     if (filter) params = params.set('filter', filter)
228
229     if (categoryOneOf) params = params.set('categoryOneOf', categoryOneOf + '')
230
231     return this.buildBaseFeedUrls(params)
232   }
233
234   getAccountFeedUrls (accountId: number) {
235     let params = this.restService.addRestGetParams(new HttpParams())
236     params = params.set('accountId', accountId.toString())
237
238     return this.buildBaseFeedUrls(params)
239   }
240
241   getVideoChannelFeedUrls (videoChannelId: number) {
242     let params = this.restService.addRestGetParams(new HttpParams())
243     params = params.set('videoChannelId', videoChannelId.toString())
244
245     return this.buildBaseFeedUrls(params)
246   }
247
248   removeVideo (id: number) {
249     return this.authHttp
250                .delete(VideoService.BASE_VIDEO_URL + id)
251                .pipe(
252                  map(this.restExtractor.extractDataBool),
253                  catchError(err => this.restExtractor.handleError(err))
254                )
255   }
256
257   loadCompleteDescription (descriptionPath: string) {
258     return this.authHttp
259                .get(environment.apiUrl + descriptionPath)
260                .pipe(
261                  map(res => res[ 'description' ]),
262                  catchError(err => this.restExtractor.handleError(err))
263                )
264   }
265
266   setVideoLike (id: number) {
267     return this.setVideoRate(id, 'like')
268   }
269
270   setVideoDislike (id: number) {
271     return this.setVideoRate(id, 'dislike')
272   }
273
274   unsetVideoLike (id: number) {
275     return this.setVideoRate(id, 'none')
276   }
277
278   getUserVideoRating (id: number) {
279     const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating'
280
281     return this.authHttp.get<UserVideoRate>(url)
282                .pipe(catchError(err => this.restExtractor.handleError(err)))
283   }
284
285   extractVideos (result: ResultList<VideoServerModel>) {
286     return this.serverService.localeObservable
287                .pipe(
288                  map(translations => {
289                    const videosJson = result.data
290                    const totalVideos = result.total
291                    const videos: Video[] = []
292
293                    for (const videoJson of videosJson) {
294                      videos.push(new Video(videoJson, translations))
295                    }
296
297                    return { videos, totalVideos }
298                  })
299                )
300   }
301
302   private setVideoRate (id: number, rateType: VideoRateType) {
303     const url = VideoService.BASE_VIDEO_URL + id + '/rate'
304     const body: UserVideoRateUpdate = {
305       rating: rateType
306     }
307
308     return this.authHttp
309                .put(url, body)
310                .pipe(
311                  map(this.restExtractor.extractDataBool),
312                  catchError(err => this.restExtractor.handleError(err))
313                )
314   }
315 }