diff --git a/backend/src/api/routes/tracks.py b/backend/src/api/routes/tracks.py index ec516eb..be1e794 100644 --- a/backend/src/api/routes/tracks.py +++ b/backend/src/api/routes/tracks.py @@ -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, ) diff --git a/backend/src/models/crud.py b/backend/src/models/crud.py index 0bc42c5..a381661 100644 --- a/backend/src/models/crud.py +++ b/backend/src/models/crud.py @@ -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() diff --git a/frontend/components/FilterPanel.tsx b/frontend/components/FilterPanel.tsx index 43fc441..72674a4 100644 --- a/frontend/components/FilterPanel.tsx +++ b/frontend/components/FilterPanel.tsx @@ -93,23 +93,40 @@ export default function FilterPanel({ - {/* Instrument Filter */} + {/* Instrument Filter - Multiple Selection */}
Aucun instrument disponible
+ ) : ( + availableInstruments.map((instrument) => { + const isSelected = localFilters.instruments?.includes(instrument) || false + return ( + + ) + }) + )} +