diff --git a/packages/tests/fixtures/custom-thumbnail-2.jpg b/packages/tests/fixtures/custom-thumbnail-2.jpg new file mode 100644 index 000000000..01abe457c Binary files /dev/null and b/packages/tests/fixtures/custom-thumbnail-2.jpg differ diff --git a/packages/tests/fixtures/default-live-preview.jpg b/packages/tests/fixtures/default-live-preview.jpg new file mode 100644 index 000000000..c93dfaa26 Binary files /dev/null and b/packages/tests/fixtures/default-live-preview.jpg differ diff --git a/packages/tests/fixtures/default-live-thumbnail.jpg b/packages/tests/fixtures/default-live-thumbnail.jpg new file mode 100644 index 000000000..5da342dac Binary files /dev/null and b/packages/tests/fixtures/default-live-thumbnail.jpg differ diff --git a/packages/tests/src/api/live/live-save-replay.ts b/packages/tests/src/api/live/live-save-replay.ts index 9e379341d..70abcf20b 100644 --- a/packages/tests/src/api/live/live-save-replay.ts +++ b/packages/tests/src/api/live/live-save-replay.ts @@ -27,6 +27,7 @@ import { waitUntilLiveReplacedByReplayOnAllServers, waitUntilLiveWaitingOnAllServers } from '@peertube/peertube-server-commands' +import { testImageGeneratedByFFmpeg } from '@tests/shared/checks.js' import { checkLiveCleanup } from '@tests/shared/live.js' import { expect } from 'chai' import { FfmpegCommand } from 'fluent-ffmpeg' @@ -36,7 +37,13 @@ describe('Save replay setting', function () { let liveVideoUUID: string let ffmpegCommand: FfmpegCommand - async function createLiveWrapper (options: { permanent: boolean, replay: boolean, replaySettings?: { privacy: VideoPrivacyType } }) { + async function createLiveWrapper (options: { + permanent: boolean + replay: boolean + replaySettings?: { privacy: VideoPrivacyType } + thumbnailfile?: string + previewfile?: string + }) { if (liveVideoUUID) { try { await servers[0].videos.remove({ id: liveVideoUUID }) @@ -51,7 +58,9 @@ describe('Save replay setting', function () { tags: [ 'tag1', 'tag2' ], saveReplay: options.replay, replaySettings: options.replaySettings, - permanentLive: options.permanent + permanentLive: options.permanent, + thumbnailfile: options.thumbnailfile, + previewfile: options.previewfile } const { uuid } = await servers[0].live.create({ fields: attributes }) @@ -142,6 +151,15 @@ describe('Save replay setting', function () { } } + async function checkVideoThumbnail (videoId: string, thumbnailfile: string, previewfile?: string) { + for (const server of servers) { + const video = await server.videos.get({ id: videoId }) + await testImageGeneratedByFFmpeg(server.url, thumbnailfile, video.thumbnailPath, '') + + if (previewfile) await testImageGeneratedByFFmpeg(server.url, previewfile, video.previewPath, '') + } + } + before(async function () { this.timeout(120000) @@ -285,6 +303,7 @@ describe('Save replay setting', function () { await checkVideosExist(liveVideoUUID, 0, HttpStatusCode.OK_200) await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE) await checkVideoPrivacy(liveVideoUUID, VideoPrivacy.PUBLIC) + await checkVideoThumbnail(liveVideoUUID, 'default-live-thumbnail.jpg', 'default-live-preview.jpg') }) it('Should correctly have updated the live and federated it when streaming in the live', async function () { @@ -298,6 +317,7 @@ describe('Save replay setting', function () { await checkVideosExist(liveVideoUUID, 1, HttpStatusCode.OK_200) await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) await checkVideoPrivacy(liveVideoUUID, VideoPrivacy.PUBLIC) + await checkVideoThumbnail(liveVideoUUID, 'default-live-thumbnail.jpg', 'default-live-preview.jpg') }) it('Should correctly have saved the live and federated it after the streaming', async function () { @@ -345,7 +365,15 @@ describe('Save replay setting', function () { it('Should update the saved live and correctly federate the updated attributes', async function () { this.timeout(120000) - await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated', privacy: VideoPrivacy.PUBLIC } }) + await servers[0].videos.update({ + id: liveVideoUUID, + attributes: { + name: 'video updated', + privacy: VideoPrivacy.PUBLIC, + thumbnailfile: 'custom-thumbnail.jpg', + previewfile: 'custom-preview.jpg' + } + }) await waitJobs(servers) for (const server of servers) { @@ -353,6 +381,8 @@ describe('Save replay setting', function () { expect(video.name).to.equal('video updated') expect(video.isLive).to.be.false expect(video.privacy.id).to.equal(VideoPrivacy.PUBLIC) + + await checkVideoThumbnail(liveVideoUUID, 'custom-thumbnail.jpg', 'custom-preview.jpg') } }) @@ -406,13 +436,20 @@ describe('Save replay setting', function () { it('Should correctly create and federate the "waiting for stream" live', async function () { this.timeout(120000) - liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } }) + liveVideoUUID = await createLiveWrapper({ + permanent: true, + replay: true, + replaySettings: { privacy: VideoPrivacy.UNLISTED }, + thumbnailfile: 'custom-thumbnail.jpg', + previewfile: 'custom-preview.jpg' + }) await waitJobs(servers) await checkVideosExist(liveVideoUUID, 0, HttpStatusCode.OK_200) await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE) await checkVideoPrivacy(liveVideoUUID, VideoPrivacy.PUBLIC) + await checkVideoThumbnail(liveVideoUUID, 'custom-thumbnail.jpg', 'custom-preview.jpg') }) it('Should correctly have updated the live and federated it when streaming in the live', async function () { @@ -484,10 +521,19 @@ describe('Save replay setting', function () { await checkVideosExist(lastReplayUUID, 1, HttpStatusCode.OK_200) await checkVideoState(lastReplayUUID, VideoState.PUBLISHED) await checkVideoPrivacy(lastReplayUUID, VideoPrivacy.PUBLIC) + await checkVideoThumbnail(lastReplayUUID, 'custom-thumbnail-from-preview.jpg', 'custom-preview.jpg') + }) + + it('Should update the live replay thumbnail', async function () { + await servers[0].videos.update({ id: lastReplayUUID, attributes: { thumbnailfile: 'custom-thumbnail-2.jpg' } }) + await waitJobs(servers) + + await checkVideoThumbnail(liveVideoUUID, 'custom-thumbnail.jpg', 'custom-preview.jpg') + await checkVideoThumbnail(lastReplayUUID, 'custom-thumbnail-2.jpg') }) }) - describe('With a second live and its replay', function () { + describe('With a second live session', function () { it('Should update the replay settings', async function () { await servers[0].live.update({ videoId: liveVideoUUID, fields: { replaySettings: { privacy: VideoPrivacy.PUBLIC } } }) @@ -498,7 +544,6 @@ describe('Save replay setting', function () { expect(live.saveReplay).to.be.true expect(live.replaySettings).to.exist expect(live.replaySettings.privacy).to.equal(VideoPrivacy.PUBLIC) - }) it('Should correctly have updated the live and federated it when streaming in the live', async function () { @@ -572,6 +617,9 @@ describe('Save replay setting', function () { await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: true, deleted: true }) }) + }) + + describe('With terminated sessions', function () { it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () { this.timeout(120000) @@ -612,6 +660,42 @@ describe('Save replay setting', function () { await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: true, deleted: true }) }) }) + + describe('With a live without custom thumbnail', function () { + + it('Should correctly set the default thumbnail to the live replay', async function () { + this.timeout(120000) + + liveVideoUUID = await createLiveWrapper({ + permanent: true, + replay: true, + replaySettings: { privacy: VideoPrivacy.PUBLIC } + }) + + ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID }) + await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) + await stopFfmpeg(ffmpegCommand) + + await waitUntilLiveWaitingOnAllServers(servers, liveVideoUUID) + await waitJobs(servers) + + const video = await findExternalSavedVideo(servers[0], liveVideoUUID) + lastReplayUUID = video.uuid + + await checkVideoThumbnail(liveVideoUUID, 'default-live-thumbnail.jpg', 'default-live-preview.jpg') + }) + + it('Should update the live replay thumbnail', async function () { + await servers[0].videos.update({ + id: lastReplayUUID, + attributes: { thumbnailfile: 'custom-thumbnail.jpg', previewfile: 'custom-preview.jpg' } + }) + await waitJobs(servers) + + await checkVideoThumbnail(liveVideoUUID, 'default-live-thumbnail.jpg', 'default-live-preview.jpg') + await checkVideoThumbnail(lastReplayUUID, 'custom-thumbnail.jpg', 'custom-preview.jpg') + }) + }) }) after(async function () { diff --git a/server/core/lib/job-queue/handlers/video-live-ending.ts b/server/core/lib/job-queue/handlers/video-live-ending.ts index ad862799f..e341ff518 100644 --- a/server/core/lib/job-queue/handlers/video-live-ending.ts +++ b/server/core/lib/job-queue/handlers/video-live-ending.ts @@ -209,7 +209,8 @@ async function copyOrRegenerateThumbnails (options: { inputPath: preview.getPath(), video: replayVideo, type, - automaticallyGenerated: false + automaticallyGenerated: false, + keepOriginal: true }) }) )