From c729caf6cc34630877a0e5a1bda1719384cd0c8a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 11 Feb 2022 10:51:33 +0100 Subject: Add basic video editor support --- server/tests/api/activitypub/refresher.ts | 6 +- server/tests/api/check-params/config.ts | 3 + server/tests/api/check-params/index.ts | 1 + server/tests/api/check-params/video-editor.ts | 385 +++++++++++++++++++++ server/tests/api/live/live.ts | 4 +- server/tests/api/search/search-channels.ts | 4 +- server/tests/api/search/search-playlists.ts | 4 +- server/tests/api/server/config.ts | 7 + server/tests/api/server/stats.ts | 8 +- server/tests/api/videos/audio-only.ts | 7 +- server/tests/api/videos/index.ts | 1 + server/tests/api/videos/video-editor.ts | 368 ++++++++++++++++++++ .../tests/api/videos/video-playlist-thumbnails.ts | 6 +- server/tests/api/videos/video-playlists.ts | 6 +- server/tests/api/videos/video-transcoder.ts | 39 ++- 15 files changed, 814 insertions(+), 35 deletions(-) create mode 100644 server/tests/api/check-params/video-editor.ts create mode 100644 server/tests/api/videos/video-editor.ts (limited to 'server/tests/api') diff --git a/server/tests/api/activitypub/refresher.ts b/server/tests/api/activitypub/refresher.ts index 71e1c40ba..bb81d4565 100644 --- a/server/tests/api/activitypub/refresher.ts +++ b/server/tests/api/activitypub/refresher.ts @@ -25,12 +25,16 @@ describe('Test AP refresher', function () { before(async function () { this.timeout(60000) - servers = await createMultipleServers(2, { transcoding: { enabled: false } }) + servers = await createMultipleServers(2) // Get the access tokens await setAccessTokensToServers(servers) await setDefaultVideoChannel(servers) + for (const server of servers) { + await server.config.disableTranscoding() + } + { videoUUID1 = (await servers[1].videos.quickUpload({ name: 'video1' })).uuid videoUUID2 = (await servers[1].videos.quickUpload({ name: 'video2' })).uuid diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index 3cccb612a..ce067a892 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts @@ -145,6 +145,9 @@ describe('Test config API validators', function () { } } }, + videoEditor: { + enabled: true + }, import: { videos: { concurrency: 1, diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts index e052296db..c088b52cd 100644 --- a/server/tests/api/check-params/index.ts +++ b/server/tests/api/check-params/index.ts @@ -25,6 +25,7 @@ import './video-blacklist' import './video-captions' import './video-channels' import './video-comments' +import './video-editor' import './video-imports' import './video-playlists' import './videos' diff --git a/server/tests/api/check-params/video-editor.ts b/server/tests/api/check-params/video-editor.ts new file mode 100644 index 000000000..db284a3cc --- /dev/null +++ b/server/tests/api/check-params/video-editor.ts @@ -0,0 +1,385 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import { HttpStatusCode, VideoEditorTask } from '@shared/models' +import { + cleanupTests, + createSingleServer, + PeerTubeServer, + setAccessTokensToServers, + VideoEditorCommand, + waitJobs +} from '@shared/server-commands' + +describe('Test video editor API validator', function () { + let server: PeerTubeServer + let command: VideoEditorCommand + let userAccessToken: string + let videoUUID: string + + // --------------------------------------------------------------- + + before(async function () { + this.timeout(120_000) + + server = await createSingleServer(1) + + await setAccessTokensToServers([ server ]) + userAccessToken = await server.users.generateUserAndToken('user1') + + await server.config.enableMinimumTranscoding() + + const { uuid } = await server.videos.quickUpload({ name: 'video' }) + videoUUID = uuid + + command = server.videoEditor + + await waitJobs([ server ]) + }) + + describe('Task creation', function () { + + describe('Config settings', function () { + + it('Should fail if editor is disabled', async function () { + await server.config.updateExistingSubConfig({ + newConfig: { + videoEditor: { + enabled: false + } + } + }) + + await command.createEditionTasks({ + videoId: videoUUID, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + + it('Should fail to enable editor if transcoding is disabled', async function () { + await server.config.updateExistingSubConfig({ + newConfig: { + videoEditor: { + enabled: true + }, + transcoding: { + enabled: false + } + }, + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + + it('Should succeed to enable video editor', async function () { + await server.config.updateExistingSubConfig({ + newConfig: { + videoEditor: { + enabled: true + }, + transcoding: { + enabled: true + } + } + }) + }) + }) + + describe('Common tasks', function () { + + it('Should fail without token', async function () { + await command.createEditionTasks({ + token: null, + videoId: videoUUID, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.UNAUTHORIZED_401 + }) + }) + + it('Should fail with another user token', async function () { + await command.createEditionTasks({ + token: userAccessToken, + videoId: videoUUID, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.FORBIDDEN_403 + }) + }) + + it('Should fail with an invalid video', async function () { + await command.createEditionTasks({ + videoId: 'tintin', + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + + it('Should fail with an unknown video', async function () { + await command.createEditionTasks({ + videoId: 42, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.NOT_FOUND_404 + }) + }) + + it('Should fail with an already in transcoding state video', async function () { + await server.jobs.pauseJobQueue() + + const { uuid } = await server.videos.quickUpload({ name: 'transcoded video' }) + + await command.createEditionTasks({ + videoId: uuid, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.CONFLICT_409 + }) + + await server.jobs.resumeJobQueue() + }) + + it('Should fail with a bad complex task', async function () { + await command.createEditionTasks({ + videoId: videoUUID, + tasks: [ + { + name: 'cut', + options: { + start: 1, + end: 2 + } + }, + { + name: 'hadock', + options: { + start: 1, + end: 2 + } + } + ] as any, + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + + it('Should fail without task', async function () { + await command.createEditionTasks({ + videoId: videoUUID, + tasks: [], + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + + it('Should fail with too many tasks', async function () { + const tasks: VideoEditorTask[] = [] + + for (let i = 0; i < 110; i++) { + tasks.push({ + name: 'cut', + options: { + start: 1 + } + }) + } + + await command.createEditionTasks({ + videoId: videoUUID, + tasks, + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + + it('Should succeed with correct parameters', async function () { + await server.jobs.pauseJobQueue() + + await command.createEditionTasks({ + videoId: videoUUID, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + }) + + it('Should fail with a video that is already waiting for edition', async function () { + this.timeout(120000) + + await command.createEditionTasks({ + videoId: videoUUID, + tasks: VideoEditorCommand.getComplexTask(), + expectedStatus: HttpStatusCode.CONFLICT_409 + }) + + await server.jobs.resumeJobQueue() + + await waitJobs([ server ]) + }) + }) + + describe('Cut task', function () { + + async function cut (start: number, end: number, expectedStatus = HttpStatusCode.BAD_REQUEST_400) { + await command.createEditionTasks({ + videoId: videoUUID, + tasks: [ + { + name: 'cut', + options: { + start, + end + } + } + ], + expectedStatus + }) + } + + it('Should fail with bad start/end', async function () { + const invalid = [ + 'tintin', + -1, + undefined + ] + + for (const value of invalid) { + await cut(value as any, undefined) + await cut(undefined, value as any) + } + }) + + it('Should fail with the same start/end', async function () { + await cut(2, 2) + }) + + it('Should fail with inconsistents start/end', async function () { + await cut(2, 1) + }) + + it('Should fail without start and end', async function () { + await cut(undefined, undefined) + }) + + it('Should succeed with the correct params', async function () { + this.timeout(120000) + + await cut(0, 2, HttpStatusCode.NO_CONTENT_204) + + await waitJobs([ server ]) + }) + }) + + describe('Watermark task', function () { + + async function addWatermark (file: string, expectedStatus = HttpStatusCode.BAD_REQUEST_400) { + await command.createEditionTasks({ + videoId: videoUUID, + tasks: [ + { + name: 'add-watermark', + options: { + file + } + } + ], + expectedStatus + }) + } + + it('Should fail without waterkmark', async function () { + await addWatermark(undefined) + }) + + it('Should fail with an invalid watermark', async function () { + await addWatermark('video_short.mp4') + }) + + it('Should succeed with the correct params', async function () { + this.timeout(120000) + + await addWatermark('thumbnail.jpg', HttpStatusCode.NO_CONTENT_204) + + await waitJobs([ server ]) + }) + }) + + describe('Intro/Outro task', function () { + + async function addIntroOutro (type: 'add-intro' | 'add-outro', file: string, expectedStatus = HttpStatusCode.BAD_REQUEST_400) { + await command.createEditionTasks({ + videoId: videoUUID, + tasks: [ + { + name: type, + options: { + file + } + } + ], + expectedStatus + }) + } + + it('Should fail without file', async function () { + await addIntroOutro('add-intro', undefined) + await addIntroOutro('add-outro', undefined) + }) + + it('Should fail with an invalid file', async function () { + await addIntroOutro('add-intro', 'thumbnail.jpg') + await addIntroOutro('add-outro', 'thumbnail.jpg') + }) + + it('Should fail with a file that does not contain video stream', async function () { + await addIntroOutro('add-intro', 'sample.ogg') + await addIntroOutro('add-outro', 'sample.ogg') + + }) + + it('Should succeed with the correct params', async function () { + this.timeout(120000) + + await addIntroOutro('add-intro', 'video_very_short_240p.mp4', HttpStatusCode.NO_CONTENT_204) + await waitJobs([ server ]) + + await addIntroOutro('add-outro', 'video_very_short_240p.mp4', HttpStatusCode.NO_CONTENT_204) + await waitJobs([ server ]) + }) + + it('Should check total quota when creating the task', async function () { + this.timeout(120000) + + const user = await server.users.create({ username: 'user_quota_1' }) + const token = await server.login.getAccessToken('user_quota_1') + const { uuid } = await server.videos.quickUpload({ token, name: 'video_quota_1', fixture: 'video_short.mp4' }) + + const addIntroOutroByUser = (type: 'add-intro' | 'add-outro', expectedStatus: HttpStatusCode) => { + return command.createEditionTasks({ + token, + videoId: uuid, + tasks: [ + { + name: type, + options: { + file: 'video_short.mp4' + } + } + ], + expectedStatus + }) + } + + await waitJobs([ server ]) + + const { videoQuotaUsed } = await server.users.getMyQuotaUsed({ token }) + await server.users.update({ userId: user.id, videoQuota: Math.round(videoQuotaUsed * 2.5) }) + + // Still valid + await addIntroOutroByUser('add-intro', HttpStatusCode.NO_CONTENT_204) + + await waitJobs([ server ]) + + // Too much quota + await addIntroOutroByUser('add-intro', HttpStatusCode.PAYLOAD_TOO_LARGE_413) + await addIntroOutroByUser('add-outro', HttpStatusCode.PAYLOAD_TOO_LARGE_413) + }) + }) + }) + + after(async function () { + await cleanupTests([ server ]) + }) +}) diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts index 3f9355d2d..d756a02c1 100644 --- a/server/tests/api/live/live.ts +++ b/server/tests/api/live/live.ts @@ -3,7 +3,7 @@ import 'mocha' import * as chai from 'chai' import { basename, join } from 'path' -import { ffprobePromise, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' +import { ffprobePromise, getVideoStream } from '@server/helpers/ffmpeg' import { checkLiveCleanupAfterSave, checkLiveSegmentHash, checkResolutionsInMasterPlaylist, testImage } from '@server/tests/shared' import { wait } from '@shared/core-utils' import { @@ -562,7 +562,7 @@ describe('Test live', function () { const segmentPath = servers[0].servers.buildDirectory(join('streaming-playlists', 'hls', video.uuid, filename)) const probe = await ffprobePromise(segmentPath) - const videoStream = await getVideoStreamFromFile(segmentPath, probe) + const videoStream = await getVideoStream(segmentPath, probe) expect(probe.format.bit_rate).to.be.below(maxBitrateLimits[videoStream.height]) expect(probe.format.bit_rate).to.be.at.least(minBitrateLimits[videoStream.height]) diff --git a/server/tests/api/search/search-channels.ts b/server/tests/api/search/search-channels.ts index 0073c71e1..cd4c053d2 100644 --- a/server/tests/api/search/search-channels.ts +++ b/server/tests/api/search/search-channels.ts @@ -26,7 +26,7 @@ describe('Test channels search', function () { const servers = await Promise.all([ createSingleServer(1), - createSingleServer(2, { transcoding: { enabled: false } }) + createSingleServer(2) ]) server = servers[0] remoteServer = servers[1] @@ -35,6 +35,8 @@ describe('Test channels search', function () { await setDefaultChannelAvatar(server) await setDefaultAccountAvatar(server) + await servers[1].config.disableTranscoding() + { await server.users.create({ username: 'user1' }) const channel = { diff --git a/server/tests/api/search/search-playlists.ts b/server/tests/api/search/search-playlists.ts index fcf2f2ee2..d9f12d316 100644 --- a/server/tests/api/search/search-playlists.ts +++ b/server/tests/api/search/search-playlists.ts @@ -29,7 +29,7 @@ describe('Test playlists search', function () { const servers = await Promise.all([ createSingleServer(1), - createSingleServer(2, { transcoding: { enabled: false } }) + createSingleServer(2) ]) server = servers[0] remoteServer = servers[1] @@ -39,6 +39,8 @@ describe('Test playlists search', function () { await setDefaultChannelAvatar([ remoteServer, server ]) await setDefaultAccountAvatar([ remoteServer, server ]) + await servers[1].config.disableTranscoding() + { const videoId = (await server.videos.upload()).uuid diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index 2356f701c..565b2953a 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts @@ -97,6 +97,8 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) { expect(data.live.transcoding.resolutions['1440p']).to.be.false expect(data.live.transcoding.resolutions['2160p']).to.be.false + expect(data.videoEditor.enabled).to.be.false + expect(data.import.videos.concurrency).to.equal(2) expect(data.import.videos.http.enabled).to.be.true expect(data.import.videos.torrent.enabled).to.be.true @@ -197,6 +199,8 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.live.transcoding.resolutions['1080p']).to.be.true expect(data.live.transcoding.resolutions['2160p']).to.be.true + expect(data.videoEditor.enabled).to.be.true + expect(data.import.videos.concurrency).to.equal(4) expect(data.import.videos.http.enabled).to.be.false expect(data.import.videos.torrent.enabled).to.be.false @@ -341,6 +345,9 @@ const newCustomConfig: CustomConfig = { } } }, + videoEditor: { + enabled: true + }, import: { videos: { concurrency: 4, diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts index f0334532b..2296c0cb9 100644 --- a/server/tests/api/server/stats.ts +++ b/server/tests/api/server/stats.ts @@ -230,13 +230,7 @@ describe('Test stats (excluding redundancy)', function () { it('Should have the correct AP stats', async function () { this.timeout(60000) - await servers[0].config.updateCustomSubConfig({ - newConfig: { - transcoding: { - enabled: false - } - } - }) + await servers[0].config.disableTranscoding() const first = await servers[1].stats.get() diff --git a/server/tests/api/videos/audio-only.ts b/server/tests/api/videos/audio-only.ts index e58360ffe..e7e73d382 100644 --- a/server/tests/api/videos/audio-only.ts +++ b/server/tests/api/videos/audio-only.ts @@ -2,7 +2,7 @@ import 'mocha' import * as chai from 'chai' -import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils' +import { getAudioStream, getVideoStreamDimensionsInfo } from '@server/helpers/ffmpeg' import { cleanupTests, createMultipleServers, @@ -91,9 +91,8 @@ describe('Test audio only video transcoding', function () { expect(audioStream['codec_name']).to.be.equal('aac') expect(audioStream['bit_rate']).to.be.at.most(384 * 8000) - const size = await getVideoStreamSize(path) - expect(size.height).to.equal(0) - expect(size.width).to.equal(0) + const size = await getVideoStreamDimensionsInfo(path) + expect(size).to.not.exist } }) diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts index bedb9b8b6..72e6ae2b4 100644 --- a/server/tests/api/videos/index.ts +++ b/server/tests/api/videos/index.ts @@ -8,6 +8,7 @@ import './video-channels' import './video-comments' import './video-create-transcoding' import './video-description' +import './video-editor' import './video-files' import './video-hls' import './video-imports' diff --git a/server/tests/api/videos/video-editor.ts b/server/tests/api/videos/video-editor.ts new file mode 100644 index 000000000..a9b6950cc --- /dev/null +++ b/server/tests/api/videos/video-editor.ts @@ -0,0 +1,368 @@ +import { expect } from 'chai' +import { expectStartWith, getAllFiles } from '@server/tests/shared' +import { areObjectStorageTestsDisabled } from '@shared/core-utils' +import { VideoEditorTask } from '@shared/models' +import { + cleanupTests, + createMultipleServers, + doubleFollow, + ObjectStorageCommand, + PeerTubeServer, + setAccessTokensToServers, + setDefaultVideoChannel, + VideoEditorCommand, + waitJobs +} from '@shared/server-commands' + +describe('Test video editor', function () { + let servers: PeerTubeServer[] = [] + let videoUUID: string + + async function checkDuration (server: PeerTubeServer, duration: number) { + const video = await server.videos.get({ id: videoUUID }) + + expect(video.duration).to.be.approximately(duration, 1) + + for (const file of video.files) { + const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl }) + + for (const stream of metadata.streams) { + expect(Math.round(stream.duration)).to.be.approximately(duration, 1) + } + } + } + + async function renewVideo (fixture = 'video_short.webm') { + const video = await servers[0].videos.quickUpload({ name: 'video', fixture }) + videoUUID = video.uuid + + await waitJobs(servers) + } + + async function createTasks (tasks: VideoEditorTask[]) { + await servers[0].videoEditor.createEditionTasks({ videoId: videoUUID, tasks }) + await waitJobs(servers) + } + + before(async function () { + this.timeout(120_000) + + servers = await createMultipleServers(2) + + await setAccessTokensToServers(servers) + await setDefaultVideoChannel(servers) + + await doubleFollow(servers[0], servers[1]) + + await servers[0].config.enableMinimumTranscoding() + + await servers[0].config.updateExistingSubConfig({ + newConfig: { + videoEditor: { + enabled: true + } + } + }) + }) + + describe('Cutting', function () { + + it('Should cut the beginning of the video', async function () { + this.timeout(120_000) + + await renewVideo() + await waitJobs(servers) + + const beforeTasks = new Date() + + await createTasks([ + { + name: 'cut', + options: { + start: 2 + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 3) + + const video = await server.videos.get({ id: videoUUID }) + expect(new Date(video.publishedAt)).to.be.below(beforeTasks) + } + }) + + it('Should cut the end of the video', async function () { + this.timeout(120_000) + await renewVideo() + + await createTasks([ + { + name: 'cut', + options: { + end: 2 + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 2) + } + }) + + it('Should cut start/end of the video', async function () { + this.timeout(120_000) + await renewVideo('video_short1.webm') // 10 seconds video duration + + await createTasks([ + { + name: 'cut', + options: { + start: 2, + end: 6 + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 4) + } + }) + }) + + describe('Intro/Outro', function () { + + it('Should add an intro', async function () { + this.timeout(120_000) + await renewVideo() + + await createTasks([ + { + name: 'add-intro', + options: { + file: 'video_short.webm' + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 10) + } + }) + + it('Should add an outro', async function () { + this.timeout(120_000) + await renewVideo() + + await createTasks([ + { + name: 'add-outro', + options: { + file: 'video_very_short_240p.mp4' + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 7) + } + }) + + it('Should add an intro/outro', async function () { + this.timeout(120_000) + await renewVideo() + + await createTasks([ + { + name: 'add-intro', + options: { + file: 'video_very_short_240p.mp4' + } + }, + { + name: 'add-outro', + options: { + // Different frame rate + file: 'video_short2.webm' + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 12) + } + }) + + it('Should add an intro to a video without audio', async function () { + this.timeout(120_000) + await renewVideo('video_short_no_audio.mp4') + + await createTasks([ + { + name: 'add-intro', + options: { + file: 'video_very_short_240p.mp4' + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 7) + } + }) + + it('Should add an outro without audio to a video with audio', async function () { + this.timeout(120_000) + await renewVideo() + + await createTasks([ + { + name: 'add-outro', + options: { + file: 'video_short_no_audio.mp4' + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 10) + } + }) + + it('Should add an outro without audio to a video with audio', async function () { + this.timeout(120_000) + await renewVideo('video_short_no_audio.mp4') + + await createTasks([ + { + name: 'add-outro', + options: { + file: 'video_short_no_audio.mp4' + } + } + ]) + + for (const server of servers) { + await checkDuration(server, 10) + } + }) + }) + + describe('Watermark', function () { + + it('Should add a watermark to the video', async function () { + this.timeout(120_000) + await renewVideo() + + const video = await servers[0].videos.get({ id: videoUUID }) + const oldFileUrls = getAllFiles(video).map(f => f.fileUrl) + + await createTasks([ + { + name: 'add-watermark', + options: { + file: 'thumbnail.png' + } + } + ]) + + for (const server of servers) { + const video = await server.videos.get({ id: videoUUID }) + const fileUrls = getAllFiles(video).map(f => f.fileUrl) + + for (const oldUrl of oldFileUrls) { + expect(fileUrls).to.not.include(oldUrl) + } + } + }) + }) + + describe('Complex tasks', function () { + it('Should run a complex task', async function () { + this.timeout(240_000) + await renewVideo() + + await createTasks(VideoEditorCommand.getComplexTask()) + + for (const server of servers) { + await checkDuration(server, 9) + } + }) + }) + + describe('HLS only video edition', function () { + + before(async function () { + // Disable webtorrent + await servers[0].config.updateExistingSubConfig({ + newConfig: { + transcoding: { + webtorrent: { + enabled: false + } + } + } + }) + }) + + it('Should run a complex task on HLS only video', async function () { + this.timeout(240_000) + await renewVideo() + + await createTasks(VideoEditorCommand.getComplexTask()) + + for (const server of servers) { + const video = await server.videos.get({ id: videoUUID }) + expect(video.files).to.have.lengthOf(0) + + await checkDuration(server, 9) + } + }) + }) + + describe('Object storage video edition', function () { + if (areObjectStorageTestsDisabled()) return + + before(async function () { + await ObjectStorageCommand.prepareDefaultBuckets() + + await servers[0].kill() + await servers[0].run(ObjectStorageCommand.getDefaultConfig()) + + await servers[0].config.enableMinimumTranscoding() + }) + + it('Should run a complex task on a video in object storage', async function () { + this.timeout(240_000) + await renewVideo() + + const video = await servers[0].videos.get({ id: videoUUID }) + const oldFileUrls = getAllFiles(video).map(f => f.fileUrl) + + await createTasks(VideoEditorCommand.getComplexTask()) + + for (const server of servers) { + const video = await server.videos.get({ id: videoUUID }) + const files = getAllFiles(video) + + for (const f of files) { + expect(oldFileUrls).to.not.include(f.fileUrl) + } + + for (const webtorrentFile of video.files) { + expectStartWith(webtorrentFile.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl()) + } + + for (const hlsFile of video.streamingPlaylists[0].files) { + expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl()) + } + + await checkDuration(server, 9) + } + }) + }) + + after(async function () { + await cleanupTests(servers) + }) +}) diff --git a/server/tests/api/videos/video-playlist-thumbnails.ts b/server/tests/api/videos/video-playlist-thumbnails.ts index 5fdb0fc03..3944dc344 100644 --- a/server/tests/api/videos/video-playlist-thumbnails.ts +++ b/server/tests/api/videos/video-playlist-thumbnails.ts @@ -45,12 +45,16 @@ describe('Playlist thumbnail', function () { before(async function () { this.timeout(120000) - servers = await createMultipleServers(2, { transcoding: { enabled: false } }) + servers = await createMultipleServers(2) // Get the access tokens await setAccessTokensToServers(servers) await setDefaultVideoChannel(servers) + for (const server of servers) { + await server.config.disableTranscoding() + } + // Server 1 and server 2 follow each other await doubleFollow(servers[0], servers[1]) diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index 1e8dbef02..c33a63df0 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts @@ -75,13 +75,17 @@ describe('Test video playlists', function () { before(async function () { this.timeout(120000) - servers = await createMultipleServers(3, { transcoding: { enabled: false } }) + servers = await createMultipleServers(3) // Get the access tokens await setAccessTokensToServers(servers) await setDefaultVideoChannel(servers) await setDefaultAccountAvatar(servers) + for (const server of servers) { + await server.config.disableTranscoding() + } + // Server 1 and server 2 follow each other await doubleFollow(servers[0], servers[1]) // Server 1 and server 3 follow each other diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index d24a8f4e1..245c4c012 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts @@ -3,10 +3,17 @@ import 'mocha' import * as chai from 'chai' import { omit } from 'lodash' -import { canDoQuickTranscode } from '@server/helpers/ffprobe-utils' -import { generateHighBitrateVideo, generateVideoWithFramerate } from '@server/tests/shared' +import { canDoQuickTranscode } from '@server/helpers/ffmpeg' +import { generateHighBitrateVideo, generateVideoWithFramerate, getAllFiles } from '@server/tests/shared' import { buildAbsoluteFixturePath, getMaxBitrate, getMinLimitBitrate } from '@shared/core-utils' -import { getAudioStream, getMetadataFromFile, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@shared/extra-utils' +import { + getAudioStream, + buildFileMetadata, + getVideoStreamBitrate, + getVideoStreamFPS, + getVideoStreamDimensionsInfo, + hasAudioStream +} from '@shared/extra-utils' import { HttpStatusCode, VideoState } from '@shared/models' import { cleanupTests, @@ -287,8 +294,7 @@ describe('Test video transcoding', function () { const file = videoDetails.files.find(f => f.resolution.id === 240) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const probe = await getAudioStream(path) - expect(probe).to.not.have.property('audioStream') + expect(await hasAudioStream(path)).to.be.false } }) @@ -478,14 +484,14 @@ describe('Test video transcoding', function () { for (const resolution of [ 144, 240, 360, 480 ]) { const file = videoDetails.files.find(f => f.resolution.id === resolution) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const fps = await getVideoFileFPS(path) + const fps = await getVideoStreamFPS(path) expect(fps).to.be.below(31) } const file = videoDetails.files.find(f => f.resolution.id === 720) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const fps = await getVideoFileFPS(path) + const fps = await getVideoStreamFPS(path) expect(fps).to.be.above(58).and.below(62) } @@ -499,7 +505,7 @@ describe('Test video transcoding', function () { { tempFixturePath = await generateVideoWithFramerate(59) - const fps = await getVideoFileFPS(tempFixturePath) + const fps = await getVideoStreamFPS(tempFixturePath) expect(fps).to.be.equal(59) } @@ -522,14 +528,14 @@ describe('Test video transcoding', function () { { const file = video.files.find(f => f.resolution.id === 240) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const fps = await getVideoFileFPS(path) + const fps = await getVideoStreamFPS(path) expect(fps).to.be.equal(25) } { const file = video.files.find(f => f.resolution.id === 720) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const fps = await getVideoFileFPS(path) + const fps = await getVideoStreamFPS(path) expect(fps).to.be.equal(59) } } @@ -563,9 +569,9 @@ describe('Test video transcoding', function () { const file = video.files.find(f => f.resolution.id === resolution) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const bitrate = await getVideoFileBitrate(path) - const fps = await getVideoFileFPS(path) - const dataResolution = await getVideoFileResolution(path) + const bitrate = await getVideoStreamBitrate(path) + const fps = await getVideoStreamFPS(path) + const dataResolution = await getVideoStreamDimensionsInfo(path) expect(resolution).to.equal(resolution) @@ -613,7 +619,7 @@ describe('Test video transcoding', function () { const file = video.files.find(f => f.resolution.id === r) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const bitrate = await getVideoFileBitrate(path) + const bitrate = await getVideoStreamBitrate(path) const inputBitrate = 60_000 const limit = getMinLimitBitrate({ fps: 10, ratio: 1, resolution: r }) @@ -637,7 +643,7 @@ describe('Test video transcoding', function () { const video = await servers[1].videos.get({ id: videoUUID }) const file = video.files.find(f => f.resolution.id === 240) const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) - const metadata = await getMetadataFromFile(path) + const metadata = await buildFileMetadata(path) // expected format properties for (const p of [ @@ -668,8 +674,7 @@ describe('Test video transcoding', function () { for (const server of servers) { const videoDetails = await server.videos.get({ id: videoUUID }) - const videoFiles = videoDetails.files - .concat(videoDetails.streamingPlaylists[0].files) + const videoFiles = getAllFiles(videoDetails) expect(videoFiles).to.have.lengthOf(10) for (const file of videoFiles) { -- cgit v1.2.3