Use when replacing the video_thumbnail, flutter_video_thumbnail_plus, get_thumbnail_video, or fc_native_video_thumbnail package with cached_video_thumbnail, or when code still calls VideoThumbnail.thumbnailData, VideoThumbnail.thumbnailFile, or FcNativeVideoThumbnail and should move to the cached engine.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cached-video-thumbnail:migrate-from-video-thumbnailThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Converts one-shot `video -> image` calls from the video_thumbnail family
Converts one-shot video -> image calls from the video_thumbnail family
into engine requests. This is an API and semantics migration: results
become cache-owned files instead of caller-owned bytes/paths, failures
become typed exceptions instead of nulls/booleans, and every call becomes
cached, scheduled, and cancellable. Mapping tables:
api-mapping.md.
grep -rn "video_thumbnail\|get_thumbnail_video\|fc_native_video_thumbnail" pubspec.yaml lib/ test/
grep -rn "thumbnailData\|thumbnailFile\|getVideoThumbnail\|ImageFormat\." lib/ test/
.pub-cache) - forks differ in return types (String? vs XFile).ImageFormat.WEBP has no equivalent (by design; only
jpeg/png). Find WebP consumers downstream (file extensions, MIME types,
upload endpoints) before converting them to jpeg.thumbnailPath/destFile it later reads,
moves, uploads, or deletes - file ownership changes below.In pubspec.yaml: add cached_video_thumbnail: ^0.1.0 (needs Dart SDK
^3.12, Flutter >= 3.44). Keep the old dependency until Step 4 verifies, so
the migration is revertible file-by-file; remove it as the final commit.
Core translation (full parameter tables in the reference file):
// OLD (video_thumbnail / get_thumbnail_video)
final bytes = await VideoThumbnail.thumbnailData(
video: url, imageFormat: ImageFormat.JPEG,
maxWidth: 128, quality: 25, timeMs: 2000);
// NEW - widget display: skip bytes entirely, use the provider
Image(image: VideoThumbnailImage(
VideoSource.network(Uri.parse(url)),
spec: const ThumbnailSpec(maxWidth: 128, quality: 25,
position: Duration(milliseconds: 2000)),
))
// NEW - when a file/bytes value is genuinely needed
final thumb = await ThumbnailEngine.instance.getThumbnail(
VideoSource.network(Uri.parse(url)),
spec: const ThumbnailSpec(maxWidth: 128, quality: 25,
position: Duration(milliseconds: 2000)),
);
// thumb.filePath is ENGINE-OWNED cache; copy out if the caller needs
// ownership, readAsBytes only if bytes are truly required.
Decision per call site:
VideoThumbnailImage (adds ImageCache dedup and
scroll cancellation for free). Prefer this whenever possible.getThumbnail + copy out of
thumb.filePath.video: parameter was a String for BOTH urls and local paths:
route to VideoSource.network(Uri.parse(s)) vs VideoSource.file(s)
explicitly (file paths must be absolute). Asset support is new
(VideoSource.asset) - old code that copied assets to temp files first
can drop that workaround entirely.== null / if (!ok) checks become
try/catch on ThumbnailException (plus ArgumentError for structurally
invalid input, thrown synchronously).exact: false snaps to the nearest keyframe. Feeds should keep
the fast default even though pixels may differ from before; use
exact: true only where the precise frame matters.quality: 0 (seen with old PNG
calls) throws ArgumentError - drop it, quality is ignored for PNG.maxWidth/maxHeight 0-defaults decoded full frames;
new spec is a fit-within box in physical px that never upscales. Feeds
should now SET a box (see the integrate-feed-thumbnails skill).wasCached: true); failures fast-fail for negativeCacheTtl (60s) -
user-initiated retries should await engine.evict(source) first.thumb.filePath: LRU owns it. Old cleanup code
that deleted thumbnailPath outputs must be removed or pointed at
copies.VideoSource.network(headers: ...)) but excluded
from cache identity; evict when content changes.dart analyze clean; dart format changed files.mock-thumbnail-engine skill.dart pub get; re-run analyze + tests.ThumbnailEngine /
VideoThumbnailImage usage - skip them idempotently."Replace get_thumbnail_video in our chat app; thumbnails for received
videos are saved next to the media file." -> getThumbnail + explicit
copy to the media dir (ownership), VideoThumbnailImage for the bubbles,
try/catch replacing XFile-null checks, retry buttons call evict first,
WebP check (chat used jpeg - safe), old package removed after tests pass.
Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
npx claudepluginhub omar-hanafy/cached_video_thumbnail --plugin cached-video-thumbnail