From d252024dbc915b8d442057b4708f9e0020f40660 Mon Sep 17 00:00:00 2001 From: nessi Date: Thu, 13 Aug 2026 11:35:55 +0200 Subject: [PATCH] feat: improve download matching with fuzzy token-based algorithm and add downloading state styling Replace exact title matching with token-based fuzzy matching that requires 75% of significant tokens (3+ chars) to match. Add "downloading" class to state column and apply purple badge styling to quality column for downloading items. Update download refresh to trigger markDownloading() and initialize download markers on page load. --- app.py | 11 ++++++++--- templates/index.html | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index 49422f6..8060826 100644 --- a/app.py +++ b/app.py @@ -158,21 +158,26 @@ def media_row(name, year, media_file, remote, local, extra=None): if not path and media_file and media_file.get("relativePath"): path = str(Path(media_file.get("basePath") or "") / media_file["relativePath"]) local_path = map_path(path, remote, local); info = cached(local_path) if local_path else {"error":"Kein Dateipfad erhalten"} info = info or {"pending": True}; audios = info.get("audio_languages", []) + downloading = bool((extra or {}).get("downloading")) quality = ((((media_file or {}).get("quality") or {}).get("quality") or {}).get("name")) or "—" - row = {"title":name, "year":year, "quality":quality if media_file else "Keine Datei", "audio_languages":audios, "subtitle_languages":info.get("subtitle_languages", []), "video_codec":info.get("video_codec", "—"), "size":format_size(info.get("size") or (media_file or {}).get("size")), "radarr_path":path or "—", "local_path":local_path or "—", "state":language_state(audios) if audios else {"class":"neutral", "label":"Scan ausstehend" if info.get("pending") else ("Fehler" if info.get("error") else "Keine Spuren")}, "error":info.get("error"), "missing":not bool(media_file), "pending":bool(info.get("pending"))} + row = {"title":name, "year":year, "quality":"Downloading" if downloading else (quality if media_file else "Keine Datei"), "audio_languages":audios, "subtitle_languages":info.get("subtitle_languages", []), "video_codec":info.get("video_codec", "—"), "size":format_size(info.get("size") or (media_file or {}).get("size")), "radarr_path":path or "—", "local_path":local_path or "—", "state":{"class":"downloading", "label":"Downloading"} if downloading else (language_state(audios) if audios else {"class":"neutral", "label":"Scan ausstehend" if info.get("pending") else ("Fehler" if info.get("error") else "Keine Spuren")}), "error":info.get("error"), "missing":not bool(media_file), "pending":bool(info.get("pending")), "downloading":downloading} if extra: row.update(extra) return row def radarr_rows(): rows = [] + queue = api_get(RADARR_URL, RADARR_API_KEY, "queue?includeUnknownMovieItems=true") + queued_movies = {item.get("movieId") for item in queue.get("records", [])} for movie in api_get(RADARR_URL, RADARR_API_KEY, "movie"): mf = movie.get("movieFile"); path = (mf or {}).get("path") or ((movie.get("path") and (mf or {}).get("relativePath")) and str(Path(movie["path"]) / mf["relativePath"])) if mf and path: mf = dict(mf); mf["path"] = path - rows.append(media_row(movie.get("title", "—"), movie.get("year"), mf, RADARR_MEDIA_PATH, LOCAL_MEDIA_PATH)) + rows.append(media_row(movie.get("title", "—"), movie.get("year"), mf, RADARR_MEDIA_PATH, LOCAL_MEDIA_PATH, {"downloading": movie.get("id") in queued_movies})) return sorted(rows, key=lambda x: x["title"].lower()) def sonarr_groups(): groups = [] + queue = api_get(SONARR_URL, SONARR_API_KEY, "queue?includeUnknownSeriesItems=true") + queued_episodes = {item.get("episodeId") for item in queue.get("records", [])} for series in api_get(SONARR_URL, SONARR_API_KEY, "series"): episodes = api_get(SONARR_URL, SONARR_API_KEY, f"episode?seriesId={series['id']}&includeSeries=false") files = {f["id"]: f for f in api_get(SONARR_URL, SONARR_API_KEY, f"episodefile?seriesId={series['id']}")} @@ -181,7 +186,7 @@ def sonarr_groups(): ef = files.get(episode.get("episodeFileId")); if ef: ef = dict(ef); ef["path"] = str(Path(series.get("path") or "") / ef.get("relativePath", "")) label = f"S{episode.get('seasonNumber', 0):02d}E{episode.get('episodeNumber', 0):02d} · {episode.get('title') or '—'}" - items.append(media_row(label, None, ef, SONARR_MEDIA_PATH, LOCAL_SERIES_PATH, {"episode":True})) + items.append(media_row(label, None, ef, SONARR_MEDIA_PATH, LOCAL_SERIES_PATH, {"episode":True, "downloading": episode.get("id") in queued_episodes})) groups.append({"title":series.get("title", "—"), "year":series.get("year"), "episodes":items}) return sorted(groups, key=lambda x: x["title"].lower()) diff --git a/templates/index.html b/templates/index.html index e407006..f279071 100644 --- a/templates/index.html +++ b/templates/index.html @@ -38,14 +38,15 @@ details.series:last-child{margin-bottom:0}