diff options
-rw-r--r-- | server/middlewares/validators/videos/shared/video-validators.ts | 8 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test/main.js | 12 | ||||
-rw-r--r-- | server/tests/plugins/action-hooks.ts | 8 | ||||
-rw-r--r-- | server/tests/plugins/filter-hooks.ts | 19 | ||||
-rw-r--r-- | shared/server-commands/videos/videos-command.ts | 30 |
5 files changed, 61 insertions, 16 deletions
diff --git a/server/middlewares/validators/videos/shared/video-validators.ts b/server/middlewares/validators/videos/shared/video-validators.ts index 72536011d..95e4fef11 100644 --- a/server/middlewares/validators/videos/shared/video-validators.ts +++ b/server/middlewares/validators/videos/shared/video-validators.ts | |||
@@ -45,7 +45,7 @@ export async function isVideoFileAccepted (options: { | |||
45 | videoFile: express.VideoUploadFile | 45 | videoFile: express.VideoUploadFile |
46 | hook: Extract<ServerFilterHookName, 'filter:api.video.upload.accept.result' | 'filter:api.video.update-file.accept.result'> | 46 | hook: Extract<ServerFilterHookName, 'filter:api.video.upload.accept.result' | 'filter:api.video.update-file.accept.result'> |
47 | }) { | 47 | }) { |
48 | const { req, res, videoFile } = options | 48 | const { req, res, videoFile, hook } = options |
49 | 49 | ||
50 | // Check we accept this video | 50 | // Check we accept this video |
51 | const acceptParameters = { | 51 | const acceptParameters = { |
@@ -53,11 +53,7 @@ export async function isVideoFileAccepted (options: { | |||
53 | videoFile, | 53 | videoFile, |
54 | user: res.locals.oauth.token.User | 54 | user: res.locals.oauth.token.User |
55 | } | 55 | } |
56 | const acceptedResult = await Hooks.wrapFun( | 56 | const acceptedResult = await Hooks.wrapFun(isLocalVideoFileAccepted, acceptParameters, hook) |
57 | isLocalVideoFileAccepted, | ||
58 | acceptParameters, | ||
59 | 'filter:api.video.upload.accept.result' | ||
60 | ) | ||
61 | 57 | ||
62 | if (!acceptedResult || acceptedResult.accepted !== true) { | 58 | if (!acceptedResult || acceptedResult.accepted !== true) { |
63 | logger.info('Refused local video file.', { acceptedResult, acceptParameters }) | 59 | logger.info('Refused local video file.', { acceptedResult, acceptParameters }) |
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js index 17032f6d9..e16bf0ca3 100644 --- a/server/tests/fixtures/peertube-plugin-test/main.js +++ b/server/tests/fixtures/peertube-plugin-test/main.js | |||
@@ -9,6 +9,8 @@ async function register ({ registerHook, registerSetting, settingsManager, stora | |||
9 | 'action:api.video.uploaded', | 9 | 'action:api.video.uploaded', |
10 | 'action:api.video.viewed', | 10 | 'action:api.video.viewed', |
11 | 11 | ||
12 | 'action:api.video.file-updated', | ||
13 | |||
12 | 'action:api.video-channel.created', | 14 | 'action:api.video-channel.created', |
13 | 'action:api.video-channel.updated', | 15 | 'action:api.video-channel.updated', |
14 | 'action:api.video-channel.deleted', | 16 | 'action:api.video-channel.deleted', |
@@ -161,6 +163,16 @@ async function register ({ registerHook, registerSetting, settingsManager, stora | |||
161 | } | 163 | } |
162 | 164 | ||
163 | registerHook({ | 165 | registerHook({ |
166 | target: 'filter:api.video.update-file.accept.result', | ||
167 | handler: ({ accepted }, { videoFile }) => { | ||
168 | if (!accepted) return { accepted: false } | ||
169 | if (videoFile.filename.includes('webm')) return { accepted: false, errorMessage: 'no webm' } | ||
170 | |||
171 | return { accepted: true } | ||
172 | } | ||
173 | }) | ||
174 | |||
175 | registerHook({ | ||
164 | target: 'filter:api.video.pre-import-url.accept.result', | 176 | target: 'filter:api.video.pre-import-url.accept.result', |
165 | handler: ({ accepted }, { videoImportBody }) => { | 177 | handler: ({ accepted }, { videoImportBody }) => { |
166 | if (!accepted) return { accepted: false } | 178 | if (!accepted) return { accepted: false } |
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts index 34b4e1891..773be0d76 100644 --- a/server/tests/plugins/action-hooks.ts +++ b/server/tests/plugins/action-hooks.ts | |||
@@ -40,6 +40,8 @@ describe('Test plugin action hooks', function () { | |||
40 | } | 40 | } |
41 | }) | 41 | }) |
42 | 42 | ||
43 | await servers[0].config.enableFileUpdate() | ||
44 | |||
43 | await doubleFollow(servers[0], servers[1]) | 45 | await doubleFollow(servers[0], servers[1]) |
44 | }) | 46 | }) |
45 | 47 | ||
@@ -70,6 +72,12 @@ describe('Test plugin action hooks', function () { | |||
70 | await checkHook('action:api.video.viewed') | 72 | await checkHook('action:api.video.viewed') |
71 | }) | 73 | }) |
72 | 74 | ||
75 | it('Should run action:api.video.file-updated', async function () { | ||
76 | await servers[0].videos.replaceSourceFile({ videoId: videoUUID, fixture: 'video_short.mp4' }) | ||
77 | |||
78 | await checkHook('action:api.video.file-updated') | ||
79 | }) | ||
80 | |||
73 | it('Should run action:api.video.deleted', async function () { | 81 | it('Should run action:api.video.deleted', async function () { |
74 | await servers[0].videos.remove({ id: videoUUID }) | 82 | await servers[0].videos.remove({ id: videoUUID }) |
75 | 83 | ||
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index a75a8c8fa..8382b400f 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts | |||
@@ -64,6 +64,11 @@ describe('Test plugin filter hooks', function () { | |||
64 | newConfig: { | 64 | newConfig: { |
65 | live: { enabled: true }, | 65 | live: { enabled: true }, |
66 | signup: { enabled: true }, | 66 | signup: { enabled: true }, |
67 | videoFile: { | ||
68 | update: { | ||
69 | enabled: true | ||
70 | } | ||
71 | }, | ||
67 | import: { | 72 | import: { |
68 | videos: { | 73 | videos: { |
69 | http: { enabled: true }, | 74 | http: { enabled: true }, |
@@ -178,7 +183,19 @@ describe('Test plugin filter hooks', function () { | |||
178 | describe('Video/live/import accept', function () { | 183 | describe('Video/live/import accept', function () { |
179 | 184 | ||
180 | it('Should run filter:api.video.upload.accept.result', async function () { | 185 | it('Should run filter:api.video.upload.accept.result', async function () { |
181 | await servers[0].videos.upload({ attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 186 | const options = { attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 } |
187 | await servers[0].videos.upload({ mode: 'legacy', ...options }) | ||
188 | await servers[0].videos.upload({ mode: 'resumable', ...options }) | ||
189 | }) | ||
190 | |||
191 | it('Should run filter:api.video.update-file.accept.result', async function () { | ||
192 | const res = await servers[0].videos.replaceSourceFile({ | ||
193 | videoId: videoUUID, | ||
194 | fixture: 'video_short1.webm', | ||
195 | completedExpectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
196 | }) | ||
197 | |||
198 | expect((res as any)?.error).to.equal('no webm') | ||
182 | }) | 199 | }) |
183 | 200 | ||
184 | it('Should run filter:api.live-video.create.accept.result', async function () { | 201 | it('Should run filter:api.live-video.create.accept.result', async function () { |
diff --git a/shared/server-commands/videos/videos-command.ts b/shared/server-commands/videos/videos-command.ts index 3fdbc348a..a58f1c545 100644 --- a/shared/server-commands/videos/videos-command.ts +++ b/shared/server-commands/videos/videos-command.ts | |||
@@ -394,6 +394,7 @@ export class VideosCommand extends AbstractCommand { | |||
394 | attributes?: VideoEdit | 394 | attributes?: VideoEdit |
395 | mode?: 'legacy' | 'resumable' // default legacy | 395 | mode?: 'legacy' | 'resumable' // default legacy |
396 | waitTorrentGeneration?: boolean // default true | 396 | waitTorrentGeneration?: boolean // default true |
397 | completedExpectedStatus?: HttpStatusCode | ||
397 | } = {}) { | 398 | } = {}) { |
398 | const { mode = 'legacy', waitTorrentGeneration = true } = options | 399 | const { mode = 'legacy', waitTorrentGeneration = true } = options |
399 | let defaultChannelId = 1 | 400 | let defaultChannelId = 1 |
@@ -461,8 +462,9 @@ export class VideosCommand extends AbstractCommand { | |||
461 | async buildResumeUpload (options: OverrideCommandOptions & { | 462 | async buildResumeUpload (options: OverrideCommandOptions & { |
462 | path: string | 463 | path: string |
463 | attributes: { fixture?: string } & { [id: string]: any } | 464 | attributes: { fixture?: string } & { [id: string]: any } |
465 | completedExpectedStatus?: HttpStatusCode // When the upload is finished | ||
464 | }): Promise<VideoCreateResult> { | 466 | }): Promise<VideoCreateResult> { |
465 | const { path, attributes, expectedStatus = HttpStatusCode.OK_200 } = options | 467 | const { path, attributes, expectedStatus = HttpStatusCode.OK_200, completedExpectedStatus } = options |
466 | 468 | ||
467 | let size = 0 | 469 | let size = 0 |
468 | let videoFilePath: string | 470 | let videoFilePath: string |
@@ -503,7 +505,8 @@ export class VideosCommand extends AbstractCommand { | |||
503 | path, | 505 | path, |
504 | pathUploadId, | 506 | pathUploadId, |
505 | videoFilePath, | 507 | videoFilePath, |
506 | size | 508 | size, |
509 | expectedStatus: completedExpectedStatus | ||
507 | }) | 510 | }) |
508 | 511 | ||
509 | if (result.statusCode === HttpStatusCode.OK_200) { | 512 | if (result.statusCode === HttpStatusCode.OK_200) { |
@@ -600,12 +603,14 @@ export class VideosCommand extends AbstractCommand { | |||
600 | try { | 603 | try { |
601 | readable.pause() | 604 | readable.pause() |
602 | 605 | ||
606 | const byterangeStart = start + chunk.length - 1 | ||
607 | |||
603 | const headers = { | 608 | const headers = { |
604 | 'Authorization': 'Bearer ' + token, | 609 | 'Authorization': 'Bearer ' + token, |
605 | 'Content-Type': 'application/octet-stream', | 610 | 'Content-Type': 'application/octet-stream', |
606 | 'Content-Range': contentRangeBuilder | 611 | 'Content-Range': contentRangeBuilder |
607 | ? contentRangeBuilder(start, chunk) | 612 | ? contentRangeBuilder(start, chunk) |
608 | : `bytes ${start}-${start + chunk.length - 1}/${size}`, | 613 | : `bytes ${start}-${byterangeStart}/${size}`, |
609 | 'Content-Length': contentLength ? contentLength + '' : chunk.length + '' | 614 | 'Content-Length': contentLength ? contentLength + '' : chunk.length + '' |
610 | } | 615 | } |
611 | 616 | ||
@@ -625,13 +630,19 @@ export class VideosCommand extends AbstractCommand { | |||
625 | 630 | ||
626 | start += chunk.length | 631 | start += chunk.length |
627 | 632 | ||
628 | if (res.statusCode === expectedStatus) { | 633 | // Last request, check final status |
629 | return resolve(res) | 634 | if (byterangeStart + 1 === size) { |
630 | } | 635 | if (res.statusCode === expectedStatus) { |
636 | return resolve(res) | ||
637 | } | ||
638 | |||
639 | if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) { | ||
640 | readable.off('data', onData) | ||
631 | 641 | ||
632 | if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) { | 642 | // eslint-disable-next-line max-len |
633 | readable.off('data', onData) | 643 | const message = `Incorrect transient behaviour sending intermediary chunks. Status code is ${res.statusCode} instead of ${expectedStatus}` |
634 | return reject(new Error('Incorrect transient behaviour sending intermediary chunks')) | 644 | return reject(new Error(message)) |
645 | } | ||
635 | } | 646 | } |
636 | 647 | ||
637 | readable.resume() | 648 | readable.resume() |
@@ -694,6 +705,7 @@ export class VideosCommand extends AbstractCommand { | |||
694 | replaceSourceFile (options: OverrideCommandOptions & { | 705 | replaceSourceFile (options: OverrideCommandOptions & { |
695 | videoId: number | string | 706 | videoId: number | string |
696 | fixture: string | 707 | fixture: string |
708 | completedExpectedStatus?: HttpStatusCode | ||
697 | }) { | 709 | }) { |
698 | return this.buildResumeUpload({ | 710 | return this.buildResumeUpload({ |
699 | ...options, | 711 | ...options, |