diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts index dca746f4e..67752c15a 100644 --- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts +++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts @@ -64,7 +64,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit { label: $localize`Switch video block to manual`, handler: videoBlock => { this.videoBlocklistService.unblockVideo(videoBlock.video.id).pipe( - switchMap(_ => this.videoBlocklistService.blockVideo(videoBlock.video.id, undefined, true)) + switchMap(_ => this.videoBlocklistService.blockVideo([ { videoId: videoBlock.video.id, unfederate: true } ])) ).subscribe({ next: () => { this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`) diff --git a/client/src/app/+admin/overview/videos/video-list.component.html b/client/src/app/+admin/overview/videos/video-list.component.html index 6b0dc3abd..9b536ec11 100644 --- a/client/src/app/+admin/overview/videos/video-list.component.html +++ b/client/src/app/+admin/overview/videos/video-list.component.html @@ -126,3 +126,5 @@ + + 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 0f98a5d33..7f268bb23 100644 --- a/client/src/app/+admin/overview/videos/video-list.component.ts +++ b/client/src/app/+admin/overview/videos/video-list.component.ts @@ -1,10 +1,11 @@ import { SortMeta } from 'primeng/api' import { finalize } from 'rxjs/operators' -import { Component, OnInit } from '@angular/core' +import { Component, OnInit, ViewChild } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { AuthService, ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' 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' @@ -15,6 +16,8 @@ import { VideoAdminService } from './video-admin.service' styleUrls: [ './video-list.component.scss' ] }) export class VideoListComponent extends RestTable implements OnInit { + @ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent + videos: Video[] = [] totalRecords = 0 @@ -48,7 +51,8 @@ export class VideoListComponent extends RestTable implements OnInit { private auth: AuthService, private notifier: Notifier, private videoService: VideoService, - private videoAdminService: VideoAdminService + private videoAdminService: VideoAdminService, + private videoBlockService: VideoBlockService ) { super() } @@ -68,6 +72,16 @@ export class VideoListComponent extends RestTable implements OnInit { label: $localize`Delete`, handler: videos => this.removeVideos(videos), isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO) + }, + { + label: $localize`Block`, + handler: videos => this.videoBlockModal.show(videos), + isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => !v.blacklisted) + }, + { + label: $localize`Unblock`, + handler: videos => this.unblockVideos(videos), + isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => v.blacklisted) } ] ] @@ -132,6 +146,10 @@ export class VideoListComponent extends RestTable implements OnInit { return files.reduce((p, f) => p += f.size, 0) } + onVideoBlocked () { + this.reloadData() + } + protected reloadData () { this.selectedVideos = [] @@ -160,7 +178,19 @@ export class VideoListComponent extends RestTable implements OnInit { this.videoService.removeVideo(videos.map(v => v.id)) .subscribe({ next: () => { - this.notifier.success($localize`${videos.length} videos deleted.`) + this.notifier.success($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($localize`Unblocked ${videos.length} videos.`) this.reloadData() }, diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html index d0eef7d4b..0d75a21d7 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html @@ -77,10 +77,6 @@
-
- Deleted -
-
{{ abuse.video.name }} diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts index b902726fa..08cf297cc 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts @@ -338,7 +338,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit { label: $localize`Block video`, isDisplayed: abuse => abuse.video && !abuse.video.deleted && !abuse.video.blacklisted, handler: abuse => { - this.videoBlocklistService.blockVideo(abuse.video.id, undefined, abuse.video.channel.isLocal) + this.videoBlocklistService.blockVideo([ { videoId: abuse.video.id, unfederate: abuse.video.channel.isLocal } ]) .subscribe({ next: () => { this.notifier.success($localize`Video blocked.`) diff --git a/client/src/app/shared/shared-moderation/video-block.component.html b/client/src/app/shared/shared-moderation/video-block.component.html index 5e9e8493c..e5793f2ca 100644 --- a/client/src/app/shared/shared-moderation/video-block.component.html +++ b/client/src/app/shared/shared-moderation/video-block.component.html @@ -1,7 +1,14 @@ @@ -18,19 +25,20 @@
-
+
- This will ask remote instances to delete it + This will ask remote instances to delete local videos + This will ask remote instances to delete this video
- - Blocking this live will automatically terminate the live stream. + + Blocking a live will automatically terminate the live stream.
@@ -39,7 +47,7 @@ (click)="hide()" (key.enter)="hide()" > - +
diff --git a/client/src/app/shared/shared-moderation/video-block.component.ts b/client/src/app/shared/shared-moderation/video-block.component.ts index a6180dd14..400913f02 100644 --- a/client/src/app/shared/shared-moderation/video-block.component.ts +++ b/client/src/app/shared/shared-moderation/video-block.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' +import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' import { Notifier } from '@app/core' import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' import { Video } from '@app/shared/shared-main' @@ -13,12 +13,12 @@ import { VideoBlockService } from './video-block.service' styleUrls: [ './video-block.component.scss' ] }) export class VideoBlockComponent extends FormReactive implements OnInit { - @Input() video: Video = null - @ViewChild('modal', { static: true }) modal: NgbModal @Output() videoBlocked = new EventEmitter() + videos: Video[] + error: string = null private openedModal: NgbModalRef @@ -41,7 +41,25 @@ export class VideoBlockComponent extends FormReactive implements OnInit { }, defaultValues) } - show () { + isMultiple () { + return this.videos.length > 1 + } + + getSingleVideo () { + return this.videos[0] + } + + hasLive () { + return this.videos.some(v => v.isLive) + } + + hasLocal () { + return this.videos.some(v => v.isLocal) + } + + show (videos: Video[]) { + this.videos = videos + this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) } @@ -51,17 +69,30 @@ export class VideoBlockComponent extends FormReactive implements OnInit { } block () { - const reason = this.form.value['reason'] || undefined - const unfederate = this.video.isLocal ? this.form.value['unfederate'] : undefined + const options = this.videos.map(v => ({ + videoId: v.id, + reason: this.form.value['reason'] || undefined, + unfederate: v.isLocal + ? this.form.value['unfederate'] + : undefined + })) - this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate) + this.videoBlocklistService.blockVideo(options) .subscribe({ next: () => { - this.notifier.success($localize`Video blocked.`) + const message = this.isMultiple + ? $localize`Blocked ${this.videos.length} videos.` + : $localize`Blocked ${this.getSingleVideo().name}` + + this.notifier.success(message) this.hide() - this.video.blacklisted = true - this.video.blacklistedReason = reason + for (const o of options) { + const video = this.videos.find(v => v.id === o.videoId) + + video.blacklisted = true + video.blacklistedReason = o.reason + } this.videoBlocked.emit() }, diff --git a/client/src/app/shared/shared-moderation/video-block.service.ts b/client/src/app/shared/shared-moderation/video-block.service.ts index c22ceefcc..5dfb0d7d4 100644 --- a/client/src/app/shared/shared-moderation/video-block.service.ts +++ b/client/src/app/shared/shared-moderation/video-block.service.ts @@ -63,16 +63,20 @@ export class VideoBlockService { ) } - blockVideo (videoId: number, reason: string, unfederate: boolean) { - const body = { - unfederate, - reason - } + blockVideo (options: { + videoId: number + reason?: string + unfederate: boolean + }[]) { + return observableFrom(options) + .pipe( + concatMap(({ videoId, unfederate, reason }) => { + const body = { unfederate, reason } - return this.authHttp.post(VideoBlockService.BASE_VIDEOS_URL + videoId + '/blacklist', body) - .pipe( - map(this.restExtractor.extractDataBool), - catchError(res => this.restExtractor.handleError(res)) - ) + return this.authHttp.post(VideoBlockService.BASE_VIDEOS_URL + videoId + '/blacklist', body) + }), + toArray(), + catchError(res => this.restExtractor.handleError(res)) + ) } } diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.html b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.html index 7a6394202..3fea2a8a4 100644 --- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.html +++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.html @@ -20,6 +20,6 @@ - + diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts index 790ae2a5e..eff56b40e 100644 --- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts @@ -128,7 +128,7 @@ export class VideoActionsDropdownComponent implements OnChanges { showBlockModal () { this.modalOpened.emit() - this.videoBlockModal.show() + this.videoBlockModal.show([ this.video ]) } showLiveInfoModal (video: Video) {