mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 01:39:37 +02:00

* Implement processing storyboards by runners * Fixed storyboard generation by runners * use common code patterns * fix import * improve debug logging for storyboard generation * config option for storyboard processing with remote-runners * refactor repetitive pattern * refactor storyboard related code to share common utlities * Fix test * Fix storyboard generation config logic * Improve logging * Added tests for storyboard generation with runners * Refactor PR --------- Co-authored-by: ilfarpro <ilfarpro@ya.ru> Co-authored-by: Chocobozzz <me@florianbigard.com>
98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
import { ffprobePromise, getVideoStreamDimensionsInfo } from '@peertube/peertube-ffmpeg'
|
|
import { retryTransactionWrapper } from '@server/helpers/database-utils.js'
|
|
import { LoggerTags, logger } from '@server/helpers/logger.js'
|
|
import { deleteFileAndCatch } from '@server/helpers/utils.js'
|
|
import { STORYBOARD } from '@server/initializers/constants.js'
|
|
import { sequelizeTypescript } from '@server/initializers/database.js'
|
|
import { StoryboardModel } from '@server/models/video/storyboard.js'
|
|
import { VideoModel } from '@server/models/video/video.js'
|
|
import { MVideo } from '@server/types/models/index.js'
|
|
import { federateVideoIfNeeded } from './activitypub/videos/federate.js'
|
|
|
|
export async function buildSpriteSize (videoPath: string) {
|
|
const probe = await ffprobePromise(videoPath)
|
|
const videoStreamInfo = await getVideoStreamDimensionsInfo(videoPath, probe)
|
|
|
|
if (videoStreamInfo.isPortraitMode) {
|
|
return {
|
|
spriteHeight: STORYBOARD.SPRITE_MAX_SIZE,
|
|
spriteWidth: Math.round(STORYBOARD.SPRITE_MAX_SIZE * videoStreamInfo.ratio)
|
|
}
|
|
}
|
|
|
|
return {
|
|
spriteWidth: STORYBOARD.SPRITE_MAX_SIZE,
|
|
spriteHeight: Math.round(STORYBOARD.SPRITE_MAX_SIZE / videoStreamInfo.ratio)
|
|
}
|
|
}
|
|
|
|
export function buildTotalSprites (video: MVideo) {
|
|
if (video.duration < 3) return { spriteDuration: undefined, totalSprites: 0 }
|
|
|
|
const maxSprites = Math.min(Math.ceil(video.duration), STORYBOARD.SPRITES_MAX_EDGE_COUNT * STORYBOARD.SPRITES_MAX_EDGE_COUNT)
|
|
|
|
const spriteDuration = Math.ceil(video.duration / maxSprites)
|
|
const totalSprites = Math.ceil(video.duration / spriteDuration)
|
|
|
|
// We can generate a single line so we don't need a prime number
|
|
if (totalSprites <= STORYBOARD.SPRITES_MAX_EDGE_COUNT) return { spriteDuration, totalSprites }
|
|
|
|
return { spriteDuration, totalSprites }
|
|
}
|
|
|
|
export function findGridSize (options: {
|
|
toFind: number
|
|
maxEdgeCount: number
|
|
}) {
|
|
const { toFind, maxEdgeCount } = options
|
|
|
|
for (let i = 1; i <= maxEdgeCount; i++) {
|
|
for (let j = i; j <= maxEdgeCount; j++) {
|
|
if (toFind <= i * j) return { width: j, height: i }
|
|
}
|
|
}
|
|
|
|
throw new Error(`Could not find grid size (to find: ${toFind}, max edge count: ${maxEdgeCount}`)
|
|
}
|
|
|
|
export async function insertStoryboardInDatabase (options: {
|
|
videoUUID: string
|
|
lTags: LoggerTags
|
|
filename: string
|
|
destination: string
|
|
imageSize: { width: number, height: number }
|
|
spriteHeight: number
|
|
spriteWidth: number
|
|
spriteDuration: number
|
|
federate: boolean
|
|
}) {
|
|
const { videoUUID, lTags, imageSize, spriteHeight, spriteWidth, spriteDuration, destination, filename, federate } = options
|
|
|
|
await retryTransactionWrapper(() => {
|
|
return sequelizeTypescript.transaction(async transaction => {
|
|
const video = await VideoModel.loadFull(videoUUID, transaction)
|
|
if (!video) {
|
|
logger.info(`Video ${videoUUID} does not exist anymore, skipping storyboard generation.`, lTags)
|
|
deleteFileAndCatch(destination)
|
|
return
|
|
}
|
|
|
|
const existing = await StoryboardModel.loadByVideo(video.id, transaction)
|
|
if (existing) await existing.destroy({ transaction })
|
|
|
|
await StoryboardModel.create({
|
|
filename,
|
|
totalHeight: imageSize.height,
|
|
totalWidth: imageSize.width,
|
|
spriteHeight,
|
|
spriteWidth,
|
|
spriteDuration,
|
|
videoId: video.id
|
|
}, { transaction })
|
|
|
|
if (federate) {
|
|
await federateVideoIfNeeded(video, false, transaction)
|
|
}
|
|
})
|
|
})
|
|
}
|