Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
# `FluentFfmpegRunner`
## Purpose
Default `FfmpegRunner` implementation used by `ThumbnailGenerator` and
`SpriteSheetGenerator` when no other runner is injected. It wraps `fluent-ffmpeg` and pins
ffmpeg to the binary shipped by `@ffmpeg-installer/ffmpeg`, and ffprobe to
`@ffprobe-installer/ffprobe`, so the package works on any host without requiring system
ffmpeg/ffprobe to be on `PATH`. Every operation respects an optional `AbortSignal` by killing
the underlying child process and rejecting with `VIDEO_THUMBNAILS.ABORTED`.
## Features
- Bundled ffmpeg + ffprobe binaries — zero system dependency.
- Override-friendly: pass `ffmpegPath` and/or `ffprobePath` to point at a custom binary.
- `extract` issues a single-frame ffmpeg pipeline with the canonical filter from
`buildScaleFilter` and quality from `buildQualityArgs`.
- `tile` issues the canonical `fps=…,scale=…,tile=COLSxROWS` sprite-sheet pipeline.
- `probe` returns a strict `ProbeResult` with duration, dimensions, codec, fps, bitrate.
- `isAvailable` spawns `ffmpeg -version` and resolves to `true` only on exit code 0.
- Every call honours `AbortSignal`: `command.kill("SIGKILL")` on abort, then reject with
`ABORTED`.
## Usage: Basic
```ts
import { FluentFfmpegRunner } from "@suaveplan/video-thumbnails/runner";
const runner = new FluentFfmpegRunner();
const ok = await runner.isAvailable();
```
## Usage: Advanced
```ts
import { FluentFfmpegRunner } from "@suaveplan/video-thumbnails/runner";
const runner = new FluentFfmpegRunner({
ffmpegPath: "/usr/local/bin/ffmpeg-hwaccel",
ffprobePath: "/usr/local/bin/ffprobe-hwaccel",
});
const controller = new AbortController();
setTimeout(() => controller.abort(), 250);
try {
await runner.extract(
{
inputPath: "/data/big.mkv",
outputPath: "/tmp/thumb.jpg",
timestamp: "00:00:10",
width: 640,
height: 360,
fit: "cover",
format: "jpg",
quality: 80,
},
controller.signal,
);
} catch (error) {
// VideoThumbnailsError { code: "VIDEO_THUMBNAILS.ABORTED" } when aborted.
}
```
## API Reference
- `class FluentFfmpegRunner implements FfmpegRunner`
- `constructor(options?: { ffmpegPath?: string; ffprobePath?: string })`
- `isAvailable(signal?): Promise<boolean>` — exit code 0 from `ffmpeg -version`.
- `extract(input, signal?): Promise<void>` — single-frame extraction.
- `tile(input, signal?): Promise<void>` — N×M sprite-sheet render.
- `probe(inputPath, signal?): Promise<ProbeResult>` — ffprobe metadata.
## Implementation Notes
`run` listens for `end` and `error` on the `FfmpegCommand`, tracks a `settled` flag so a late
abort cannot double-resolve, and attaches the abort listener with `{ once: true }` to keep
event-emitter memory bounded. `probe` re-pins the ffprobe path on the module-level
`fluent-ffmpeg` singleton before each call because `ffmpeg.ffprobe` (the top-level helper)
does not consult per-instance state — without the re-pin the singleton would happily fall
back to system `ffprobe`. The probe failure paths split into two: a real ffprobe crash
(`PROBE_FAILED`) and a structurally invalid response (`VIDEO_INVALID`, raised when the file
has no video stream or a non-positive duration).