Save.TV: Film-Download auf pve-hetzner Festplatte (HD, werbefrei, /mnt/savetv Bind-Mount)
This commit is contained in:
parent
6c7872bb96
commit
ff11575d4c
1 changed files with 105 additions and 1 deletions
|
|
@ -1,15 +1,17 @@
|
||||||
"""Save.TV Online-Videorecorder — EPG Scanner + Film-Tipps + Aufnahme-Steuerung.
|
"""Save.TV Online-Videorecorder — EPG Scanner + Film-Tipps + Aufnahme + Download.
|
||||||
|
|
||||||
Architektur:
|
Architektur:
|
||||||
- EPG-Daten von Save.TV: TvProgrammFilm.cfm (3 Tage) + TvProgrammFilmHighlights.cfm (4 Wochen)
|
- EPG-Daten von Save.TV: TvProgrammFilm.cfm (3 Tage) + TvProgrammFilmHighlights.cfm (4 Wochen)
|
||||||
- Nur TVCATEGORYID 1 (Spielfilm), Spam-Genres rausgefiltert
|
- Nur TVCATEGORYID 1 (Spielfilm), Spam-Genres rausgefiltert
|
||||||
- Seen-Cache: Nur neue Filme werden gemeldet (nicht erneut bei jedem Scan)
|
- Seen-Cache: Nur neue Filme werden gemeldet (nicht erneut bei jedem Scan)
|
||||||
- Aufnahmen per tcJWriteRecord.cfm
|
- Aufnahmen per tcJWriteRecord.cfm
|
||||||
|
- Downloads per croGetDownloadUrl2.cfm -> /mnt/savetv/ (Bind-Mount auf 2.7 TB Platte)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import subprocess
|
||||||
import requests
|
import requests
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -34,6 +36,10 @@ EPG_PAGES = [
|
||||||
SEEN_CACHE = Path("/tmp/savetv_seen_ids.json")
|
SEEN_CACHE = Path("/tmp/savetv_seen_ids.json")
|
||||||
SEEN_MAX_AGE_DAYS = 30
|
SEEN_MAX_AGE_DAYS = 30
|
||||||
|
|
||||||
|
DOWNLOAD_DIR = Path("/mnt/savetv")
|
||||||
|
DOWNLOAD_FORMAT_HD = 6
|
||||||
|
DOWNLOAD_FORMAT_SD = 5
|
||||||
|
|
||||||
AUTO_RECORD_SCORE = 80
|
AUTO_RECORD_SCORE = 80
|
||||||
SUGGEST_SCORE = 60
|
SUGGEST_SCORE = 60
|
||||||
|
|
||||||
|
|
@ -94,18 +100,38 @@ TOOLS = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "savetv_download",
|
||||||
|
"description": "Save.TV Film downloaden auf Hetzner-Festplatte (HD, werbefrei). "
|
||||||
|
"Nutze wenn User sagt 'download', 'runterladen', 'sichern', 'speichern'. "
|
||||||
|
"Filme landen auf pve-hetzner /var/lib/vz/savetv/ (2.7 TB frei).",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"telecast_id": {"type": "number", "description": "TelecastId der Sendung"},
|
||||||
|
"title": {"type": "string", "description": "Filmtitel fuer den Dateinamen"},
|
||||||
|
},
|
||||||
|
"required": ["telecast_id"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
SYSTEM_PROMPT_EXTRA = """TV / Save.TV Tools:
|
SYSTEM_PROMPT_EXTRA = """TV / Save.TV Tools:
|
||||||
- get_savetv_tipps: Zeigt sehenswerte Spielfilme der naechsten Tage/Wochen
|
- get_savetv_tipps: Zeigt sehenswerte Spielfilme der naechsten Tage/Wochen
|
||||||
- get_savetv_archive_filme: Bewertet alle fertigen Aufnahmen im Archiv nach Qualitaet
|
- get_savetv_archive_filme: Bewertet alle fertigen Aufnahmen im Archiv nach Qualitaet
|
||||||
- savetv_record: Nimmt einen Film per TelecastId auf
|
- savetv_record: Nimmt einen Film per TelecastId auf
|
||||||
|
- savetv_download: Laedt fertigen Film auf pve-hetzner Festplatte (HD, werbefrei, 2.7 TB frei)
|
||||||
- get_savetv_status: Zeigt Archiv und geplante Aufnahmen
|
- get_savetv_status: Zeigt Archiv und geplante Aufnahmen
|
||||||
Wenn der User nach Archiv-Filmen/Bewertung fragt, nutze get_savetv_archive_filme.
|
Wenn der User nach Archiv-Filmen/Bewertung fragt, nutze get_savetv_archive_filme.
|
||||||
WICHTIG bei Archiv-Bewertung: Das Tool liefert KINO-HIGHLIGHTS (echte Kinofilme, Klassiker,
|
WICHTIG bei Archiv-Bewertung: Das Tool liefert KINO-HIGHLIGHTS (echte Kinofilme, Klassiker,
|
||||||
preisgekroente Filme) getrennt von deutschem Fernsehprogramm. Praesentiere dem User die
|
preisgekroente Filme) getrennt von deutschem Fernsehprogramm. Praesentiere dem User die
|
||||||
KINO-HIGHLIGHTS zuerst und erklaere kurz warum jeder Film sehenswert ist (Regisseur, Preise,
|
KINO-HIGHLIGHTS zuerst und erklaere kurz warum jeder Film sehenswert ist (Regisseur, Preise,
|
||||||
Stars). Hebe DRINGEND ablaufende Kino-Highlights besonders hervor — die muss er schnell sichern.
|
Stars). Hebe DRINGEND ablaufende Kino-Highlights besonders hervor — die muss er schnell sichern.
|
||||||
|
Download: Wenn User einen Film sichern/downloaden will, nutze savetv_download mit der TelecastId.
|
||||||
|
Dateien landen auf pve-hetzner (/var/lib/vz/savetv/) und koennen spaeter manuell geholt werden.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -364,6 +390,54 @@ def _record_telecast(telecast_id):
|
||||||
return "Fehler: " + str(e)
|
return "Fehler: " + str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_download_url(telecast_id, fmt=DOWNLOAD_FORMAT_HD, adfree=True):
|
||||||
|
"""Holt die temporaere Download-URL fuer eine fertige Aufnahme."""
|
||||||
|
s = _get_session()
|
||||||
|
if not s:
|
||||||
|
return None, "Login fehlgeschlagen"
|
||||||
|
try:
|
||||||
|
r = s.get(
|
||||||
|
SAVETV_URL + "/STV/M/obj/cRecordOrder/croGetDownloadUrl2.cfm",
|
||||||
|
params={
|
||||||
|
"TelecastId": telecast_id,
|
||||||
|
"iFormat": fmt,
|
||||||
|
"bAdFree": str(adfree).lower(),
|
||||||
|
},
|
||||||
|
headers={"X-Requested-With": "XMLHttpRequest"},
|
||||||
|
timeout=15,
|
||||||
|
)
|
||||||
|
data = r.json()
|
||||||
|
if data.get("SUCCESS"):
|
||||||
|
return data["DOWNLOADURL"], None
|
||||||
|
return None, data.get("ERROR", "Unbekannter Fehler")
|
||||||
|
except Exception as e:
|
||||||
|
return None, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def _download_film(telecast_id, title="film"):
|
||||||
|
"""Startet HD-Download im Hintergrund nach /mnt/savetv/."""
|
||||||
|
url, err = _get_download_url(telecast_id)
|
||||||
|
if err:
|
||||||
|
url, err = _get_download_url(telecast_id, fmt=DOWNLOAD_FORMAT_SD)
|
||||||
|
if err:
|
||||||
|
return None, f"Download-URL Fehler: {err}"
|
||||||
|
|
||||||
|
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
safe_title = re.sub(r'[^\w\-.]', '_', title)[:80]
|
||||||
|
filename = f"{safe_title}_{telecast_id}.mp4"
|
||||||
|
target = DOWNLOAD_DIR / filename
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.Popen(
|
||||||
|
["wget", "-q", "-O", str(target), url],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
return filename, None
|
||||||
|
except Exception as e:
|
||||||
|
return None, f"wget Fehler: {e}"
|
||||||
|
|
||||||
|
|
||||||
def _format_film(f, with_tid=True):
|
def _format_film(f, with_tid=True):
|
||||||
"""Formatiert einen Film als Text."""
|
"""Formatiert einen Film als Text."""
|
||||||
title = f.get("STITLE", "?")
|
title = f.get("STITLE", "?")
|
||||||
|
|
@ -669,9 +743,39 @@ def handle_savetv_record(telecast_id=0, **kw):
|
||||||
return "Save.TV: " + result + "\nSendung: " + title
|
return "Save.TV: " + result + "\nSendung: " + title
|
||||||
|
|
||||||
|
|
||||||
|
def handle_savetv_download(telecast_id=0, title="", **kw):
|
||||||
|
"""Film von Save.TV auf pve-hetzner Festplatte downloaden."""
|
||||||
|
if not telecast_id:
|
||||||
|
return "Keine TelecastId angegeben."
|
||||||
|
tid = int(telecast_id)
|
||||||
|
|
||||||
|
if not title:
|
||||||
|
entries = _get_full_archive()
|
||||||
|
for e in entries:
|
||||||
|
tc = e.get("STRTELECASTENTRY", {})
|
||||||
|
if int(tc.get("ITELECASTID", 0)) == tid:
|
||||||
|
title = tc.get("STITLE", f"film_{tid}")
|
||||||
|
break
|
||||||
|
if not title:
|
||||||
|
title = f"film_{tid}"
|
||||||
|
|
||||||
|
filename, err = _download_film(tid, title)
|
||||||
|
if err:
|
||||||
|
return f"Download fehlgeschlagen: {err}"
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"Download gestartet: {title}\n"
|
||||||
|
f"Datei: /var/lib/vz/savetv/{filename}\n"
|
||||||
|
f"Format: H.264 HD (werbefrei)\n"
|
||||||
|
f"Server: pve-hetzner (2.7 TB frei)\n"
|
||||||
|
f"Hinweis: Download laeuft im Hintergrund (1-3 GB, ca. 5-15 Min)."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
HANDLERS = {
|
HANDLERS = {
|
||||||
"get_savetv_status": handle_get_savetv_status,
|
"get_savetv_status": handle_get_savetv_status,
|
||||||
"get_savetv_tipps": handle_get_savetv_tipps,
|
"get_savetv_tipps": handle_get_savetv_tipps,
|
||||||
"get_savetv_archive_filme": handle_get_savetv_archive_filme,
|
"get_savetv_archive_filme": handle_get_savetv_archive_filme,
|
||||||
"savetv_record": handle_savetv_record,
|
"savetv_record": handle_savetv_record,
|
||||||
|
"savetv_download": handle_savetv_download,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue