File size: 2,359 Bytes
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
import { tmpdir } from "node:os"
import { join } from "node:path"
import { mkdtemp } from "node:fs/promises"
import { v4 as uuidv4 } from "uuid"

import { ClapProject } from "./core/clap/types.mts";
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"

/**
 * Generate a .mp4 video inside a direcory (if none is provided, it will be created in /tmp)
 * 
 * @param clap 
 * @returns file path to the final .mp4
 */
export async function clapToTmpVideoFilePath(clap: ClapProject, dir = ""): Promise<string> {

  dir = dir || (await mkdtemp(join(tmpdir(), uuidv4())))

  const videoFilePaths: string[] = []
  const videoSegments = clap.segments.filter(s => s.category === "video" && s.assetUrl.startsWith("data:video/"))

  for (const segment of videoSegments) {
    videoFilePaths.push(
      await writeBase64ToFile(
        segment.assetUrl,
        join(dir, `tmp_asset_${segment.id}.mp4`)
      )
    )
  }

  const concatenatedVideosNoSound = await concatenateVideos({
    videoFilePaths,
    output: join(dir, `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(dir, `tmp_asset_${segment.id}.wav`)
      )
    )
  }

  const concatenatedAudio = await concatenateAudio({
    output: join(dir, `tmp_asset_concatenated_audio.wav`),
    audioTracks,
    crossfadeDurationInSec: 2 // 2 seconds
  })

  const finalFilePathOfVideoWithSound = await concatenateVideosWithAudio({
    output: join(dir, `final_video.mp4`),
    audioFilePath: concatenatedAudio.filepath,
    videoFilePaths: [concatenatedVideosNoSound.filepath],
    // videos are silent, so they can stay at 0
    videoTracksVolume: 0.0,
    audioTrackVolume: 1.0,
  })

  // we delete all the temporary assets
  await deleteFilesWithName(dir, `tmp_asset_`)

  return finalFilePathOfVideoWithSound
}