import { unlink, writeFile } from "node:fs/promises";
import path from "node:path";
import ffmpeg from "fluent-ffmpeg";
import ffmpegStatic from "ffmpeg-static";
/**
 * Concatenate the ordered per-shot clips into the final video, then (optionally)
 * mix background music under the narration. Concat is a stream-copy demuxer pass
 * (all shots share encode settings → fast, lossless); BGM is a second pass that
 * re-encodes audio only (video copied). Adapted from huobao-drama's ffmpeg-merge.
 */
if (ffmpegStatic)
    ffmpeg.setFfmpegPath(ffmpegStatic);
function concat(clipPaths, out) {
    return (async () => {
        const listPath = `${out}.concat.txt`;
        const listBody = clipPaths.map((p) => `file '${p.replace(/'/g, "'\\''")}'`).join("\n");
        await writeFile(listPath, listBody, "utf-8");
        await new Promise((resolve, reject) => {
            ffmpeg()
                .input(listPath)
                .inputOptions(["-f", "concat", "-safe", "0"])
                .outputOptions(["-c", "copy", "-movflags", "+faststart"])
                .on("error", (err) => reject(new Error(`ffmpeg concat failed: ${err.message}`)))
                .on("end", () => resolve())
                .save(out);
        });
    })();
}
/** Mix bgm under the existing audio of `video`, looping bgm and ending with video. */
function mixBgm(video, bgm, volume, out) {
    return new Promise((resolve, reject) => {
        ffmpeg()
            .input(video)
            .input(bgm)
            .inputOptions(["-stream_loop", "-1"]) // applies to the most recent input (bgm) → loop to cover video
            .complexFilter([
            `[1:a]volume=${volume}[bg]`,
            `[0:a][bg]amix=inputs=2:duration=first:dropout_transition=2[a]`,
        ])
            .outputOptions(["-map", "0:v", "-map", "[a]", "-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-shortest", "-movflags", "+faststart"])
            .on("error", (err) => reject(new Error(`ffmpeg bgm mix failed: ${err.message}`)))
            .on("end", () => resolve())
            .save(out);
    });
}
export async function mergeClips(clipPaths, out, opts = {}) {
    if (!opts.bgm) {
        await concat(clipPaths, out);
        return;
    }
    // Concat to a temp, then mix BGM into the final output.
    const tmp = path.join(path.dirname(out), `concat-${Date.now()}.mp4`);
    await concat(clipPaths, tmp);
    try {
        await mixBgm(tmp, opts.bgm, opts.bgmVolume ?? 0.2, out);
    }
    finally {
        await unlink(tmp).catch(() => undefined);
    }
}
//# sourceMappingURL=ffmpeg-merge.js.map