fix: use playback selector in MediaPanel

This commit is contained in:
William Valentin
2026-02-01 16:49:47 -08:00
parent 8479f50daa
commit 4cd6dfef40
3 changed files with 33 additions and 10 deletions

View File

@@ -2,6 +2,8 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { pickVideoPlaybackVariant } from "../lib/playback";
type Asset = { type Asset = {
id: string; id: string;
media_type: "image" | "video"; media_type: "image" | "video";
@@ -25,6 +27,7 @@ type SignedUrlResponse = {
type PreviewUrlState = Record<string, string | undefined>; type PreviewUrlState = Record<string, string | undefined>;
type VideoPlaybackVariant = { kind: "original" } | { kind: "video_mp4"; size: 720 }; type VideoPlaybackVariant = { kind: "original" } | { kind: "video_mp4"; size: 720 };
type VariantsResponse = Array<{ kind: string; size: number; key: string }>;
function startOfDayUtc(iso: string) { function startOfDayUtc(iso: string) {
const d = new Date(iso); const d = new Date(iso);
@@ -125,12 +128,32 @@ export function MediaPanel(props: { selectedDayIso: string | null }) {
assetId: string, assetId: string,
): Promise<{ url: string; variant: VideoPlaybackVariant }> { ): Promise<{ url: string; variant: VideoPlaybackVariant }> {
try { try {
const url = await loadSignedUrl(assetId, "video_mp4_720"); const res = await fetch(`/api/assets/${assetId}/variants`, {
return { url, variant: { kind: "video_mp4", size: 720 } }; 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 { } catch {
const url = await loadSignedUrl(assetId, "original"); // fall through to original
return { url, variant: { kind: "original" } };
} }
const url = await loadSignedUrl(assetId, "original");
return { url, variant: { kind: "original" } };
} }
async function openViewer(asset: Asset) { async function openViewer(asset: Asset) {

View File

@@ -9,14 +9,14 @@ type PlaybackInput = {
variants: Variant[]; variants: Variant[];
}; };
export function pickVideoPlaybackVariant(input: PlaybackInput): export function pickVideoPlaybackVariant(
| { kind: "video_mp4"; size: number } input: PlaybackInput,
| { kind: "original" } { ): { kind: "video_mp4"; size: number } | null {
const mp4Variant = input.variants.find( const mp4Variant = input.variants.find(
(variant) => variant.kind === "video_mp4" && variant.size === 720, (variant) => variant.kind === "video_mp4" && variant.size === 720,
); );
if (mp4Variant) { if (mp4Variant) {
return { kind: "video_mp4", size: mp4Variant.size }; return { kind: "video_mp4", size: mp4Variant.size };
} }
return { kind: "original" }; return null;
} }

View File

@@ -9,10 +9,10 @@ test("prefer mp4 derived over original", () => {
expect(picked).toEqual({ kind: "video_mp4", size: 720 }); 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({ const picked = pickVideoPlaybackVariant({
originalMimeType: "video/x-matroska", originalMimeType: "video/x-matroska",
variants: [], variants: [],
}); });
expect(picked).toEqual({ kind: "original" }); expect(picked).toBeNull();
}); });