playlist edition
This commit is contained in:
0
app/views/__init__.py
Normal file
0
app/views/__init__.py
Normal file
107
app/views/label_views.py
Normal file
107
app/views/label_views.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import streamlit as st
|
||||
import db
|
||||
from models import Video
|
||||
from views.video_views import show_video_row
|
||||
from controllers.label_controller import label_widget
|
||||
|
||||
def video_filter_sidebar():
|
||||
"""Affiche les filtres dans la barre latérale et renvoie les paramètres de recherche."""
|
||||
st.sidebar.header("⚙️ Filtres et affichage")
|
||||
max_height = st.sidebar.slider("Hauteur max (px)", 100, 800, 300, 50)
|
||||
|
||||
all_labels = db.load_labels()
|
||||
unique_days = db.get_unique_days()
|
||||
unique_difficulties = db.get_unique_difficulties()
|
||||
unique_addresses = db.get_unique_addresses()
|
||||
|
||||
selected_labels = st.sidebar.multiselect("Filtrer par labels", all_labels)
|
||||
label_logic = st.sidebar.radio(
|
||||
"Logique entre labels",
|
||||
["OR", "AND"],
|
||||
help="Détermine si la vidéo doit contenir tous les labels sélectionnés (AND) ou au moins un (OR)"
|
||||
)
|
||||
day_filter = st.sidebar.selectbox("Jour de la semaine", ["Tous"] + unique_days)
|
||||
difficulty_filter = st.sidebar.selectbox("Niveau de difficulté", ["Tous"] + unique_difficulties)
|
||||
address_keyword = st.sidebar.selectbox("Adresse (mot-clé)", [""] + unique_addresses)
|
||||
start_date = st.sidebar.date_input("Date de début", value=None)
|
||||
end_date = st.sidebar.date_input("Date de fin", value=None)
|
||||
show_unlabeled_only = st.sidebar.checkbox("Afficher uniquement les vidéos sans labels")
|
||||
|
||||
return dict(
|
||||
max_height=max_height,
|
||||
selected_labels=selected_labels,
|
||||
label_logic=label_logic,
|
||||
day_filter=None if day_filter == "Tous" else day_filter,
|
||||
difficulty_filter=difficulty_filter,
|
||||
address_keyword=address_keyword if address_keyword else None,
|
||||
start_date=start_date.isoformat() if start_date else None,
|
||||
end_date=end_date.isoformat() if end_date else None,
|
||||
show_unlabeled_only=show_unlabeled_only
|
||||
)
|
||||
|
||||
def video_list_view(filters: dict, editable_labels=True, editable_difficulty=True):
|
||||
"""Affiche les vidéos selon les filtres fournis."""
|
||||
st.markdown(f"""
|
||||
<style>
|
||||
img, video {{
|
||||
max-height: {filters["max_height"]}px !important;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}}
|
||||
.unlabeled {{
|
||||
border: 3px solid #f39c12;
|
||||
box-shadow: 0 0 10px #f39c12;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
df_videos = db.search_videos(
|
||||
label_names=filters["selected_labels"],
|
||||
label_logic=filters["label_logic"],
|
||||
day_of_week=filters["day_filter"],
|
||||
address_keyword=filters["address_keyword"],
|
||||
start_date=filters["start_date"],
|
||||
end_date=filters["end_date"],
|
||||
difficulty=filters["difficulty_filter"]
|
||||
)
|
||||
|
||||
if df_videos.empty:
|
||||
st.warning("Aucune vidéo trouvée avec ces critères.")
|
||||
return []
|
||||
|
||||
# préchargement des labels
|
||||
video_labels_map = {row["file_name"]: db.load_video_labels(row["file_name"]) for _, row in df_videos.iterrows()}
|
||||
if filters["show_unlabeled_only"]:
|
||||
df_videos = df_videos[df_videos["file_name"].apply(lambda fn: not video_labels_map.get(fn))]
|
||||
|
||||
videos = [Video(**row) for _, row in df_videos.iterrows()]
|
||||
|
||||
# lazy loading
|
||||
page_size = 20
|
||||
st.session_state.setdefault("video_page", 1)
|
||||
start = 0
|
||||
end = st.session_state.video_page * page_size
|
||||
subset = videos[start:end]
|
||||
|
||||
# affichage
|
||||
for video in subset:
|
||||
preselected = video_labels_map.get(video.file_name, [])
|
||||
css_class = "unlabeled" if not preselected else ""
|
||||
with st.container():
|
||||
st.markdown(f"<div class='{css_class}'>", unsafe_allow_html=True)
|
||||
show_video_row(video, preselected, editable_labels, editable_difficulty)
|
||||
st.markdown("</div>", unsafe_allow_html=True)
|
||||
|
||||
# lazy loading bouton
|
||||
if end < len(videos):
|
||||
if st.button("📦 Charger plus de vidéos"):
|
||||
st.session_state.video_page += 1
|
||||
st.rerun()
|
||||
else:
|
||||
st.info("✅ Toutes les vidéos sont affichées.")
|
||||
|
||||
return subset
|
||||
32
app/views/video_views.py
Normal file
32
app/views/video_views.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import streamlit as st
|
||||
from controllers.label_controller import label_widget
|
||||
import db
|
||||
|
||||
def show_video_row(video, preselected_labels, editable_labels=True, editable_difficulty=True):
|
||||
col1, col2, col3 = st.columns([1, 3, 2])
|
||||
with col1:
|
||||
if video.thumbnail_file:
|
||||
st.image(video.thumbnail_file)
|
||||
st.caption(video.mp4_file_name)
|
||||
|
||||
with col2:
|
||||
st.markdown(f"**📅 {video.record_datetime or ''}** — {video.day_of_week or ''}")
|
||||
st.text(f"🏷️ Labels: {', '.join(preselected_labels) or 'Aucun'}")
|
||||
playlists = db.get_video_playlists(video.file_name)
|
||||
if playlists:
|
||||
st.text(f"🎵 Playlists: {', '.join(playlists)}")
|
||||
|
||||
with col3:
|
||||
if editable_labels:
|
||||
label_widget(video, preselected=preselected_labels)
|
||||
if editable_difficulty:
|
||||
levels = ["Tout niveau", "Débutant", "Intermédiaire", "Avancé", "Star"]
|
||||
new_level = st.selectbox(
|
||||
"🎚 Niveau",
|
||||
levels,
|
||||
index=levels.index(video.difficulty_display),
|
||||
key=f"diff_{video.file_name}"
|
||||
)
|
||||
if new_level != video.difficulty_display:
|
||||
db.update_video_difficulty(video.file_name, new_level)
|
||||
st.success(f"Niveau mis à jour pour {video.file_name}")
|
||||
Reference in New Issue
Block a user