"use client"; import { usePoll } from "@/lib/use-poll"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { CardSkeleton } from "@/components/skeleton"; interface TdarrWorker { id: string; type: string; file: string; percentage: number; fps: number; eta: string; } interface TdarrNode { id: string; name: string; paused: boolean; hardware: string; workers: TdarrWorker[]; active: number; } interface TdarrCluster { server_version?: string; nodes: TdarrNode[]; total_active: number; stats: { total_files: number; transcoded: number; health_checked: number; size_saved_gb: number; queue_transcode: number; error_transcode: number; tdarr_score: string; }; error?: string; } // Node hardware colors const hwColors: Record = { "NVENC (RTX 5090)": "text-green-400", "VAAPI (Radeon 760M)": "text-amber-400", "QSV (Intel)": "text-cyan-400", "CPU": "text-muted-foreground", }; export function TdarrCard() { const { data } = usePoll("/api/tdarr/cluster", 10000); // 10s refresh for live worker updates return ( Tdarr Cluster
{data && !data.error && ( <> {data.total_active} active {data.stats.error_transcode > 0 && ( {data.stats.error_transcode} errors )} )}
{!data ? ( ) : data.error ? (

{data.error}

) : (
{/* Stats row */}
{data.stats.total_files.toLocaleString()} files {data.stats.transcoded.toLocaleString()} transcoded {data.stats.health_checked.toLocaleString()} health checked {data.stats.size_saved_gb} GB saved {data.stats.queue_transcode > 0 && ( {data.stats.queue_transcode} in queue )}
{/* Nodes */}
{data.nodes.map((node) => (
0 ? "bg-green-500" : node.paused ? "bg-amber-500" : "bg-gray-500"}`} style={{ boxShadow: node.active > 0 ? "0 0 6px rgba(34,197,94,0.5)" : "none" }} /> {node.name} {node.hardware}
{node.paused && Paused}
{node.workers.length > 0 ? (
{node.workers.map((w) => (
{w.file}
{w.fps} fps {w.eta} {w.percentage}%
80 ? "#22c55e" : "#8b5cf6"})`, boxShadow: "0 0 8px rgba(59, 130, 246, 0.3)", }} />
))}
) : (

Idle

)}
))}
)} ); }