Save.TV: Archiv-Bewertung Tool + _get_archive POST-Fix
This commit is contained in:
parent
51abcb06eb
commit
7ac401c3a5
1 changed files with 206 additions and 9 deletions
|
|
@ -68,6 +68,17 @@ TOOLS = [
|
|||
"parameters": {"type": "object", "properties": {}, "required": []},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_savetv_archive_filme",
|
||||
"description": "Save.TV Archiv-Filme bewerten: Alle fertigen Aufnahmen holen, "
|
||||
"nach Qualitaet bewerten, deduplizieren. Zeigt Top-Filme, dringende "
|
||||
"(bald ablaufend) und weitere. Nutze bei 'gute Filme im Archiv', "
|
||||
"'welche Filme habe ich', 'was ist sehenswert', 'Archiv bewerten'.",
|
||||
"parameters": {"type": "object", "properties": {}, "required": []},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
|
|
@ -87,9 +98,10 @@ TOOLS = [
|
|||
|
||||
SYSTEM_PROMPT_EXTRA = """TV / Save.TV Tools:
|
||||
- get_savetv_tipps: Zeigt sehenswerte Spielfilme der naechsten Tage/Wochen
|
||||
- get_savetv_archive_filme: Bewertet alle fertigen Aufnahmen im Archiv nach Qualitaet
|
||||
- savetv_record: Nimmt einen Film per TelecastId auf
|
||||
- get_savetv_status: Zeigt Archiv und geplante Aufnahmen
|
||||
Wenn der User einen Film aufnehmen will, nutze savetv_record mit der TelecastId.
|
||||
Wenn der User nach Archiv-Filmen/Bewertung fragt, nutze get_savetv_archive_filme.
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -159,17 +171,21 @@ def _save_seen(seen):
|
|||
|
||||
|
||||
def _get_archive(state=0, count=20):
|
||||
"""Archiv abrufen. state: 0=geplant, 1=fertig."""
|
||||
"""Archiv abrufen (POST, wie Web-UI). state: 0=geplant, 1=fertig."""
|
||||
s = _get_session()
|
||||
if not s:
|
||||
return {"error": "Login fehlgeschlagen"}
|
||||
try:
|
||||
r = s.get(
|
||||
end = datetime.now().strftime("%Y-%m-%d")
|
||||
start = (datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d")
|
||||
r = s.post(
|
||||
SAVETV_URL + "/STV/M/obj/archive/JSON/VideoArchiveApi.cfm",
|
||||
params={
|
||||
data={
|
||||
"bAggregateEntries": "false",
|
||||
"iEntriesPerPage": str(count),
|
||||
"iRecordingState": str(state),
|
||||
"dStartdate": start,
|
||||
"dEnddate": end,
|
||||
},
|
||||
headers={"X-Requested-With": "XMLHttpRequest"},
|
||||
timeout=15,
|
||||
|
|
@ -179,6 +195,40 @@ def _get_archive(state=0, count=20):
|
|||
return {"error": str(e)}
|
||||
|
||||
|
||||
def _get_full_archive():
|
||||
"""Alle fertigen Aufnahmen paginiert holen."""
|
||||
s = _get_session()
|
||||
if not s:
|
||||
return []
|
||||
end = datetime.now().strftime("%Y-%m-%d")
|
||||
start = (datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d")
|
||||
all_entries = []
|
||||
for page in range(1, 20):
|
||||
try:
|
||||
r = s.post(
|
||||
SAVETV_URL + "/STV/M/obj/archive/JSON/VideoArchiveApi.cfm",
|
||||
data={
|
||||
"bAggregateEntries": "false",
|
||||
"iEntriesPerPage": "100",
|
||||
"iCurrentPage": str(page),
|
||||
"iRecordingState": "1",
|
||||
"dStartdate": start,
|
||||
"dEnddate": end,
|
||||
},
|
||||
headers={"X-Requested-With": "XMLHttpRequest"},
|
||||
timeout=15,
|
||||
)
|
||||
data = r.json()
|
||||
entries = data.get("ARRVIDEOARCHIVEENTRIES", [])
|
||||
if not entries:
|
||||
break
|
||||
all_entries.extend(entries)
|
||||
except Exception as e:
|
||||
log.error("Archive page %d: %s", page, e)
|
||||
break
|
||||
return all_entries
|
||||
|
||||
|
||||
def _scrape_epg():
|
||||
"""Holt Filme aus Save.TV Programmseiten (JSON im HTML).
|
||||
|
||||
|
|
@ -342,9 +392,154 @@ def _format_film(f, with_tid=True):
|
|||
return "\n".join(lines)
|
||||
|
||||
|
||||
DOKU_KEYWORDS = {
|
||||
"schlangen", "giftig", "gefährlich", "tiere", "tierwelt",
|
||||
"wildtiere", "safari", "ozean", "meer", "ozeane",
|
||||
"doku", "dokumentation", "reportage", "magazin",
|
||||
"botschafter der meere", "dynastie", "leoparden",
|
||||
"schildkröten", "wale", "australien", "afrika",
|
||||
"gehirn unter strom",
|
||||
}
|
||||
|
||||
|
||||
def _score_archive_film(title, station, highlight, subtitle="", thema=""):
|
||||
"""Bewertet einen Archiv-Film heuristisch (0-100)."""
|
||||
t = title.lower()
|
||||
s = station.lower()
|
||||
|
||||
if "programmänderung" in t:
|
||||
return -1
|
||||
|
||||
for kw in DOKU_KEYWORDS:
|
||||
if kw in t:
|
||||
return -1
|
||||
|
||||
score = 50
|
||||
|
||||
premium_stations = {"arte", "zdf", "das erste", "mdr", "swr", "ndr", "wdr", "br"}
|
||||
action_stations = {"prosieben", "sat.1", "kabel 1", "vox", "rtl", "tele 5", "zdf_neo"}
|
||||
if s in premium_stations:
|
||||
score += 5
|
||||
elif s in action_stations:
|
||||
score += 3
|
||||
|
||||
if highlight:
|
||||
score += 15
|
||||
|
||||
desc = (thema or subtitle or "").lower()
|
||||
if len(desc) > 30:
|
||||
score += 5
|
||||
|
||||
quality_hints = [
|
||||
"oscar", "golden globe", "cannes", "berlinale", "venedig",
|
||||
"preisgekrönt", "meisterwerk", "bestseller", "basiert auf",
|
||||
]
|
||||
for hint in quality_hints:
|
||||
if hint in desc or hint in t:
|
||||
score += 10
|
||||
break
|
||||
|
||||
if any(c.isascii() and c.isalpha() for c in title) and not all(c.isascii() for c in title if c.isalpha()):
|
||||
pass
|
||||
elif re.search(r'[A-Z][a-z]+ [A-Z][a-z]+', title) and not re.search(r'[äöüÄÖÜß]', title):
|
||||
score += 8
|
||||
|
||||
return score
|
||||
|
||||
|
||||
def handle_get_savetv_archive_filme(**kw):
|
||||
"""Alle fertigen Archiv-Filme holen, bewerten, deduplizieren, sortiert ausgeben."""
|
||||
entries = _get_full_archive()
|
||||
if not entries:
|
||||
return "Keine Archiv-Eintraege gefunden."
|
||||
|
||||
films = []
|
||||
seen_titles = {}
|
||||
series_count = 0
|
||||
|
||||
for e in entries:
|
||||
tc = e.get("STRTELECASTENTRY", {})
|
||||
episode = tc.get("SFOLGE", "")
|
||||
if episode:
|
||||
series_count += 1
|
||||
continue
|
||||
|
||||
title = tc.get("STITLE", "?")
|
||||
station = tc.get("STVSTATIONNAME", "?")
|
||||
highlight = tc.get("BISHIGHLIGHT", False)
|
||||
subtitle = tc.get("SSUBTITLE", "")
|
||||
thema = tc.get("STHEMA", "")
|
||||
date = tc.get("DSTARTDATE", "?")[:10]
|
||||
days_left = int(tc.get("IDAYSLEFTBEFOREDELETE", 0))
|
||||
tid = int(tc.get("ITELECASTID", 0))
|
||||
|
||||
score = _score_archive_film(title, station, highlight, subtitle, thema)
|
||||
if score < 0:
|
||||
continue
|
||||
|
||||
key = title.lower().strip()
|
||||
if key in seen_titles:
|
||||
if days_left > seen_titles[key]["days_left"]:
|
||||
seen_titles[key]["days_left"] = days_left
|
||||
seen_titles[key]["date"] = date
|
||||
seen_titles[key]["tid"] = tid
|
||||
continue
|
||||
|
||||
seen_titles[key] = {
|
||||
"title": title, "station": station, "date": date,
|
||||
"days_left": days_left, "score": score, "tid": tid,
|
||||
"highlight": highlight,
|
||||
}
|
||||
|
||||
films = sorted(seen_titles.values(), key=lambda x: (-x["score"], x["days_left"]))
|
||||
|
||||
total_archive = len(entries)
|
||||
urgent = sorted(
|
||||
[f for f in films if f["days_left"] <= 7],
|
||||
key=lambda x: (x["days_left"], -x["score"]),
|
||||
)
|
||||
|
||||
lines = [
|
||||
f"Save.TV Archiv-Bewertung: {len(films)} Filme "
|
||||
f"(von {total_archive} Aufnahmen, {series_count} Serien-Episoden gefiltert)\n"
|
||||
]
|
||||
|
||||
if urgent:
|
||||
lines.append(f"DRINGEND — {len(urgent)} Filme laufen in <=7 Tagen ab:")
|
||||
for f in urgent:
|
||||
hl = " *" if f["highlight"] else ""
|
||||
lines.append(
|
||||
f" [{f['days_left']}d] {f['title'][:45]} | {f['station']} | "
|
||||
f"Score {f['score']}{hl} | TID {f['tid']}"
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
top = [f for f in films if f["score"] >= 60 and f["days_left"] > 7]
|
||||
if top:
|
||||
lines.append(f"TOP-FILME ({len(top)}):")
|
||||
for f in top[:30]:
|
||||
hl = " *" if f["highlight"] else ""
|
||||
lines.append(
|
||||
f" {f['title'][:45]:45s} | {f['station']:12s} | "
|
||||
f"{f['date']} | {f['days_left']:2d}d | Score {f['score']}{hl}"
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
rest = [f for f in films if f["score"] < 60 and f["days_left"] > 7]
|
||||
if rest:
|
||||
lines.append(f"WEITERE ({len(rest)}):")
|
||||
for f in rest[:20]:
|
||||
lines.append(
|
||||
f" {f['title'][:45]:45s} | {f['station']:12s} | "
|
||||
f"{f['date']} | {f['days_left']:2d}d"
|
||||
)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def handle_get_savetv_status(**kw):
|
||||
archive = _get_archive(state=1, count=5)
|
||||
planned = _get_archive(state=0, count=10)
|
||||
archive = _get_archive(state=1, count=20)
|
||||
planned = _get_archive(state=0, count=20)
|
||||
|
||||
if "error" in archive:
|
||||
return "Save.TV Fehler: " + archive["error"]
|
||||
|
|
@ -352,12 +547,13 @@ def handle_get_savetv_status(**kw):
|
|||
lines = ["Save.TV Status\n"]
|
||||
|
||||
total = int(archive.get("ITOTALENTRIESINARCHIVE", 0))
|
||||
lines.append("Archiv: " + str(total) + " Aufnahmen gesamt")
|
||||
fertig_total = int(archive.get("ITOTALENTRIES", 0))
|
||||
lines.append(f"Archiv: {total} Aufnahmen gesamt, {fertig_total} fertig")
|
||||
|
||||
fertig = archive.get("ARRVIDEOARCHIVEENTRIES", [])
|
||||
if fertig:
|
||||
lines.append("\nLetzte fertige Aufnahmen:")
|
||||
for e in fertig[:5]:
|
||||
for e in fertig[:10]:
|
||||
tc = e.get("STRTELECASTENTRY", {})
|
||||
lines.append(
|
||||
" " + tc.get("STITLE", "?")[:40] + " | "
|
||||
|
|
@ -368,7 +564,7 @@ def handle_get_savetv_status(**kw):
|
|||
geplant = planned.get("ARRVIDEOARCHIVEENTRIES", [])
|
||||
plan_total = int(planned.get("ITOTALENTRIES", 0))
|
||||
if geplant:
|
||||
lines.append("\nGeplante Aufnahmen (" + str(plan_total) + "):")
|
||||
lines.append(f"\nGeplante Aufnahmen ({plan_total}):")
|
||||
for e in geplant[:10]:
|
||||
tc = e.get("STRTELECASTENTRY", {})
|
||||
lines.append(
|
||||
|
|
@ -455,5 +651,6 @@ def handle_savetv_record(telecast_id=0, **kw):
|
|||
HANDLERS = {
|
||||
"get_savetv_status": handle_get_savetv_status,
|
||||
"get_savetv_tipps": handle_get_savetv_tipps,
|
||||
"get_savetv_archive_filme": handle_get_savetv_archive_filme,
|
||||
"savetv_record": handle_savetv_record,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue