Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
File size: 3,553 Bytes
2cae2a9 2f67628 2cae2a9 46fcec6 2ed47da 2cae2a9 2ed47da 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2ed47da 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 46fcec6 2cae2a9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
import { join } from "node:path"
import { ClapProject } from "@aitube/clap";
import { concatenateAudio } from "./core/ffmpeg/concatenateAudio.mts";
import { concatenateVideosWithAudio } from "./core/ffmpeg/concatenateVideosWithAudio.mts";
import { writeBase64ToFile } from "./core/files/writeBase64ToFile.mts";
import { concatenateVideos } from "./core/ffmpeg/concatenateVideos.mts"
import { deleteFilesWithName } from "./core/files/deleteFileWithName.mts"
import { getRandomDirectory } from "./core/files/getRandomDirectory.mts";
import { clapWithVideosToVideoFile } from "./core/exporters/clapWithVideosToVideoFile.mts";
import { clapWithStoryboardsToVideoFile } from "./core/exporters/clapWithStoryboardsToVideoFile.mts";
/**
* Generate a .mp4 video inside a directory (if none is provided, it will be created in /tmp)
*
* @param clap
* @returns file path to the final .mp4
*/
export async function clapToTmpVideoFilePath({
clap,
outputDir = "",
clearTmpFilesAtEnd = false
}: {
clap: ClapProject
outputDir?: string
// if you leave this to false, you will have to clear files yourself
// (eg. after sending the final video file over)
clearTmpFilesAtEnd?: boolean
}): Promise<{
tmpWorkDir: string
outputFilePath: string
}> {
outputDir = outputDir || (await getRandomDirectory())
const videoFilePaths: string[] = []
const videoSegments = clap.segments.filter(s => s.category === "video" && s.assetUrl.startsWith("data:video/"))
const storyboardSegments = clap.segments.filter(s => s.category === "storyboard" && s.assetUrl.startsWith("data:image/"))
const canUseVideos = videoSegments.length > 0
const canUseStoryboards = !canUseVideos && storyboardSegments.length > 0
// two possibilities:
// we can either generate from the video files, or from the storyboards
// the storyboard video will be a bit more boring, but at least it should process faster
if (canUseVideos) {
await clapWithVideosToVideoFile({
clap,
videoSegments,
outputDir,
})
} else if (canUseStoryboards) {
await clapWithStoryboardsToVideoFile({
clap,
storyboardSegments,
outputDir,
})
} else {
throw new Error(`the provided Clap doesn't contain any video or storyboard`)
}
const concatenatedVideosNoMusic = await concatenateVideos({
videoFilePaths,
output: join(outputDir, `tmp_asset_concatenated_videos.mp4`)
})
const audioTracks: string[] = []
const musicSegments = clap.segments.filter(s =>
s.category === "music" &&
s.assetUrl.startsWith("data:audio/")
)
for (const segment of musicSegments) {
audioTracks.push(
await writeBase64ToFile(
segment.assetUrl,
join(outputDir, `tmp_asset_${segment.id}.wav`)
)
)
}
const concatenatedAudio = await concatenateAudio({
output: join(outputDir, `tmp_asset_concatenated_audio.wav`),
audioTracks,
crossfadeDurationInSec: 2 // 2 seconds
})
const finalFilePathOfVideoWithMusic = await concatenateVideosWithAudio({
output: join(outputDir, `final_video.mp4`),
audioFilePath: concatenatedAudio.filepath,
videoFilePaths: [concatenatedVideosNoMusic.filepath],
// videos are silent, so they can stay at 0
videoTracksVolume: 0.85,
audioTrackVolume: 0.15, // let's keep the music volume low
})
if (clearTmpFilesAtEnd) {
// we delete all the temporary assets
await deleteFilesWithName(outputDir, `tmp_asset_`)
}
return {
tmpWorkDir: outputDir,
outputFilePath: finalFilePathOfVideoWithMusic
}
} |