# playlists/playlist_db.py import db import json from playlists.playlist_model import Playlist, RuleSet def load_all_playlists(): """Retourne une liste de Playlist (Pydantic) — tolérant aux rules_json nuls / vides.""" with db.get_conn() as conn: # s'assurer que les lignes sont accessibles par nom (si get_conn ne l'a pas fait) try: conn.row_factory = conn.row_factory # no-op si déjà réglé except Exception: pass rows = conn.execute("SELECT * FROM playlists ORDER BY created_at DESC").fetchall() playlists = [] for row in rows: # si row est sqlite3.Row, on peut accéder par nom, sinon c'est un tuple et on mappe par index if hasattr(row, "__getitem__") and isinstance(row, dict) is False and getattr(row, "keys", None): # sqlite3.Row behaves like mapping row_dict = {k: row[k] for k in row.keys()} elif isinstance(row, dict): row_dict = row else: # fallback: convert tuple -> dict using cursor.description # but here we assume conn.row_factory set in db.get_conn; keep robust fallback cols = [d[0] for d in conn.execute("PRAGMA table_info(playlists)").fetchall()] row_dict = dict(zip(cols, row)) try: pl = Playlist( id=row_dict.get("id"), name=row_dict.get("name") or "", description=row_dict.get("description") or "", type=row_dict.get("type") or "manual", rules=row_dict.get("rules_json") ) playlists.append(pl) except Exception as e: # Ne bloque pas toute la lecture : logue et passe à la suivante print(f"⚠️ Ignored invalid playlist row (id={row_dict.get('id')}, name={row_dict.get('name')}): {e}") return playlists def delete_playlist(playlist_id): with db.get_conn() as conn: conn.execute("DELETE FROM playlists WHERE id = ?", (playlist_id,)) conn.commit() def get_videos_for_playlist(playlist: Playlist): """Retourne les vidéos selon le type""" with db.get_conn() as conn: if playlist.type == "manual": q = """ SELECT v.* FROM videos v JOIN video_playlists vp ON vp.video_file_name = v.file_name WHERE vp.playlist_id = ? ORDER BY vp.position """ return conn.execute(q, (playlist.id,)).fetchall() else: rules = playlist.rules clauses = [] params = [] if rules.include_labels: placeholders = ",".join("?" * len(rules.include_labels)) clauses.append(f"v.file_name IN (SELECT vl.video_file_name FROM video_labels vl JOIN labels l ON l.id=vl.label_id WHERE l.name IN ({placeholders}))") params += rules.include_labels if rules.exclude_labels: placeholders = ",".join("?" * len(rules.exclude_labels)) clauses.append(f"v.file_name NOT IN (SELECT vl.video_file_name FROM video_labels vl JOIN labels l ON l.id=vl.label_id WHERE l.name IN ({placeholders}))") params += rules.exclude_labels if rules.include_playlists: placeholders = ",".join("?" * len(rules.include_playlists)) clauses.append(f"v.file_name IN (SELECT vp.video_file_name FROM video_playlists vp JOIN playlists p ON vp.playlist_id=p.id WHERE p.name IN ({placeholders}))") params += rules.include_playlists if rules.exclude_playlists: placeholders = ",".join("?" * len(rules.exclude_playlists)) clauses.append(f"v.file_name NOT IN (SELECT vp.video_file_name FROM video_playlists vp JOIN playlists p ON vp.playlist_id=p.id WHERE p.name IN ({placeholders}))") params += rules.exclude_playlists if rules.date_after: clauses.append("v.record_datetime >= ?") params.append(rules.date_after) if rules.date_before: clauses.append("v.record_datetime <= ?") params.append(rules.date_before) where_clause = f" {' AND ' if rules.logic == 'AND' else ' OR '} ".join(clauses) if clauses else "1=1" q = f"SELECT v.* FROM videos v WHERE {where_clause} ORDER BY v.record_datetime DESC" return conn.execute(q, params).fetchall()