diff options
author | Chocobozzz <me@florianbigard.com> | 2021-03-23 11:54:08 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-03-24 18:18:41 +0100 |
commit | 4bc45da342597fb49593fc14c40f8dc5a97bb64e (patch) | |
tree | 9fa876e21995b27827fbc4467bd71b8d427312e2 | |
parent | c0ab041c2c749db05ce564d3078c2ad10d18f35f (diff) | |
download | PeerTube-4bc45da342597fb49593fc14c40f8dc5a97bb64e.tar.gz PeerTube-4bc45da342597fb49593fc14c40f8dc5a97bb64e.tar.zst PeerTube-4bc45da342597fb49593fc14c40f8dc5a97bb64e.zip |
Add hooks support for video download
-rw-r--r-- | client/src/app/shared/shared-video-miniature/video-download.component.ts | 19 | ||||
-rw-r--r-- | server/controllers/download.ts | 85 | ||||
-rw-r--r-- | server/lib/files-cache/videos-torrent-cache.ts | 13 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test/main.js | 26 | ||||
-rw-r--r-- | server/tests/plugins/filter-hooks.ts | 63 | ||||
-rw-r--r-- | shared/models/plugins/client-hook.model.ts | 6 | ||||
-rw-r--r-- | shared/models/plugins/server-hook.model.ts | 6 |
7 files changed, 198 insertions, 20 deletions
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts index 90f4daf7c..a57e4ce6d 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts | |||
@@ -1,7 +1,9 @@ | |||
1 | import { mapValues, pick } from 'lodash-es' | 1 | import { mapValues, pick } from 'lodash-es' |
2 | import { pipe } from 'rxjs' | ||
3 | import { tap } from 'rxjs/operators' | ||
2 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' | 4 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' |
3 | import { AuthService, Notifier } from '@app/core' | 5 | import { AuthService, HooksService, Notifier } from '@app/core' |
4 | import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
5 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' | 7 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' |
6 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' | 8 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' |
7 | 9 | ||
@@ -26,7 +28,7 @@ export class VideoDownloadComponent { | |||
26 | videoFileMetadataVideoStream: FileMetadata | undefined | 28 | videoFileMetadataVideoStream: FileMetadata | undefined |
27 | videoFileMetadataAudioStream: FileMetadata | undefined | 29 | videoFileMetadataAudioStream: FileMetadata | undefined |
28 | videoCaptions: VideoCaption[] | 30 | videoCaptions: VideoCaption[] |
29 | activeModal: NgbActiveModal | 31 | activeModal: NgbModalRef |
30 | 32 | ||
31 | type: DownloadType = 'video' | 33 | type: DownloadType = 'video' |
32 | 34 | ||
@@ -38,7 +40,8 @@ export class VideoDownloadComponent { | |||
38 | private notifier: Notifier, | 40 | private notifier: Notifier, |
39 | private modalService: NgbModal, | 41 | private modalService: NgbModal, |
40 | private videoService: VideoService, | 42 | private videoService: VideoService, |
41 | private auth: AuthService | 43 | private auth: AuthService, |
44 | private hooks: HooksService | ||
42 | ) { | 45 | ) { |
43 | this.bytesPipe = new BytesPipe() | 46 | this.bytesPipe = new BytesPipe() |
44 | this.numbersPipe = new NumberFormatterPipe(this.localeId) | 47 | this.numbersPipe = new NumberFormatterPipe(this.localeId) |
@@ -64,7 +67,12 @@ export class VideoDownloadComponent { | |||
64 | 67 | ||
65 | this.resolutionId = this.getVideoFiles()[0].resolution.id | 68 | this.resolutionId = this.getVideoFiles()[0].resolution.id |
66 | this.onResolutionIdChange() | 69 | this.onResolutionIdChange() |
70 | |||
67 | if (this.videoCaptions) this.subtitleLanguageId = this.videoCaptions[0].language.id | 71 | if (this.videoCaptions) this.subtitleLanguageId = this.videoCaptions[0].language.id |
72 | |||
73 | this.activeModal.shown.subscribe(() => { | ||
74 | this.hooks.runAction('action:modal.video-download.shown', 'common') | ||
75 | }) | ||
68 | } | 76 | } |
69 | 77 | ||
70 | onClose () { | 78 | onClose () { |
@@ -88,6 +96,7 @@ export class VideoDownloadComponent { | |||
88 | if (this.videoFile.metadata || !this.videoFile.metadataUrl) return | 96 | if (this.videoFile.metadata || !this.videoFile.metadataUrl) return |
89 | 97 | ||
90 | await this.hydrateMetadataFromMetadataUrl(this.videoFile) | 98 | await this.hydrateMetadataFromMetadataUrl(this.videoFile) |
99 | if (!this.videoFile.metadata) return | ||
91 | 100 | ||
92 | this.videoFileMetadataFormat = this.videoFile | 101 | this.videoFileMetadataFormat = this.videoFile |
93 | ? this.getMetadataFormat(this.videoFile.metadata.format) | 102 | ? this.getMetadataFormat(this.videoFile.metadata.format) |
@@ -201,7 +210,7 @@ export class VideoDownloadComponent { | |||
201 | 210 | ||
202 | private hydrateMetadataFromMetadataUrl (file: VideoFile) { | 211 | private hydrateMetadataFromMetadataUrl (file: VideoFile) { |
203 | const observable = this.videoService.getVideoFileMetadata(file.metadataUrl) | 212 | const observable = this.videoService.getVideoFileMetadata(file.metadataUrl) |
204 | observable.subscribe(res => file.metadata = res) | 213 | .pipe(tap(res => file.metadata = res)) |
205 | 214 | ||
206 | return observable.toPromise() | 215 | return observable.toPromise() |
207 | } | 216 | } |
diff --git a/server/controllers/download.ts b/server/controllers/download.ts index 27caa1518..fd44f10e9 100644 --- a/server/controllers/download.ts +++ b/server/controllers/download.ts | |||
@@ -1,8 +1,10 @@ | |||
1 | import * as cors from 'cors' | 1 | import * as cors from 'cors' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { logger } from '@server/helpers/logger' | ||
3 | import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' | 4 | import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' |
5 | import { Hooks } from '@server/lib/plugins/hooks' | ||
4 | import { getVideoFilePath } from '@server/lib/video-paths' | 6 | import { getVideoFilePath } from '@server/lib/video-paths' |
5 | import { MVideoFile, MVideoFullLight } from '@server/types/models' | 7 | import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' |
6 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
7 | import { VideoStreamingPlaylistType } from '@shared/models' | 9 | import { VideoStreamingPlaylistType } from '@shared/models' |
8 | import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' | 10 | import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' |
@@ -14,19 +16,19 @@ downloadRouter.use(cors()) | |||
14 | 16 | ||
15 | downloadRouter.use( | 17 | downloadRouter.use( |
16 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':filename', | 18 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':filename', |
17 | downloadTorrent | 19 | asyncMiddleware(downloadTorrent) |
18 | ) | 20 | ) |
19 | 21 | ||
20 | downloadRouter.use( | 22 | downloadRouter.use( |
21 | STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', | 23 | STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', |
22 | asyncMiddleware(videosDownloadValidator), | 24 | asyncMiddleware(videosDownloadValidator), |
23 | downloadVideoFile | 25 | asyncMiddleware(downloadVideoFile) |
24 | ) | 26 | ) |
25 | 27 | ||
26 | downloadRouter.use( | 28 | downloadRouter.use( |
27 | STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', | 29 | STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', |
28 | asyncMiddleware(videosDownloadValidator), | 30 | asyncMiddleware(videosDownloadValidator), |
29 | downloadHLSVideoFile | 31 | asyncMiddleware(downloadHLSVideoFile) |
30 | ) | 32 | ) |
31 | 33 | ||
32 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
@@ -41,28 +43,58 @@ async function downloadTorrent (req: express.Request, res: express.Response) { | |||
41 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) | 43 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) |
42 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 44 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) |
43 | 45 | ||
46 | const allowParameters = { torrentPath: result.path, downloadName: result.downloadName } | ||
47 | |||
48 | const allowedResult = await Hooks.wrapFun( | ||
49 | isTorrentDownloadAllowed, | ||
50 | allowParameters, | ||
51 | 'filter:api.download.torrent.allowed.result' | ||
52 | ) | ||
53 | |||
54 | if (!checkAllowResult(res, allowParameters, allowedResult)) return | ||
55 | |||
44 | return res.download(result.path, result.downloadName) | 56 | return res.download(result.path, result.downloadName) |
45 | } | 57 | } |
46 | 58 | ||
47 | function downloadVideoFile (req: express.Request, res: express.Response) { | 59 | async function downloadVideoFile (req: express.Request, res: express.Response) { |
48 | const video = res.locals.videoAll | 60 | const video = res.locals.videoAll |
49 | 61 | ||
50 | const videoFile = getVideoFile(req, video.VideoFiles) | 62 | const videoFile = getVideoFile(req, video.VideoFiles) |
51 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() | 63 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
52 | 64 | ||
65 | const allowParameters = { video, videoFile } | ||
66 | |||
67 | const allowedResult = await Hooks.wrapFun( | ||
68 | isVideoDownloadAllowed, | ||
69 | allowParameters, | ||
70 | 'filter:api.download.video.allowed.result' | ||
71 | ) | ||
72 | |||
73 | if (!checkAllowResult(res, allowParameters, allowedResult)) return | ||
74 | |||
53 | return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) | 75 | return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) |
54 | } | 76 | } |
55 | 77 | ||
56 | function downloadHLSVideoFile (req: express.Request, res: express.Response) { | 78 | async function downloadHLSVideoFile (req: express.Request, res: express.Response) { |
57 | const video = res.locals.videoAll | 79 | const video = res.locals.videoAll |
58 | const playlist = getHLSPlaylist(video) | 80 | const streamingPlaylist = getHLSPlaylist(video) |
59 | if (!playlist) return res.status(HttpStatusCode.NOT_FOUND_404).end | 81 | if (!streamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).end |
60 | 82 | ||
61 | const videoFile = getVideoFile(req, playlist.VideoFiles) | 83 | const videoFile = getVideoFile(req, streamingPlaylist.VideoFiles) |
62 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() | 84 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
63 | 85 | ||
64 | const filename = `${video.name}-${videoFile.resolution}p-${playlist.getStringType()}${videoFile.extname}` | 86 | const allowParameters = { video, streamingPlaylist, videoFile } |
65 | return res.download(getVideoFilePath(playlist, videoFile), filename) | 87 | |
88 | const allowedResult = await Hooks.wrapFun( | ||
89 | isVideoDownloadAllowed, | ||
90 | allowParameters, | ||
91 | 'filter:api.download.video.allowed.result' | ||
92 | ) | ||
93 | |||
94 | if (!checkAllowResult(res, allowParameters, allowedResult)) return | ||
95 | |||
96 | const filename = `${video.name}-${videoFile.resolution}p-${streamingPlaylist.getStringType()}${videoFile.extname}` | ||
97 | return res.download(getVideoFilePath(streamingPlaylist, videoFile), filename) | ||
66 | } | 98 | } |
67 | 99 | ||
68 | function getVideoFile (req: express.Request, files: MVideoFile[]) { | 100 | function getVideoFile (req: express.Request, files: MVideoFile[]) { |
@@ -76,3 +108,34 @@ function getHLSPlaylist (video: MVideoFullLight) { | |||
76 | 108 | ||
77 | return Object.assign(playlist, { Video: video }) | 109 | return Object.assign(playlist, { Video: video }) |
78 | } | 110 | } |
111 | |||
112 | type AllowedResult = { | ||
113 | allowed: boolean | ||
114 | errorMessage?: string | ||
115 | } | ||
116 | |||
117 | function isTorrentDownloadAllowed (_object: { | ||
118 | torrentPath: string | ||
119 | }): AllowedResult { | ||
120 | return { allowed: true } | ||
121 | } | ||
122 | |||
123 | function isVideoDownloadAllowed (_object: { | ||
124 | video: MVideo | ||
125 | videoFile: MVideoFile | ||
126 | streamingPlaylist?: MStreamingPlaylist | ||
127 | }): AllowedResult { | ||
128 | return { allowed: true } | ||
129 | } | ||
130 | |||
131 | function checkAllowResult (res: express.Response, allowParameters: any, result?: AllowedResult) { | ||
132 | if (!result || result.allowed !== true) { | ||
133 | logger.info('Download is not allowed.', { result, allowParameters }) | ||
134 | res.status(HttpStatusCode.FORBIDDEN_403) | ||
135 | .json({ error: result.errorMessage || 'Refused download' }) | ||
136 | |||
137 | return false | ||
138 | } | ||
139 | |||
140 | return true | ||
141 | } | ||
diff --git a/server/lib/files-cache/videos-torrent-cache.ts b/server/lib/files-cache/videos-torrent-cache.ts index 881fa9ced..23217f140 100644 --- a/server/lib/files-cache/videos-torrent-cache.ts +++ b/server/lib/files-cache/videos-torrent-cache.ts | |||
@@ -5,6 +5,7 @@ import { CONFIG } from '../../initializers/config' | |||
5 | import { FILES_CACHE } from '../../initializers/constants' | 5 | import { FILES_CACHE } from '../../initializers/constants' |
6 | import { VideoModel } from '../../models/video/video' | 6 | import { VideoModel } from '../../models/video/video' |
7 | import { AbstractVideoStaticFileCache } from './abstract-video-static-file-cache' | 7 | import { AbstractVideoStaticFileCache } from './abstract-video-static-file-cache' |
8 | import { MVideo, MVideoFile } from '@server/types/models' | ||
8 | 9 | ||
9 | class VideosTorrentCache extends AbstractVideoStaticFileCache <string> { | 10 | class VideosTorrentCache extends AbstractVideoStaticFileCache <string> { |
10 | 11 | ||
@@ -22,7 +23,11 @@ class VideosTorrentCache extends AbstractVideoStaticFileCache <string> { | |||
22 | const file = await VideoFileModel.loadWithVideoOrPlaylistByTorrentFilename(filename) | 23 | const file = await VideoFileModel.loadWithVideoOrPlaylistByTorrentFilename(filename) |
23 | if (!file) return undefined | 24 | if (!file) return undefined |
24 | 25 | ||
25 | if (file.getVideo().isOwned()) return { isOwned: true, path: join(CONFIG.STORAGE.TORRENTS_DIR, file.torrentFilename) } | 26 | if (file.getVideo().isOwned()) { |
27 | const downloadName = this.buildDownloadName(file.getVideo(), file) | ||
28 | |||
29 | return { isOwned: true, path: join(CONFIG.STORAGE.TORRENTS_DIR, file.torrentFilename), downloadName } | ||
30 | } | ||
26 | 31 | ||
27 | return this.loadRemoteFile(filename) | 32 | return this.loadRemoteFile(filename) |
28 | } | 33 | } |
@@ -43,10 +48,14 @@ class VideosTorrentCache extends AbstractVideoStaticFileCache <string> { | |||
43 | 48 | ||
44 | await doRequestAndSaveToFile(remoteUrl, destPath) | 49 | await doRequestAndSaveToFile(remoteUrl, destPath) |
45 | 50 | ||
46 | const downloadName = `${video.name}-${file.resolution}p.torrent` | 51 | const downloadName = this.buildDownloadName(video, file) |
47 | 52 | ||
48 | return { isOwned: false, path: destPath, downloadName } | 53 | return { isOwned: false, path: destPath, downloadName } |
49 | } | 54 | } |
55 | |||
56 | private buildDownloadName (video: MVideo, file: MVideoFile) { | ||
57 | return `${video.name}-${file.resolution}p.torrent` | ||
58 | } | ||
50 | } | 59 | } |
51 | 60 | ||
52 | export { | 61 | export { |
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js index 305d92002..9913d0846 100644 --- a/server/tests/fixtures/peertube-plugin-test/main.js +++ b/server/tests/fixtures/peertube-plugin-test/main.js | |||
@@ -184,6 +184,32 @@ async function register ({ registerHook, registerSetting, settingsManager, stora | |||
184 | return result | 184 | return result |
185 | } | 185 | } |
186 | }) | 186 | }) |
187 | |||
188 | registerHook({ | ||
189 | target: 'filter:api.download.torrent.allowed.result', | ||
190 | handler: (result, params) => { | ||
191 | if (params && params.downloadName.includes('bad torrent')) { | ||
192 | return { allowed: false, errorMessage: 'Liu Bei' } | ||
193 | } | ||
194 | |||
195 | return result | ||
196 | } | ||
197 | }) | ||
198 | |||
199 | registerHook({ | ||
200 | target: 'filter:api.download.video.allowed.result', | ||
201 | handler: (result, params) => { | ||
202 | if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) { | ||
203 | return { allowed: false, errorMessage: 'Cao Cao' } | ||
204 | } | ||
205 | |||
206 | if (params && params.streamingPlaylist && params.video.name.includes('bad playlist file')) { | ||
207 | return { allowed: false, errorMessage: 'Sun Jian' } | ||
208 | } | ||
209 | |||
210 | return result | ||
211 | } | ||
212 | }) | ||
187 | } | 213 | } |
188 | 214 | ||
189 | async function unregister () { | 215 | async function unregister () { |
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index d88170201..6996ae788 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts | |||
@@ -20,12 +20,14 @@ import { | |||
20 | getVideoThreadComments, | 20 | getVideoThreadComments, |
21 | getVideoWithToken, | 21 | getVideoWithToken, |
22 | installPlugin, | 22 | installPlugin, |
23 | makeRawRequest, | ||
23 | registerUser, | 24 | registerUser, |
24 | setAccessTokensToServers, | 25 | setAccessTokensToServers, |
25 | setDefaultVideoChannel, | 26 | setDefaultVideoChannel, |
26 | updateCustomSubConfig, | 27 | updateCustomSubConfig, |
27 | updateVideo, | 28 | updateVideo, |
28 | uploadVideo, | 29 | uploadVideo, |
30 | uploadVideoAndGetId, | ||
29 | waitJobs | 31 | waitJobs |
30 | } from '../../../shared/extra-utils' | 32 | } from '../../../shared/extra-utils' |
31 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo } from '../../../shared/extra-utils/server/servers' | 33 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo } from '../../../shared/extra-utils/server/servers' |
@@ -355,6 +357,67 @@ describe('Test plugin filter hooks', function () { | |||
355 | }) | 357 | }) |
356 | }) | 358 | }) |
357 | 359 | ||
360 | describe('Download hooks', function () { | ||
361 | const downloadVideos: VideoDetails[] = [] | ||
362 | |||
363 | before(async function () { | ||
364 | this.timeout(60000) | ||
365 | |||
366 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | ||
367 | transcoding: { | ||
368 | webtorrent: { | ||
369 | enabled: true | ||
370 | }, | ||
371 | hls: { | ||
372 | enabled: true | ||
373 | } | ||
374 | } | ||
375 | }) | ||
376 | |||
377 | const uuids: string[] = [] | ||
378 | |||
379 | for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) { | ||
380 | const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid | ||
381 | uuids.push(uuid) | ||
382 | } | ||
383 | |||
384 | await waitJobs(servers) | ||
385 | |||
386 | for (const uuid of uuids) { | ||
387 | const res = await getVideo(servers[0].url, uuid) | ||
388 | downloadVideos.push(res.body) | ||
389 | } | ||
390 | }) | ||
391 | |||
392 | it('Should run filter:api.download.torrent.allowed.result', async function () { | ||
393 | const res = await makeRawRequest(downloadVideos[0].files[0].torrentDownloadUrl, 403) | ||
394 | expect(res.body.error).to.equal('Liu Bei') | ||
395 | |||
396 | await makeRawRequest(downloadVideos[1].files[0].torrentDownloadUrl, 200) | ||
397 | await makeRawRequest(downloadVideos[2].files[0].torrentDownloadUrl, 200) | ||
398 | }) | ||
399 | |||
400 | it('Should run filter:api.download.video.allowed.result', async function () { | ||
401 | { | ||
402 | const res = await makeRawRequest(downloadVideos[1].files[0].fileDownloadUrl, 403) | ||
403 | expect(res.body.error).to.equal('Cao Cao') | ||
404 | |||
405 | await makeRawRequest(downloadVideos[0].files[0].fileDownloadUrl, 200) | ||
406 | await makeRawRequest(downloadVideos[2].files[0].fileDownloadUrl, 200) | ||
407 | } | ||
408 | |||
409 | { | ||
410 | const res = await makeRawRequest(downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl, 403) | ||
411 | expect(res.body.error).to.equal('Sun Jian') | ||
412 | |||
413 | await makeRawRequest(downloadVideos[2].files[0].fileDownloadUrl, 200) | ||
414 | |||
415 | await makeRawRequest(downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl, 200) | ||
416 | await makeRawRequest(downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl, 200) | ||
417 | } | ||
418 | }) | ||
419 | }) | ||
420 | |||
358 | after(async function () { | 421 | after(async function () { |
359 | await cleanupTests(servers) | 422 | await cleanupTests(servers) |
360 | }) | 423 | }) |
diff --git a/shared/models/plugins/client-hook.model.ts b/shared/models/plugins/client-hook.model.ts index 7b7144676..19622e09e 100644 --- a/shared/models/plugins/client-hook.model.ts +++ b/shared/models/plugins/client-hook.model.ts | |||
@@ -85,8 +85,12 @@ export const clientActionHookObject = { | |||
85 | // Fired when the registration page is being initialized | 85 | // Fired when the registration page is being initialized |
86 | 'action:signup.register.init': true, | 86 | 'action:signup.register.init': true, |
87 | 87 | ||
88 | // Fired when the modal to download a video/caption is shown | ||
89 | 'action:modal.video-download.shown': true, | ||
90 | |||
88 | // ####### Embed hooks ####### | 91 | // ####### Embed hooks ####### |
89 | // In embed scope, peertube helpers are not available | 92 | // /!\ In embed scope, peertube helpers are not available |
93 | // ########################### | ||
90 | 94 | ||
91 | // Fired when the embed loaded the player | 95 | // Fired when the embed loaded the player |
92 | 'action:embed.player.loaded': true | 96 | 'action:embed.player.loaded': true |
diff --git a/shared/models/plugins/server-hook.model.ts b/shared/models/plugins/server-hook.model.ts index 082b4b591..1f7806d0d 100644 --- a/shared/models/plugins/server-hook.model.ts +++ b/shared/models/plugins/server-hook.model.ts | |||
@@ -50,7 +50,11 @@ export const serverFilterHookObject = { | |||
50 | 'filter:video.auto-blacklist.result': true, | 50 | 'filter:video.auto-blacklist.result': true, |
51 | 51 | ||
52 | // Filter result used to check if a user can register on the instance | 52 | // Filter result used to check if a user can register on the instance |
53 | 'filter:api.user.signup.allowed.result': true | 53 | 'filter:api.user.signup.allowed.result': true, |
54 | |||
55 | // Filter result used to check if video/torrent download is allowed | ||
56 | 'filter:api.download.video.allowed.result': true, | ||
57 | 'filter:api.download.torrent.allowed.result': true | ||
54 | } | 58 | } |
55 | 59 | ||
56 | export type ServerFilterHookName = keyof typeof serverFilterHookObject | 60 | export type ServerFilterHookName = keyof typeof serverFilterHookObject |