File size: 1,554 Bytes
3165afb
 
2cae2a9
3165afb
 
 
2cae2a9
3165afb
2cae2a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3165afb
2cae2a9
 
 
 
 
3165afb
2cae2a9
3165afb
2cae2a9
 
3165afb
2cae2a9
 
3165afb
2cae2a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3165afb
 
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
import { existsSync } from "node:fs"
import path from "node:path"

import { v4 as uuidv4 } from "uuid"
import ffmpeg, { FfmpegCommand } from "fluent-ffmpeg"
import { getRandomDirectory } from "@aitube/io"

import { getMediaInfo } from "../analyze/getMediaInfo"

export type ConcatenateVideoOutput = {
  filepath: string;
  durationInSec: number;
}

export async function concatenateVideos({
  output,
  videoFilePaths = [],
}: {
  output?: string;

  // those are videos PATHs, not base64 strings!
  videoFilePaths: string[];
}): Promise<ConcatenateVideoOutput> {
  if (!Array.isArray(videoFilePaths)) {
    throw new Error("Videos must be provided in an array")
  }

  videoFilePaths = videoFilePaths.filter((videoPath) => existsSync(videoPath))

  // Create a temporary working directory
  const tempDir = await getRandomDirectory()

  const filePath = output ? output : path.join(tempDir, `${uuidv4()}.mp4`)

  if (!filePath) {
    throw new Error("Failed to generate a valid temporary file path")
  }

  let cmd: FfmpegCommand = ffmpeg()

  videoFilePaths.forEach((video) => {
    cmd = cmd.addInput(video)
  })

  return new Promise<{ filepath: string; durationInSec: number }>(
    (resolve, reject) => {
      cmd
        .on('error', reject)
        .on('end', async () => {
          try {
            const { durationInSec } = await getMediaInfo(filePath);
            resolve({ filepath: filePath, durationInSec });
          } catch (err) {
            reject(err);
          }
        })
        .mergeToFile(filePath, tempDir);
    }
  )
}