From 847b120172c6aef2a55bd50f030c8ab0ede24ca3 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 16 Mar 2026 22:23:35 +0700 Subject: [PATCH] feat: Auto-Aufnahme fuer Top-Filme (Score>=95), Vorschlaege mit Buttons fuer den Rest --- homelab-ai-bot/telegram_bot.py | 48 +++++++++++++++++++++++++--------- homelab-ai-bot/tools/savetv.py | 38 +++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/homelab-ai-bot/telegram_bot.py b/homelab-ai-bot/telegram_bot.py index 9d1f1a2c..b72c2029 100644 --- a/homelab-ai-bot/telegram_bot.py +++ b/homelab-ai-bot/telegram_bot.py @@ -759,7 +759,7 @@ async def handle_callback(update: Update, ctx: ContextTypes.DEFAULT_TYPE): async def _send_daily_filmtipps(context): - """Täglicher Cronjob: Filmtipps via Telegram senden. + """Täglicher Cronjob: EPG scannen, Top-Filme auto-aufnehmen, Rest vorschlagen. context kann ein CallbackContext (JobQueue) oder eine Application (asyncio-Loop) sein. """ @@ -768,28 +768,50 @@ async def _send_daily_filmtipps(context): bot = getattr(context, "bot", None) or context try: from tools import savetv - telecasts = savetv._scrape_epg() - if not telecasts: - return - films = savetv._filter_films(telecasts) - if not films: + auto_recorded, suggestions = savetv.get_new_films() + + if not auto_recorded and not suggestions: + log.info("Filmtipp-Scan: keine neuen Filme") return - header = f"🎬 TV-Filmtipps für heute ({datetime.now().strftime('%d.%m.%Y')})\n" - await bot.send_message(chat_id=CHAT_ID, text=header) + if auto_recorded: + lines = [f"✅ *{len(auto_recorded)} Filme automatisch aufgenommen:*\n"] + for f in auto_recorded: + title = f.get("STITLE", "?") + station = f.get("STVSTATIONNAME", "?") + start = f.get("DSTARTDATE", "?")[:16] + subcat = f.get("SSUBCATEGORYNAME", "") + try: + start_dt = datetime.strptime(f.get("DSTARTDATE", ""), "%Y-%m-%d %H:%M:%S") + delta = (start_dt.date() - datetime.now().date()).days + when = f" (in {delta}d)" if delta > 1 else " (morgen)" if delta == 1 else " (heute)" + except (ValueError, TypeError): + when = "" + lines.append(f"🎬 {title}{when}\n 📺 {station} | ⏰ {start} | 🎭 {subcat}") + await bot.send_message(chat_id=CHAT_ID, text="\n".join(lines), parse_mode="Markdown") - for f in films[:6]: + for f in suggestions[:6]: tid = int(f.get("ITELECASTID", 0)) title = f.get("STITLE", "?") station = f.get("STVSTATIONNAME", "?") start = f.get("DSTARTDATE", "?")[:16] subcat = f.get("SSUBCATEGORYNAME", "") desc = (f.get("STHEMA") or f.get("SFULLSUBTITLE") or "")[:150] - recorded = " ✅ Bereits geplant" if f.get("BEXISTRECORD") else "" - text = f"🎬 *{title}*{recorded}\n📺 {station} | ⏰ {start}\n🎭 {subcat}" + try: + start_dt = datetime.strptime(f.get("DSTARTDATE", ""), "%Y-%m-%d %H:%M:%S") + delta = (start_dt.date() - datetime.now().date()).days + when = f"in {delta}d" if delta > 1 else "morgen" if delta == 1 else "heute" + except (ValueError, TypeError): + when = "" + + text = f"🤔 *{title}*" + if when: + text += f" ({when})" + text += f"\n📺 {station} | ⏰ {start}\n🎭 {subcat}" if desc: - text += f"\n_{desc}_" + desc_escaped = desc.replace("_", "\\_").replace("*", "\\*") + text += f"\n_{desc_escaped}_" keyboard = InlineKeyboardMarkup([ [ @@ -804,7 +826,7 @@ async def _send_daily_filmtipps(context): parse_mode="Markdown", ) - log.info("Tägliche Filmtipps gesendet: %d Filme", min(len(films), 6)) + log.info("Filmtipps: %d auto-aufgenommen, %d Vorschlaege", len(auto_recorded), len(suggestions)) except Exception: log.exception("Fehler beim Senden der Filmtipps") diff --git a/homelab-ai-bot/tools/savetv.py b/homelab-ai-bot/tools/savetv.py index f5bc05b7..eb2e9643 100644 --- a/homelab-ai-bot/tools/savetv.py +++ b/homelab-ai-bot/tools/savetv.py @@ -34,6 +34,9 @@ EPG_PAGES = [ SEEN_CACHE = Path("/tmp/savetv_seen_ids.json") SEEN_MAX_AGE_DAYS = 30 +AUTO_RECORD_SCORE = 95 +SUGGEST_SCORE = 60 + SPAM_SUBCATEGORIES = { "teleshop", "shopping", "dauerwerbesendung", "volksmusik", "casting", "reality", "quiz/spiel", "comic", "zeichentrick", @@ -404,16 +407,41 @@ def handle_get_savetv_tipps(**kw): def get_new_films(): - """Fuer den Cronjob: Nur NEUE Filme seit dem letzten Scan.""" + """Fuer den Cronjob: Neue Filme scannen, Top-Filme automatisch aufnehmen. + + Returns: (auto_recorded, suggestions) + auto_recorded: Filme die automatisch aufgenommen wurden (Score >= 85) + suggestions: Filme die dem User vorgeschlagen werden (Score 60-84) + """ telecasts = _scrape_epg() if not telecasts: - return [] + return [], [] films = _filter_films(telecasts, only_new=True) - good_films = [f for f in films if f["_score"] >= 60] - _mark_seen(films) - return good_films + + auto_recorded = [] + suggestions = [] + + for f in films: + score = f["_score"] + if score < SUGGEST_SCORE: + continue + + if f.get("BEXISTRECORD"): + continue + + if score >= AUTO_RECORD_SCORE: + tid = int(f.get("ITELECASTID", 0)) + result = _record_telecast(tid) + f["_record_result"] = result + auto_recorded.append(f) + log.info("Auto-Aufnahme: %s (Score %d) -> %s", + f.get("STITLE"), score, result) + else: + suggestions.append(f) + + return auto_recorded, suggestions def handle_savetv_record(telecast_id=0, **kw):