diff options
author | Chocobozzz <me@florianbigard.com> | 2020-06-23 14:10:17 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-06-23 16:00:49 +0200 |
commit | 67ed6552b831df66713bac9e672738796128d33f (patch) | |
tree | 59c97d41e0b49d75a90aa3de987968ab9b1ff447 /client/src/app/shared/shared-main/video/video.service.ts | |
parent | 0c4bacbff53bc732f5a2677d62a6ead7752e2405 (diff) | |
download | PeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.gz PeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.zst PeerTube-67ed6552b831df66713bac9e672738796128d33f.zip |
Reorganize client shared modules
Diffstat (limited to 'client/src/app/shared/shared-main/video/video.service.ts')
-rw-r--r-- | client/src/app/shared/shared-main/video/video.service.ts | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts new file mode 100644 index 000000000..20d13fa10 --- /dev/null +++ b/client/src/app/shared/shared-main/video/video.service.ts | |||
@@ -0,0 +1,380 @@ | |||
1 | import { FfprobeData } from 'fluent-ffmpeg' | ||
2 | import { Observable } from 'rxjs' | ||
3 | import { catchError, map, switchMap } from 'rxjs/operators' | ||
4 | import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' | ||
5 | import { Injectable } from '@angular/core' | ||
6 | import { ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core' | ||
7 | import { objectToFormData } from '@app/helpers' | ||
8 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
9 | import { | ||
10 | FeedFormat, | ||
11 | NSFWPolicyType, | ||
12 | ResultList, | ||
13 | UserVideoRate, | ||
14 | UserVideoRateType, | ||
15 | UserVideoRateUpdate, | ||
16 | Video as VideoServerModel, | ||
17 | VideoConstant, | ||
18 | VideoDetails as VideoDetailsServerModel, | ||
19 | VideoFilter, | ||
20 | VideoPrivacy, | ||
21 | VideoSortField, | ||
22 | VideoUpdate | ||
23 | } from '@shared/models' | ||
24 | import { environment } from '../../../../environments/environment' | ||
25 | import { Account, AccountService } from '../account' | ||
26 | import { VideoChannel, VideoChannelService } from '../video-channel' | ||
27 | import { VideoDetails } from './video-details.model' | ||
28 | import { VideoEdit } from './video-edit.model' | ||
29 | import { Video } from './video.model' | ||
30 | |||
31 | export interface VideosProvider { | ||
32 | getVideos (parameters: { | ||
33 | videoPagination: ComponentPaginationLight, | ||
34 | sort: VideoSortField, | ||
35 | filter?: VideoFilter, | ||
36 | categoryOneOf?: number[], | ||
37 | languageOneOf?: string[] | ||
38 | nsfwPolicy: NSFWPolicyType | ||
39 | }): Observable<ResultList<Video>> | ||
40 | } | ||
41 | |||
42 | @Injectable() | ||
43 | export class VideoService implements VideosProvider { | ||
44 | static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' | ||
45 | static BASE_FEEDS_URL = environment.apiUrl + '/feeds/videos.' | ||
46 | |||
47 | constructor ( | ||
48 | private authHttp: HttpClient, | ||
49 | private restExtractor: RestExtractor, | ||
50 | private restService: RestService, | ||
51 | private serverService: ServerService, | ||
52 | private i18n: I18n | ||
53 | ) {} | ||
54 | |||
55 | getVideoViewUrl (uuid: string) { | ||
56 | return VideoService.BASE_VIDEO_URL + uuid + '/views' | ||
57 | } | ||
58 | |||
59 | getUserWatchingVideoUrl (uuid: string) { | ||
60 | return VideoService.BASE_VIDEO_URL + uuid + '/watching' | ||
61 | } | ||
62 | |||
63 | getVideo (options: { videoId: string }): Observable<VideoDetails> { | ||
64 | return this.serverService.getServerLocale() | ||
65 | .pipe( | ||
66 | switchMap(translations => { | ||
67 | return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + options.videoId) | ||
68 | .pipe(map(videoHash => ({ videoHash, translations }))) | ||
69 | }), | ||
70 | map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)), | ||
71 | catchError(err => this.restExtractor.handleError(err)) | ||
72 | ) | ||
73 | } | ||
74 | |||
75 | updateVideo (video: VideoEdit) { | ||
76 | const language = video.language || null | ||
77 | const licence = video.licence || null | ||
78 | const category = video.category || null | ||
79 | const description = video.description || null | ||
80 | const support = video.support || null | ||
81 | const scheduleUpdate = video.scheduleUpdate || null | ||
82 | const originallyPublishedAt = video.originallyPublishedAt || null | ||
83 | |||
84 | const body: VideoUpdate = { | ||
85 | name: video.name, | ||
86 | category, | ||
87 | licence, | ||
88 | language, | ||
89 | support, | ||
90 | description, | ||
91 | channelId: video.channelId, | ||
92 | privacy: video.privacy, | ||
93 | tags: video.tags, | ||
94 | nsfw: video.nsfw, | ||
95 | waitTranscoding: video.waitTranscoding, | ||
96 | commentsEnabled: video.commentsEnabled, | ||
97 | downloadEnabled: video.downloadEnabled, | ||
98 | thumbnailfile: video.thumbnailfile, | ||
99 | previewfile: video.previewfile, | ||
100 | scheduleUpdate, | ||
101 | originallyPublishedAt | ||
102 | } | ||
103 | |||
104 | const data = objectToFormData(body) | ||
105 | |||
106 | return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, data) | ||
107 | .pipe( | ||
108 | map(this.restExtractor.extractDataBool), | ||
109 | catchError(err => this.restExtractor.handleError(err)) | ||
110 | ) | ||
111 | } | ||
112 | |||
113 | uploadVideo (video: FormData) { | ||
114 | const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) | ||
115 | |||
116 | return this.authHttp | ||
117 | .request<{ video: { id: number, uuid: string } }>(req) | ||
118 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
119 | } | ||
120 | |||
121 | getMyVideos (videoPagination: ComponentPaginationLight, sort: VideoSortField, search?: string): Observable<ResultList<Video>> { | ||
122 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | ||
123 | |||
124 | let params = new HttpParams() | ||
125 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
126 | params = this.restService.addObjectParams(params, { search }) | ||
127 | |||
128 | return this.authHttp | ||
129 | .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params }) | ||
130 | .pipe( | ||
131 | switchMap(res => this.extractVideos(res)), | ||
132 | catchError(err => this.restExtractor.handleError(err)) | ||
133 | ) | ||
134 | } | ||
135 | |||
136 | getAccountVideos ( | ||
137 | account: Account, | ||
138 | videoPagination: ComponentPaginationLight, | ||
139 | sort: VideoSortField | ||
140 | ): Observable<ResultList<Video>> { | ||
141 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | ||
142 | |||
143 | let params = new HttpParams() | ||
144 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
145 | |||
146 | return this.authHttp | ||
147 | .get<ResultList<Video>>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params }) | ||
148 | .pipe( | ||
149 | switchMap(res => this.extractVideos(res)), | ||
150 | catchError(err => this.restExtractor.handleError(err)) | ||
151 | ) | ||
152 | } | ||
153 | |||
154 | getVideoChannelVideos ( | ||
155 | videoChannel: VideoChannel, | ||
156 | videoPagination: ComponentPaginationLight, | ||
157 | sort: VideoSortField, | ||
158 | nsfwPolicy?: NSFWPolicyType | ||
159 | ): Observable<ResultList<Video>> { | ||
160 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | ||
161 | |||
162 | let params = new HttpParams() | ||
163 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
164 | |||
165 | if (nsfwPolicy) { | ||
166 | params = params.set('nsfw', this.nsfwPolicyToParam(nsfwPolicy)) | ||
167 | } | ||
168 | |||
169 | return this.authHttp | ||
170 | .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/videos', { params }) | ||
171 | .pipe( | ||
172 | switchMap(res => this.extractVideos(res)), | ||
173 | catchError(err => this.restExtractor.handleError(err)) | ||
174 | ) | ||
175 | } | ||
176 | |||
177 | getVideos (parameters: { | ||
178 | videoPagination: ComponentPaginationLight, | ||
179 | sort: VideoSortField, | ||
180 | filter?: VideoFilter, | ||
181 | categoryOneOf?: number[], | ||
182 | languageOneOf?: string[], | ||
183 | skipCount?: boolean, | ||
184 | nsfwPolicy?: NSFWPolicyType | ||
185 | }): Observable<ResultList<Video>> { | ||
186 | const { videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfwPolicy } = parameters | ||
187 | |||
188 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | ||
189 | |||
190 | let params = new HttpParams() | ||
191 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
192 | |||
193 | if (filter) params = params.set('filter', filter) | ||
194 | if (skipCount) params = params.set('skipCount', skipCount + '') | ||
195 | |||
196 | if (nsfwPolicy) { | ||
197 | params = params.set('nsfw', this.nsfwPolicyToParam(nsfwPolicy)) | ||
198 | } | ||
199 | |||
200 | if (languageOneOf) { | ||
201 | for (const l of languageOneOf) { | ||
202 | params = params.append('languageOneOf[]', l) | ||
203 | } | ||
204 | } | ||
205 | |||
206 | if (categoryOneOf) { | ||
207 | for (const c of categoryOneOf) { | ||
208 | params = params.append('categoryOneOf[]', c + '') | ||
209 | } | ||
210 | } | ||
211 | |||
212 | return this.authHttp | ||
213 | .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params }) | ||
214 | .pipe( | ||
215 | switchMap(res => this.extractVideos(res)), | ||
216 | catchError(err => this.restExtractor.handleError(err)) | ||
217 | ) | ||
218 | } | ||
219 | |||
220 | buildBaseFeedUrls (params: HttpParams) { | ||
221 | const feeds = [ | ||
222 | { | ||
223 | format: FeedFormat.RSS, | ||
224 | label: 'rss 2.0', | ||
225 | url: VideoService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase() | ||
226 | }, | ||
227 | { | ||
228 | format: FeedFormat.ATOM, | ||
229 | label: 'atom 1.0', | ||
230 | url: VideoService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase() | ||
231 | }, | ||
232 | { | ||
233 | format: FeedFormat.JSON, | ||
234 | label: 'json 1.0', | ||
235 | url: VideoService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase() | ||
236 | } | ||
237 | ] | ||
238 | |||
239 | if (params && params.keys().length !== 0) { | ||
240 | for (const feed of feeds) { | ||
241 | feed.url += '?' + params.toString() | ||
242 | } | ||
243 | } | ||
244 | |||
245 | return feeds | ||
246 | } | ||
247 | |||
248 | getVideoFeedUrls (sort: VideoSortField, filter?: VideoFilter, categoryOneOf?: number[]) { | ||
249 | let params = this.restService.addRestGetParams(new HttpParams(), undefined, sort) | ||
250 | |||
251 | if (filter) params = params.set('filter', filter) | ||
252 | |||
253 | if (categoryOneOf) { | ||
254 | for (const c of categoryOneOf) { | ||
255 | params = params.append('categoryOneOf[]', c + '') | ||
256 | } | ||
257 | } | ||
258 | |||
259 | return this.buildBaseFeedUrls(params) | ||
260 | } | ||
261 | |||
262 | getAccountFeedUrls (accountId: number) { | ||
263 | let params = this.restService.addRestGetParams(new HttpParams()) | ||
264 | params = params.set('accountId', accountId.toString()) | ||
265 | |||
266 | return this.buildBaseFeedUrls(params) | ||
267 | } | ||
268 | |||
269 | getVideoChannelFeedUrls (videoChannelId: number) { | ||
270 | let params = this.restService.addRestGetParams(new HttpParams()) | ||
271 | params = params.set('videoChannelId', videoChannelId.toString()) | ||
272 | |||
273 | return this.buildBaseFeedUrls(params) | ||
274 | } | ||
275 | |||
276 | getVideoFileMetadata (metadataUrl: string) { | ||
277 | return this.authHttp | ||
278 | .get<FfprobeData>(metadataUrl) | ||
279 | .pipe( | ||
280 | catchError(err => this.restExtractor.handleError(err)) | ||
281 | ) | ||
282 | } | ||
283 | |||
284 | removeVideo (id: number) { | ||
285 | return this.authHttp | ||
286 | .delete(VideoService.BASE_VIDEO_URL + id) | ||
287 | .pipe( | ||
288 | map(this.restExtractor.extractDataBool), | ||
289 | catchError(err => this.restExtractor.handleError(err)) | ||
290 | ) | ||
291 | } | ||
292 | |||
293 | loadCompleteDescription (descriptionPath: string) { | ||
294 | return this.authHttp | ||
295 | .get<{ description: string }>(environment.apiUrl + descriptionPath) | ||
296 | .pipe( | ||
297 | map(res => res.description), | ||
298 | catchError(err => this.restExtractor.handleError(err)) | ||
299 | ) | ||
300 | } | ||
301 | |||
302 | setVideoLike (id: number) { | ||
303 | return this.setVideoRate(id, 'like') | ||
304 | } | ||
305 | |||
306 | setVideoDislike (id: number) { | ||
307 | return this.setVideoRate(id, 'dislike') | ||
308 | } | ||
309 | |||
310 | unsetVideoLike (id: number) { | ||
311 | return this.setVideoRate(id, 'none') | ||
312 | } | ||
313 | |||
314 | getUserVideoRating (id: number) { | ||
315 | const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating' | ||
316 | |||
317 | return this.authHttp.get<UserVideoRate>(url) | ||
318 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
319 | } | ||
320 | |||
321 | extractVideos (result: ResultList<VideoServerModel>) { | ||
322 | return this.serverService.getServerLocale() | ||
323 | .pipe( | ||
324 | map(translations => { | ||
325 | const videosJson = result.data | ||
326 | const totalVideos = result.total | ||
327 | const videos: Video[] = [] | ||
328 | |||
329 | for (const videoJson of videosJson) { | ||
330 | videos.push(new Video(videoJson, translations)) | ||
331 | } | ||
332 | |||
333 | return { total: totalVideos, data: videos } | ||
334 | }) | ||
335 | ) | ||
336 | } | ||
337 | |||
338 | explainedPrivacyLabels (privacies: VideoConstant<VideoPrivacy>[]) { | ||
339 | const base = [ | ||
340 | { | ||
341 | id: VideoPrivacy.PRIVATE, | ||
342 | label: this.i18n('Only I can see this video') | ||
343 | }, | ||
344 | { | ||
345 | id: VideoPrivacy.UNLISTED, | ||
346 | label: this.i18n('Only people with the private link can see this video') | ||
347 | }, | ||
348 | { | ||
349 | id: VideoPrivacy.PUBLIC, | ||
350 | label: this.i18n('Anyone can see this video') | ||
351 | }, | ||
352 | { | ||
353 | id: VideoPrivacy.INTERNAL, | ||
354 | label: this.i18n('Only users of this instance can see this video') | ||
355 | } | ||
356 | ] | ||
357 | |||
358 | return base.filter(o => !!privacies.find(p => p.id === o.id)) | ||
359 | } | ||
360 | |||
361 | nsfwPolicyToParam (nsfwPolicy: NSFWPolicyType) { | ||
362 | return nsfwPolicy === 'do_not_list' | ||
363 | ? 'false' | ||
364 | : 'both' | ||
365 | } | ||
366 | |||
367 | private setVideoRate (id: number, rateType: UserVideoRateType) { | ||
368 | const url = VideoService.BASE_VIDEO_URL + id + '/rate' | ||
369 | const body: UserVideoRateUpdate = { | ||
370 | rating: rateType | ||
371 | } | ||
372 | |||
373 | return this.authHttp | ||
374 | .put(url, body) | ||
375 | .pipe( | ||
376 | map(this.restExtractor.extractDataBool), | ||
377 | catchError(err => this.restExtractor.handleError(err)) | ||
378 | ) | ||
379 | } | ||
380 | } | ||