Feature: Sélection multiple d'instruments dans les filtres

Frontend:
- FilterPanel: Remplacer select par checkboxes pour instruments
- Zone scrollable (max-height 12rem) pour la liste
- Affichage des instruments sélectionnés dans résumé filtres actifs

Backend:
- API tracks: Nouveau paramètre instruments (List[str])
- Backward compatible avec ancien paramètre instrument
- CRUD: Filtrage AND (track doit avoir TOUS les instruments)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-26 20:10:39 +01:00
parent 34fcbe1223
commit f3f321511d
4 changed files with 55 additions and 24 deletions

View File

@@ -24,6 +24,7 @@ async def get_tracks(
has_vocals: Optional[bool] = None,
key: Optional[str] = None,
instrument: Optional[str] = None,
instruments: Optional[List[str]] = Query(None),
tempo_range: Optional[str] = Query(None, regex="^(slow|medium|fast)$"),
sort_by: str = Query("analyzed_at", regex="^(analyzed_at|tempo_bpm|duration_seconds|filename|energy)$"),
sort_desc: bool = True,
@@ -42,7 +43,8 @@ async def get_tracks(
energy_max: Maximum energy
has_vocals: Filter by vocal presence
key: Filter by musical key
instrument: Filter by instrument
instrument: Filter by instrument (deprecated, use instruments)
instruments: Filter by multiple instruments (must have ALL)
tempo_range: Filter by tempo range (slow: <100, medium: 100-140, fast: >140)
sort_by: Field to sort by
sort_desc: Sort descending
@@ -61,6 +63,9 @@ async def get_tracks(
elif tempo_range == "fast":
bpm_min = 140.0 if bpm_min is None else max(bpm_min, 140.0)
# Use instruments if provided, otherwise fall back to instrument
final_instruments = instruments if instruments else ([instrument] if instrument else None)
tracks, total = crud.get_tracks(
db=db,
skip=skip,
@@ -73,7 +78,7 @@ async def get_tracks(
energy_max=energy_max,
has_vocals=has_vocals,
key=key,
instrument=instrument,
instruments=final_instruments,
sort_by=sort_by,
sort_desc=sort_desc,
)

View File

@@ -104,7 +104,7 @@ def get_tracks(
energy_max: Optional[float] = None,
has_vocals: Optional[bool] = None,
key: Optional[str] = None,
instrument: Optional[str] = None,
instruments: Optional[List[str]] = None,
sort_by: str = "analyzed_at",
sort_desc: bool = True,
) -> Tuple[List[AudioTrack], int]:
@@ -122,7 +122,7 @@ def get_tracks(
energy_max: Maximum energy (0-1)
has_vocals: Filter by vocal presence
key: Filter by musical key
instrument: Filter by instrument
instruments: Filter by instruments (track must have ALL instruments in the list)
sort_by: Field to sort by
sort_desc: Sort descending if True
@@ -168,8 +168,10 @@ def get_tracks(
if key:
query = query.filter(AudioTrack.key == key)
if instrument:
query = query.filter(AudioTrack.instruments.any(instrument))
if instruments:
# Track must have ALL specified instruments
for instrument in instruments:
query = query.filter(AudioTrack.instruments.any(instrument))
# Get total count before pagination
total = query.count()