From 6506f99447889797ae8dc530577091e78bf6d379 Mon Sep 17 00:00:00 2001 From: Homelab Cursor Date: Thu, 21 May 2026 16:05:54 +0200 Subject: [PATCH] fix(savetv): keine Aufnahme wenn Film bereits in Jellyfin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Jellyfin-Abgleich galt nur der Web-UI-Anzeige. Auto- und manuelle Aufnahmen prüfen jetzt die normalisierte Mediathek. --- homelab-ai-bot/tools/savetv.py | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/homelab-ai-bot/tools/savetv.py b/homelab-ai-bot/tools/savetv.py index f8bba7a14..8443e6d53 100644 --- a/homelab-ai-bot/tools/savetv.py +++ b/homelab-ai-bot/tools/savetv.py @@ -43,6 +43,48 @@ DOWNLOAD_FORMAT_SD = 5 AUTO_RECORD_SCORE = 80 SUGGEST_SCORE = 60 +_jellyfin_set_cache = None +_jellyfin_set_ts = 0.0 +JELLYFIN_SET_TTL = 3600 + + +def _jellyfin_normalized_set(): + """Set normalisierter Jellyfin-Titel (1h Cache). None = Abgleich fehlgeschlagen.""" + global _jellyfin_set_cache, _jellyfin_set_ts + import time + now = time.time() + if _jellyfin_set_cache is not None and now - _jellyfin_set_ts < JELLYFIN_SET_TTL: + return _jellyfin_set_cache + try: + from savetv_extra_routes import _jellyfin_fetch_library + data = _jellyfin_fetch_library() + titles = data.get("normalized_titles") or [] + _jellyfin_set_cache = set(titles) + _jellyfin_set_ts = now + log.info( + "Jellyfin-Abgleich: %d Titel geladen (cached=%s)", + len(_jellyfin_set_cache), + data.get("cached"), + ) + return _jellyfin_set_cache + except Exception as e: + log.warning("Jellyfin-Abgleich fehlgeschlagen: %s", e) + return None + + +def _normalize_epg_title(title): + from savetv_extra_routes import _normalize_film_title, _strip_trailing_year_in_parens + return _normalize_film_title(_strip_trailing_year_in_parens(title or "")) + + +def _is_in_jellyfin(title): + """True wenn Film (normalisierter Titel) bereits in Jellyfin-Bibliothek.""" + jf = _jellyfin_normalized_set() + if jf is None: + return False + key = _normalize_epg_title(title) + return bool(key and key in jf) + def _title_pattern(title): """Titel exakt genug matchen, aber Leerzeichen/Interpunktion tolerant halten.""" @@ -887,6 +929,13 @@ def get_new_films(): if f.get("BEXISTRECORD"): continue + if _is_in_jellyfin(f.get("STITLE", "")): + log.info( + "Auto-Aufnahme übersprungen (bereits in Jellyfin): %s", + f.get("STITLE"), + ) + continue + if score >= AUTO_RECORD_SCORE: tid = int(f.get("ITELECASTID", 0)) result = _record_telecast(tid) @@ -916,6 +965,15 @@ def handle_savetv_record(telecast_id=0, **kw): title = tc.get("STITLE", title) break + if _is_in_jellyfin(title): + return ( + "Save.TV: nicht aufgenommen — bereits in Jellyfin: " + + title + + "\n(TelecastId " + + str(tid) + + ")" + ) + result = _record_telecast(tid) return "Save.TV: " + result + "\nSendung: " + title