{"version":3,"sources":["../src/config/Config.ts","../src/generator/VideoPreviewer.ts","../src/utils/FileUtils.ts","../src/utils/SystemUtils.ts"],"sourcesContent":["import path from \"path\";\nimport { tmpdir } from \"os\";\nconst CLIP_COUNT = 5;\nconst CLIP_TIME = 5;\nconst SPEED_MULTI = 1;\nconst CLIP_SELECT_STRATEGY = \"max-size\"; // max-size min-size random\nconst CLIP_RANGE = [0.1, 0.9];\nconst FPS_RATE = -1;\n\nexport const TEMP_PATH = path.join(tmpdir(), \"video-previewer\");\nexport const OUTPUT_PATH = \"output.webp\";\nexport const defaultOptions: FFmpegOptions = {\n  clipCount: CLIP_COUNT,\n  clipTime: CLIP_TIME,\n  clipSelectStrategy: CLIP_SELECT_STRATEGY,\n  clipRange: CLIP_RANGE,\n  fpsRate: FPS_RATE,\n  output: { type: \"buffer\", path: \"/\" },\n  speedMulti: SPEED_MULTI,\n};\n\nexport const defaultCaptions: Caption[] = [\n  { start: \"00:00:01\", end: \"00:00:02\" },\n];\n","import { defaultCaptions, defaultOptions, TEMP_PATH } from \"../config/Config\";\nimport fs from \"fs/promises\";\nimport {\n  writeVideo,\n  extractClip,\n  mergeClips,\n  transToWebp,\n  checkUseHardwareAcceleration,\n} from \"../utils/FileUtils\";\nimport path from \"path\";\n\nclass VideoPreviewer {\n  private tempDir: string;\n  private optionsStr: string = \"\";\n  constructor(\n    private video: string,\n    private outputDir: string,\n    private captions: Caption[] = defaultCaptions,\n    readonly options: FFmpegOptions = defaultOptions\n  ) {\n    this.tempDir = TEMP_PATH;\n    this.outputDir = outputDir.endsWith(\".webp\")\n      ? outputDir\n      : outputDir + \".webp\";\n  }\n\n  private async cleanUp() {\n    try {\n      await fs.rm(this.tempDir, { recursive: true, force: true });\n    } catch (e) {\n      throw new Error(\"Error removing temp directory\");\n    }\n  }\n\n  private async optionsToString(options: FFmpegOptions) {\n    let optionStr = \"\";\n    options.useHardwareAcceleration = await checkUseHardwareAcceleration();\n    if (options.width && options.height) {\n      optionStr += ` -s ${options.width}x${options.height}`;\n    }\n    if (options.fpsRate && options.fpsRate > 0) {\n      optionStr += ` -r ${options.fpsRate}`;\n    }\n    if (options.speedMulti && options.speedMulti > 1) {\n      optionStr += ` -filter:v \"setpts=${1 / options.speedMulti}*PTS\"`;\n    }\n    if (options.videoCodec) {\n      optionStr += ` -c:v ${options.videoCodec}`;\n    }\n    if (options.audioCodec) {\n      optionStr += ` -c:a ${options.audioCodec}`;\n    }\n    if (options.bitrate) {\n      optionStr += ` -b:v ${options.bitrate}`;\n    }\n    if (options.audioBitrate) {\n      optionStr += ` -b:a ${options.audioBitrate}`;\n    }\n    if (options.format) {\n      optionStr += ` -f ${options.format}`;\n    }\n    if (options.useHardwareAcceleration) {\n      optionStr += \" -hwaccel cuda -c:v h264_cuvid\";\n    }\n    if (options.additionalOptions) {\n      optionStr += ` ${options.additionalOptions}`;\n    }\n    this.optionsStr = optionStr.trim();\n  }\n\n  private async ensureOutputDirExists() {\n    const dir = path.dirname(this.outputDir);\n    try {\n      await fs.mkdir(dir, { recursive: true });\n    } catch (e) {\n      throw new Error(\"Error creating output directory\");\n    }\n  }\n\n  public async exec() {\n    try {\n      await this.cleanUp();\n      await this.optionsToString(this.options);\n      await fs.mkdir(this.tempDir, { recursive: true });\n      const videoOutputPath = path.join(this.tempDir, \"output.mp4\");\n      const fileListPath = path.join(this.tempDir, \"filelist.txt\");\n      const clipsPath = path.join(this.tempDir, \"clips.mp4\");\n      const webpOutputPath = this.outputDir;\n      await writeVideo(this.video, videoOutputPath);\n      if (!this.captions) throw new Error(\"Frames are not specified\");\n      const snapshotPaths: string[] = [];\n      for (let i = 0; i < this.captions.length; i++) {\n        const { start, end } = this.captions[i];\n        const snapshotPath = path.join(this.tempDir, `snapshot_${i + 1}.mp4`);\n        await extractClip(\n          videoOutputPath,\n          snapshotPath,\n          { start, end },\n          this.optionsStr\n        );\n        snapshotPaths.push(snapshotPath);\n      }\n      await mergeClips(snapshotPaths, clipsPath, fileListPath, this.optionsStr);\n      await this.ensureOutputDirExists();\n      await transToWebp(clipsPath, webpOutputPath, this.optionsStr);\n      return webpOutputPath;\n    } catch (e) {\n      console.log(e);\n      throw new Error(\"Error executing command\");\n    } finally {\n      await this.cleanUp();\n    }\n  }\n}\n\nexport default VideoPreviewer;\n","import { exec } from \"child_process\";\nimport { promisify } from \"util\";\nimport { checkHasCuda, checkHasGPU, checkHasNpp } from \"../utils/SystemUtils\";\nimport axios from \"axios\";\nimport fs from \"fs\";\n\nconst execPromise = promisify(exec);\n\nconst runFFmpegCommand = async (command: string) => {\n  try {\n    const { stdout } = await execPromise(command);\n    return stdout;\n  } catch (error) {\n    throw error;\n  }\n};\n\nasync function downloadVideo(\n  url: string,\n  filename: string\n): Promise<string | null> {\n  if (!filename.endsWith(\".mp4\")) filename += \".mp4\";\n  const directory = filename.substring(0, filename.lastIndexOf(\"/\"));\n  if (directory && !fs.existsSync(directory)) {\n    fs.mkdirSync(directory, { recursive: true });\n  }\n  const response = await axios.get(url, { responseType: \"stream\" });\n  if (response.status === 200) {\n    const writer = fs.createWriteStream(filename);\n    response.data.pipe(writer);\n    return new Promise((resolve, reject) => {\n      writer.on(\"finish\", () => resolve(filename));\n      writer.on(\"error\", reject);\n    });\n  } else {\n    return null;\n  }\n}\n\nexport const writeVideo = async (input: string, output: string) => {\n  return downloadVideo(input, output);\n};\n\nexport const extractClip = async (\n  input: string,\n  output: string,\n  time: { start: string; end: string },\n  options: string = \"\"\n) => {\n  const command = `ffmpeg -i ${input} -ss ${time.start} -to ${time.end} ${options} ${output}`;\n  return runFFmpegCommand(command);\n};\n\nexport const mergeClips = async (\n  inputs: string[],\n  output: string,\n  fileListPath: string,\n  options: string\n) => {\n  let command: string;\n  if (inputs.length === 1) {\n    command = `ffmpeg -i ${inputs[0]} ${options} ${output}`;\n  } else {\n    const fileList = inputs.map((file) => `file '${file}'`).join(\"\\n\");\n    fs.writeFileSync(fileListPath, fileList);\n    command = `ffmpeg -f concat -safe 0 -i ${fileListPath} ${options} ${output}`;\n  }\n  await runFFmpegCommand(command);\n};\n\nexport const transToWebp = async (\n  input: string,\n  output: string,\n  options: string\n) => {\n  const command = `ffmpeg -i ${input} -c:v libwebp ${options} ${output}`;\n  return runFFmpegCommand(command);\n};\n\nexport const checkUseHardwareAcceleration = async (): Promise<boolean> => {\n  const hasGpu = await checkHasGPU();\n  const hasCuda = hasGpu && (await checkHasCuda());\n  const hasNpp = hasCuda && (await checkHasNpp());\n  return hasNpp;\n};\n","import { exec } from \"child_process\";\nimport { promisify } from \"util\";\nimport os from \"os\";\n\nconst execPromise = promisify(exec);\n\nasync function runCommand(command: string): Promise<string> {\n  try {\n    const { stdout } = await execPromise(command);\n    return stdout;\n  } catch (error) {\n    return \"\";\n  }\n}\n\nexport async function checkHasGPU(): Promise<boolean> {\n  const platform = os.platform();\n  let command: string;\n\n  if (platform === \"win32\") {\n    command = \"wmic path win32_VideoController get caption\";\n  } else if (platform === \"linux\") {\n    command = \"lspci | grep -i nvidia\";\n  } else {\n    return false;\n  }\n  const stdout = await runCommand(command);\n  return stdout.toLowerCase().includes(\"nvidia\");\n}\n\nexport async function checkHasCuda(): Promise<boolean> {\n  const platform = os.platform();\n  let command: string;\n\n  if (platform === \"win32\") {\n    command = \"nvcc --version\";\n  } else if (platform === \"linux\") {\n    command = \"nvcc --version\";\n  } else {\n    return false;\n  }\n  const stdout = await runCommand(command);\n  return stdout.includes(\"release\");\n}\n\nexport async function checkHasNpp(): Promise<boolean> {\n  const platform = os.platform();\n  let command: string;\n\n  if (platform === \"win32\") {\n    command = \"nvidia-smi\";\n  } else if (platform === \"linux\") {\n    command = \"nvidia-smi\";\n  } else {\n    return false;\n  }\n  const stdout = await runCommand(command);\n  return stdout.includes(\"NPP\");\n}\n"],"mappings":"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","names":["fs","exec","promisify","execPromise","promisify","exec","path","fs"]}