This commit is contained in:
team3
2026-09-30 14:31:50 +02:00
parent 624df7e7ee
commit fd7893b5d2
7 changed files with 302 additions and 247 deletions

View File

@@ -28,6 +28,13 @@ CREATE TABLE IF NOT EXISTS clips (
datei TEXT,
created_at TEXT NOT NULL
);
-- Der Film einer Aufnahme, vom Host gesetzt. Ohne Eintrag gilt der Abend
-- mit dem gleichen Datum — oder keiner.
CREATE TABLE IF NOT EXISTS aufnahme_film (
start TEXT PRIMARY KEY,
movie_id INTEGER NOT NULL
);
"""
STREAM_PFAD = "filmabend"
@@ -223,33 +230,9 @@ async def _segmente(von, bis):
return resp.json()
async def tage_mit_aufnahme():
"""An welchen Abenden es Aufnahmen gibt — als Datum, wobei der Abend
bis zum nächsten Mittag reicht. Für die Auswahl im Clips-Reiter."""
segmente = await _segmente(datetime(2000, 1, 1, tzinfo=timezone.utc), _jetzt())
tage = set()
for s in segmente:
start = datetime.fromisoformat(s["start"].replace("Z", "+00:00"))
tage.add((start - timedelta(hours=12)).strftime("%Y-%m-%d"))
return sorted(tage)
async def aufnahmen_des_abends(watched_at):
"""Die Aufnahmen zu einem Abend: alles vom Mittag des Tages bis zum
nächsten Mittag — so gehört auch die Nacht nach Mitternacht dazu.
Wurde der Stream zwischendurch beendet, sind es mehrere Stücke,
nach Beginn geordnet."""
tag = datetime.fromisoformat(watched_at[:10]).replace(tzinfo=timezone.utc)
mittag = tag + timedelta(hours=12)
segmente = await _segmente(mittag, mittag + timedelta(days=1))
stuecke = [
{
"start": _iso(datetime.fromisoformat(s["start"].replace("Z", "+00:00"))),
"dauer": round(s["duration"], 1),
}
for s in segmente
]
return sorted(stuecke, key=lambda s: s["start"])
# ── Aufnahmen: die Stücke aus MediaMTX, jedes mit seinem Film ──────────
# Eine Aufnahme hängt an keinem Abend. Der Host weist ihr einen Film zu;
# ohne Zuweisung gilt der Abend mit dem gleichen Datum, sonst keiner.
def _stueck(s):
@@ -259,10 +242,83 @@ def _stueck(s):
}
def _abendtag(iso):
"""Der Abend reicht bis zum nächsten Mittag — die Nacht gehört dazu."""
zeit = datetime.fromisoformat(iso.replace("Z", "+00:00"))
return (zeit - timedelta(hours=12)).strftime("%Y-%m-%d")
def film_zuweisen(start, movie_id):
"""Der Host hängt einen Film an eine Aufnahme — oder nimmt ihn weg."""
with connect() as conn:
if movie_id is None:
conn.execute("DELETE FROM aufnahme_film WHERE start = ?", (start,))
else:
conn.execute(
"INSERT OR REPLACE INTO aufnahme_film (start, movie_id) VALUES (?, ?)",
(start, movie_id),
)
def _filme(conn):
"""Zuweisungen je Start und Abende je Tag — um Aufnahmen ihren Film zu geben."""
zugewiesen = {
r["start"]: {"id": r["movie_id"], "title": r["title"], "poster_path": r["poster_path"]}
for r in conn.execute(
"SELECT a.start, a.movie_id, m.title, m.poster_path FROM aufnahme_film a "
"LEFT JOIN movies m ON m.id = a.movie_id"
)
}
abende = {}
for w in conn.execute(
"SELECT movie_id, title, poster_path, watched_at FROM watched ORDER BY id"
):
abende.setdefault(
_abendtag(w["watched_at"]),
{"id": w["movie_id"], "title": w["title"], "poster_path": w["poster_path"]},
)
return zugewiesen, abende
def _mit_film(stueck, zugewiesen, abende):
film = zugewiesen.get(stueck["start"])
return {
**stueck,
"film": film or abende.get(_abendtag(stueck["start"])),
"zugewiesen": film is not None,
}
async def aufnahmen_alle():
"""Alle Aufnahmen, neueste zuerst — der Host räumt damit auf."""
"""Alle Aufnahmen, neueste zuerst, jede mit ihrem Film."""
segmente = await _segmente(datetime(2000, 1, 1, tzinfo=timezone.utc), _jetzt())
return sorted((_stueck(s) for s in segmente), key=lambda s: s["start"], reverse=True)
with connect() as conn:
zugewiesen, abende = _filme(conn)
stuecke = [_mit_film(_stueck(s), zugewiesen, abende) for s in segmente]
return sorted(stuecke, key=lambda s: s["start"], reverse=True)
def film_der_aufnahme(start):
"""Nur die id — für neue Marken."""
with connect() as conn:
zugewiesen, abende = _filme(conn)
film = _mit_film({"start": start}, zugewiesen, abende)["film"]
return film["id"] if film else None
async def film_live():
"""Der Film der gerade laufenden Aufnahme — für die Taste C. None,
wenn nichts aufgenommen wird oder MediaMTX nicht antwortet."""
try:
segmente = await _segmente(_jetzt() - timedelta(hours=6), _jetzt())
except httpx.HTTPError:
return None
for s in segmente:
stueck = _stueck(s)
ende = datetime.fromisoformat(stueck["start"]) + timedelta(seconds=stueck["dauer"])
if ende >= _jetzt() - timedelta(seconds=90):
return film_der_aufnahme(stueck["start"])
return None
def _im_stueck(segmente, start, dauer):