import type { GeneratedVideoReceipt, VideoArtifactPlaybackSource } from "@opengeni/sdk"; import { useEffect, useState } from "react"; import { cn } from "../lib/cn"; import { useErrorMessage } from "../lib/error-message"; import type { VideoArtifactPlaybackLoader } from "../timeline"; import { useOpenGeniText } from "../messages/runtime"; export type GeneratedVideoPlayerProps = { receipt: GeneratedVideoReceipt; loadPlaybackSource: VideoArtifactPlaybackLoader; className?: string | undefined; label?: string | undefined; }; type SourceState = | { kind: "loading" } | { kind: "ready"; source: VideoArtifactPlaybackSource } | { kind: "error"; message: string }; /** Native, Range-based playback. Video bytes never pass through React or the SDK heap. */ export function GeneratedVideoPlayer({ receipt, loadPlaybackSource, className, label: labelProp, }: GeneratedVideoPlayerProps) { const t = useOpenGeniText(); const label = labelProp === undefined ? t("generatedVideoPlayer.generatedVideo") : labelProp; const formatError = useErrorMessage(); const [retry, setRetry] = useState(0); const [state, setState] = useState({ kind: "loading" }); useEffect(() => { const controller = new AbortController(); setState({ kind: "loading" }); void loadPlaybackSource(receipt.artifact.artifactId, controller.signal).then( (source) => { if (!controller.signal.aborted) setState({ kind: "ready", source }); }, (error: unknown) => { if (controller.signal.aborted) return; setState({ kind: "error", message: formatError(error), }); }, ); return () => controller.abort(); }, [loadPlaybackSource, receipt.artifact.artifactId, retry, formatError]); if (state.kind === "loading") { return (
); } if (state.kind === "error") { return (
{state.message}
); } return (