Benutzer, Sammlungen, Wünsche und zwei Kartenansichten
Die Runde ist jetzt benannt. Der Host pflegt die Namensliste im neuen Reiter "Verwaltung", alle anderen wählen sich oben rechts einen aus. Der Name landet serverseitig an Vorschlägen und Wertungen, statt aus einem Textfeld zu kommen, in das jeder schreiben konnte, was er wollte. Dazu zwei Sammelstellen, beide nach Person gruppiert und für alle sichtbar, aber nur selbst bearbeitbar: - "Sammlungen": Filme, die man sehen möchte. Getrennt vom Rad-Vorschlag, von dem es weiterhin einen je Person gibt. - "Wünsche": was der Seite noch fehlt. Der Host darf hier alle Einträge ändern und löschen. Die Filmliste hat jetzt zwei Ansichten. Kompakt ist die gewohnte Karte, Details stellt Cover, Text und vier Szenenbilder nebeneinander — eine Karte so breit wie vier kompakte oder die halbe Reihe, je nachdem was zuerst zutrifft. Reihen und Einzelfilme sind dabei exakt gleich hoch; die Blätterleiste liegt dafür über dem Cover statt darunter. Die Filmansicht nutzt die Breite: links Cover, Text und Knöpfe, rechts der Trailer, darunter die Bilder. Ein Klick zeigt ein Bild in voller Größe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013XVnRop5c3or3B8xE2hpgZ
This commit is contained in:
@@ -9,7 +9,18 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from . import db, events, search, streamproxy, suggestions, sync, watched
|
||||
from . import (
|
||||
db,
|
||||
events,
|
||||
features,
|
||||
search,
|
||||
streamproxy,
|
||||
suggestions,
|
||||
sync,
|
||||
users,
|
||||
watched,
|
||||
wishlist,
|
||||
)
|
||||
from .config import IMAGE_BASE, STATIC_DIR
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||
@@ -71,6 +82,9 @@ async def startup():
|
||||
db.init()
|
||||
suggestions.init()
|
||||
watched.init()
|
||||
users.init()
|
||||
wishlist.init()
|
||||
features.init()
|
||||
scheduler.add_job(guarded_sync, "cron", hour=4, minute=30, id="daily-sync")
|
||||
scheduler.start()
|
||||
if not db.get_meta("last_sync"):
|
||||
@@ -227,13 +241,12 @@ def liste_vorschlaege(response: Response, filmabend_sid: str | None = Cookie(Non
|
||||
async def vorschlagen(
|
||||
response: Response,
|
||||
movie_id: int = Body(..., embed=True),
|
||||
name: str | None = Body(None, embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
movie = await film_holen(movie_id)
|
||||
# Ein zweiter Vorschlag ersetzt den ersten, statt abgelehnt zu werden
|
||||
eintrag = suggestions.ersetzen(sid, movie, name)
|
||||
eintrag = suggestions.ersetzen(sid, movie, users.name_von(sid))
|
||||
events.senden("suggestions")
|
||||
return {k: v for k, v in eintrag.items() if k != "session_id"}
|
||||
|
||||
@@ -258,6 +271,110 @@ async def film_holen(movie_id: int):
|
||||
raise HTTPException(status_code=404, detail="Film nicht gefunden")
|
||||
|
||||
|
||||
@app.get("/api/features")
|
||||
def wuensche(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
return features.alle(sid)
|
||||
|
||||
|
||||
@app.post("/api/features")
|
||||
def wunsch_anlegen(
|
||||
response: Response,
|
||||
text: str = Body(..., embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
liste = features.anlegen(sid, text)
|
||||
if liste is None:
|
||||
raise HTTPException(status_code=400, detail="Kein Text")
|
||||
events.senden("features")
|
||||
return liste
|
||||
|
||||
|
||||
@app.patch("/api/features/{feature_id}")
|
||||
def wunsch_aendern(feature_id: int, text: str = Body(..., embed=True)):
|
||||
if not features.aendern(feature_id, text):
|
||||
raise HTTPException(status_code=404, detail="Wunsch nicht gefunden")
|
||||
events.senden("features")
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.delete("/api/features/{feature_id}")
|
||||
def wunsch_loeschen(feature_id: int):
|
||||
entfernt = features.loeschen(feature_id)
|
||||
if entfernt:
|
||||
events.senden("features")
|
||||
return {"removed": entfernt}
|
||||
|
||||
|
||||
@app.get("/api/wishlist")
|
||||
def merkliste(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
"""Was diese Sitzung sich vorgemerkt hat."""
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
return wishlist.alle(sid)
|
||||
|
||||
|
||||
@app.post("/api/wishlist")
|
||||
async def merken(
|
||||
response: Response,
|
||||
movie_id: int = Body(..., embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
return wishlist.merken(sid, await film_holen(movie_id))
|
||||
|
||||
|
||||
@app.delete("/api/wishlist/{movie_id}")
|
||||
def vergessen(
|
||||
movie_id: int,
|
||||
response: Response,
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
return {"removed": wishlist.vergessen(sid, movie_id)}
|
||||
|
||||
|
||||
@app.get("/api/users")
|
||||
def runde(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
return {"items": users.alle(), "mine": users.aktuell(sid)}
|
||||
|
||||
|
||||
@app.post("/api/users")
|
||||
def name_anlegen(name: str = Body(..., embed=True)):
|
||||
"""Der Host erweitert die Runde."""
|
||||
benutzer = users.anlegen(name)
|
||||
if not benutzer:
|
||||
raise HTTPException(status_code=400, detail="Name leer oder schon vergeben")
|
||||
events.senden("users")
|
||||
return benutzer
|
||||
|
||||
|
||||
@app.delete("/api/users/{user_id}")
|
||||
def name_entfernen(user_id: int):
|
||||
if not users.entfernen(user_id):
|
||||
raise HTTPException(status_code=404, detail="Unbekannter Name")
|
||||
events.senden("users")
|
||||
return {"removed": 1}
|
||||
|
||||
|
||||
@app.post("/api/users/waehlen")
|
||||
def name_waehlen(
|
||||
response: Response,
|
||||
user_id: int | None = Body(None, embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
"""Wer bin ich? Ohne user_id wieder anonym."""
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
if user_id is None:
|
||||
users.abwaehlen(sid)
|
||||
return {"mine": None}
|
||||
benutzer = users.waehlen(sid, user_id)
|
||||
if not benutzer:
|
||||
raise HTTPException(status_code=404, detail="Unbekannter Name")
|
||||
return {"mine": benutzer}
|
||||
|
||||
|
||||
@app.get("/api/watched")
|
||||
def liste_gesehen(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
@@ -303,12 +420,12 @@ def bewerten(
|
||||
watched_id: int,
|
||||
response: Response,
|
||||
stars: int = Body(..., embed=True),
|
||||
name: str | None = Body(None, embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
stars = max(1, min(watched.MAX_STERNE, stars))
|
||||
eintrag = watched.bewerten(watched_id, sid, stars, name)
|
||||
# Der Name kommt aus der Runde, nicht aus der Anfrage
|
||||
eintrag = watched.bewerten(watched_id, sid, stars, users.name_von(sid))
|
||||
if not eintrag:
|
||||
raise HTTPException(status_code=404, detail="Film steht nicht in der Liste")
|
||||
events.senden("watched")
|
||||
|
||||
Reference in New Issue
Block a user