"use client"; import { useEffect, useRef, useState } from "react"; type Kind = "audio" | "video"; type LoadState = | { phase: "loading" } | { phase: "ready"; duration: number | null } | { phase: "error"; message: string }; type Props = { src: string; kind: Kind; filename: string; }; const MEDIA_ERROR_REASONS: Record = { 1: "Playback aborted.", 2: "Network error while loading the file.", 3: "Decoder failed — file may be malformed or use an unsupported codec.", 4: "Source not supported — browser cannot decode this format.", }; export function MediaPlayer({ src, kind, filename }: Props) { const ref = useRef(null); const [state, setState] = useState({ phase: "loading" }); useEffect(() => { const el = ref.current; if (!el) return; setState({ phase: "loading" }); const onLoadedMeta = () => { const d = el.duration; setState({ phase: "ready", duration: Number.isFinite(d) ? d : null, }); }; const onError = () => { const code = el.error?.code; const detail = code !== undefined ? (MEDIA_ERROR_REASONS[code] ?? "Unknown decode error.") : "Unknown decode error."; setState({ phase: "error", message: detail }); }; el.addEventListener("loadedmetadata", onLoadedMeta); el.addEventListener("error", onError); return () => { el.removeEventListener("loadedmetadata", onLoadedMeta); el.removeEventListener("error", onError); }; }, [src]); const refCallback = (node: HTMLMediaElement | null) => { ref.current = node; }; return (
{kind === "audio" ? (
); } function StatusLine({ state, filename, }: { state: LoadState; filename: string; }) { if (state.phase === "loading") { return (

Loading metadata…

); } if (state.phase === "error") { return (

Malformed: {state.message}

); } return (

Decodes OK {state.duration !== null && ` · ${formatDuration(state.duration)}`}

); } function formatDuration(seconds: number): string { const s = Math.round(seconds); const h = Math.floor(s / 3600); const m = Math.floor((s % 3600) / 60); const sec = s % 60; if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`; return `${m}:${String(sec).padStart(2, "0")}`; }