|
import { v4 as uuidv4 } from "uuid" |
|
|
|
import { Video, VideoShot } from "../types.mts" |
|
|
|
import { generateVideo } from "../production/generateVideo.mts" |
|
import { upscaleVideo } from "../production/upscaleVideo.mts" |
|
import { interpolateVideo } from "../production/interpolateVideo.mts" |
|
import { postInterpolation } from "../production/postInterpolation.mts" |
|
import { generateAudio } from "../production/generateAudio.mts" |
|
import { addAudioToVideo } from "../production/addAudioToVideo.mts" |
|
|
|
import { downloadFileToTmp } from "../utils/downloadFileToTmp.mts" |
|
import { copyVideoFromTmpToPending } from "../utils/copyVideoFromTmpToPending.mts" |
|
|
|
import { saveAndCheckIfNeedToStop } from "./saveAndCheckIfNeedToStop.mts" |
|
import { enrichVideoSpecsUsingLLM } from "../llm/enrichVideoSpecsUsingLLM.mts" |
|
import { updateShotPreview } from "./updateShotPreview.mts" |
|
|
|
export const processVideo = async (video: Video) => { |
|
|
|
|
|
if (["pause", "completed", "abort", "delete"].includes(video.status)) { return } |
|
|
|
console.log(`processing video video ${video.id}`) |
|
|
|
|
|
|
|
let nbTotalSteps = 2 |
|
|
|
for (const shot of video.shots) { |
|
nbTotalSteps += shot.nbTotalSteps |
|
} |
|
|
|
let nbCompletedSteps = 0 |
|
|
|
if (!video.hasGeneratedSpecs) { |
|
try { |
|
await enrichVideoSpecsUsingLLM(video) |
|
} catch (err) { |
|
console.error(`LLM error: ${err}`) |
|
video.error = `LLM error: ${err}` |
|
video.status = "delete" |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} |
|
|
|
nbCompletedSteps++ |
|
video.hasGeneratedSpecs = true |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} |
|
|
|
|
|
for (const shot of video.shots) { |
|
nbCompletedSteps += shot.nbCompletedSteps |
|
|
|
|
|
if (shot.completed) { |
|
continue |
|
} |
|
|
|
console.log(`need to complete shot ${shot.id}`) |
|
|
|
|
|
|
|
|
|
|
|
|
|
const nbFramesForBaseModel = 24 |
|
|
|
|
|
if (!shot.hasGeneratedPreview) { |
|
console.log("generating a preview of the final result..") |
|
let generatedPreviewVideoUrl = "" |
|
try { |
|
generatedPreviewVideoUrl = await generateVideo(shot.shotPrompt, { |
|
seed: shot.seed, |
|
nbFrames: nbFramesForBaseModel, |
|
nbSteps: 10, |
|
}) |
|
|
|
console.log("downloading preview video..") |
|
|
|
|
|
await downloadFileToTmp(generatedPreviewVideoUrl, shot.fileName) |
|
|
|
await copyVideoFromTmpToPending(shot.fileName) |
|
|
|
shot.hasGeneratedPreview = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
await updateShotPreview(video, shot) |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} catch (err) { |
|
console.error(`failed to generate preview for shot ${shot.id} (${err})`) |
|
|
|
video.error = `failed to generate preview for shot ${shot.id} (will try again later)` |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
|
|
|
|
return |
|
} |
|
|
|
} |
|
|
|
const notAllShotsHavePreview = video.shots.some(s => !s.hasGeneratedPreview) |
|
|
|
if (notAllShotsHavePreview) { |
|
console.log(`step 2 isn't unlocked yet, because not all videos have generated preview`) |
|
continue |
|
} |
|
|
|
if (!shot.hasGeneratedVideo) { |
|
console.log("generating primordial pixel soup (raw video)..") |
|
let generatedVideoUrl = "" |
|
|
|
|
|
const nbFramesForBaseModel = 24 |
|
|
|
try { |
|
generatedVideoUrl = await generateVideo(shot.shotPrompt, { |
|
seed: shot.seed, |
|
nbFrames: nbFramesForBaseModel, |
|
nbSteps: shot.steps, |
|
}) |
|
|
|
console.log("downloading video..") |
|
|
|
await downloadFileToTmp(generatedVideoUrl, shot.fileName) |
|
|
|
await copyVideoFromTmpToPending(shot.fileName) |
|
|
|
shot.hasGeneratedVideo = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
await updateShotPreview(video, shot) |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} catch (err) { |
|
console.error(`failed to generate shot ${shot.id} (${err})`) |
|
|
|
video.error = `failed to generate shot ${shot.id} (will try again later)` |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
|
|
break |
|
} |
|
|
|
} |
|
|
|
if (!shot.hasUpscaledVideo) { |
|
console.log("upscaling video..") |
|
try { |
|
await upscaleVideo(shot.fileName, shot.shotPrompt) |
|
|
|
shot.hasUpscaledVideo = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
await updateShotPreview(video, shot) |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
|
|
} catch (err) { |
|
console.error(`failed to upscale shot ${shot.id} (${err})`) |
|
|
|
video.error = `failed to upscale shot ${shot.id} (will try again later)` |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
|
|
break |
|
} |
|
} |
|
|
|
if (!shot.hasInterpolatedVideo) { |
|
console.log("interpolating video..") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const interpolationSteps = 2 |
|
const interpolatedFramesPerSecond = 30 |
|
console.log('creating slow-mo video (910x512 @ 30 FPS)') |
|
try { |
|
await interpolateVideo( |
|
shot.fileName, |
|
interpolationSteps, |
|
interpolatedFramesPerSecond |
|
) |
|
|
|
shot.hasInterpolatedVideo = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('performing final scaling (1280x720 @ 30 FPS)') |
|
|
|
try { |
|
await postInterpolation(shot.fileName, shot.durationMs, shot.fps, shot.noiseAmount) |
|
|
|
shot.hasPostProcessedVideo = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
await updateShotPreview(video, shot) |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} catch (err) { |
|
throw err |
|
} |
|
} catch (err) { |
|
console.error(`failed to interpolate and post-process shot ${shot.id} (${err})`) |
|
|
|
video.error = `failed to interpolate and shot ${shot.id} (will try again later)` |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
break |
|
} |
|
} |
|
|
|
|
|
let foregroundAudioFileName = `${video.ownerId}_${video.id}_${shot.id}_${uuidv4()}.m4a` |
|
|
|
if (!shot.hasGeneratedForegroundAudio) { |
|
if (shot.foregroundAudioPrompt) { |
|
console.log("generating foreground audio..") |
|
|
|
try { |
|
await generateAudio(shot.foregroundAudioPrompt, foregroundAudioFileName) |
|
|
|
shot.hasGeneratedForegroundAudio = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
|
|
await addAudioToVideo(shot.fileName, foregroundAudioFileName) |
|
|
|
await updateShotPreview(video, shot) |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
|
|
} catch (err) { |
|
console.error(`failed to generate foreground audio for ${shot.id} (${err})`) |
|
|
|
video.error = `failed to generate foreground audio ${shot.id} (will try again later)` |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
break |
|
} |
|
} else { |
|
shot.hasGeneratedForegroundAudio = true |
|
shot.nbCompletedSteps++ |
|
nbCompletedSteps++ |
|
shot.progressPercent = Math.round((shot.nbCompletedSteps / shot.nbTotalSteps) * 100) |
|
video.progressPercent = Math.round((nbCompletedSteps / nbTotalSteps) * 100) |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} |
|
} |
|
|
|
shot.completed = true |
|
shot.completedAt = new Date().toISOString() |
|
shot.progressPercent = 100 |
|
|
|
video.nbCompletedShots++ |
|
|
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} |
|
|
|
console.log(`end of the loop:`) |
|
console.log(`nb completed shots: ${video.nbCompletedShots}`) |
|
console.log(`len of the shot array: ${video.shots.length}`) |
|
|
|
|
|
|
|
|
|
if (video.nbCompletedShots === video.shots.length) { |
|
console.log(`we have finished each individual shot!`) |
|
|
|
if (!video.hasAssembledVideo) { |
|
video.hasAssembledVideo = true |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nbCompletedSteps++ |
|
video.completed = true |
|
if (await saveAndCheckIfNeedToStop(video)) { return } |
|
} |
|
} |