This commit is contained in:
team3
2026-09-30 01:16:47 +02:00
parent c589e389df
commit 70b37054ba
4 changed files with 146 additions and 31 deletions

View File

@@ -234,16 +234,22 @@ async def tage_mit_aufnahme():
return sorted(tage)
async def aufnahme_des_abends(watched_at):
"""Die Aufnahme zu einem Abend: alles vom Mittag des Tages bis zum
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.
Liefert den Beginn des ersten Stücks, oder None ohne Aufnahme."""
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))
if not segmente:
return None
return min(datetime.fromisoformat(s["start"].replace("Z", "+00:00")) for s in segmente)
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"])
async def aufnahme(um, spanne=SPANNE):

View File

@@ -1,5 +1,5 @@
import asyncio
from datetime import timedelta
from datetime import datetime, timedelta
import logging
import secrets
import time
@@ -332,25 +332,47 @@ async def clip_aufnahmen():
return {"tage": []}
@app.post("/api/clips/von-film")
async def clip_von_film(
response: Response,
movie_id: int = Body(..., embed=True),
filmabend_sid: str | None = Cookie(None),
@app.get("/api/clips/von-film/{movie_id}")
async def clips_stuecke(
movie_id: int, response: Response, filmabend_sid: str | None = Cookie(None)
):
"""Ein Clip zu einem alten Abend: die Marke kommt an den Anfang der
Aufnahme, das Fenster schiebt man dann im Editor hin."""
"""Die Aufnahmen zu einem Abend — mehrere, wenn der Stream neu
gestartet wurde. Daraus wählt man das Stück für den Clip."""
sid = sitzung(response, filmabend_sid)
mitglied(sid)
abend = watched.von_film(movie_id)
if not abend:
raise HTTPException(status_code=404, detail="Dieser Film steht nicht unter Gesehen.")
try:
beginn = await clips.aufnahme_des_abends(abend["watched_at"])
return {"stuecke": await clips.aufnahmen_des_abends(abend["watched_at"])}
except httpx.HTTPError:
beginn = None
if beginn is None:
raise HTTPException(status_code=404, detail="Zu diesem Abend gibt es keine Aufnahme.")
return {"stuecke": []}
@app.post("/api/clips/von-film")
async def clip_von_film(
response: Response,
movie_id: int = Body(..., embed=True),
start: str | None = Body(None, embed=True),
filmabend_sid: str | None = Cookie(None),
):
"""Ein Clip zu einem alten Abend: die Marke kommt an den Anfang des
gewählten Stücks — ohne Wahl an den des ersten. Das Fenster schiebt
man dann im Editor hin."""
sid = sitzung(response, filmabend_sid)
mitglied(sid)
abend = watched.von_film(movie_id)
if not abend:
raise HTTPException(status_code=404, detail="Dieser Film steht nicht unter Gesehen.")
if start is None:
try:
stuecke = await clips.aufnahmen_des_abends(abend["watched_at"])
except httpx.HTTPError:
stuecke = []
if not stuecke:
raise HTTPException(status_code=404, detail="Zu diesem Abend gibt es keine Aufnahme.")
start = stuecke[0]["start"]
beginn = datetime.fromisoformat(start)
clip = clips.markieren(sid, movie_id, beginn + timedelta(seconds=clips.VORHER + 30))
events.senden("clips")
return clip

View File

@@ -60,23 +60,52 @@ def test_clip_zu_altem_abend(client, film, monkeypatch):
# Keine Aufnahme an dem Tag
async def keine(watched_at):
return None
return []
monkeypatch.setattr(clips, "aufnahme_des_abends", keine)
monkeypatch.setattr(clips, "aufnahmen_des_abends", keine)
assert client.get(f"/api/clips/von-film/{film(4)}").json()["stuecke"] == []
antwort = client.post("/api/clips/von-film", json={"movie_id": film(4)})
assert antwort.status_code == 404 and "Aufnahme" in antwort.json()["detail"]
# Mit Aufnahme: die Marke liegt kurz nach ihrem Beginn
beginn = datetime(2026, 9, 23, 19, 0, tzinfo=timezone.utc)
# Zwei Stücke, weil der Stream neu gestartet wurde
stuecke = [
{"start": "2026-09-23T19:00:00+00:00", "dauer": 1800.0},
{"start": "2026-09-23T20:00:00+00:00", "dauer": 600.0},
]
async def da(watched_at):
return beginn
return stuecke
monkeypatch.setattr(clips, "aufnahme_des_abends", da)
monkeypatch.setattr(clips, "aufnahmen_des_abends", da)
assert client.get(f"/api/clips/von-film/{film(4)}").json()["stuecke"] == stuecke
# Ohne Wahl: die Marke liegt kurz nach dem Beginn des ersten Stücks
m = client.post("/api/clips/von-film", json={"movie_id": film(4)}).json()
assert m["movie_id"] == film(4)
assert m["marke_at"] == "2026-09-23T19:01:00+00:00"
# Mit Wahl: nach dem Beginn des gewählten Stücks
m = client.post(
"/api/clips/von-film", json={"movie_id": film(4), "start": stuecke[1]["start"]}
).json()
assert m["marke_at"] == "2026-09-23T20:01:00+00:00"
def test_stuecke_eines_abends_nach_beginn_geordnet(monkeypatch):
import asyncio
async def segmente(von, bis):
return [
{"start": "2026-09-23T21:00:00Z", "duration": 600.0},
{"start": "2026-09-23T19:00:00Z", "duration": 1800.04},
]
monkeypatch.setattr(clips, "_segmente", segmente)
assert asyncio.run(clips.aufnahmen_des_abends("2026-09-23T20:00:00+00:00")) == [
{"start": "2026-09-23T19:00:00+00:00", "dauer": 1800.0},
{"start": "2026-09-23T21:00:00+00:00", "dauer": 600.0},
]
def test_fremde_sehen_nichts_der_host_alles(client, client2, zuschauer, hostmodus):
m = marke(client)