fix(audio): serve real Content-Type so imports decode in WaveSurfer

/audio/{id} and /audio/version/{id} hardcoded media_type="audio/wav" on
the FileResponse. That was a no-op when every generation came out of
TTS (everything on disk was a .wav anyway), but imported audio keeps
its source format — .mp3 / .m4a / .ogg — and the WaveSurfer MediaElement
backend uses an <audio> tag that checks Content-Type before letting the
clip play, so an MP3 announced as audio/wav silently failed to load.

Both endpoints now derive the type via mimetypes.guess_type and fall
back to audio/wav for unknown suffixes. Download filenames also keep
the real extension instead of always saying ".wav".
This commit is contained in:
Jamie Pine
2026-04-25 02:00:54 -07:00
parent c43f2d45cc
commit 3d4d0a9335
+17 -4
View File
@@ -1,5 +1,8 @@
"""Audio file serving endpoints."""
import mimetypes
from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
@@ -11,6 +14,16 @@ from ..database import get_db
router = APIRouter()
def _audio_media_type(path: Path) -> str:
"""Derive the Content-Type from the file extension.
Imported audio retains its source format (.mp3, .m4a, .ogg, …) so a
blanket ``audio/wav`` would mislead strict clients trying to decode
via the response header instead of sniffing the bytes."""
guessed, _ = mimetypes.guess_type(path.name)
return guessed or "audio/wav"
@router.get("/audio/version/{version_id}")
async def get_version_audio(version_id: str, db: Session = Depends(get_db)):
"""Serve audio for a specific version."""
@@ -26,8 +39,8 @@ async def get_version_audio(version_id: str, db: Session = Depends(get_db)):
return FileResponse(
audio_path,
media_type="audio/wav",
filename=f"generation_{version.generation_id}_{version.label}.wav",
media_type=_audio_media_type(audio_path),
filename=f"generation_{version.generation_id}_{version.label}{audio_path.suffix}",
)
@@ -44,8 +57,8 @@ async def get_audio(generation_id: str, db: Session = Depends(get_db)):
return FileResponse(
audio_path,
media_type="audio/wav",
filename=f"generation_{generation_id}.wav",
media_type=_audio_media_type(audio_path),
filename=f"generation_{generation_id}{audio_path.suffix}",
)