WordloopWordloop
WorkMeeting RecordingTechnical Design DocMilestones10 Audio Playback And Review

Core — Audio URL

GET /meetings/:id/audio-url returning a time-limited signed URL, with mime_type and duration_ms in the response, and the recoverable-error contract for expired or forged links.

Core — Audio URL

Owner: Core Engineer Domain: core Complexity: S Prerequisite: Milestone 05 merged (composed audio.webm exists)

When this slice is complete, GET /meetings/{id}/audio-url returns a time-limited playback link — never a raw storage path — with {url, mime_type, duration_ms, expires_at}, bounded by RECORDING_AUDIO_URL_TTL_SECONDS (900s default). The link serves the composed audio without a bearer token. A forged, tampered, replayed, or otherwise-scoped signature is refused 403; an expired link answers 410 with problem type audio_url_expired; a meeting with no recorded audio answers 404; and requesting a fresh link at any later time recovers playback.

Required Capabilities

  • GET /meetings/{id}/audio-url returns {url, mime_type, duration_ms, expires_at}.
  • url is never a raw storage object path (no bucket-relative path, no bare .webm/.wav suffix exposed).
  • expires_at is no further out than RECORDING_AUDIO_URL_TTL_SECONDS (900s) from issuance.
  • The link serves the composed audio bytes with the declared mime_type, without requiring a bearer token — a media element can consume it directly.
  • A missing, tampered, or forged signature on the link is refused 403.
  • A signature replayed with a different expiry, or under a different user id, is refused 403 — the link is scoped to its exact signed parameters and to the issuing user.
  • Once past expires_at, the link answers 410 with problem type audio_url_expired.
  • GET /meetings/{id}/audio-url for a meeting with no recorded audio answers 404.
  • Requesting a fresh link (GET /audio-url again) always recovers playback, regardless of any earlier link's state.

Dependencies

  • Milestone 05 (live-audio-durability-recovery) must be merged — the composed audio.webm object this endpoint links to.

Test Cases

Test cases map to tests/bets/meeting-recording/test_milestone_10_audio_playback_and_review.py. Run via ./dev test bet meeting-recording.

TestLocationAssertion
test_finalized_meeting_review_shows_expected_tabstest_milestone_10A finalized meeting exposes every surface the review tabs read: completed transcription, non-empty synthesis, a tasks list, and GET /audio-url returning 200.
test_audio_playback_uses_signed_urltest_milestone_10GET /audio-url returns all four required fields; the URL is never a raw storage path; it carries a valid signature (or is an HTTPS GCS-signed URL); mime_type starts with audio/; duration_ms > 0; expires_at is in the future and bounded by 900s; fetching the URL with no bearer token returns the audio bytes with matching Content-Type.
test_user_can_play_pause_and_seek_audiotest_milestone_10The composed audio object is fully fetchable (Content-Length matches the byte count), non-empty, and duration_ms is consistent with the captured audio — sufficient for player seek support.
test_expired_signed_url_has_recoverable_errortest_milestone_10A missing signature, a tampered signature, a signature replayed with a different exp, and a signature reused under a different uid are all refused 403; a genuinely expired link answers 410 with type: "audio_url_expired"; a meeting with no recorded audio answers 404; a freshly requested link plays again.

Completion Checklist

  • Code merged and deployed
  • Bet progress tests pass (./dev test bet meeting-recording)
  • Permanent service tests implemented per testing strategy
  • Code review completed
  • API review completed (GET /meetings/{id}/audio-url signed-link contract)
  • Testing review completed
  • System documentation updated (as applicable)

On this page