diff --git a/docker/api/main.py b/docker/api/main.py
index 3f1c017..54166e5 100644
--- a/docker/api/main.py
+++ b/docker/api/main.py
@@ -377,12 +377,28 @@ async def cancel_job(job_id: str):
@app.get("/capabilities")
async def capabilities():
- """Welche Encoder sind auf welchen Workern WIRKLICH verfügbar?
+ """Welche Encoder sind auf welchen Workern WIRKLICH verfügbar — inkl.
+ Live-Erreichbarkeit (Celery-Ping + Herzschlag-Alter).
- Jeder Worker meldet sich beim Start selbst (caps.py) — auch optionale
+ Jeder Worker meldet sich selbst (caps.py, minütlich) — auch optionale
Remote-GPU-Worker tauchen hier automatisch auf.
"""
- return {"workers": await asyncio.to_thread(db.list_workers)}
+ def sammle():
+ zeilen = db.list_workers()
+ try:
+ antworten = celery_client.control.ping(timeout=1.0) or []
+ online_namen = {
+ knoten.split("@", 1)[-1]
+ for antwort in antworten
+ for knoten in antwort.keys()
+ }
+ except Exception:
+ online_namen = set()
+ for zeile in zeilen:
+ zeile["online"] = zeile["name"] in online_namen
+ return zeilen
+
+ return {"workers": await asyncio.to_thread(sammle)}
class MountRequest(BaseModel):
diff --git a/docker/ui/src/components/DeviceDiscovery.tsx b/docker/ui/src/components/DeviceDiscovery.tsx
index 8454e83..5871426 100644
--- a/docker/ui/src/components/DeviceDiscovery.tsx
+++ b/docker/ui/src/components/DeviceDiscovery.tsx
@@ -3,6 +3,7 @@ import { HardDrive, Disc, AlertCircle, CheckCircle, RefreshCw } from 'lucide-rea
import { api } from '../lib/api'
import { useDarkMode } from '../context/ThemeContext'
import RipTargetModal from './RipTargetModal'
+import MetadataKorrektur from './MetadataKorrektur'
interface DiscInfo {
title: string
@@ -41,6 +42,7 @@ export default function DeviceDiscovery() {
const [actionFeedback, setActionFeedback] = useState
+ Deine Wahl gilt für diese Disc — auch beim nächsten Einlegen. +
+{fehler}
} + + {kandidaten.length > 0 && ( +Noch kein Worker gemeldet.
+ ) : ( +{w.name}
++ {w.online ? 'online' : 'offline'} · Lebenszeichen {relativeZeit(w.last_seen)} +
++ Auf der Zielmaschine (Linux mit Docker, oder Windows mit Docker Desktop) diesen + Befehl ausführen — der Worker verbindet sich, taucht oben in der Liste auf und + übernimmt ab dann automatisch Kompressions-Jobs. GPU vorhanden? Wird erkannt + und angezeigt. Voraussetzung: die Maschine erreicht diesen Rippy-Host im Netz. +
++ Eine geführte Ein-Klick-Installation (SSH) ist als Ausbaustufe geplant. +
+{Math.floor(result.metadata.runtime / 60)}h {result.metadata.runtime % 60}m
diff --git a/docker/ui/src/pages/Settings.tsx b/docker/ui/src/pages/Settings.tsx index 0cf0f4f..86589e3 100644 --- a/docker/ui/src/pages/Settings.tsx +++ b/docker/ui/src/pages/Settings.tsx @@ -3,6 +3,7 @@ import { Save, Disc, Cpu, Database, Globe, AlertCircle, CheckCircle, HardDrive } import { api } from '../lib/api' import { useDarkMode } from '../context/ThemeContext' import StorageMounts from '../components/StorageMounts' +import WorkerVerwaltung from '../components/WorkerVerwaltung' interface SettingsState { tmdbApiKey: string @@ -38,7 +39,7 @@ const defaultSettings: SettingsState = { keepOriginal: false, } -type SettingsTab = 'ripping' | 'verarbeitung' | 'speicherziele' | 'apis' | 'benachrichtigungen' +type SettingsTab = 'ripping' | 'verarbeitung' | 'worker' | 'speicherziele' | 'apis' | 'benachrichtigungen' interface WorkerInfo { name: string @@ -125,6 +126,7 @@ export default function SettingsPage() {