Préférence de contenus par compte : animés / séries / les deux
- Colonne users.content_preference (anime|serie|both, défaut both) avec migration automatique des bases existantes. - GET /auth/me expose la préférence ; PUT /auth/preferences la met à jour (422 si valeur invalide). - /api/search : filtre les résultats par media_type ET ignore les sources hors périmètre (une source anime-only n'est pas interrogée en mode séries). Les films restent associés aux animés. - /api/discover : nouveautés filtrées selon la même règle. - Sélecteur compact dans la topbar (⛩/📺/✨), persisté serveur. - 7 tests dédiés (roundtrip, rejet invalide, filtrage search/discover, skip de sources) — 114 au total, ruff clean. Prête pour la première source de séries : il ne reste qu'un module SourceScraper avec media_types=("serie",) à écrire.
This commit is contained in:
+26
-4
@@ -25,6 +25,10 @@ class User:
|
||||
username: str
|
||||
is_admin: bool
|
||||
is_active: bool
|
||||
content_preference: str = "both" # anime | serie | both
|
||||
|
||||
|
||||
VALID_CONTENT_PREFERENCES = ("anime", "serie", "both")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- mots de passe
|
||||
@@ -56,7 +60,8 @@ async def create_user(username: str, password: str) -> User:
|
||||
|
||||
async def authenticate(username: str, password: str) -> User | None:
|
||||
row = await db.fetchone(
|
||||
"SELECT id, username, password_hash, is_admin, is_active FROM users WHERE username = ?",
|
||||
"SELECT id, username, password_hash, is_admin, is_active, content_preference "
|
||||
"FROM users WHERE username = ?",
|
||||
(username.strip(),),
|
||||
)
|
||||
if row is None or not verify_password(password, row["password_hash"]):
|
||||
@@ -66,18 +71,35 @@ async def authenticate(username: str, password: str) -> User | None:
|
||||
logger.warning("Compte désactivé : %r", username)
|
||||
return None
|
||||
return User(
|
||||
id=row["id"], username=row["username"], is_admin=bool(row["is_admin"]), is_active=True
|
||||
id=row["id"],
|
||||
username=row["username"],
|
||||
is_admin=bool(row["is_admin"]),
|
||||
is_active=True,
|
||||
content_preference=row["content_preference"],
|
||||
)
|
||||
|
||||
|
||||
async def get_user(user_id: int) -> User | None:
|
||||
row = await db.fetchone(
|
||||
"SELECT id, username, is_admin, is_active FROM users WHERE id = ?", (user_id,)
|
||||
"SELECT id, username, is_admin, is_active, content_preference FROM users WHERE id = ?",
|
||||
(user_id,),
|
||||
)
|
||||
if row is None or not row["is_active"]:
|
||||
return None
|
||||
return User(
|
||||
id=row["id"], username=row["username"], is_admin=bool(row["is_admin"]), is_active=True
|
||||
id=row["id"],
|
||||
username=row["username"],
|
||||
is_admin=bool(row["is_admin"]),
|
||||
is_active=True,
|
||||
content_preference=row["content_preference"],
|
||||
)
|
||||
|
||||
|
||||
async def set_content_preference(user_id: int, preference: str) -> None:
|
||||
if preference not in VALID_CONTENT_PREFERENCES:
|
||||
raise ValueError(f"Préférence invalide : {preference!r}")
|
||||
await db.execute(
|
||||
"UPDATE users SET content_preference = ? WHERE id = ?", (preference, user_id)
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user