189 lines
6.4 KiB
TypeScript
189 lines
6.4 KiB
TypeScript
"use client";
|
|
|
|
import { usePoll } from "@/lib/use-poll";
|
|
import { JellyfinCard } from "@/components/jellyfin-card";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { StatusBadge } from "@/components/status-badge";
|
|
|
|
interface QueueItem {
|
|
title: string;
|
|
status: string;
|
|
size?: string;
|
|
timeleft?: string;
|
|
progress?: number;
|
|
}
|
|
|
|
interface SonarrQueueItem {
|
|
title: string;
|
|
status: string;
|
|
sizeleft?: string;
|
|
timeleft?: string;
|
|
}
|
|
|
|
interface RadarrQueueItem {
|
|
title: string;
|
|
status: string;
|
|
sizeleft?: string;
|
|
timeleft?: string;
|
|
}
|
|
|
|
interface SabQueue {
|
|
slots: QueueItem[];
|
|
speed: string;
|
|
paused: boolean;
|
|
}
|
|
|
|
export default function MediaPage() {
|
|
const { data: sonarrRaw } = usePoll<Record<string, unknown>>("/api/sonarr/queue", 30000);
|
|
const { data: radarrRaw } = usePoll<Record<string, unknown>>("/api/radarr/queue", 30000);
|
|
const { data: sabRaw } = usePoll<Record<string, unknown>>("/api/sabnzbd/queue", 30000);
|
|
const sonarr = (sonarrRaw?.records ?? sonarrRaw?.items ?? []) as SonarrQueueItem[];
|
|
const radarr = (radarrRaw?.records ?? radarrRaw?.items ?? []) as RadarrQueueItem[];
|
|
const sab = sabRaw?.queue as SabQueue | undefined;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<h1 className="text-lg font-semibold">Media</h1>
|
|
|
|
<JellyfinCard />
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{/* Sonarr Queue */}
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium">Sonarr Queue</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{!sonarr ? (
|
|
<p className="text-xs text-muted-foreground">Loading...</p>
|
|
) : sonarr.length === 0 ? (
|
|
<p className="text-xs text-muted-foreground">Queue empty</p>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{sonarr.map((item, i) => (
|
|
<div key={i} className="text-xs space-y-0.5">
|
|
<p className="text-foreground font-medium truncate">
|
|
{item.title}
|
|
</p>
|
|
<div className="flex items-center gap-2">
|
|
<StatusBadge
|
|
color={
|
|
item.status === "completed"
|
|
? "green"
|
|
: item.status === "downloading"
|
|
? "blue"
|
|
: "amber"
|
|
}
|
|
label={item.status}
|
|
/>
|
|
{item.timeleft && (
|
|
<span className="text-muted-foreground">
|
|
{item.timeleft}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Radarr Queue */}
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium">Radarr Queue</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{!radarr ? (
|
|
<p className="text-xs text-muted-foreground">Loading...</p>
|
|
) : radarr.length === 0 ? (
|
|
<p className="text-xs text-muted-foreground">Queue empty</p>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{radarr.map((item, i) => (
|
|
<div key={i} className="text-xs space-y-0.5">
|
|
<p className="text-foreground font-medium truncate">
|
|
{item.title}
|
|
</p>
|
|
<div className="flex items-center gap-2">
|
|
<StatusBadge
|
|
color={
|
|
item.status === "completed"
|
|
? "green"
|
|
: item.status === "downloading"
|
|
? "blue"
|
|
: "amber"
|
|
}
|
|
label={item.status}
|
|
/>
|
|
{item.timeleft && (
|
|
<span className="text-muted-foreground">
|
|
{item.timeleft}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* SABnzbd Queue */}
|
|
<Card>
|
|
<CardHeader className="pb-2 flex flex-row items-center justify-between">
|
|
<CardTitle className="text-sm font-medium">
|
|
SABnzbd Queue
|
|
</CardTitle>
|
|
{sab && (
|
|
<StatusBadge
|
|
color={sab.paused ? "amber" : "green"}
|
|
label={sab.paused ? "Paused" : sab.speed}
|
|
/>
|
|
)}
|
|
</CardHeader>
|
|
<CardContent>
|
|
{!sab ? (
|
|
<p className="text-xs text-muted-foreground">Loading...</p>
|
|
) : sab.slots.length === 0 ? (
|
|
<p className="text-xs text-muted-foreground">Queue empty</p>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{sab.slots.map((item, i) => (
|
|
<div key={i} className="text-xs space-y-1">
|
|
<p className="text-foreground font-medium truncate">
|
|
{item.title}
|
|
</p>
|
|
<div className="flex items-center gap-2">
|
|
<StatusBadge
|
|
color={
|
|
item.status === "Downloading" ? "blue" : "amber"
|
|
}
|
|
label={item.status}
|
|
/>
|
|
{item.timeleft && (
|
|
<span className="text-muted-foreground">
|
|
{item.timeleft}
|
|
</span>
|
|
)}
|
|
</div>
|
|
{item.progress != null && (
|
|
<div className="h-1 rounded-full bg-secondary overflow-hidden">
|
|
<div
|
|
className="h-full rounded-full bg-primary"
|
|
style={{ width: `${item.progress}%` }}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|