From d8f39b126d9fe4bec1c12fb213548cc6edc87867 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 1 Jun 2023 14:51:16 +0200 Subject: Add storyboard support --- server/tests/api/check-params/config.ts | 3 + server/tests/api/check-params/index.ts | 1 + server/tests/api/check-params/video-storyboards.ts | 45 +++++ server/tests/api/check-params/videos-overviews.ts | 2 +- server/tests/api/server/config.ts | 5 + server/tests/api/videos/index.ts | 1 + server/tests/api/videos/video-storyboard.ts | 184 +++++++++++++++++++++ server/tests/fixtures/video_very_long_10p.mp4 | Bin 0 -> 185338 bytes 8 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 server/tests/api/check-params/video-storyboards.ts create mode 100644 server/tests/api/videos/video-storyboard.ts create mode 100644 server/tests/fixtures/video_very_long_10p.mp4 (limited to 'server/tests') diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index 472cad182..3c752cc3e 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts @@ -74,6 +74,9 @@ describe('Test config API validators', function () { }, torrents: { size: 4 + }, + storyboards: { + size: 5 } }, signup: { diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts index 400d312d3..c2a7ccd78 100644 --- a/server/tests/api/check-params/index.ts +++ b/server/tests/api/check-params/index.ts @@ -34,6 +34,7 @@ import './video-comments' import './video-files' import './video-imports' import './video-playlists' +import './video-storyboards' import './video-source' import './video-studio' import './video-token' diff --git a/server/tests/api/check-params/video-storyboards.ts b/server/tests/api/check-params/video-storyboards.ts new file mode 100644 index 000000000..a43d8fc48 --- /dev/null +++ b/server/tests/api/check-params/video-storyboards.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import { HttpStatusCode, VideoPrivacy } from '@shared/models' +import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' + +describe('Test video storyboards API validator', function () { + let server: PeerTubeServer + + let publicVideo: { uuid: string } + let privateVideo: { uuid: string } + + // --------------------------------------------------------------- + + before(async function () { + this.timeout(30000) + + server = await createSingleServer(1) + await setAccessTokensToServers([ server ]) + + publicVideo = await server.videos.quickUpload({ name: 'public' }) + privateVideo = await server.videos.quickUpload({ name: 'private', privacy: VideoPrivacy.PRIVATE }) + }) + + it('Should fail without a valid uuid', async function () { + await server.storyboard.list({ id: '4da6fde3-88f7-4d16-b119-108df563d0b0', expectedStatus: HttpStatusCode.NOT_FOUND_404 }) + }) + + it('Should receive 404 when passing a non existing video id', async function () { + await server.storyboard.list({ id: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 }) + }) + + it('Should not get the private storyboard without the appropriate token', async function () { + await server.storyboard.list({ id: privateVideo.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401, token: null }) + await server.storyboard.list({ id: publicVideo.uuid, expectedStatus: HttpStatusCode.OK_200, token: null }) + }) + + it('Should succeed with the correct parameters', async function () { + await server.storyboard.list({ id: privateVideo.uuid }) + await server.storyboard.list({ id: publicVideo.uuid }) + }) + + after(async function () { + await cleanupTests([ server ]) + }) +}) diff --git a/server/tests/api/check-params/videos-overviews.ts b/server/tests/api/check-params/videos-overviews.ts index f9cdb7ab3..ae7de24dd 100644 --- a/server/tests/api/check-params/videos-overviews.ts +++ b/server/tests/api/check-params/videos-overviews.ts @@ -2,7 +2,7 @@ import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/server-commands' -describe('Test videos overview', function () { +describe('Test videos overview API validator', function () { let server: PeerTubeServer // --------------------------------------------------------------- diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index 011ba268c..efa7b50e3 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts @@ -46,6 +46,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) { expect(data.cache.previews.size).to.equal(1) expect(data.cache.captions.size).to.equal(1) expect(data.cache.torrents.size).to.equal(1) + expect(data.cache.storyboards.size).to.equal(1) expect(data.signup.enabled).to.be.true expect(data.signup.limit).to.equal(4) @@ -154,6 +155,7 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.cache.previews.size).to.equal(2) expect(data.cache.captions.size).to.equal(3) expect(data.cache.torrents.size).to.equal(4) + expect(data.cache.storyboards.size).to.equal(5) expect(data.signup.enabled).to.be.false expect(data.signup.limit).to.equal(5) @@ -290,6 +292,9 @@ const newCustomConfig: CustomConfig = { }, torrents: { size: 4 + }, + storyboards: { + size: 5 } }, signup: { diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts index 357c08199..9c79b3aa6 100644 --- a/server/tests/api/videos/index.ts +++ b/server/tests/api/videos/index.ts @@ -20,3 +20,4 @@ import './videos-history' import './videos-overview' import './video-source' import './video-static-file-privacy' +import './video-storyboard' diff --git a/server/tests/api/videos/video-storyboard.ts b/server/tests/api/videos/video-storyboard.ts new file mode 100644 index 000000000..7ccdca8f7 --- /dev/null +++ b/server/tests/api/videos/video-storyboard.ts @@ -0,0 +1,184 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import { expect } from 'chai' +import { FIXTURE_URLS } from '@server/tests/shared' +import { areHttpImportTestsDisabled } from '@shared/core-utils' +import { HttpStatusCode, VideoPrivacy } from '@shared/models' +import { + cleanupTests, + createMultipleServers, + doubleFollow, + makeGetRequest, + PeerTubeServer, + sendRTMPStream, + setAccessTokensToServers, + setDefaultVideoChannel, + stopFfmpeg, + waitJobs +} from '@shared/server-commands' + +async function checkStoryboard (options: { + server: PeerTubeServer + uuid: string + tilesCount?: number + minSize?: number +}) { + const { server, uuid, tilesCount, minSize = 1000 } = options + + const { storyboards } = await server.storyboard.list({ id: uuid }) + + expect(storyboards).to.have.lengthOf(1) + + const storyboard = storyboards[0] + + expect(storyboard.spriteDuration).to.equal(1) + expect(storyboard.spriteHeight).to.equal(108) + expect(storyboard.spriteWidth).to.equal(192) + expect(storyboard.storyboardPath).to.exist + + if (tilesCount) { + expect(storyboard.totalWidth).to.equal(192 * Math.min(tilesCount, 10)) + expect(storyboard.totalHeight).to.equal(108 * Math.max((tilesCount / 10), 1)) + } + + const { body } = await makeGetRequest({ url: server.url, path: storyboard.storyboardPath, expectedStatus: HttpStatusCode.OK_200 }) + expect(body.length).to.be.above(minSize) +} + +describe('Test video storyboard', function () { + let servers: PeerTubeServer[] + + before(async function () { + this.timeout(120000) + + servers = await createMultipleServers(2) + await setAccessTokensToServers(servers) + await setDefaultVideoChannel(servers) + + await doubleFollow(servers[0], servers[1]) + }) + + it('Should generate a storyboard after upload without transcoding', async function () { + this.timeout(60000) + + // 5s video + const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_short.webm' }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid, tilesCount: 5 }) + } + }) + + it('Should generate a storyboard after upload without transcoding with a long video', async function () { + this.timeout(60000) + + // 124s video + const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_very_long_10p.mp4' }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid, tilesCount: 100 }) + } + }) + + it('Should generate a storyboard after upload with transcoding', async function () { + this.timeout(60000) + + await servers[0].config.enableMinimumTranscoding() + + // 5s video + const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_short.webm' }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid, tilesCount: 5 }) + } + }) + + it('Should generate a storyboard after an audio upload', async function () { + this.timeout(60000) + + // 6s audio + const attributes = { name: 'audio', fixture: 'sample.ogg' } + const { uuid } = await servers[0].videos.upload({ attributes, mode: 'legacy' }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid, tilesCount: 6, minSize: 250 }) + } + }) + + it('Should generate a storyboard after HTTP import', async function () { + this.timeout(60000) + + if (areHttpImportTestsDisabled()) return + + // 3s video + const { video } = await servers[0].imports.importVideo({ + attributes: { + targetUrl: FIXTURE_URLS.goodVideo, + channelId: servers[0].store.channel.id, + privacy: VideoPrivacy.PUBLIC + } + }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid: video.uuid, tilesCount: 3 }) + } + }) + + it('Should generate a storyboard after torrent import', async function () { + this.timeout(60000) + + if (areHttpImportTestsDisabled()) return + + // 10s video + const { video } = await servers[0].imports.importVideo({ + attributes: { + magnetUri: FIXTURE_URLS.magnet, + channelId: servers[0].store.channel.id, + privacy: VideoPrivacy.PUBLIC + } + }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid: video.uuid, tilesCount: 10 }) + } + }) + + it('Should generate a storyboard after a live', async function () { + this.timeout(240000) + + await servers[0].config.enableLive({ allowReplay: true, transcoding: true, resolutions: 'min' }) + + const { live, video } = await servers[0].live.quickCreate({ + saveReplay: true, + permanentLive: false, + privacy: VideoPrivacy.PUBLIC + }) + + const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) + await servers[0].live.waitUntilPublished({ videoId: video.id }) + + await stopFfmpeg(ffmpegCommand) + + await servers[0].live.waitUntilReplacedByReplay({ videoId: video.id }) + await waitJobs(servers) + + for (const server of servers) { + await checkStoryboard({ server, uuid: video.uuid }) + } + }) + + it('Should generate a storyboard with different video durations', async function () { + this.timeout(60000) + + }) + + after(async function () { + await cleanupTests(servers) + }) +}) diff --git a/server/tests/fixtures/video_very_long_10p.mp4 b/server/tests/fixtures/video_very_long_10p.mp4 new file mode 100644 index 000000000..852297933 Binary files /dev/null and b/server/tests/fixtures/video_very_long_10p.mp4 differ -- cgit v1.2.3