From 7ccddd7b5250bd25a917a6e77e58b87b9484a2a4 Mon Sep 17 00:00:00 2001 From: Josh Morel Date: Tue, 2 Apr 2019 05:26:47 -0400 Subject: add quarantine videos feature (#1637) * add quarantine videos feature * increase Notification settings test timeout to 20000ms. was completing 7000 locally but timing out after 10000 on travis * fix quarantine video test issues -propagate misspelling -remove skip from server/tests/client.ts * WIP use blacklist for moderator video approval instead of video.quarantine boolean * finish auto-blacklist feature --- client/src/app/+admin/admin.module.ts | 8 +- .../edit-custom-config.component.html | 17 ++++ .../edit-custom-config.component.ts | 7 ++ client/src/app/+admin/moderation/index.ts | 1 + .../+admin/moderation/moderation.component.html | 4 +- .../app/+admin/moderation/moderation.component.ts | 11 ++- .../src/app/+admin/moderation/moderation.routes.ts | 17 ++++ .../moderation/video-auto-blacklist-list/index.ts | 1 + .../video-auto-blacklist-list.component.html | 49 ++++++++++ .../video-auto-blacklist-list.component.scss | 94 +++++++++++++++++++ .../video-auto-blacklist-list.component.ts | 100 +++++++++++++++++++++ .../video-blacklist-list.component.ts | 13 ++- ...y-account-notification-preferences.component.ts | 3 + .../my-account-videos.component.scss | 1 + client/src/app/core/server/server.service.ts | 7 ++ .../app/shared/users/user-notification.model.ts | 6 ++ .../shared/users/user-notifications.component.html | 8 ++ .../video-blacklist/video-blacklist.service.ts | 51 ++++++++--- 18 files changed, 381 insertions(+), 17 deletions(-) create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts (limited to 'client/src') diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index f7f347105..282d59634 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts @@ -11,7 +11,12 @@ import { JobsComponent } from './jobs/job.component' import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' import { JobService } from './jobs/shared/job.service' import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users' -import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' +import { + ModerationCommentModalComponent, + VideoAbuseListComponent, + VideoBlacklistListComponent, + VideoAutoBlacklistListComponent +} from './moderation' import { ModerationComponent } from '@app/+admin/moderation/moderation.component' import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' @@ -42,6 +47,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f ModerationComponent, VideoBlacklistListComponent, VideoAbuseListComponent, + VideoAutoBlacklistListComponent, ModerationCommentModalComponent, InstanceServerBlocklistComponent, InstanceAccountBlocklistComponent, diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 6b654c67d..00a0d98f8 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html @@ -161,6 +161,23 @@ +
Auto-blacklist
+ + + + + +
+ +
+ +
+
+
+
Administrator
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index 45605e0fe..d8eb55da7 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts @@ -117,6 +117,13 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { threads: this.customConfigValidatorsService.TRANSCODING_THREADS, allowAdditionalExtensions: null, resolutions: {} + }, + autoBlacklist: { + videos: { + ofUsers: { + enabled: null + } + } } } diff --git a/client/src/app/+admin/moderation/index.ts b/client/src/app/+admin/moderation/index.ts index 66e2c6a39..3c683a28c 100644 --- a/client/src/app/+admin/moderation/index.ts +++ b/client/src/app/+admin/moderation/index.ts @@ -1,4 +1,5 @@ export * from './video-abuse-list' +export * from './video-auto-blacklist-list' export * from './video-blacklist-list' export * from './moderation.component' export * from './moderation.routes' diff --git a/client/src/app/+admin/moderation/moderation.component.html b/client/src/app/+admin/moderation/moderation.component.html index 01457936c..b70027957 100644 --- a/client/src/app/+admin/moderation/moderation.component.html +++ b/client/src/app/+admin/moderation/moderation.component.html @@ -4,7 +4,9 @@
Video abuses - Blacklisted videos + {{ autoBlacklistVideosEnabled ? 'Manually blacklisted videos' : 'Blacklisted videos' }} + + Auto-blacklisted videos Muted accounts diff --git a/client/src/app/+admin/moderation/moderation.component.ts b/client/src/app/+admin/moderation/moderation.component.ts index 2b2618933..47154af3f 100644 --- a/client/src/app/+admin/moderation/moderation.component.ts +++ b/client/src/app/+admin/moderation/moderation.component.ts @@ -1,13 +1,20 @@ import { Component } from '@angular/core' import { UserRight } from '../../../../../shared' -import { AuthService } from '@app/core/auth/auth.service' +import { AuthService, ServerService } from '@app/core' @Component({ templateUrl: './moderation.component.html', styleUrls: [ './moderation.component.scss' ] }) export class ModerationComponent { - constructor (private auth: AuthService) {} + autoBlacklistVideosEnabled: boolean + + constructor ( + private auth: AuthService, + private serverService: ServerService + ) { + this.autoBlacklistVideosEnabled = this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled + } hasVideoAbusesRight () { return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) diff --git a/client/src/app/+admin/moderation/moderation.routes.ts b/client/src/app/+admin/moderation/moderation.routes.ts index 6f6dde290..a024f2bee 100644 --- a/client/src/app/+admin/moderation/moderation.routes.ts +++ b/client/src/app/+admin/moderation/moderation.routes.ts @@ -3,6 +3,7 @@ import { UserRight } from '../../../../../shared' import { UserRightGuard } from '@app/core' import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' +import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list' import { ModerationComponent } from '@app/+admin/moderation/moderation.component' import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' @@ -26,6 +27,11 @@ export const ModerationRoutes: Routes = [ redirectTo: 'video-blacklist/list', pathMatch: 'full' }, + { + path: 'video-auto-blacklist', + redirectTo: 'video-auto-blacklist/list', + pathMatch: 'full' + }, { path: 'video-abuses/list', component: VideoAbuseListComponent, @@ -37,6 +43,17 @@ export const ModerationRoutes: Routes = [ } } }, + { + path: 'video-auto-blacklist/list', + component: VideoAutoBlacklistListComponent, + canActivate: [ UserRightGuard ], + data: { + userRight: UserRight.MANAGE_VIDEO_BLACKLIST, + meta: { + title: 'Auto-blacklisted videos' + } + } + }, { path: 'video-blacklist/list', component: VideoBlacklistListComponent, diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts b/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts new file mode 100644 index 000000000..e3522f68c --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts @@ -0,0 +1 @@ +export * from './video-auto-blacklist-list.component' diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html new file mode 100644 index 000000000..fe579ffd7 --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html @@ -0,0 +1,49 @@ +
No results.
+
+
+
+
+ +
+ + +
+ {{ video.name }} +
{{ video.account.displayName }}
+
{{ video.publishedAt | myFromNow }}
+
Privacy: {{ video.privacy.label }}
+
Sensitve: {{ video.nsfw }}
+
+ + +
+
+ + Cancel + + + + + Unblacklist + +
+
+ +
+ +
+
+ +
\ No newline at end of file diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss new file mode 100644 index 000000000..a73c17eb9 --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss @@ -0,0 +1,94 @@ +@import '_variables'; +@import '_mixins'; + +.action-selection-mode { + width: 194px; + display: flex; + justify-content: flex-end; + + .action-selection-mode-child { + position: fixed; + + .action-button { + display: inline-block; + } + + .action-button-cancel-selection { + @include peertube-button; + @include grey-button; + + margin-right: 10px; + } + + .action-button-unblacklist-selection { + @include peertube-button; + @include orange-button; + @include button-with-icon(21px); + + my-global-icon { + @include apply-svg-color(#fff); + } + } + } +} + +.video { + @include row-blocks; + + &:first-child { + margin-top: 47px; + } + + .checkbox-container { + display: flex; + align-items: center; + margin-right: 20px; + margin-left: 12px; + } + + my-video-thumbnail { + margin-right: 10px; + } + + .video-info { + flex-grow: 1; + + .video-info-name { + @include disable-default-a-behaviour; + + color: var(--mainForegroundColor); + display: block; + width: fit-content; + font-size: 16px; + font-weight: $font-semibold; + } + } + + .video-buttons { + min-width: 190px; + } +} + +@media screen and (max-width: $small-view) { + .video { + flex-direction: column; + height: auto; + text-align: center; + + .video-info-name { + margin: auto; + } + + input[type=checkbox] { + display: none; + } + + my-video-thumbnail { + margin-right: 0; + } + + .video-buttons { + margin-top: 10px; + } + } +} diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts new file mode 100644 index 000000000..b79f574c9 --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts @@ -0,0 +1,100 @@ +import { Component, OnInit, OnDestroy } from '@angular/core' +import { Location } from '@angular/common' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { Router, ActivatedRoute } from '@angular/router' +import { AbstractVideoList } from '@app/shared/video/abstract-video-list' +import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { Notifier, AuthService } from '@app/core' +import { Video } from '@shared/models' +import { VideoBlacklistService } from '@app/shared' +import { immutableAssign } from '@app/shared/misc/utils' +import { ScreenService } from '@app/shared/misc/screen.service' + +@Component({ + selector: 'my-video-auto-blacklist-list', + templateUrl: './video-auto-blacklist-list.component.html', + styleUrls: [ './video-auto-blacklist-list.component.scss' ] +}) +export class VideoAutoBlacklistListComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage: string + currentRoute = '/admin/moderation/video-auto-blacklist/list' + checkedVideos: { [ id: number ]: boolean } = {} + pagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 5, + totalItems: null + } + + protected baseVideoWidth = -1 + protected baseVideoHeight = 155 + + constructor ( + protected router: Router, + protected route: ActivatedRoute, + protected i18n: I18n, + protected notifier: Notifier, + protected location: Location, + protected authService: AuthService, + protected screenService: ScreenService, + private videoBlacklistService: VideoBlacklistService, + ) { + super() + + this.titlePage = this.i18n('Auto-blacklisted videos') + } + + ngOnInit () { + super.ngOnInit() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + abortSelectionMode () { + this.checkedVideos = {} + } + + isInSelectionMode () { + return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true) + } + + getVideosObservable (page: number) { + const newPagination = immutableAssign(this.pagination, { currentPage: page }) + + return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination) + } + + generateSyndicationList () { + throw new Error('Method not implemented.') + } + + removeVideoFromBlacklist (entry: Video) { + this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe( + () => { + this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name })) + this.reloadVideos() + }, + + error => this.notifier.error(error.message) + ) + } + + removeSelectedVideosFromBlacklist () { + const toReleaseVideosIds = Object.keys(this.checkedVideos) + .filter(k => this.checkedVideos[ k ] === true) + .map(k => parseInt(k, 10)) + + this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe( + () => { + this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length })) + + this.abortSelectionMode() + this.reloadVideos() + }, + + error => this.notifier.error(error.message) + ) + } + +} diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts index 5443d816d..f4bce7c48 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core' import { SortMeta } from 'primeng/components/common/sortmeta' -import { Notifier } from '@app/core' +import { Notifier, ServerService } from '@app/core' import { ConfirmService } from '../../../core' import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared' -import { VideoBlacklist } from '../../../../../../shared' +import { VideoBlacklist, VideoBlacklistType } from '../../../../../../shared' import { I18n } from '@ngx-translate/i18n-polyfill' import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' import { Video } from '../../../shared/video/video.model' @@ -20,11 +20,13 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { rowsPerPage = 10 sort: SortMeta = { field: 'createdAt', order: 1 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 } + listBlacklistTypeFilter: VideoBlacklistType = undefined videoBlacklistActions: DropdownAction[] = [] constructor ( private notifier: Notifier, + private serverService: ServerService, private confirmService: ConfirmService, private videoBlacklistService: VideoBlacklistService, private markdownRenderer: MarkdownService, @@ -32,6 +34,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { ) { super() + // don't filter if auto-blacklist not enabled as this will be only list + if (this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled) { + this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL + } + this.videoBlacklistActions = [ { label: this.i18n('Unblacklist'), @@ -77,7 +84,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { } protected loadData () { - this.videoBlacklistService.listBlacklist(this.pagination, this.sort) + this.videoBlacklistService.listBlacklist(this.pagination, this.sort, this.listBlacklistTypeFilter) .subscribe( async resultList => { this.totalRecords = resultList.total diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts index 8d4f2c837..67ddf54da 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts @@ -31,10 +31,12 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { private serverService: ServerService, private notifier: Notifier ) { + this.labelNotifications = { newVideoFromSubscription: this.i18n('New video from your subscriptions'), newCommentOnMyVideo: this.i18n('New comment on your video'), videoAbuseAsModerator: this.i18n('New video abuse'), + videoAutoBlacklistAsModerator: this.i18n('Video auto-blacklisted waiting review'), blacklistOnMyVideo: this.i18n('One of your video is blacklisted/unblacklisted'), myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'), myVideoImportFinished: this.i18n('Video import finished'), @@ -46,6 +48,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { this.rightNotifications = { videoAbuseAsModerator: UserRight.MANAGE_VIDEO_ABUSES, + videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST, newUserRegistration: UserRight.MANAGE_USERS } diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss index f6b5faa45..d2df6f290 100644 --- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss +++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss @@ -82,6 +82,7 @@ } } } + } } diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index acaca8a01..b0c5d1130 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -98,6 +98,13 @@ export class ServerService { videos: { intervalDays: 0 } + }, + autoBlacklist: { + videos: { + ofUsers: { + enabled: false + } + } } } private videoCategories: Array> = [] diff --git a/client/src/app/shared/users/user-notification.model.ts b/client/src/app/shared/users/user-notification.model.ts index 097830752..7d0eb5ea2 100644 --- a/client/src/app/shared/users/user-notification.model.ts +++ b/client/src/app/shared/users/user-notification.model.ts @@ -54,6 +54,7 @@ export class UserNotification implements UserNotificationServer { videoUrl?: string commentUrl?: any[] videoAbuseUrl?: string + videoAutoBlacklistUrl?: string accountUrl?: string videoImportIdentifier?: string videoImportUrl?: string @@ -107,6 +108,11 @@ export class UserNotification implements UserNotificationServer { this.videoUrl = this.buildVideoUrl(this.videoAbuse.video) break + case UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS: + this.videoAutoBlacklistUrl = '/admin/moderation/video-auto-blacklist/list' + this.videoUrl = this.buildVideoUrl(this.video) + break + case UserNotificationType.BLACKLIST_ON_MY_VIDEO: this.videoUrl = this.buildVideoUrl(this.videoBlacklist.video) break diff --git a/client/src/app/shared/users/user-notifications.component.html b/client/src/app/shared/users/user-notifications.component.html index 1c0af1bb0..6d2f2750e 100644 --- a/client/src/app/shared/users/user-notifications.component.html +++ b/client/src/app/shared/users/user-notifications.component.html @@ -36,6 +36,14 @@
+ + + +
+ The recently added video {{ notification.video.name }} has been auto-blacklisted +
+
+ diff --git a/client/src/app/shared/video-blacklist/video-blacklist.service.ts b/client/src/app/shared/video-blacklist/video-blacklist.service.ts index 94e46d7c2..a9eab9b6f 100644 --- a/client/src/app/shared/video-blacklist/video-blacklist.service.ts +++ b/client/src/app/shared/video-blacklist/video-blacklist.service.ts @@ -1,11 +1,13 @@ -import { catchError, map } from 'rxjs/operators' +import { catchError, map, concatMap, toArray } from 'rxjs/operators' import { HttpClient, HttpParams } from '@angular/common/http' import { Injectable } from '@angular/core' import { SortMeta } from 'primeng/components/common/sortmeta' -import { Observable } from 'rxjs' -import { VideoBlacklist, ResultList } from '../../../../../shared' +import { from as observableFrom, Observable } from 'rxjs' +import { VideoBlacklist, VideoBlacklistType, ResultList } from '../../../../../shared' +import { Video } from '../video/video.model' import { environment } from '../../../environments/environment' import { RestExtractor, RestPagination, RestService } from '../rest' +import { ComponentPagination } from '../rest/component-pagination.model' @Injectable() export class VideoBlacklistService { @@ -17,10 +19,14 @@ export class VideoBlacklistService { private restExtractor: RestExtractor ) {} - listBlacklist (pagination: RestPagination, sort: SortMeta): Observable> { + listBlacklist (pagination: RestPagination, sort: SortMeta, type?: VideoBlacklistType): Observable> { let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) + if (type) { + params = params.set('type', type.toString()) + } + return this.authHttp.get>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params }) .pipe( map(res => this.restExtractor.convertResultListDateToHuman(res)), @@ -28,12 +34,37 @@ export class VideoBlacklistService { ) } - removeVideoFromBlacklist (videoId: number) { - return this.authHttp.delete(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist') - .pipe( - map(this.restExtractor.extractDataBool), - catchError(res => this.restExtractor.handleError(res)) - ) + getAutoBlacklistedAsVideoList (videoPagination: ComponentPagination): Observable<{ videos: Video[], totalVideos: number}> { + const pagination = this.restService.componentPaginationToRestPagination(videoPagination) + + // prioritize first created since waiting longest + const AUTO_BLACKLIST_SORT = 'createdAt' + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, AUTO_BLACKLIST_SORT) + + params = params.set('type', VideoBlacklistType.AUTO_BEFORE_PUBLISHED.toString()) + + return this.authHttp.get>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params }) + .pipe( + map(res => { + const videos = res.data.map(videoBlacklist => new Video(videoBlacklist.video)) + const totalVideos = res.total + return { videos, totalVideos } + }), + catchError(res => this.restExtractor.handleError(res)) + ) + } + + removeVideoFromBlacklist (videoIdArgs: number | number[]) { + const videoIds = Array.isArray(videoIdArgs) ? videoIdArgs : [ videoIdArgs ] + + return observableFrom(videoIds) + .pipe( + concatMap(id => this.authHttp.delete(VideoBlacklistService.BASE_VIDEOS_URL + id + '/blacklist')), + toArray(), + catchError(err => this.restExtractor.handleError(err)) + ) } blacklistVideo (videoId: number, reason: string, unfederate: boolean) { -- cgit v1.2.3