import { type XskillAwaitOptions } from './providers/xskill.js'; import type { VideoMediaProbe } from './final-media.js'; export type FinishBackend = 'topaz-starlight' | 'topaz-proteus' | 'topaz-gaia' | 'magnific-precision' | 'runway-topaz-free' | 'realesrgan-x4plus' | 'topaz-local'; export declare const FINISH_BACKEND_IDS: FinishBackend[]; /** Hosted backends fetch their input by URL and run on the aggregator/fal/Magnific. */ export declare function isHostedBackend(backend: FinishBackend): boolean; export interface FinishOptions { backend: FinishBackend; /** Anti-plastic preset: denoise/sharpen off, film grain kept, detail recovered. Default true. */ detailNotSharp?: boolean; /** Upscale factor (default 2). 1.5 = 720p->1080p exactly. */ scale?: number; /** Override film grain (0..0.1; clamped). */ grain?: number; /** Override noise reduction (0..1). detailNotSharp forces 0. */ noise?: number; /** Override detail recovery (0..1). detailNotSharp defaults 0.85. */ recoverDetail?: number; } /** Topaz request params (the subset the aggregator's Topaz endpoint accepts). */ export interface TopazParams { model: string; upscale_factor: number; grain: number; noise: number; halo: number; recover_detail: number; H264_output: boolean; } /** The published max for Topaz `grain` — the docs incorrectly say 0..1. */ export declare const TOPAZ_GRAIN_MAX = 0.1; /** * Map finish options to Topaz request params for a HOSTED backend. PURE. Applies * the anti-plastic recipe by default and clamps `grain` to the real 0.1 ceiling. * Throws for a non-Topaz backend. */ export declare function topazParamsFor(opts: FinishOptions): TopazParams; /** * Magnific Video Upscaler Precision request params. Field names + ranges are the * LIVE Magnific contract (verified 2026-06-16 against api.magnific.com): the input * video is the separate `video` field on the request body; `resolution` is an enum, * `strength` is a 0..100 blend, `sharpen`/`smart_grain`/`fps_boost` are booleans. */ export interface MagnificUpscaleParams { resolution: '720p' | '1k' | '2k' | '4k'; strength: number; sharpen: boolean; smart_grain: boolean; fps_boost: boolean; } /** Magnific Video Upscaler input limits (Precision). */ export declare const MAGNIFIC_MAX_SECONDS = 15; export declare const MAGNIFIC_MAX_FRAMES = 450; export declare const MAGNIFIC_MAX_BYTES: number; export declare const MAGNIFIC_MAX_DIM = 3840; /** * ffmpeg args that re-encode an over-limit clip to fit Magnific's caps: trim to * MAGNIFIC_MAX_SECONDS, cap fps at 30 (30×15s = 450 frames, the frame cap), and * downscale (never upscale) to fit a 3840-px box preserving aspect with even dims. */ export declare function magnificNormalizeCommand(input: string, output: string): string[]; /** * Map finish options to Magnific upscale params. PURE. Reuses the anti-plastic * recipe knobs (detailNotSharp keeps grain, drops sharpen) and clamps to real ranges. */ export declare function magnificUpscaleParamsFor(opts: FinishOptions & { targetResolution?: '720p' | '1k' | '2k' | '4k'; }): MagnificUpscaleParams; /** * Validate a video against Magnific's Video Upscaler limits. PURE. `nb_frames` is * not exposed by probeMedia, so frame count is derived from duration × fps. With * `normalize:true` the caller re-encodes to fit, so violations are non-fatal. */ export declare function assertMagnificVideoInput(probe: Pick, opts: { normalize: boolean; }): { ok: boolean; violations: string[]; }; /** A frame-aligned window of the source to upscale as an independent chunk. */ export interface ChunkWindow { index: number; startSec: number; durationSec: number; } /** * Split `totalDurationSec` into frame-aligned chunk windows of ~`chunkSeconds` * each. PURE. Boundaries land on exact frames so concatenating the upscaled * chunks preserves the source's frame count (and therefore its lip-sync). The * last chunk takes the remainder. Returns a single full-length window when the * source is at or under one chunk. */ export declare function planFinishChunks(totalDurationSec: number, chunkSeconds?: number, fps?: number): ChunkWindow[]; /** The apiz/xskill model id for hosted Topaz video upscale. */ export declare const TOPAZ_MODEL_ID = "fal-ai/topaz/upscale/video"; export interface RunFinishOptions extends FinishOptions { input: string; output: string; /** Plan only — no upload, submit, or spawn. */ dryRun?: boolean; /** apiz key override (else APIZ_API_KEY / XSKILL_API_KEY). */ apiKey?: string; env?: NodeJS.ProcessEnv; /** Await tuning for the hosted task (poll interval / timeout / onTick). */ awaitOptions?: XskillAwaitOptions; /** Injectable hosted transports (default the real public-host + xskill client). */ upload?: (localPath: string) => Promise; submit?: (modelId: string, params: Record) => Promise; awaitTask?: (taskId: string) => Promise; download?: (url: string, dest: string) => Promise; /** Magnific Video Upscaler target output resolution (default 2k). */ targetResolution?: '720p' | '1k' | '2k' | '4k'; /** Auto-re-encode an out-of-limit input to fit Magnific's caps instead of failing. */ normalize?: boolean; /** Injectable ffprobe (default final-media probeMedia) — magnific-precision preflight. */ probe?: (path: string) => Promise; /** Injectable Magnific upscale submit (default native-magnific submitMagnificUpscale). */ submitUpscale?: (videoUrl: string, params: MagnificUpscaleParams) => Promise; /** Injectable Magnific upscale await (default native-magnific awaitMagnificUpscale). */ awaitUpscale?: (taskId: string) => Promise; /** Injectable normalizer used when --normalize re-encodes an over-limit input (default ffmpeg). */ normalizeInput?: (input: string, output: string) => Promise; /** Local Topaz CLI path (else VCLAW_TOPAZ_CLI); used by topaz-local. */ topazCliPath?: string; /** Injectable local runner (default execFile) — for offline tests. */ runLocal?: (command: string[]) => Promise; /** Injectable runway-topaz-free asset upload (default uploadRunwayUpscaleAsset). */ runwayUpload?: (input: { filePath: string; name: string; }) => Promise<{ assetId: string; }>; /** Injectable runway-topaz-free submit (default submitRunwayUpscale, exploreMode on). */ runwaySubmit?: (input: { videoAssetId: string; exploreMode?: boolean; }) => Promise<{ taskUuid: string; status: string; }>; /** Injectable runway-topaz-free await (default awaitRunwayUpscale — polls the NAMESPACED id). */ runwayAwait?: (input: { assetId: string; taskUuid: string; }) => Promise<{ outputUrl: string; }>; } export interface RunFinishResult { backend: FinishBackend; hosted: boolean; output: string; dryRun: boolean; /** The hosted request params (Topaz or Magnific) for logging/repro. */ params?: (TopazParams | MagnificUpscaleParams) & { video_url?: string; }; /** The local CLI command (topaz-local). */ command?: string[]; /** The hosted task id (real run). */ taskId?: string; /** The public input URL the backend fetched (real hosted run). */ sourceUrl?: string; /** The remote output URL before download (real hosted run). */ outputUrl?: string; /** runway-topaz-free: the ≤40s chunk windows the source was split into. */ chunks?: ChunkWindow[]; } /** * Execute a finish pass. HOSTED Topaz: upload → submit `fal-ai/topaz/upscale/video` * → await → download. LOCAL `topaz-local`: shell the Topaz CLI via * {@link topazUpscalePlan}. `realesrgan-x4plus` is plan-only here (needs an * external frame pipeline) and throws an actionable error on a real run. On * `dryRun`, returns the resolved plan without spending. */ export declare function runFinish(opts: RunFinishOptions): Promise; //# sourceMappingURL=finish.d.ts.map