"use client"; import { ListMusic, Play, Radio, Repeat, SkipForward, Square, Volume2, } from "lucide-react"; import { useEffect, useState } from "react"; import { useAmbient } from "@/components/ambient/ambient-context"; import { Button, GlassPanel, Input, toast } from "@/components/primitives"; import { ErrorState, SectionHeader, SkeletonHero, SkeletonPanel, } from "@/components/shared"; import { useMediaLoop, useMediaQueue, useMediaSkip, useMediaState, useMediaStop, useMediaWsSync, } from "@/hooks"; import { formatDuration } from "@/lib/format"; import type { MediaState } from "@/lib/types"; import { staggerDelay } from "@/lib/utils"; import { useWebSocket } from "@/lib/ws/context"; export function MediaView({ initialStatus }: { initialStatus?: MediaState }) { const ws = useWebSocket(); const { data: media, isLoading, error } = useMediaState(initialStatus); const queue = useMediaQueue(); const skip = useMediaSkip(); const stop = useMediaStop(); const loop = useMediaLoop(); useMediaWsSync(ws); const ambient = useAmbient(); const [url, setUrl] = useState(""); const playing = media?.playing ?? false; const current = media?.current ?? null; const queueList = media?.queue ?? []; const queueTotal = queueList.reduce( (acc, it) => acc + (it.durationMs ?? 0), 0, ); const tone = playing ? "signal" : queueList.length ? "amber" : "signal"; useEffect(() => { ambient.set( tone, playing ? 0.5 : 0.25, playing ? "now playing" : "media idle", ); }, [tone, playing, ambient]); const onPlay = async () => { const u = url.trim(); if (!u) { toast({ title: "Enter a media URL", tone: "vermilion" }); return; } try { await queue.mutateAsync({ url: u, mode: "music" }); setUrl(""); toast({ title: "Queued", tone: "signal" }); } catch (e) { toast({ title: "Queue failed", description: String(e), tone: "vermilion", }); } }; if (error && !media) return ; if (!media && isLoading) return (
); return (
{current?.thumbnailUrl ? ( // biome-ignore lint/performance/noImgElement: external CDN thumbnails, next/image needs remote allowlist ) : ( )}
{playing ? ( <> {[0, 1, 2].map((i) => ( ))} Now playing ) : current ? ( "Paused" ) : ( "Nothing queued" )}

{current?.title ?? "Nothing queued"}

{current?.source && ( {current.source} )} {current?.mode && ( {current.mode} )} {formatDuration(current?.durationMs) && ( {formatDuration(current?.durationMs)} )}
setUrl(e.target.value)} onKeyDown={(e) => e.key === "Enter" && onPlay()} />
{Math.round((media?.musicVolume ?? 0) * 100)}%
{queueList.length} track{queueList.length === 1 ? "" : "s"} {queueTotal && ` · ${formatDuration(queueTotal)}`} } /> {queueList.length === 0 ? (
Queue is empty
Paste a URL above to start playback.
) : (
{queueList.map((item, i) => { const isNext = i === 0 && playing; return (
{i + 1} {item.thumbnailUrl ? ( // biome-ignore lint/performance/noImgElement: external CDN thumbnails, next/image needs remote allowlist ) : ( )}
{item.title}
{item.source}
{isNext && ( up next )} {item.mode ?? "music"} {formatDuration(item.durationMs) && ( {formatDuration(item.durationMs)} )}
); })}
)}
); }