"""Audio streaming and download endpoints.""" from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import FileResponse from sqlalchemy.orm import Session from uuid import UUID from pathlib import Path from ...models.database import get_db from ...models import crud from ...core.waveform_generator import get_waveform_data from ...utils.logging import get_logger router = APIRouter() logger = get_logger(__name__) @router.get("/stream/{track_id}") async def stream_audio( track_id: UUID, request: Request, db: Session = Depends(get_db), ): """Stream audio file with range request support. Uses the transcoded MP3 128kbps file for fast streaming if available, otherwise falls back to the original file. Args: track_id: Track UUID request: HTTP request db: Database session Returns: Audio file for streaming Raises: HTTPException: 404 if track not found or file doesn't exist """ track = crud.get_track_by_id(db, track_id) if not track: raise HTTPException(status_code=404, detail="Track not found") # Prefer stream_filepath (transcoded MP3) if available if track.stream_filepath and Path(track.stream_filepath).exists(): file_path = Path(track.stream_filepath) media_type = "audio/mpeg" logger.debug(f"Streaming transcoded file: {file_path}") else: # Fallback to original file file_path = Path(track.filepath) if not file_path.exists(): logger.error(f"File not found: {track.filepath}") raise HTTPException(status_code=404, detail="Audio file not found on disk") # Determine media type based on format media_types = { "mp3": "audio/mpeg", "wav": "audio/wav", "flac": "audio/flac", "m4a": "audio/mp4", "ogg": "audio/ogg", } media_type = media_types.get(track.format, "audio/mpeg") logger.debug(f"Streaming original file: {file_path}") return FileResponse( path=str(file_path), media_type=media_type, filename=track.filename, headers={ "Accept-Ranges": "bytes", "Content-Disposition": f'inline; filename="{track.filename}"', }, ) @router.get("/download/{track_id}") async def download_audio( track_id: UUID, db: Session = Depends(get_db), ): """Download audio file. Args: track_id: Track UUID db: Database session Returns: Audio file for download Raises: HTTPException: 404 if track not found or file doesn't exist """ track = crud.get_track_by_id(db, track_id) if not track: raise HTTPException(status_code=404, detail="Track not found") file_path = Path(track.filepath) if not file_path.exists(): logger.error(f"File not found: {track.filepath}") raise HTTPException(status_code=404, detail="Audio file not found on disk") # Determine media type media_types = { "mp3": "audio/mpeg", "wav": "audio/wav", "flac": "audio/flac", "m4a": "audio/mp4", "ogg": "audio/ogg", } media_type = media_types.get(track.format, "audio/mpeg") return FileResponse( path=str(file_path), media_type=media_type, filename=track.filename, headers={ "Content-Disposition": f'attachment; filename="{track.filename}"', }, ) @router.get("/waveform/{track_id}") async def get_waveform( track_id: UUID, num_peaks: int = 800, db: Session = Depends(get_db), ): """Get waveform peak data for visualization. Uses pre-computed waveform if available, otherwise generates on-the-fly. Args: track_id: Track UUID num_peaks: Number of peaks to generate db: Database session Returns: Waveform data with peaks and duration Raises: HTTPException: 404 if track not found or file doesn't exist """ track = crud.get_track_by_id(db, track_id) if not track: raise HTTPException(status_code=404, detail="Track not found") file_path = Path(track.filepath) if not file_path.exists(): logger.error(f"File not found: {track.filepath}") raise HTTPException(status_code=404, detail="Audio file not found on disk") try: # Use pre-computed waveform if available waveform_cache_path = track.waveform_filepath if track.waveform_filepath else None waveform_data = get_waveform_data( str(file_path), num_peaks=num_peaks, waveform_cache_path=waveform_cache_path ) return waveform_data except Exception as e: logger.error(f"Failed to generate waveform for {track_id}: {e}") raise HTTPException(status_code=500, detail="Failed to generate waveform")