Serve audio as whatever it actually is
Build & Deploy / Build & push images (push) Successful in 4m4s
Build & Deploy / Deploy to VM (push) Successful in 1m53s
Build & Deploy / Report a failed deploy (push) Skipped

audio/mpeg was hardcoded at both points call audio is written and served, from
back when the node produced nothing but 16 kbps MP3. It now uploads FLAC, and a
browser will not play a FLAC body labelled audio/mpeg.

storage.py grows one extension -> Content-Type map, used by the GCS upload and
by /media. Keyed off the object's real extension, so every existing .mp3
recording keeps working with no migration -- and _safe_audio_filename already
accepted .flac, so object naming needed nothing.

Also flags what this costs: /media sends the whole body with Accept-Ranges:
none, which was fine at ~60 KB per call and is not fine at ~1.3 MB/min. Noted
at the header and in DEFERRED.md, whose stated reason for deferring Range
support was the old file size.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-08-23 12:48:47 -04:00
co-authored by Claude Opus 5
parent 457e6d7e0f
commit 1bfa856d1b
2 changed files with 35 additions and 7 deletions
+11 -5
View File
@@ -13,7 +13,7 @@ service-account private key on the VM — is involved anywhere in this path.
"""
from fastapi import APIRouter, HTTPException, Query, Response
from app.internal import firestore as fstore
from app.internal.storage import verify_audio_link, gcs_uri_for_call, download_audio
from app.internal.storage import verify_audio_link, gcs_uri_for_call, download_audio, content_type_for
router = APIRouter(prefix="/media", tags=["media"])
@@ -42,12 +42,18 @@ async def get_call_audio(
return Response(
content=data,
media_type="audio/mpeg",
# Was hardcoded audio/mpeg. The node now uploads FLAC, and a browser
# will not play a FLAC body labelled audio/mpeg. Derived from the stored
# object's extension so old .mp3 recordings keep working unchanged.
media_type=content_type_for(gcs_uri),
headers={
"Content-Length": str(len(data)),
# Recordings are small (16 kbps mono — a 30s call is ~60 KB), so the
# whole body is sent at once and the browser seeks within its own
# buffer. Range support would only matter for long files.
# Whole body at once, no Range support. This was comfortable at
# 16 kbps mono (~60 KB for a 30 s call); FLAC is ~1.3 MB/min, so a
# long call is now tens of MB and the browser must download all of
# it before playback starts. Acceptable for typical few-second
# transmissions, but this is the change that makes Range support
# actually matter — see DEFERRED.md.
"Accept-Ranges": "none",
# Immutable content, but the URL expires — cache privately only.
"Cache-Control": "private, max-age=3600",