Sanitized mirror from private repository - 2026-04-05 05:34:18 UTC
This commit is contained in:
188
dashboard/ui/app/media/page.tsx
Normal file
188
dashboard/ui/app/media/page.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user