"""Save.TV Web-UI — Film-Archiv durchsuchen und downloaden. Läuft auf Port 8765 in CT 116. Erreichbar via Tailscale: http://100.123.47.7:8765 """ import os import sys import json import threading from datetime import datetime from pathlib import Path sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, '/opt') from flask import Flask, jsonify, render_template_string, request from tools import savetv app = Flask(__name__) DOWNLOAD_LOG = Path("/mnt/savetv/.download_log.json") def _load_download_log(): if DOWNLOAD_LOG.exists(): try: return json.loads(DOWNLOAD_LOG.read_text()) except Exception: pass return {} def _save_download_log(log): try: DOWNLOAD_LOG.write_text(json.dumps(log, ensure_ascii=False, indent=2)) except Exception: pass HTML = r""" Save.TV Archiv
Lade...
Lade Archiv von Save.TV...
0 ausgewählt
""" @app.route("/") def index(): return render_template_string(HTML) @app.route("/api/films") def api_films(): entries = savetv._get_full_archive() seen_titles = {} series_count = 0 excluded_count = 0 for e in entries: tc = e.get("STRTELECASTENTRY", {}) if tc.get("SFOLGE", ""): series_count += 1 continue title = tc.get("STITLE", "?") if savetv._is_excluded(title): excluded_count += 1 continue station = tc.get("STVSTATIONNAME", "?") days_left = int(tc.get("IDAYSLEFTBEFOREDELETE", 0)) tid = int(tc.get("ITELECASTID", 0)) is_cinema = savetv._is_known_cinema(title) key = title.lower().strip() if key in seen_titles: if days_left > seen_titles[key]["days_left"]: seen_titles[key].update(days_left=days_left, tid=tid) continue seen_titles[key] = { "tid": tid, "title": title, "station": station, "days_left": days_left, "cinema": is_cinema, } films = sorted(seen_titles.values(), key=lambda x: (x["days_left"], not x["cinema"])) dl_log = _load_download_log() return jsonify({ "films": films, "total": len(entries), "kino": sum(1 for f in films if f["cinema"]), "urgent": sum(1 for f in films if f["days_left"] <= 7), "downloads": dl_log, }) @app.route("/api/download", methods=["POST"]) def api_download(): data = request.get_json() tids = data.get("tids", []) dl_log = _load_download_log() results = [] films_by_tid = {} entries = savetv._get_full_archive() for e in entries: tc = e.get("STRTELECASTENTRY", {}) tid = int(tc.get("ITELECASTID", 0)) if tid: films_by_tid[tid] = tc.get("STITLE", f"film_{tid}") def download_one(tid): title = films_by_tid.get(tid, f"film_{tid}") filename, err = savetv._download_film(tid, title) if err: app.logger.error("Download TID %s: %s", tid, err) return {"tid": tid, "ok": False, "error": err} dl_log[str(tid)] = "done" return {"tid": tid, "ok": True, "filename": filename} threads = [] result_list = [None] * len(tids) def worker(i, tid): result_list[i] = download_one(tid) for i, tid in enumerate(tids): t = threading.Thread(target=worker, args=(i, tid)) t.daemon = True t.start() threads.append(t) for t in threads: t.join(timeout=15) results = [r for r in result_list if r] _save_download_log(dl_log) return jsonify({"results": results}) # Extra-Routes (Downloads, Status, Health) - lokal in /opt/savetv_extra_routes.py try: from savetv_extra_routes import register_extra_routes register_extra_routes(app) except ImportError: pass if __name__ == "__main__": app.run(host="0.0.0.0", port=8765, debug=False)