]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add ability to bulk block videos
authorChocobozzz <me@florianbigard.com>
Wed, 17 Nov 2021 10:18:49 +0000 (11:18 +0100)
committerChocobozzz <me@florianbigard.com>
Wed, 17 Nov 2021 10:18:49 +0000 (11:18 +0100)
client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
client/src/app/+admin/overview/videos/video-list.component.html
client/src/app/+admin/overview/videos/video-list.component.ts
client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-moderation/video-block.component.html
client/src/app/shared/shared-moderation/video-block.component.ts
client/src/app/shared/shared-moderation/video-block.service.ts
client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.html
client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts

index dca746f4edd1cbaf6ff5444d9527bcd668def172..67752c15a60297832f0f659fa2c836d959420ee7 100644 (file)
@@ -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.`)
index 6b0dc3abd241203f6c9de49b7a4b759965e2d3b5..9b536ec11a5b269fc7d53c23ba1e98608cafed73 100644 (file)
     </tr>
   </ng-template>
 </p-table>
+
+<my-video-block #videoBlockModal (videoBlocked)="onVideoBlocked()"></my-video-block>
index 0f98a5d33b5d698ff1541caa963f1de828d3dee7..7f268bb23c2952a4fcac0e8b5e247af86a8c1ff2 100644 (file)
@@ -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()
         },
 
index d0eef7d4b26e72f7568f3eeba93f9b9e82109bc4..0d75a21d7e5e5b009188ab3ea1e727b2023d67a3 100644 (file)
 
         <td *ngIf="abuse.video.deleted" class="c-hand" [pRowToggler]="abuse">
           <div class="table-video" i18n-title title="Video was deleted">
-            <div class="table-video-image">
-              <span i18n>Deleted</span>
-            </div>
-
             <div class="table-video-text">
               <div>
                 {{ abuse.video.name }}
index b902726faa1fb42c4bf6960ece364df6911fa7f6..08cf297ccfa4c091c9287a85d50abcc8b714acba 100644 (file)
@@ -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.`)
index 5e9e8493ca5f60beb1f5f7cecf9b6d7520d7061b..e5793f2cab09380547d6422467b5b92a67ecbb75 100644 (file)
@@ -1,7 +1,14 @@
 <ng-template #modal>
   <div class="modal-header">
-    <h4 i18n class="modal-title" *ngIf="!video.isLive">Block video "{{ video.name }}"</h4>
-    <h4 i18n class="modal-title" *ngIf="video.isLive">Block live "{{ video.name }}"</h4>
+    <ng-container *ngIf="isMultiple()">
+      <h4 i18n class="modal-title">Block {{ videos.length }} videos</h4>
+    </ng-container>
+
+    <ng-container *ngIf="!isMultiple()">
+      <h4 i18n class="modal-title" *ngIf="!getSingleVideo().isLive">Block video "{{ getSingleVideo().name }}"</h4>
+      <h4 i18n class="modal-title" *ngIf="getSingleVideo().isLive">Block live "{{ getSingleVideo().name }}"</h4>
+    </ng-container>
+
     <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
   </div>
 
         </div>
       </div>
 
-      <div class="form-group" *ngIf="video.isLocal">
+      <div class="form-group" *ngIf="hasLocal()">
         <my-peertube-checkbox
           inputName="unfederate" formControlName="unfederate"
-          i18n-labelText labelText="Unfederate the video"
+          i18n-labelText labelText="Unfederate"
         >
           <ng-container ngProjectAs="description">
-            <span i18n>This will ask remote instances to delete it</span>
+            <span *ngIf="isMultiple()" i18n>This will ask remote instances to delete local videos</span>
+            <span *ngIf="!isMultiple()" i18n>This will ask remote instances to delete this video</span>
           </ng-container>
         </my-peertube-checkbox>
       </div>
 
-      <strong class="live-info" *ngIf="video.isLive" i18n>
-        Blocking this live will automatically terminate the live stream.
+      <strong class="live-info" *ngIf="hasLive()" i18n>
+        Blocking a live will automatically terminate the live stream.
       </strong>
 
       <div class="form-group inputs">
@@ -39,7 +47,7 @@
           (click)="hide()" (key.enter)="hide()"
         >
 
-        <input type="submit" i18n-value value="Submit" class="peertube-button orange-button" [disabled]="!form.valid" />
+        <input type="submit" i18n-value value="Block" class="peertube-button orange-button" [disabled]="!form.valid" />
       </div>
     </form>
 
index a6180dd14680872064536f83a395c5f948dfb2b8..400913f0239998904ca5bc2e58d65cb722f3e927 100644 (file)
@@ -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()
           },
index c22ceefcce4d6d5a16a75ba89c3636c2e62a9d81..5dfb0d7d451d3574ab8bb034a877b176aadb8d8a 100644 (file)
@@ -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))
+      )
   }
 }
index 7a63942029bf1655a68c58dd25155927bcde8536..3fea2a8a4f915fa72ad0d2353fc0d9425f171b7d 100644 (file)
@@ -20,6 +20,6 @@
 
   <my-video-download #videoDownloadModal></my-video-download>
   <my-video-report #videoReportModal [video]="video"></my-video-report>
-  <my-video-block #videoBlockModal [video]="video" (videoBlocked)="onVideoBlocked()"></my-video-block>
+  <my-video-block #videoBlockModal (videoBlocked)="onVideoBlocked()"></my-video-block>
   <my-live-stream-information #liveStreamInformationModal *ngIf="displayOptions.liveInfo"></my-live-stream-information>
 </ng-container>
index 790ae2a5e574e38c17a274ff378d354fefc4ec42..eff56b40e49802f788998812033e9ea7a1f4dda5 100644 (file)
@@ -128,7 +128,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
   showBlockModal () {
     this.modalOpened.emit()
 
-    this.videoBlockModal.show()
+    this.videoBlockModal.show([ this.video ])
   }
 
   showLiveInfoModal (video: Video) {