aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.html4
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.ts55
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts11
-rw-r--r--client/src/app/shared/shared-main/video/video.service.ts9
-rw-r--r--client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts39
5 files changed, 101 insertions, 17 deletions
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 9b536ec11..6e4fb4c6f 100644
--- a/client/src/app/+admin/overview/videos/video-list.component.html
+++ b/client/src/app/+admin/overview/videos/video-list.component.html
@@ -57,7 +57,7 @@
57 <td class="action-cell"> 57 <td class="action-cell">
58 <my-video-actions-dropdown 58 <my-video-actions-dropdown
59 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" 59 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video"
60 [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()" 60 [displayOptions]="videoActionsOptions" (videoRemoved)="reloadData()" (videoFilesRemoved)="reloadData()"
61 ></my-video-actions-dropdown> 61 ></my-video-actions-dropdown>
62 </td> 62 </td>
63 63
@@ -127,4 +127,4 @@
127 </ng-template> 127 </ng-template>
128</p-table> 128</p-table>
129 129
130<my-video-block #videoBlockModal (videoBlocked)="onVideoBlocked()"></my-video-block> 130<my-video-block #videoBlockModal (videoBlocked)="reloadData()"></my-video-block>
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 7f268bb23..3c21adb44 100644
--- a/client/src/app/+admin/overview/videos/video-list.component.ts
+++ b/client/src/app/+admin/overview/videos/video-list.component.ts
@@ -39,7 +39,8 @@ export class VideoListComponent extends RestTable implements OnInit {
39 report: false, 39 report: false,
40 duplicate: true, 40 duplicate: true,
41 mute: true, 41 mute: true,
42 liveInfo: false 42 liveInfo: false,
43 removeFiles: true
43 } 44 }
44 45
45 loading = true 46 loading = true
@@ -71,17 +72,34 @@ export class VideoListComponent extends RestTable implements OnInit {
71 { 72 {
72 label: $localize`Delete`, 73 label: $localize`Delete`,
73 handler: videos => this.removeVideos(videos), 74 handler: videos => this.removeVideos(videos),
74 isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO) 75 isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO),
76 iconName: 'delete'
75 }, 77 },
76 { 78 {
77 label: $localize`Block`, 79 label: $localize`Block`,
78 handler: videos => this.videoBlockModal.show(videos), 80 handler: videos => this.videoBlockModal.show(videos),
79 isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => !v.blacklisted) 81 isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => !v.blacklisted),
82 iconName: 'no'
80 }, 83 },
81 { 84 {
82 label: $localize`Unblock`, 85 label: $localize`Unblock`,
83 handler: videos => this.unblockVideos(videos), 86 handler: videos => this.unblockVideos(videos),
84 isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => v.blacklisted) 87 isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => v.blacklisted),
88 iconName: 'undo'
89 }
90 ],
91 [
92 {
93 label: $localize`Delete HLS files`,
94 handler: videos => this.removeVideoFiles(videos, 'hls'),
95 isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_FILES) && videos.every(v => v.hasHLS() && v.hasWebTorrent()),
96 iconName: 'delete'
97 },
98 {
99 label: $localize`Delete WebTorrent files`,
100 handler: videos => this.removeVideoFiles(videos, 'webtorrent'),
101 isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_FILES) && videos.every(v => v.hasHLS() && v.hasWebTorrent()),
102 iconName: 'delete'
85 } 103 }
86 ] 104 ]
87 ] 105 ]
@@ -95,10 +113,6 @@ export class VideoListComponent extends RestTable implements OnInit {
95 return this.selectedVideos.length !== 0 113 return this.selectedVideos.length !== 0
96 } 114 }
97 115
98 onVideoRemoved () {
99 this.reloadData()
100 }
101
102 getPrivacyBadgeClass (video: Video) { 116 getPrivacyBadgeClass (video: Video) {
103 if (video.privacy.id === VideoPrivacy.PUBLIC) return 'badge-green' 117 if (video.privacy.id === VideoPrivacy.PUBLIC) return 'badge-green'
104 118
@@ -146,11 +160,7 @@ export class VideoListComponent extends RestTable implements OnInit {
146 return files.reduce((p, f) => p += f.size, 0) 160 return files.reduce((p, f) => p += f.size, 0)
147 } 161 }
148 162
149 onVideoBlocked () { 163 reloadData () {
150 this.reloadData()
151 }
152
153 protected reloadData () {
154 this.selectedVideos = [] 164 this.selectedVideos = []
155 165
156 this.loading = true 166 this.loading = true
@@ -197,4 +207,23 @@ export class VideoListComponent extends RestTable implements OnInit {
197 error: err => this.notifier.error(err.message) 207 error: err => this.notifier.error(err.message)
198 }) 208 })
199 } 209 }
210
211 private async removeVideoFiles (videos: Video[], type: 'hls' | 'webtorrent') {
212 const message = type === 'hls'
213 ? $localize`Are you sure you want to delete ${videos.length} HLS streaming playlists?`
214 : $localize`Are you sure you want to delete WebTorrent files of ${videos.length} videos?`
215
216 const res = await this.confirmService.confirm(message, $localize`Delete`)
217 if (res === false) return
218
219 this.videoService.removeVideoFiles(videos.map(v => v.id), type)
220 .subscribe({
221 next: () => {
222 this.notifier.success($localize`Files were removed.`)
223 this.reloadData()
224 },
225
226 error: err => this.notifier.error(err.message)
227 })
228 }
200} 229}
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index 472a8c810..4203ff1c0 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -14,7 +14,8 @@ import {
14 VideoPrivacy, 14 VideoPrivacy,
15 VideoScheduleUpdate, 15 VideoScheduleUpdate,
16 VideoState, 16 VideoState,
17 VideoStreamingPlaylist 17 VideoStreamingPlaylist,
18 VideoStreamingPlaylistType
18} from '@shared/models' 19} from '@shared/models'
19 20
20export class Video implements VideoServerModel { 21export class Video implements VideoServerModel {
@@ -219,6 +220,14 @@ export class Video implements VideoServerModel {
219 return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.UPDATE_ANY_VIDEO)) 220 return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.UPDATE_ANY_VIDEO))
220 } 221 }
221 222
223 hasHLS () {
224 return this.streamingPlaylists?.some(p => p.type === VideoStreamingPlaylistType.HLS)
225 }
226
227 hasWebTorrent () {
228 return this.files && this.files.length !== 0
229 }
230
222 isLiveInfoAvailableBy (user: AuthUser) { 231 isLiveInfoAvailableBy (user: AuthUser) {
223 return this.isLive && 232 return this.isLive &&
224 user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.GET_ANY_LIVE)) 233 user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.GET_ANY_LIVE))
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts
index 570e8e3be..d135a27dc 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -299,6 +299,15 @@ export class VideoService {
299 ) 299 )
300 } 300 }
301 301
302 removeVideoFiles (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
303 return from(videoIds)
304 .pipe(
305 concatMap(id => this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + id + '/' + type)),
306 toArray(),
307 catchError(err => this.restExtractor.handleError(err))
308 )
309 }
310
302 loadCompleteDescription (descriptionPath: string) { 311 loadCompleteDescription (descriptionPath: string) {
303 return this.authHttp 312 return this.authHttp
304 .get<{ description: string }>(environment.apiUrl + descriptionPath) 313 .get<{ description: string }>(environment.apiUrl + descriptionPath)
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 eff56b40e..82c084791 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
@@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@a
2import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core' 2import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core'
3import { BlocklistService, VideoBlockComponent, VideoBlockService, VideoReportComponent } from '@app/shared/shared-moderation' 3import { BlocklistService, VideoBlockComponent, VideoBlockService, VideoReportComponent } from '@app/shared/shared-moderation'
4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' 4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
5import { VideoCaption } from '@shared/models' 5import { UserRight, VideoCaption } from '@shared/models'
6import { 6import {
7 Actor, 7 Actor,
8 DropdownAction, 8 DropdownAction,
@@ -27,6 +27,7 @@ export type VideoActionsDisplayType = {
27 duplicate?: boolean 27 duplicate?: boolean
28 mute?: boolean 28 mute?: boolean
29 liveInfo?: boolean 29 liveInfo?: boolean
30 removeFiles?: boolean
30} 31}
31 32
32@Component({ 33@Component({
@@ -65,6 +66,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
65 @Input() buttonSize: DropdownButtonSize = 'normal' 66 @Input() buttonSize: DropdownButtonSize = 'normal'
66 @Input() buttonDirection: DropdownDirection = 'vertical' 67 @Input() buttonDirection: DropdownDirection = 'vertical'
67 68
69 @Output() videoFilesRemoved = new EventEmitter()
68 @Output() videoRemoved = new EventEmitter() 70 @Output() videoRemoved = new EventEmitter()
69 @Output() videoUnblocked = new EventEmitter() 71 @Output() videoUnblocked = new EventEmitter()
70 @Output() videoBlocked = new EventEmitter() 72 @Output() videoBlocked = new EventEmitter()
@@ -174,6 +176,10 @@ export class VideoActionsDropdownComponent implements OnChanges {
174 return this.video.account.id !== this.user.account.id 176 return this.video.account.id !== this.user.account.id
175 } 177 }
176 178
179 canRemoveVideoFiles () {
180 return this.user.hasRight(UserRight.MANAGE_VIDEO_FILES) && this.video.hasHLS() && this.video.hasWebTorrent()
181 }
182
177 /* Action handlers */ 183 /* Action handlers */
178 184
179 async unblockVideo () { 185 async unblockVideo () {
@@ -245,6 +251,23 @@ export class VideoActionsDropdownComponent implements OnChanges {
245 }) 251 })
246 } 252 }
247 253
254 async removeVideoFiles (video: Video, type: 'hls' | 'webtorrent') {
255 const confirmMessage = $localize`Do you really want to remove "${this.video.name}" files?`
256
257 const res = await this.confirmService.confirm(confirmMessage, $localize`Remove "${this.video.name}" files`)
258 if (res === false) return
259
260 this.videoService.removeVideoFiles([ video.id ], type)
261 .subscribe({
262 next: () => {
263 this.notifier.success($localize`Removed files of ${video.name}.`)
264 this.videoFilesRemoved.emit()
265 },
266
267 error: err => this.notifier.error(err.message)
268 })
269 }
270
248 onVideoBlocked () { 271 onVideoBlocked () {
249 this.videoBlocked.emit() 272 this.videoBlocked.emit()
250 } 273 }
@@ -317,6 +340,20 @@ export class VideoActionsDropdownComponent implements OnChanges {
317 iconName: 'flag' 340 iconName: 'flag'
318 } 341 }
319 ], 342 ],
343 [
344 {
345 label: $localize`Delete HLS files`,
346 handler: ({ video }) => this.removeVideoFiles(video, 'hls'),
347 isDisplayed: () => this.displayOptions.removeFiles && this.canRemoveVideoFiles(),
348 iconName: 'delete'
349 },
350 {
351 label: $localize`Delete WebTorrent files`,
352 handler: ({ video }) => this.removeVideoFiles(video, 'webtorrent'),
353 isDisplayed: () => this.displayOptions.removeFiles && this.canRemoveVideoFiles(),
354 iconName: 'delete'
355 }
356 ],
320 [ // actions regarding the account/its server 357 [ // actions regarding the account/its server
321 { 358 {
322 label: $localize`Mute account`, 359 label: $localize`Mute account`,