docs: add playback selector plan
This commit is contained in:
109
docs/plans/2026-02-01-use-playback-selector.md
Normal file
109
docs/plans/2026-02-01-use-playback-selector.md
Normal file
@@ -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"
|
||||
```
|
||||
Reference in New Issue
Block a user