program2 exports the playlists
This commit is contained in:
26
app/cache/video_summary.py
vendored
Normal file
26
app/cache/video_summary.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# cache/video_summary.py
|
||||
import db
|
||||
|
||||
def rebuild_video_summary():
|
||||
with db.get_conn() as conn:
|
||||
conn.execute("DROP TABLE IF EXISTS video_summary")
|
||||
conn.execute("""
|
||||
CREATE TABLE video_summary AS
|
||||
SELECT
|
||||
v.file_name,
|
||||
v.raw_file,
|
||||
v.mp4_file,
|
||||
v.record_datetime,
|
||||
v.day_of_week,
|
||||
v.difficulty_level,
|
||||
v.address,
|
||||
GROUP_CONCAT(DISTINCT l.name) AS labels,
|
||||
GROUP_CONCAT(DISTINCT p.name) AS playlists
|
||||
FROM videos v
|
||||
LEFT JOIN video_labels vl ON vl.video_file_name = v.file_name
|
||||
LEFT JOIN labels l ON l.id = vl.label_id
|
||||
LEFT JOIN video_playlists vp ON vp.video_file_name = v.file_name
|
||||
LEFT JOIN playlists p ON p.id = vp.playlist_id
|
||||
GROUP BY v.file_name
|
||||
""")
|
||||
conn.commit()
|
||||
@@ -41,9 +41,10 @@ def load_all_playlists():
|
||||
print(f"⚠️ Ignored invalid playlist row (id={row_dict.get('id')}, name={row_dict.get('name')}): {e}")
|
||||
return playlists
|
||||
|
||||
def delete_playlist(playlist_id):
|
||||
def delete_playlist(playlist_id: int):
|
||||
with db.get_conn() as conn:
|
||||
conn.execute("DELETE FROM playlists WHERE id = ?", (playlist_id,))
|
||||
conn.execute("DELETE FROM video_playlists WHERE playlist_id = ?", (playlist_id,))
|
||||
conn.commit()
|
||||
|
||||
def get_videos_for_playlist(playlist: Playlist):
|
||||
|
||||
@@ -36,6 +36,8 @@ class Playlist(BaseModel):
|
||||
description: Optional[str] = ""
|
||||
type: Literal["manual", "dynamic"] = "manual"
|
||||
rules: RuleSet = Field(default_factory=RuleSet)
|
||||
created_at: Optional[str] = None
|
||||
updated_at: Optional[str] = None
|
||||
|
||||
@validator("rules", pre=True, always=True)
|
||||
def ensure_rules(cls, v):
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
# playlists/playlist_page.py
|
||||
import streamlit as st
|
||||
from playlists import playlist_db
|
||||
from playlists.playlist_model import Playlist
|
||||
from playlists.playlist_model import Playlist, RuleSet
|
||||
from playlists.playlist_controller import playlist_manual_editor, playlist_dynamic_editor
|
||||
from playlists.playlist_controller import RuleSet
|
||||
from datetime import datetime
|
||||
from cache.video_summary import rebuild_video_summary
|
||||
|
||||
def main():
|
||||
st.title("🎵 Gestion des Playlists")
|
||||
if st.button("🔁 Recalculer le cache vidéo"):
|
||||
rebuild_video_summary()
|
||||
st.success("Cache mis à jour !")
|
||||
|
||||
# --- Barre latérale : recherche & filtres ---
|
||||
st.sidebar.header("🔎 Recherche de playlists")
|
||||
search_term = st.sidebar.text_input("Filtrer par nom ou description")
|
||||
date_filter = st.sidebar.date_input("Créées après", value=None)
|
||||
|
||||
playlists = playlist_db.load_all_playlists()
|
||||
selected = st.selectbox("Playlists existantes", ["(Nouvelle)"] + [p.name for p in playlists])
|
||||
|
||||
# playlists/playlist_page.py (partie création)
|
||||
if selected == "(Nouvelle)":
|
||||
st.subheader("➕ Nouvelle playlist")
|
||||
# Appliquer les filtres
|
||||
filtered = []
|
||||
for p in playlists:
|
||||
if search_term.lower() not in p.name.lower() and search_term.lower() not in (p.description or "").lower():
|
||||
continue
|
||||
if date_filter and datetime.fromisoformat(p.created_at) < datetime.combine(date_filter, datetime.min.time()):
|
||||
continue
|
||||
filtered.append(p)
|
||||
|
||||
# --- Sélection ou création ---
|
||||
names = ["(➕ Nouvelle playlist)"] + [p.name for p in filtered]
|
||||
selected_name = st.selectbox("Sélectionnez une playlist", names, key="playlist_select")
|
||||
|
||||
# --- Création ---
|
||||
if selected_name == "(➕ Nouvelle playlist)":
|
||||
st.subheader("Créer une nouvelle playlist")
|
||||
name = st.text_input("Nom")
|
||||
desc = st.text_area("Description")
|
||||
type_choice = st.radio("Type", ["manual", "dynamic"])
|
||||
@@ -21,15 +41,53 @@ def main():
|
||||
if not name.strip():
|
||||
st.error("Le nom ne peut pas être vide.")
|
||||
else:
|
||||
pl = Playlist(name=name.strip(), description=desc or "", type=type_choice, rules=RuleSet())
|
||||
pl = Playlist(name=name.strip(), description=desc, type=type_choice, rules=RuleSet())
|
||||
pl.save()
|
||||
st.success("Playlist créée ✅")
|
||||
# Recharge immédiate : rerun force tout à re-exécuter
|
||||
st.rerun()
|
||||
st.session_state["playlist_select"] = pl.name # ✅ sélectionne automatiquement
|
||||
if hasattr(st, "rerun"):
|
||||
st.rerun()
|
||||
else:
|
||||
st.experimental_rerun()
|
||||
return
|
||||
|
||||
else:
|
||||
current = next(p for p in playlists if p.name == selected)
|
||||
if current.type == "manual":
|
||||
playlist_manual_editor(current)
|
||||
# --- Mode édition ---
|
||||
current = next((p for p in playlists if p.name == selected_name), None)
|
||||
if not current:
|
||||
st.warning("Aucune playlist sélectionnée.")
|
||||
return
|
||||
|
||||
# --- Barre d’actions ---
|
||||
st.subheader(f"🎞️ Playlist : {current.name}")
|
||||
new_name = st.text_input("Renommer", value=current.name)
|
||||
new_desc = st.text_area("Description", value=current.description or "")
|
||||
if st.button("💾 Sauvegarder les métadonnées"):
|
||||
current.name = new_name
|
||||
current.description = new_desc
|
||||
current.save()
|
||||
st.success("Mise à jour enregistrée ✅")
|
||||
if hasattr(st, "rerun"):
|
||||
st.rerun()
|
||||
else:
|
||||
playlist_dynamic_editor(current)
|
||||
st.experimental_rerun()
|
||||
|
||||
col1, col2, col3 = st.columns([1, 1, 2])
|
||||
with col1:
|
||||
if st.button("🗑️ Supprimer"):
|
||||
playlist_db.delete_playlist(current.id)
|
||||
st.success("Playlist supprimée ✅")
|
||||
if hasattr(st, "rerun"):
|
||||
st.rerun()
|
||||
else:
|
||||
st.experimental_rerun()
|
||||
with col2:
|
||||
if st.button("⏪ Retour à la liste"):
|
||||
st.session_state.pop("playlist_select", None)
|
||||
st.rerun()
|
||||
|
||||
st.divider()
|
||||
|
||||
# --- Éditeur selon le type ---
|
||||
if current.type == "manual":
|
||||
playlist_manual_editor(current)
|
||||
else:
|
||||
playlist_dynamic_editor(current)
|
||||
|
||||
Reference in New Issue
Block a user