X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=client%2Fsrc%2Fapp%2F%2Badmin%2Foverview%2Fvideos%2Fvideo-list.component.ts;h=67e52d100e80cc47c3555fe861f807cb12d43140;hb=eaa529528cafcfb291009f9f99d296c81e792899;hp=a445bc209a813a978fc9e2fe2819d1a16639424d;hpb=33f6dce136ca6e969fe374efa099bee3f2a3599d;p=github%2FChocobozzz%2FPeerTube.git diff --git a/client/src/app/+admin/overview/videos/video-list.component.ts b/client/src/app/+admin/overview/videos/video-list.component.ts index a445bc209..67e52d100 100644 --- a/client/src/app/+admin/overview/videos/video-list.component.ts +++ b/client/src/app/+admin/overview/videos/video-list.component.ts @@ -1,11 +1,15 @@ import { SortMeta } from 'primeng/api' -import { Component, OnInit } from '@angular/core' +import { finalize } from 'rxjs/operators' +import { Component, OnInit, ViewChild } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { AuthService, ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' -import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' -import { UserRight } from '@shared/models' +import { prepareIcu } from '@app/helpers' import { AdvancedInputFilter } from '@app/shared/shared-forms' +import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' +import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation' import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature' +import { UserRight, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models' +import { VideoAdminService } from './video-admin.service' @Component({ selector: 'my-video-list', @@ -13,27 +17,19 @@ import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature' styleUrls: [ './video-list.component.scss' ] }) export class VideoListComponent extends RestTable implements OnInit { + @ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent + videos: Video[] = [] totalRecords = 0 - sort: SortMeta = { field: 'publishedAt', order: 1 } + sort: SortMeta = { field: 'publishedAt', order: -1 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 } bulkVideoActions: DropdownAction[][] = [] selectedVideos: Video[] = [] - inputFilters: AdvancedInputFilter[] = [ - { - title: $localize`Advanced filters`, - children: [ - { - queryParams: { search: 'local:true' }, - label: $localize`Only local videos` - } - ] - } - ] + inputFilters: AdvancedInputFilter[] videoActionsOptions: VideoActionsDisplayType = { playlist: false, @@ -44,16 +40,24 @@ export class VideoListComponent extends RestTable implements OnInit { report: false, duplicate: true, mute: true, - liveInfo: false + liveInfo: false, + removeFiles: true, + transcoding: true, + studio: true, + stats: true } + loading = true + constructor ( protected route: ActivatedRoute, protected router: Router, private confirmService: ConfirmService, private auth: AuthService, private notifier: Notifier, - private videoService: VideoService + private videoService: VideoService, + private videoAdminService: VideoAdminService, + private videoBlockService: VideoBlockService ) { super() } @@ -65,12 +69,53 @@ export class VideoListComponent extends RestTable implements OnInit { ngOnInit () { this.initialize() + this.inputFilters = this.videoAdminService.buildAdminInputFilter() + this.bulkVideoActions = [ [ { label: $localize`Delete`, handler: videos => this.removeVideos(videos), - isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO) + isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO), + iconName: 'delete' + }, + { + label: $localize`Block`, + handler: videos => this.videoBlockModal.show(videos), + isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => !v.blacklisted), + iconName: 'no' + }, + { + label: $localize`Unblock`, + handler: videos => this.unblockVideos(videos), + isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => v.blacklisted), + iconName: 'undo' + } + ], + [ + { + label: $localize`Run HLS transcoding`, + handler: videos => this.runTranscoding(videos, 'hls'), + isDisplayed: videos => videos.every(v => v.canRunTranscoding(this.authUser)), + iconName: 'cog' + }, + { + label: $localize`Run WebTorrent transcoding`, + handler: videos => this.runTranscoding(videos, 'webtorrent'), + isDisplayed: videos => videos.every(v => v.canRunTranscoding(this.authUser)), + iconName: 'cog' + }, + { + label: $localize`Delete HLS files`, + handler: videos => this.removeVideoFiles(videos, 'hls'), + isDisplayed: videos => videos.every(v => v.canRemoveFiles(this.authUser)), + iconName: 'delete' + }, + { + label: $localize`Delete WebTorrent files`, + handler: videos => this.removeVideoFiles(videos, 'webtorrent'), + isDisplayed: videos => videos.every(v => v.canRemoveFiles(this.authUser)), + iconName: 'delete' } ] ] @@ -84,36 +129,154 @@ export class VideoListComponent extends RestTable implements OnInit { return this.selectedVideos.length !== 0 } - onVideoRemoved () { - this.reloadData() + getPrivacyBadgeClass (video: Video) { + if (video.privacy.id === VideoPrivacy.PUBLIC) return 'badge-green' + + return 'badge-yellow' + } + + isUnpublished (video: Video) { + return video.state.id !== VideoState.LIVE_ENDED && video.state.id !== VideoState.PUBLISHED + } + + isAccountBlocked (video: Video) { + return video.blockedOwner + } + + isServerBlocked (video: Video) { + return video.blockedServer + } + + isVideoBlocked (video: Video) { + return video.blacklisted + } + + isImport (video: Video) { + return video.state.id === VideoState.TO_IMPORT + } + + isHLS (video: Video) { + const p = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) + if (!p) return false + + return p.files.length !== 0 + } + + isWebTorrent (video: Video) { + return video.files.length !== 0 + } + + getFilesSize (video: Video) { + let files = video.files + + if (this.isHLS(video)) { + files = files.concat(video.streamingPlaylists[0].files) + } + + return files.reduce((p, f) => p += f.size, 0) } - protected reloadData () { + reloadData () { this.selectedVideos = [] - this.videoService.getAdminVideos({ + this.loading = true + + this.videoAdminService.getAdminVideos({ pagination: this.pagination, sort: this.sort, search: this.search - }).subscribe({ - next: resultList => { - this.videos = resultList.data - this.totalRecords = resultList.total - }, + }).pipe(finalize(() => this.loading = false)) + .subscribe({ + next: resultList => { + this.videos = resultList.data + this.totalRecords = resultList.total + }, - error: err => this.notifier.error(err.message) - }) + error: err => this.notifier.error(err.message) + }) } private async removeVideos (videos: Video[]) { - const message = $localize`Are you sure you want to delete these ${videos.length} videos?` + const message = prepareIcu($localize`Are you sure you want to delete {count, plural, =1 {this video} other {these {count} videos}}?`)( + { count: videos.length }, + $localize`Are you sure you want to delete these ${videos.length} videos?` + ) + const res = await this.confirmService.confirm(message, $localize`Delete`) if (res === false) return this.videoService.removeVideo(videos.map(v => v.id)) .subscribe({ next: () => { - this.notifier.success($localize`${videos.length} videos deleted.`) + this.notifier.success( + prepareIcu($localize`Deleted {count, plural, =1 {1 video} other {{count} videos}}.`)( + { count: videos.length }, + $localize`Deleted ${videos.length} videos.` + ) + ) + + this.reloadData() + }, + + error: err => this.notifier.error(err.message) + }) + } + + private unblockVideos (videos: Video[]) { + this.videoBlockService.unblockVideo(videos.map(v => v.id)) + .subscribe({ + next: () => { + this.notifier.success( + prepareIcu($localize`Unblocked {count, plural, =1 {1 video} other {{count} videos}}.`)( + { count: videos.length }, + $localize`Unblocked ${videos.length} videos.` + ) + ) + + this.reloadData() + }, + + error: err => this.notifier.error(err.message) + }) + } + + private async removeVideoFiles (videos: Video[], type: 'hls' | 'webtorrent') { + let message: string + + if (type === 'hls') { + // eslint-disable-next-line max-len + message = prepareIcu($localize`Are you sure you want to delete {count, plural, =1 {1 HLS streaming playlist} other {{count} HLS streaming playlists}}?`)( + { count: videos.length }, + $localize`Are you sure you want to delete ${videos.length} HLS streaming playlists?` + ) + } else { + // eslint-disable-next-line max-len + message = prepareIcu($localize`Are you sure you want to delete WebTorrent files of {count, plural, =1 {1 video} other {{count} videos}}?`)( + { count: videos.length }, + $localize`Are you sure you want to delete WebTorrent files of ${videos.length} videos?` + ) + } + + const res = await this.confirmService.confirm(message, $localize`Delete`) + if (res === false) return + + this.videoService.removeVideoFiles(videos.map(v => v.id), type) + .subscribe({ + next: () => { + this.notifier.success($localize`Files were removed.`) + this.reloadData() + }, + + error: err => this.notifier.error(err.message) + }) + } + + private runTranscoding (videos: Video[], type: 'hls' | 'webtorrent') { + this.videoService.runTranscoding(videos.map(v => v.id), type) + .subscribe({ + next: () => { + this.notifier.success($localize`Transcoding jobs created.`) + this.reloadData() },