← Files Cecil-IA Labs FFmpegARCHIVED FILE

dist/cli/progress-renderer.js

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

↓ Download file

import path from "node:path";
import { colorizeProgressLine } from "./colors.js";
import { CLI_ICONS, progressSourceIcon } from "./icons.js";
function finite(value) {
    return value !== undefined && Number.isFinite(value) ? value : undefined;
}
export function formatProgressDuration(seconds) {
    if (seconds === undefined || !Number.isFinite(seconds) || seconds < 0)
        return "--:--:--";
    const rounded = Math.max(0, Math.round(seconds));
    const hours = Math.floor(rounded / 3600);
    const minutes = Math.floor((rounded % 3600) / 60);
    const secs = rounded % 60;
    return [hours, minutes, secs].map((value) => String(value).padStart(2, "0")).join(":");
}
function labelFor(event) {
    if (!event.source)
        return event.runId;
    if (/^[a-z][a-z0-9+.-]*:\/\//i.test(event.source))
        return event.source;
    return path.basename(event.source);
}
export function formatHumanProgress(event, friendly = false) {
    const source = labelFor(event);
    const parts = [friendly ? `${progressSourceIcon(event.source)} ${source}` : source];
    if (event.percentage !== undefined) {
        const status = event.state === "end" ? CLI_ICONS.completed : CLI_ICONS.progress;
        parts.push(friendly ? `${status}\t\b\b\b${Math.round(event.percentage)}%` : `${Math.round(event.percentage)}%`);
    }
    if (event.frame !== undefined) {
        parts.push(friendly ? `${CLI_ICONS.frame}\t\b\b\b${event.frame}frames` : `frame ${event.frame}`);
    }
    if (event.fps !== undefined) {
        parts.push(friendly
            ? `${CLI_ICONS.fps} ${event.fps}fps`
            : `${event.fps.toFixed(1)} fps`);
    }
    if (event.speedMultiplier !== undefined) {
        parts.push(friendly
            ? `${CLI_ICONS.speed} ${event.speedMultiplier.toFixed(1)}x`
            : `${event.speedMultiplier.toFixed(2)}x`);
    }
    if (event.etaSeconds !== undefined) {
        parts.push(friendly
            ? `${CLI_ICONS.eta} ETA ${formatProgressDuration(event.etaSeconds)}`
            : `ETA ${formatProgressDuration(event.etaSeconds)}`);
    }
    return parts.join(friendly ? "\t| " : " | ");
}
export class CliProgressReporter {
    enabled;
    isTTY;
    friendly;
    write;
    now;
    runs = new Map();
    lastRenderedAt = new Map();
    lastBucket = new Map();
    ttyLineOpen = false;
    constructor(options) {
        this.enabled = options.enabled;
        this.isTTY = options.isTTY ?? Boolean(process.stderr.isTTY);
        this.friendly = options.friendly ?? this.isTTY;
        this.write = options.write ?? ((text) => process.stderr.write(text));
        this.now = options.now ?? (() => Date.now());
    }
    onEvent = (event) => {
        const totalSeconds = finite(event.totalSeconds);
        const processedSeconds = finite(event.processedSeconds);
        const percentage = finite(event.percentage);
        const fps = finite(event.fps);
        const speedMultiplier = finite(event.speedMultiplier);
        const etaSeconds = finite(event.etaSeconds);
        const summary = {
            runId: event.runId,
            state: event.state,
            estimated: event.estimated,
            ...(event.source !== undefined ? { source: event.source } : {}),
            ...(totalSeconds !== undefined ? { totalSeconds } : {}),
            ...(processedSeconds !== undefined ? { processedSeconds } : {}),
            ...(percentage !== undefined ? { percentage } : {}),
            ...(event.frame !== undefined ? { frame: event.frame } : {}),
            ...(fps !== undefined ? { fps } : {}),
            ...(speedMultiplier !== undefined ? { speedMultiplier } : {}),
            ...(etaSeconds !== undefined ? { etaSeconds } : {}),
        };
        this.runs.set(event.runId, summary);
        if (!this.enabled)
            return;
        if (this.isTTY) {
            const now = this.now();
            const last = this.lastRenderedAt.get(event.runId) ?? 0;
            if (event.state !== "end" && now - last < 125)
                return;
            this.lastRenderedAt.set(event.runId, now);
            const line = formatHumanProgress(event, this.friendly);
            this.write(`\r\u001b[2K${colorizeProgressLine(line, this.friendly)}`);
            this.ttyLineOpen = true;
            if (event.state === "end") {
                this.write("\n");
                // this.write("\n");
                this.ttyLineOpen = false;
            }
            return;
        }
        const renderPercentage = event.percentage;
        const bucket = renderPercentage === undefined
            ? undefined
            : Math.floor(Math.min(100, renderPercentage) / 25) * 25;
        const lastBucket = this.lastBucket.get(event.runId);
        if (event.state === "end" || (bucket !== undefined && bucket !== lastBucket)) {
            if (bucket !== undefined)
                this.lastBucket.set(event.runId, bucket);
            this.write(`${formatHumanProgress(event, false)}\n`);
        }
    };
    finish() {
        if (this.enabled && this.ttyLineOpen) {
            this.write("\n");
            this.ttyLineOpen = false;
        }
    }
    summary() {
        if (this.runs.size === 0)
            return undefined;
        const runs = [...this.runs.values()];
        return {
            runs,
            completedRuns: runs.filter((run) => run.state === "end").length,
        };
    }
}
//# sourceMappingURL=progress-renderer.js.map

SHA-256: 72ddb430b71e095048e1e07b4ed90b984a53a9c23583bcf158cd4692451d9e2c