-import { Observable } from 'rxjs'
-import { catchError, map, switchMap } from 'rxjs/operators'
+import { SortMeta } from 'primeng/api'
+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, RestService, ServerService, UserService } from '@app/core'
+import { AuthService, ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core'
import { objectToFormData } from '@app/helpers'
import {
BooleanBothQuery,
UserVideoRateType,
UserVideoRateUpdate,
Video as VideoServerModel,
+ VideoChannel as VideoChannelServerModel,
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'
import { Video } from './video.model'
export type CommonVideoParams = {
- videoPagination: ComponentPaginationLight
- sort: VideoSortField
- filter?: VideoFilter
+ videoPagination?: ComponentPaginationLight
+ sort: VideoSortField | SortMeta
+ include?: VideoInclude
+ isLocal?: boolean
categoryOneOf?: number[]
languageOneOf?: string[]
+ privacyOneOf?: VideoPrivacy[]
isLive?: boolean
skipCount?: boolean
+
// FIXME: remove?
nsfwPolicy?: NSFWPolicyType
nsfw?: BooleanBothQuery
@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,
) {}
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)),
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)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
- getMyVideos (videoPagination: ComponentPaginationLight, sort: VideoSortField, search?: string): Observable<ResultList<Video>> {
- const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
+ getMyVideos (options: {
+ videoPagination: ComponentPaginationLight
+ sort: VideoSortField
+ userChannels?: VideoChannelServerModel[]
+ search?: string
+ }): Observable<ResultList<Video>> {
+ const { videoPagination, sort, userChannels = [], search } = options
+
+ const pagination = this.restService.componentToRestPagination(videoPagination)
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
isLive: {
prefix: 'isLive:',
isBoolean: true
+ },
+ channelId: {
+ prefix: 'channel:',
+ handler: (name: string) => {
+ const channel = userChannels.find(c => c.name === name)
+
+ if (channel) return channel.id
+
+ return undefined
+ }
}
})
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) {
)
}
- removeVideo (id: number) {
- return this.authHttp
- .delete(VideoService.BASE_VIDEO_URL + id)
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(err => this.restExtractor.handleError(err))
- )
+ removeVideo (idArg: number | number[]) {
+ const ids = Array.isArray(idArg) ? idArg : [ idArg ]
+
+ return from(ids)
+ .pipe(
+ 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))
+ )
}
loadCompleteDescription (descriptionPath: string) {
)
}
+ 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: number) {
return this.setVideoRate(id, 'like')
}
: 'both'
}
- private setVideoRate (id: number, rateType: UserVideoRateType) {
- const url = VideoService.BASE_VIDEO_URL + id + '/rate'
- const body: UserVideoRateUpdate = {
- rating: rateType
- }
+ buildCommonVideosParams (options: CommonVideoParams & { params: HttpParams }) {
+ const {
+ params,
+ videoPagination,
+ sort,
+ isLocal,
+ include,
+ categoryOneOf,
+ languageOneOf,
+ privacyOneOf,
+ skipCount,
+ nsfwPolicy,
+ isLive,
+ nsfw
+ } = options
+
+ const pagination = videoPagination
+ ? this.restService.componentToRestPagination(videoPagination)
+ : undefined
+
+ let newParams = this.restService.addRestGetParams(params, pagination, this.buildListSort(sort))
- return this.authHttp
- .put(url, body)
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(err => this.restExtractor.handleError(err))
- )
- }
-
- private buildCommonVideosParams (options: CommonVideoParams & { params: HttpParams }) {
- const { params, videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfwPolicy, isLive, nsfw } = options
-
- const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
- let newParams = this.restService.addRestGetParams(params, pagination, sort)
-
- if (filter) newParams = newParams.set('filter', filter)
if (skipCount) newParams = newParams.set('skipCount', skipCount + '')
+ if (isLocal) newParams = newParams.set('isLocal', isLocal)
+ if (include) newParams = newParams.set('include', include)
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 (privacyOneOf) newParams = this.restService.addArrayParams(newParams, 'privacyOneOf', privacyOneOf)
return newParams
}
+
+ private buildListSort (sortArg: VideoSortField | SortMeta) {
+ const sort = this.restService.buildSortString(sortArg)
+
+ 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 sort
+ }
+ }
+
+ 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(catchError(err => this.restExtractor.handleError(err)))
+ }
}