Files
2026-05-27 23:29:53 +01:00

56 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenSpec Change: enterprise-video-thumbnails
- **Change ID**: `enterprise-video-thumbnails`
- **Package**: `@suaveplan/video-thumbnails`
- **Category**: `media` / **Plugin**: `video`
- **Target path**: `packages/media/video/video-thumbnails`
- **Status**: PROPOSED
- **Date**: 2026-05-25
- **Design**: ./design.md
- **Spec delta**: ./specs/video-thumbnails/spec.md
- **Upstream**: [`../suaveplan/packages/universal/video-thumbnails`](/mnt/s/web/suaveplan/packages/universal/video-thumbnails)
- **Enterprise audit**: [`../suaveplan/enterprise/video-thumbnails.md`](/mnt/s/web/suaveplan/enterprise/video-thumbnails.md)
## Summary
Migrate `@suaveplan/video-thumbnails` from main into the genesis monorepo at enterprise quality. The package generates video thumbnails (single-frame extraction at arbitrary timestamps, batched time-based extracts, sliding-window sampling) and sprite sheets (NxM grid via ffmpeg `tile` filter) over JPEG/PNG/WebP outputs, on top of an injectable `FfmpegRunner` and the bundled `@ffmpeg-installer/ffmpeg` binary surfaced through `fluent-ffmpeg`. The migration adds OTel histograms/counters, structured `VideoThumbnailsError` (extends `SuaveplanError`) codes via `defineCodes`, Zod-validated option boundaries, `AbortSignal` propagation, a sliding-window extractor that upstream lacked, and 100/100/100/100 coverage using real fixture videos generated on-the-fly with ffmpeg (no mocks).
## Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
| Single-frame extract `ThumbnailGenerator.generate` | ✅ | ✅ | Same `timestamp`, `fit`, `outputDir`, `format`, `quality` semantics. |
| Multi-timestamp extract `generateMultiple` | ✅ | ✅ | `AbortSignal`-aware. |
| `getMetadata` via ffprobe | ✅ | ✅ | Through `FfmpegRunner.probe`. |
| `isAvailable()` | ✅ | ✅ | Through `runner.isAvailable()`. |
| Sprite-sheet `SpriteSheetGenerator.generate` | ✅ | ✅ | Same N×M tile algorithm; auto-detect duration. |
| `CommandExecutor` injection | ✅ | ⬆️ | Replaced with the stronger `FfmpegRunner` interface (typed `extract`, `tile`, `probe`, `isAvailable`); default = `FluentFfmpegRunner`. |
| Error codes `THUMBNAIL_*` | ✅ | ⬆️ | Re-issued under `VIDEO_THUMBNAILS.*` namespace via `defineCodes`. Added: `RUNNER_UNAVAILABLE`, `INVALID_TIMESTAMP`, `INVALID_OPTIONS`, `ABORTED`. |
| Sliding-window extraction | 🚫 | ⬆️ | New `extractSlidingWindow(count, durationSeconds)` evenly samples N frames. |
| OTel counters + histograms | 🚫 | ⬆️ | `video_thumbnails.extract.duration_ms`, `extract.total{outcome,format}`, `sprite_sheet.duration_ms`, `sprite_sheet.frames`. |
| Zod option validation | 🚫 | ⬆️ | All public surfaces validate at the boundary. |
| `AbortSignal` propagation | 🚫 | ⬆️ | Threaded through `FfmpegRunner` (kills the child process). |
| Output jpeg / png / webp | partial | ✅ | All three exercised against real ffmpeg + fixtures. |
| 100/100/100/100 coverage | 🚫 | ✅ | Vitest istanbul, no mocks, real fixtures. |
## Current-state audit (genesis)
Package does not exist on `genesis`. Status: `–` in `STATUS.md`. Direct upstream consumers in genesis: none. The `video` plugin is added to the `media` super-category in `scripts/folder-reorg/assignment.yml` as part of this change (the closed-vocabulary supercategory list is unchanged).
## Dependencies
- Workspace: `@suaveplan/error`
- External (via catalog `audio`): `fluent-ffmpeg`, `@ffmpeg-installer/ffmpeg`, `@types/fluent-ffmpeg`
- External (other catalogs): `tslib` (`build`), `zod` (`forms`), `@opentelemetry/api` (`observability`)
- Dev: `@suaveplan/testing` (`workspace:^`), `vitest` + `@vitest/coverage-istanbul` (`testing`)
## Impact
- Adds package `packages/media/video/video-thumbnails`.
- Adds spec capability `video-thumbnails`.
- Adds `video` plugin to the `media` super-category in `scripts/folder-reorg/assignment.yml`.
### Affected dependents
None — fresh genesis migration, no existing consumers.