diff --git a/app.py b/app.py
index e7112db..0412620 100644
--- a/app.py
+++ b/app.py
@@ -4,6 +4,7 @@ import os
import sqlite3
import subprocess
import threading
+import re
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from pathlib import Path
@@ -59,6 +60,23 @@ def sab_get(mode, **params):
raise RuntimeError(data["error"])
return data
+def parse_size_bytes(value):
+ if isinstance(value, (int, float)):
+ return int(value)
+ match = re.search(r"([\d.,]+)\s*(B|KB|MB|GB|TB)", str(value or ""), re.IGNORECASE)
+ if not match:
+ return 0
+ number = float(match.group(1).replace(',', '.'))
+ multiplier = {"B": 1, "KB": 1024, "MB": 1024**2, "GB": 1024**3, "TB": 1024**4}[match.group(2).upper()]
+ return int(number * multiplier)
+
+def format_bytes(size):
+ value = float(size or 0)
+ for unit in ("B", "KB", "MB", "GB", "TB"):
+ if value < 1024 or unit == "TB":
+ return f"{value:.1f} {unit}"
+ value /= 1024
+
def release_languages(name):
value = f" {name.lower().replace('.', ' ').replace('-', ' ')} "
found = []
@@ -90,7 +108,11 @@ def sab_downloads():
items = [make_item(item) for item in queue.get("slots", [])]
items.extend(make_item(item, completed=True) for item in history.get("slots", [])[:15])
- return {"items": items, "queue_status": queue.get("status", "Idle"), "speed": queue.get("speed", "0 B/s"), "paused": bool(queue.get("paused_all")), "error": None}
+ remaining_bytes = sum(parse_size_bytes(item.get("sizeleft")) for item in queue.get("slots", []))
+ return {"items": items, "queue_status": queue.get("status", "Idle"), "speed": queue.get("speed", "0 B/s"),
+ "paused": bool(queue.get("paused_all")), "active_count": len(queue.get("slots", [])),
+ "history_count": len(history.get("slots", [])[:15]), "remaining_bytes": remaining_bytes,
+ "remaining": format_bytes(remaining_bytes), "error": None}
def norm_lang(code):
code = (code or "").strip().lower()
@@ -257,12 +279,12 @@ def scan_status():
@app.get("/api/downloads")
def downloads():
if not SAB_URL or not SAB_API_KEY:
- return jsonify({"items": [], "queue_status": "Nicht konfiguriert", "speed": "—", "paused": False, "error": "SAB_URL oder SAB_API_KEY ist nicht gesetzt."})
+ return jsonify({"items": [], "queue_status": "Nicht konfiguriert", "speed": "—", "paused": False, "active_count": 0, "history_count": 0, "remaining": "—", "error": "SAB_URL oder SAB_API_KEY ist nicht gesetzt."})
try:
return jsonify(sab_downloads())
except Exception as exc:
log.exception("SABnzbd konnte nicht abgefragt werden")
- return jsonify({"items": [], "queue_status": "Fehler", "speed": "—", "paused": False, "error": str(exc)}), 502
+ return jsonify({"items": [], "queue_status": "Fehler", "speed": "—", "paused": False, "active_count": 0, "history_count": 0, "remaining": "—", "error": str(exc)}), 502
@app.get("/health")
def health(): return jsonify({"ok":True, "radarr_url":RADARR_URL, "sonarr_enabled":bool(SONARR_URL and SONARR_API_KEY), "sab_enabled":bool(SAB_URL and SAB_API_KEY)})
diff --git a/static/media-mark.svg b/static/media-mark.svg
new file mode 100644
index 0000000..ae34839
--- /dev/null
+++ b/static/media-mark.svg
@@ -0,0 +1,12 @@
+
diff --git a/templates/index.html b/templates/index.html
index 538028a..ea40361 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,9 +1,9 @@
-
Language Dashboard
+Language Dashboard
-
+
{% if error %}Fehler: {{ error }}
{% endif %}
@@ -20,7 +20,7 @@
| Film | Qualität | Status | Audio | Untertitel | Video | Größe |
{% for r in missing_german_movies %}| {{ r.title }}{{ r.year or '' }} | {{ r.quality }} | Ohne Deutsch | {% for l in r.audio_languages %}{{ l.flag }} {{ l.name }}{% else %}—{% endfor %} | {% for l in r.subtitle_languages %}{{ l.flag }} {{ l.name }}{% else %}—{% endfor %} | {{ r.video_codec }} | {{ r.size }} |
{% endfor %}
{% for group in missing_german_series %}{{ group.title }} ({{ group.year or '—' }}) · {{ group.episodes|length }} Episoden ohne Deutsch
| Episode | Qualität | Status | Audio | Untertitel | Video | Größe |
{% for r in group.episodes %}| {{ r.title }} | {{ r.quality }} | Ohne Deutsch | {% for l in r.audio_languages %}{{ l.flag }} {{ l.name }}{% else %}—{% endfor %} | {% for l in r.subtitle_languages %}{{ l.flag }} {{ l.name }}{% else %}—{% endfor %} | {{ r.video_codec }} | {{ r.size }} |
{% endfor %}
{% endfor %}
{% if not missing_german_movies and not missing_german_series %}Alle vorhandenen Filme und Episoden haben deutsches Audio oder deutsche Untertitel.
{% endif %}
-{% if sab_enabled %}Downloads werden geladen …SABnzbd …Geschwindigkeit: —
| Download | Status | Fortschritt | Größe / Rest | Restzeit | Quelle | Sprache |
|---|
| Downloads werden geladen … |
{% endif %}
+{% if sab_enabled %}SABNZBD · DOWNLOAD CENTER
Download-Übersicht
Queue, Fortschritt und verbleibender Speicherplatz auf einen Blick.
Verbinde mit SABnzbd …
↓Noch zu laden—Wird berechnet …
◉Aktive Downloads—Verlauf: —
↯Geschwindigkeit—Aktuelle Queue
| Download | Status | Fortschritt | Größe / Rest | Restzeit | Quelle | Sprache |
|---|
| Downloads werden geladen … |
{% endif %}
Einstellungen
Deine Anzeige- und Sprachpräferenzen