diff --git a/apps/web/app/components/MediaPanel.tsx b/apps/web/app/components/MediaPanel.tsx index 29a4c98..b386cc4 100644 --- a/apps/web/app/components/MediaPanel.tsx +++ b/apps/web/app/components/MediaPanel.tsx @@ -2,6 +2,8 @@ import { useEffect, useMemo, useState } from "react"; +import { pickVideoPlaybackVariant } from "../lib/playback"; + type Asset = { id: string; media_type: "image" | "video"; @@ -25,6 +27,7 @@ type SignedUrlResponse = { type PreviewUrlState = Record; type VideoPlaybackVariant = { kind: "original" } | { kind: "video_mp4"; size: 720 }; +type VariantsResponse = Array<{ kind: string; size: number; key: string }>; function startOfDayUtc(iso: string) { const d = new Date(iso); @@ -125,12 +128,32 @@ export function MediaPanel(props: { selectedDayIso: string | null }) { assetId: string, ): Promise<{ url: string; variant: VideoPlaybackVariant }> { try { - const url = await loadSignedUrl(assetId, "video_mp4_720"); - return { url, variant: { kind: "video_mp4", size: 720 } }; + const res = await fetch(`/api/assets/${assetId}/variants`, { + cache: "no-store", + }); + if (!res.ok) throw new Error(`variants_fetch_failed:${res.status}`); + const variants = (await res.json()) as VariantsResponse; + const picked = pickVideoPlaybackVariant({ + originalMimeType: null, + variants: variants + .filter((variant) => variant.kind === "video_mp4") + .map((variant) => ({ + kind: "video_mp4", + size: variant.size, + key: variant.key, + })), + }); + + if (picked?.kind === "video_mp4") { + const url = await loadSignedUrl(assetId, "video_mp4_720"); + return { url, variant: { kind: "video_mp4", size: 720 } }; + } } catch { - const url = await loadSignedUrl(assetId, "original"); - return { url, variant: { kind: "original" } }; + // fall through to original } + + const url = await loadSignedUrl(assetId, "original"); + return { url, variant: { kind: "original" } }; } async function openViewer(asset: Asset) { diff --git a/apps/web/app/lib/playback.ts b/apps/web/app/lib/playback.ts index 2a9d812..df9375e 100644 --- a/apps/web/app/lib/playback.ts +++ b/apps/web/app/lib/playback.ts @@ -9,14 +9,14 @@ type PlaybackInput = { variants: Variant[]; }; -export function pickVideoPlaybackVariant(input: PlaybackInput): - | { kind: "video_mp4"; size: number } - | { kind: "original" } { +export function pickVideoPlaybackVariant( + input: PlaybackInput, +): { kind: "video_mp4"; size: number } | null { const mp4Variant = input.variants.find( (variant) => variant.kind === "video_mp4" && variant.size === 720, ); if (mp4Variant) { return { kind: "video_mp4", size: mp4Variant.size }; } - return { kind: "original" }; + return null; } diff --git a/apps/web/src/__tests__/prefer-derived.test.ts b/apps/web/src/__tests__/prefer-derived.test.ts index 42df1ac..dc41bd8 100644 --- a/apps/web/src/__tests__/prefer-derived.test.ts +++ b/apps/web/src/__tests__/prefer-derived.test.ts @@ -9,10 +9,10 @@ test("prefer mp4 derived over original", () => { expect(picked).toEqual({ kind: "video_mp4", size: 720 }); }); -test("falls back to original when no derived", () => { +test("returns null when no mp4 variants", () => { const picked = pickVideoPlaybackVariant({ originalMimeType: "video/x-matroska", variants: [], }); - expect(picked).toEqual({ kind: "original" }); + expect(picked).toBeNull(); });