← Files Cecil-IA Labs FFmpegARCHIVED FILE

dist/core/progress.js

8.33 KB · Sep 30, 2026 · 23:17 UTC

↓ Download file

function optionalNumber(value) {
    if (value === undefined || value === "N/A")
        return undefined;
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : undefined;
}
function toSnapshot(values) {
    const frame = optionalNumber(values["frame"]);
    const fps = optionalNumber(values["fps"]);
    const stream0_0Q = optionalNumber(values["stream_0_0_q"]);
    const totalSize = optionalNumber(values["total_size"]);
    const outTimeUs = optionalNumber(values["out_time_us"]);
    const outTimeMs = optionalNumber(values["out_time_ms"]);
    const dupFrames = optionalNumber(values["dup_frames"]);
    const dropFrames = optionalNumber(values["drop_frames"]);
    const bitrate = values["bitrate"];
    const outTime = values["out_time"];
    const speed = values["speed"];
    return {
        ...(frame !== undefined ? { frame } : {}),
        ...(fps !== undefined ? { fps } : {}),
        ...(stream0_0Q !== undefined ? { stream0_0Q } : {}),
        ...(bitrate !== undefined ? { bitrate } : {}),
        ...(totalSize !== undefined ? { totalSize } : {}),
        ...(outTimeUs !== undefined ? { outTimeUs } : {}),
        ...(outTimeMs !== undefined ? { outTimeMs } : {}),
        ...(outTime !== undefined ? { outTime } : {}),
        ...(dupFrames !== undefined ? { dupFrames } : {}),
        ...(dropFrames !== undefined ? { dropFrames } : {}),
        ...(speed !== undefined ? { speed } : {}),
        progress: values["progress"] ?? "continue",
        raw: { ...values },
    };
}
export function parseClockSeconds(value) {
    if (!value)
        return undefined;
    if (/^\d+(?:\.\d+)?$/.test(value)) {
        const direct = Number(value);
        return Number.isFinite(direct) ? direct : undefined;
    }
    const match = /^(\d+):(\d{2}):(\d{2}(?:\.\d+)?)$/.exec(value);
    if (!match)
        return undefined;
    const hours = Number(match[1]);
    const minutes = Number(match[2]);
    const seconds = Number(match[3]);
    if (![hours, minutes, seconds].every(Number.isFinite))
        return undefined;
    return hours * 3600 + minutes * 60 + seconds;
}
export function parseSpeedMultiplier(value) {
    if (!value)
        return undefined;
    const normalized = value.endsWith("x") ? value.slice(0, -1) : value;
    const parsed = Number(normalized);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
}
function processedSeconds(snapshot) {
    if (snapshot.outTimeUs !== undefined)
        return snapshot.outTimeUs / 1_000_000;
    if (snapshot.outTimeMs !== undefined)
        return snapshot.outTimeMs / 1_000_000;
    return parseClockSeconds(snapshot.outTime);
}
export function deriveProgressEvent(runId, snapshot, options = {}) {
    const processed = processedSeconds(snapshot);
    const speedMultiplier = parseSpeedMultiplier(snapshot.speed);
    const totalSeconds = options.duration?.seconds;
    const ended = snapshot.progress === "end";
    const percentage = totalSeconds !== undefined && processed !== undefined
        ? (ended ? 100 : Math.max(0, Math.min(100, (processed / totalSeconds) * 100)))
        : undefined;
    const etaSeconds = ended
        ? (totalSeconds !== undefined ? 0 : undefined)
        : totalSeconds !== undefined && processed !== undefined && speedMultiplier !== undefined
            ? Math.max(0, (totalSeconds - processed) / speedMultiplier)
            : undefined;
    return {
        runId,
        state: ended ? "end" : "continue",
        estimated: options.duration?.estimated ?? false,
        ...(options.source !== undefined ? { source: options.source } : {}),
        ...(snapshot.frame !== undefined ? { frame: snapshot.frame } : {}),
        ...(snapshot.fps !== undefined ? { fps: snapshot.fps } : {}),
        ...(speedMultiplier !== undefined ? { speedMultiplier } : {}),
        ...(processed !== undefined ? { processedSeconds: processed } : {}),
        ...(totalSeconds !== undefined ? { totalSeconds } : {}),
        ...(percentage !== undefined ? { percentage } : {}),
        ...(etaSeconds !== undefined ? { etaSeconds } : {}),
    };
}
export function inputSources(args) {
    const sources = [];
    for (let index = 0; index < args.length - 1; index += 1) {
        if (args[index] === "-i")
            sources.push(args[index + 1]);
    }
    return sources;
}
function outputScopedTime(args, option) {
    let lastInputIndex = -1;
    for (let index = 0; index < args.length; index += 1) {
        if (args[index] === "-i")
            lastInputIndex = index;
    }
    for (let index = args.length - 2; index > lastInputIndex; index -= 1) {
        if (args[index] === option)
            return parseClockSeconds(args[index + 1]);
    }
    return undefined;
}
function firstSeekSeconds(args) {
    const index = args.indexOf("-ss");
    return index >= 0 ? parseClockSeconds(args[index + 1]) : undefined;
}
function speedFactor(args) {
    for (const value of args) {
        const match = /setpts=PTS\/([0-9]+(?:\.[0-9]+)?)/.exec(value);
        if (match?.[1]) {
            const parsed = Number(match[1]);
            if (Number.isFinite(parsed) && parsed > 0)
                return parsed;
        }
    }
    return undefined;
}
function transitionDuration(args) {
    let total = 0;
    for (const value of args) {
        const matches = value.matchAll(/xfade=[^;[]*?duration=([0-9]+(?:\.[0-9]+)?)/g);
        for (const match of matches)
            total += Number(match[1] ?? 0);
    }
    return total;
}
function isComposition(args) {
    return args.some((value) => value.includes("xfade=") || /concat=n=\d+/.test(value));
}
export function estimateProgressDuration(args, durationForSource) {
    const explicitDuration = outputScopedTime(args, "-t");
    if (explicitDuration !== undefined && explicitDuration > 0) {
        return { seconds: explicitDuration, source: "explicit", estimated: false };
    }
    const explicitEnd = outputScopedTime(args, "-to");
    if (explicitEnd !== undefined && explicitEnd > 0) {
        const seek = firstSeekSeconds(args) ?? 0;
        const seconds = explicitEnd - seek;
        if (seconds > 0)
            return { seconds, source: "explicit", estimated: false };
    }
    const sources = inputSources(args);
    const known = sources
        .map((source) => durationForSource(source))
        .filter((value) => value !== undefined && Number.isFinite(value) && value > 0);
    if (known.length === 0)
        return undefined;
    let seconds;
    let source;
    if (isComposition(args) && known.length > 1) {
        seconds = known.reduce((sum, value) => sum + value, 0) - transitionDuration(args);
        source = "composition";
    }
    else {
        seconds = known[0];
        source = "probe";
    }
    const seek = firstSeekSeconds(args);
    if (seek !== undefined && seek > 0)
        seconds = Math.max(0, seconds - seek);
    const factor = speedFactor(args);
    if (factor !== undefined)
        seconds /= factor;
    if (!(seconds > 0))
        return undefined;
    return { seconds, source, estimated: true };
}
/** Incremental parser for FFmpeg `-progress pipe:N` key/value output. */
export class FFmpegProgressParser {
    buffer = "";
    values = {};
    push(chunk) {
        this.buffer += chunk;
        const snapshots = [];
        while (true) {
            const newline = this.buffer.indexOf("\n");
            if (newline < 0)
                break;
            const line = this.buffer.slice(0, newline).replace(/\r$/, "");
            this.buffer = this.buffer.slice(newline + 1);
            const snapshot = this.consumeLine(line);
            if (snapshot)
                snapshots.push(snapshot);
        }
        return snapshots;
    }
    flush() {
        const snapshots = [];
        if (this.buffer.length > 0) {
            const snapshot = this.consumeLine(this.buffer.replace(/\r$/, ""));
            if (snapshot)
                snapshots.push(snapshot);
            this.buffer = "";
        }
        return snapshots;
    }
    consumeLine(line) {
        if (!line)
            return undefined;
        const separator = line.indexOf("=");
        if (separator <= 0)
            return undefined;
        const key = line.slice(0, separator);
        const value = line.slice(separator + 1);
        this.values[key] = value;
        if (key !== "progress")
            return undefined;
        const snapshot = toSnapshot(this.values);
        this.values = {};
        return snapshot;
    }
}
//# sourceMappingURL=progress.js.map

SHA-256: 0c96351c54f8101682ccaea3959ebaa24cefa20660b0bab2e9b3ee01cb812417