Initial Commit
This commit is contained in:
91
packages/media/video/video-thumbnails/src/runner/runner.md
Normal file
91
packages/media/video/video-thumbnails/src/runner/runner.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# `runner`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The runner module defines the `FfmpegRunner` interface plus a handful of pure helpers used by
|
||||
every implementation: `buildScaleFilter`, `buildQualityArgs`, and `classifyRunnerError`.
|
||||
Centralising filter construction means the generator classes stay focused on orchestration
|
||||
while every implementation (the bundled `FluentFfmpegRunner` and any future native or browser
|
||||
variant) shares the exact same algorithmic shape. `classifyRunnerError` is the single point
|
||||
where an `AbortSignal` distinguishes an `ABORTED` failure from a real ffmpeg crash.
|
||||
|
||||
## Features
|
||||
|
||||
- `FfmpegRunner` interface — four methods (`isAvailable`, `extract`, `tile`, `probe`), each
|
||||
taking an optional `AbortSignal`.
|
||||
- Strong typed payloads: `ExtractInput`, `TileInput`, `ProbeResult`.
|
||||
- `buildScaleFilter` — encodes the `cover | contain | fill` fit semantics into ffmpeg
|
||||
filter-graph syntax once and only once.
|
||||
- `buildQualityArgs` — translates the 1–100 quality scale into ffmpeg's inverted 1–31 `-q:v`
|
||||
scale (PNG returns an empty arg list).
|
||||
- `classifyRunnerError` — single guarded helper that turns any raw ffmpeg failure into a
|
||||
typed `VideoThumbnailsError`, distinguishing `ABORTED` from `EXTRACT_FAILED` /
|
||||
`PROBE_FAILED` based on the supplied `AbortSignal`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { buildScaleFilter, buildQualityArgs } from "@suaveplan/video-thumbnails/runner";
|
||||
|
||||
buildScaleFilter(320, 180, "cover");
|
||||
// "scale=320:180:force_original_aspect_ratio=increase,crop=320:180"
|
||||
|
||||
buildQualityArgs("jpg", 80);
|
||||
// ["-q:v", "7"]
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
classifyRunnerError,
|
||||
type FfmpegRunner,
|
||||
} from "@suaveplan/video-thumbnails/runner";
|
||||
import { VIDEO_THUMBNAILS_CODES } from "@suaveplan/video-thumbnails/error-codes";
|
||||
|
||||
class CustomRunner implements FfmpegRunner {
|
||||
async extract(input, signal) {
|
||||
try {
|
||||
await runViaCustomTransport(input, signal);
|
||||
} catch (error) {
|
||||
throw classifyRunnerError(
|
||||
error,
|
||||
signal,
|
||||
VIDEO_THUMBNAILS_CODES.EXTRACT_FAILED,
|
||||
"videoThumbnails.errors.extractFailed",
|
||||
);
|
||||
}
|
||||
}
|
||||
// ... isAvailable, tile, probe
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `FfmpegRunner` — `{ isAvailable(signal?), extract(input, signal?), tile(input, signal?),
|
||||
probe(inputPath, signal?) }`.
|
||||
- `ExtractInput` — `{ inputPath, outputPath, timestamp, width, height, fit, format,
|
||||
quality }`.
|
||||
- `TileInput` — `{ inputPath, outputPath, columns, rows, thumbnailWidth, thumbnailHeight,
|
||||
interval, format }`.
|
||||
- `ProbeResult` — `{ duration, width, height, codec, fps, bitrate }`.
|
||||
- `buildScaleFilter(width, height, fit): string`.
|
||||
- `buildQualityArgs(format, quality): readonly string[]`.
|
||||
- `classifyRunnerError(cause, signal, failureCode, failureKey): VideoThumbnailsError` — when
|
||||
`signal.aborted`, returns `ABORTED`; otherwise the supplied `failureCode`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Quality mapping uses ffmpeg's inverted scale where `1` is best and `31` is worst — the
|
||||
inverse arithmetic (`31 - quality/100 × 30`) keeps the round-trip stable for the canonical
|
||||
80% quality (returning `-q:v 7`, the most commonly cited "good MJPEG" value). The fit
|
||||
strategies are deliberately limited to ffmpeg-native filter chains so no second-pass image
|
||||
processing is needed; consumers wanting more exotic compositing (gravity, focal point) can
|
||||
implement a custom `FfmpegRunner` and reuse `buildQualityArgs` while ignoring
|
||||
`buildScaleFilter`. `classifyRunnerError` reads `signal.aborted` lazily and only after the
|
||||
underlying call has thrown — this avoids a race where the signal aborts after the work
|
||||
already finished successfully.
|
||||
Reference in New Issue
Block a user