2023-06-01 07:51:16 -05:00
|
|
|
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
|
|
|
|
|
|
|
import { expect } from 'chai'
|
2023-06-02 08:32:53 -05:00
|
|
|
import { readdir } from 'fs-extra'
|
|
|
|
import { basename } from 'path'
|
2023-06-01 07:51:16 -05:00
|
|
|
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[]
|
|
|
|
|
2023-06-02 08:32:53 -05:00
|
|
|
let baseUUID: string
|
|
|
|
|
2023-06-01 07:51:16 -05:00
|
|
|
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 () {
|
2023-07-19 03:34:21 -05:00
|
|
|
this.timeout(120000)
|
2023-06-01 07:51:16 -05:00
|
|
|
|
|
|
|
// 5s video
|
|
|
|
const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_short.webm' })
|
2023-06-02 08:32:53 -05:00
|
|
|
baseUUID = uuid
|
2023-06-01 07:51:16 -05:00
|
|
|
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 () {
|
2023-07-19 03:34:21 -05:00
|
|
|
this.timeout(120000)
|
2023-06-01 07:51:16 -05:00
|
|
|
|
|
|
|
// 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 () {
|
2023-07-19 03:34:21 -05:00
|
|
|
this.timeout(120000)
|
2023-06-01 07:51:16 -05:00
|
|
|
|
|
|
|
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 () {
|
2023-07-19 03:34:21 -05:00
|
|
|
this.timeout(120000)
|
2023-06-01 07:51:16 -05:00
|
|
|
|
|
|
|
// 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) {
|
2023-06-06 04:14:13 -05:00
|
|
|
try {
|
|
|
|
await checkStoryboard({ server, uuid, tilesCount: 6, minSize: 250 })
|
|
|
|
} catch { // FIXME: to remove after ffmpeg CI upgrade, ffmpeg CI version (4.3) generates a 7.6s length video
|
|
|
|
await checkStoryboard({ server, uuid, tilesCount: 8, minSize: 250 })
|
|
|
|
}
|
2023-06-01 07:51:16 -05:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
it('Should generate a storyboard after HTTP import', async function () {
|
2023-07-19 03:34:21 -05:00
|
|
|
this.timeout(120000)
|
2023-06-01 07:51:16 -05:00
|
|
|
|
|
|
|
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 () {
|
2023-07-19 03:34:21 -05:00
|
|
|
this.timeout(120000)
|
2023-06-01 07:51:16 -05:00
|
|
|
|
|
|
|
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 })
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-06-02 08:32:53 -05:00
|
|
|
it('Should cleanup storyboards on video deletion', async function () {
|
2023-06-01 07:51:16 -05:00
|
|
|
this.timeout(60000)
|
|
|
|
|
2023-06-02 08:32:53 -05:00
|
|
|
const { storyboards } = await servers[0].storyboard.list({ id: baseUUID })
|
|
|
|
const storyboardName = basename(storyboards[0].storyboardPath)
|
|
|
|
|
|
|
|
const listFiles = () => {
|
|
|
|
const storyboardPath = servers[0].getDirectoryPath('storyboards')
|
|
|
|
return readdir(storyboardPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const storyboads = await listFiles()
|
|
|
|
expect(storyboads).to.include(storyboardName)
|
|
|
|
}
|
|
|
|
|
|
|
|
await servers[0].videos.remove({ id: baseUUID })
|
|
|
|
await waitJobs(servers)
|
|
|
|
|
|
|
|
{
|
|
|
|
const storyboads = await listFiles()
|
|
|
|
expect(storyboads).to.not.include(storyboardName)
|
|
|
|
}
|
2023-06-01 07:51:16 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
after(async function () {
|
|
|
|
await cleanupTests(servers)
|
|
|
|
})
|
|
|
|
})
|