From b6d588843d7c50b8ac67e0bd054aa76e9c9b6d1d Mon Sep 17 00:00:00 2001 From: William Valentin Date: Sun, 1 Feb 2026 16:52:38 -0800 Subject: [PATCH] docs: add playback selector plan --- .../plans/2026-02-01-use-playback-selector.md | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/plans/2026-02-01-use-playback-selector.md diff --git a/docs/plans/2026-02-01-use-playback-selector.md b/docs/plans/2026-02-01-use-playback-selector.md new file mode 100644 index 0000000..55d59dc --- /dev/null +++ b/docs/plans/2026-02-01-use-playback-selector.md @@ -0,0 +1,109 @@ +# Use Playback Selector Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Add an asset variants endpoint and wire MediaPanel to use `pickVideoPlaybackVariant` for derived MP4 selection with a safe fallback. + +**Architecture:** Introduce a minimal `/api/assets/:id/variants` route that returns `{ kind, size, key }` from `asset_variants`. MediaPanel fetches variants on-demand for videos, uses `pickVideoPlaybackVariant` to decide whether to request `video_mp4` (size 720), and falls back to original if the derived URL fails. + +**Tech Stack:** Next.js App Router API routes, Postgres via `@tline/db`, Bun test runner. + +### Task 1: Add variants API route + +**Files:** +- Create: `apps/web/app/api/assets/[id]/variants/route.ts` +- Test: `apps/web/src/__tests__/variants-route.test.ts` + +**Step 1: Write the failing test** + +Create `apps/web/src/__tests__/variants-route.test.ts`: + +```ts +import { test, expect } from "bun:test"; + +test("variants route returns only kind/size/key fields", async () => { + const { shapeVariants } = await import("../../app/api/assets/[id]/variants/shape"); + const rows = [ + { kind: "video_mp4", size: 720, key: "derived/video/a.mp4", mime_type: "video/mp4" }, + ]; + expect(shapeVariants(rows)).toEqual([{ kind: "video_mp4", size: 720, key: "derived/video/a.mp4" }]); +}); +``` + +**Step 2: Run test to verify it fails** + +Run: `bun test apps/web/src/__tests__/variants-route.test.ts` +Expected: FAIL (missing module or function) + +**Step 3: Write minimal implementation** + +- Create `apps/web/app/api/assets/[id]/variants/shape.ts` with `shapeVariants(rows)` that returns `{ kind, size, key }` only. +- Create `apps/web/app/api/assets/[id]/variants/route.ts`: + - Validate `id` with `z.string().uuid()` + - Query `asset_variants` by `asset_id` + - Return JSON array of `shapeVariants(rows)` + +**Step 4: Run test to verify it passes** + +Run: `bun test apps/web/src/__tests__/variants-route.test.ts` +Expected: PASS + +**Step 5: Commit** + +```bash +git add apps/web/app/api/assets/[id]/variants/route.ts apps/web/app/api/assets/[id]/variants/shape.ts \ + apps/web/src/__tests__/variants-route.test.ts +git commit -m "feat: add asset variants endpoint" +``` + +### Task 2: Use playback selector in MediaPanel + +**Files:** +- Modify: `apps/web/app/components/MediaPanel.tsx` +- Modify: `apps/web/app/lib/playback.ts` +- Test: `apps/web/src/__tests__/prefer-derived.test.ts` + +**Step 1: Write the failing test** + +Add to `apps/web/src/__tests__/prefer-derived.test.ts`: + +```ts +import { test, expect } from "bun:test"; +import { pickVideoPlaybackVariant } from "../../app/lib/playback"; + +test("pickVideoPlaybackVariant returns null when no variants", () => { + expect( + pickVideoPlaybackVariant({ + originalMimeType: "video/x-matroska", + variants: [], + }), + ).toBeNull(); +}); +``` + +**Step 2: Run test to verify it fails** + +Run: `bun test apps/web/src/__tests__/prefer-derived.test.ts` +Expected: FAIL (function does not handle empty variants) + +**Step 3: Write minimal implementation** + +- Update `pickVideoPlaybackVariant` to return `null` when no `video_mp4` variants exist. +- Update `MediaPanel` video URL loader to: + 1) Fetch `/api/assets/:id/variants` + 2) Call `pickVideoPlaybackVariant` + 3) If variant found → request `kind=video_mp4&size=720` + 4) If not found or fetch fails → request `variant=original` + +**Step 4: Run test to verify it passes** + +Run: `bun test apps/web/src/__tests__/prefer-derived.test.ts` +Expected: PASS + +**Step 5: Commit** + +```bash +git add apps/web/app/components/MediaPanel.tsx apps/web/app/lib/playback.ts \ + apps/web/src/__tests__/prefer-derived.test.ts +git commit -m "fix: use playback selector in MediaPanel" +```