]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/app/shared/shared-main/video/video.service.ts
Merge branch 'release/4.2.0' into develop
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / shared-main / video / video.service.ts
index 9e3aa1e6a0093847d7c9be53838ce3d3ecf0bc91..4fbc4f7f62ae3389177a9cd0f11c9459bf3770a8 100644 (file)
@@ -1,9 +1,9 @@
 import { SortMeta } from 'primeng/api'
-import { from, Observable } from 'rxjs'
+import { from, Observable, of } from 'rxjs'
 import { catchError, concatMap, map, switchMap, toArray } from 'rxjs/operators'
 import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'
 import { Injectable } from '@angular/core'
-import { ComponentPaginationLight, RestExtractor, RestPagination, RestService, ServerService, UserService } from '@app/core'
+import { AuthService, ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core'
 import { objectToFormData } from '@app/helpers'
 import {
   BooleanBothQuery,
@@ -18,11 +18,13 @@ import {
   VideoConstant,
   VideoDetails as VideoDetailsServerModel,
   VideoFileMetadata,
-  VideoFilter,
+  VideoInclude,
   VideoPrivacy,
   VideoSortField,
+  VideoTranscodingCreate,
   VideoUpdate
 } from '@shared/models'
+import { VideoSource } from '@shared/models/videos/video-source'
 import { environment } from '../../../../environments/environment'
 import { Account } from '../account/account.model'
 import { AccountService } from '../account/account.service'
@@ -34,11 +36,14 @@ import { Video } from './video.model'
 export type CommonVideoParams = {
   videoPagination?: ComponentPaginationLight
   sort: VideoSortField | SortMeta
-  filter?: VideoFilter
+  include?: VideoInclude
+  isLocal?: boolean
   categoryOneOf?: number[]
   languageOneOf?: string[]
+  privacyOneOf?: VideoPrivacy[]
   isLive?: boolean
   skipCount?: boolean
+
   // FIXME: remove?
   nsfwPolicy?: NSFWPolicyType
   nsfw?: BooleanBothQuery
@@ -46,11 +51,12 @@ export type CommonVideoParams = {
 
 @Injectable()
 export class VideoService {
-  static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
+  static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos'
   static BASE_FEEDS_URL = environment.apiUrl + '/feeds/videos.'
   static BASE_SUBSCRIPTION_FEEDS_URL = environment.apiUrl + '/feeds/subscriptions.'
 
   constructor (
+    private auth: AuthService,
     private authHttp: HttpClient,
     private restExtractor: RestExtractor,
     private restService: RestService,
@@ -58,18 +64,14 @@ export class VideoService {
   ) {}
 
   getVideoViewUrl (uuid: string) {
-    return VideoService.BASE_VIDEO_URL + uuid + '/views'
-  }
-
-  getUserWatchingVideoUrl (uuid: string) {
-    return VideoService.BASE_VIDEO_URL + uuid + '/watching'
+    return `${VideoService.BASE_VIDEO_URL}/${uuid}/views`
   }
 
   getVideo (options: { videoId: string }): Observable<VideoDetails> {
     return this.serverService.getServerLocale()
                .pipe(
                  switchMap(translations => {
-                   return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + options.videoId)
+                   return this.authHttp.get<VideoDetailsServerModel>(`${VideoService.BASE_VIDEO_URL}/${options.videoId}`)
                               .pipe(map(videoHash => ({ videoHash, translations })))
                  }),
                  map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)),
@@ -109,15 +111,12 @@ export class VideoService {
 
     const data = objectToFormData(body)
 
-    return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, data)
-               .pipe(
-                 map(this.restExtractor.extractDataBool),
-                 catchError(err => this.restExtractor.handleError(err))
-               )
+    return this.authHttp.put(`${VideoService.BASE_VIDEO_URL}/${video.id}`, data)
+               .pipe(catchError(err => this.restExtractor.handleError(err)))
   }
 
   uploadVideo (video: FormData) {
-    const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true })
+    const req = new HttpRequest('POST', `${VideoService.BASE_VIDEO_URL}/upload`, video, { reportProgress: true })
 
     return this.authHttp
                .request<{ video: { id: number, uuid: string } }>(req)
@@ -201,31 +200,6 @@ export class VideoService {
                )
   }
 
-  getAdminVideos (
-    parameters: Omit<CommonVideoParams, 'filter'> & { pagination: RestPagination, search?: string }
-  ): Observable<ResultList<Video>> {
-    const { pagination, search } = parameters
-
-    let params = new HttpParams()
-    params = this.buildCommonVideosParams({ params, ...parameters })
-
-    params = params.set('start', pagination.start.toString())
-                   .set('count', pagination.count.toString())
-
-    if (search) {
-      params = this.buildAdminParamsFromSearch(search, params)
-    }
-
-    if (!params.has('filter')) params = params.set('filter', 'all')
-
-    return this.authHttp
-               .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
-               .pipe(
-                 switchMap(res => this.extractVideos(res)),
-                 catchError(err => this.restExtractor.handleError(err))
-               )
-  }
-
   getVideos (parameters: CommonVideoParams): Observable<ResultList<Video>> {
     let params = new HttpParams()
     params = this.buildCommonVideosParams({ params, ...parameters })
@@ -266,10 +240,10 @@ export class VideoService {
     return feeds
   }
 
-  getVideoFeedUrls (sort: VideoSortField, filter?: VideoFilter, categoryOneOf?: number[]) {
+  getVideoFeedUrls (sort: VideoSortField, isLocal: boolean, categoryOneOf?: number[]) {
     let params = this.restService.addRestGetParams(new HttpParams(), undefined, sort)
 
-    if (filter) params = params.set('filter', filter)
+    if (isLocal) params = params.set('isLocal', isLocal)
 
     if (categoryOneOf) {
       for (const c of categoryOneOf) {
@@ -315,7 +289,27 @@ export class VideoService {
 
     return from(ids)
       .pipe(
-        concatMap(id => this.authHttp.delete(VideoService.BASE_VIDEO_URL + id)),
+        concatMap(id => this.authHttp.delete(`${VideoService.BASE_VIDEO_URL}/${id}`)),
+        toArray(),
+        catchError(err => this.restExtractor.handleError(err))
+      )
+  }
+
+  removeVideoFiles (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
+    return from(videoIds)
+      .pipe(
+        concatMap(id => this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + id + '/' + type)),
+        toArray(),
+        catchError(err => this.restExtractor.handleError(err))
+      )
+  }
+
+  runTranscoding (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
+    const body: VideoTranscodingCreate = { transcodingType: type }
+
+    return from(videoIds)
+      .pipe(
+        concatMap(id => this.authHttp.post(VideoService.BASE_VIDEO_URL + '/' + id + '/transcoding', body)),
         toArray(),
         catchError(err => this.restExtractor.handleError(err))
       )
@@ -330,19 +324,33 @@ export class VideoService {
                )
   }
 
-  setVideoLike (id: number) {
+  getSource (videoId: number) {
+    return this.authHttp
+               .get<{ source: VideoSource }>(VideoService.BASE_VIDEO_URL + '/' + videoId + '/source')
+               .pipe(
+                 catchError(err => {
+                   if (err.status === 404) {
+                     return of(undefined)
+                   }
+
+                   this.restExtractor.handleError(err)
+                 })
+               )
+  }
+
+  setVideoLike (id: string) {
     return this.setVideoRate(id, 'like')
   }
 
-  setVideoDislike (id: number) {
+  setVideoDislike (id: string) {
     return this.setVideoRate(id, 'dislike')
   }
 
-  unsetVideoLike (id: number) {
+  unsetVideoLike (id: string) {
     return this.setVideoRate(id, 'none')
   }
 
-  getUserVideoRating (id: number) {
+  getUserVideoRating (id: string) {
     const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating'
 
     return this.authHttp.get<UserVideoRate>(url)
@@ -406,28 +414,16 @@ export class VideoService {
       : 'both'
   }
 
-  private setVideoRate (id: number, rateType: UserVideoRateType) {
-    const url = VideoService.BASE_VIDEO_URL + id + '/rate'
-    const body: UserVideoRateUpdate = {
-      rating: rateType
-    }
-
-    return this.authHttp
-               .put(url, body)
-               .pipe(
-                 map(this.restExtractor.extractDataBool),
-                 catchError(err => this.restExtractor.handleError(err))
-               )
-  }
-
-  private buildCommonVideosParams (options: CommonVideoParams & { params: HttpParams }) {
+  buildCommonVideosParams (options: CommonVideoParams & { params: HttpParams }) {
     const {
       params,
       videoPagination,
       sort,
-      filter,
+      isLocal,
+      include,
       categoryOneOf,
       languageOneOf,
+      privacyOneOf,
       skipCount,
       nsfwPolicy,
       isLive,
@@ -438,32 +434,46 @@ export class VideoService {
       ? this.restService.componentToRestPagination(videoPagination)
       : undefined
 
-    let newParams = this.restService.addRestGetParams(params, pagination, sort)
+    let newParams = this.restService.addRestGetParams(params, pagination, this.buildListSort(sort))
 
-    if (filter) newParams = newParams.set('filter', filter)
     if (skipCount) newParams = newParams.set('skipCount', skipCount + '')
 
-    if (isLive) newParams = newParams.set('isLive', isLive)
-    if (nsfw) newParams = newParams.set('nsfw', nsfw)
-    if (nsfwPolicy) newParams = newParams.set('nsfw', this.nsfwPolicyToParam(nsfwPolicy))
-    if (languageOneOf) newParams = this.restService.addArrayParams(newParams, 'languageOneOf', languageOneOf)
-    if (categoryOneOf) newParams = this.restService.addArrayParams(newParams, 'categoryOneOf', categoryOneOf)
+    if (isLocal !== undefined) newParams = newParams.set('isLocal', isLocal)
+    if (include !== undefined) newParams = newParams.set('include', include)
+    if (isLive !== undefined) newParams = newParams.set('isLive', isLive)
+    if (nsfw !== undefined) newParams = newParams.set('nsfw', nsfw)
+    if (nsfwPolicy !== undefined) newParams = newParams.set('nsfw', this.nsfwPolicyToParam(nsfwPolicy))
+    if (languageOneOf !== undefined) newParams = this.restService.addArrayParams(newParams, 'languageOneOf', languageOneOf)
+    if (categoryOneOf !== undefined) newParams = this.restService.addArrayParams(newParams, 'categoryOneOf', categoryOneOf)
+    if (privacyOneOf !== undefined) newParams = this.restService.addArrayParams(newParams, 'privacyOneOf', privacyOneOf)
 
     return newParams
   }
 
-  private buildAdminParamsFromSearch (search: string, params: HttpParams) {
-    const filters = this.restService.parseQueryStringFilter(search, {
-      filter: {
-        prefix: 'local:',
-        handler: v => {
-          if (v === 'true') return 'all-local'
+  private buildListSort (sortArg: VideoSortField | SortMeta) {
+    const sort = this.restService.buildSortString(sortArg)
 
-          return 'all'
-        }
+    if (typeof sort === 'string') {
+      // Silently use the best algorithm for logged in users if they chose the hot algorithm
+      if (
+        this.auth.isLoggedIn() &&
+        (sort === 'hot' || sort === '-hot')
+      ) {
+        return sort.replace('hot', 'best')
       }
-    })
 
-    return this.restService.addObjectParams(params, filters)
+      return sort
+    }
+  }
+
+  private setVideoRate (id: string, rateType: UserVideoRateType) {
+    const url = `${VideoService.BASE_VIDEO_URL}/${id}/rate`
+    const body: UserVideoRateUpdate = {
+      rating: rateType
+    }
+
+    return this.authHttp
+               .put(url, body)
+               .pipe(catchError(err => this.restExtractor.handleError(err)))
   }
 }