Skip to content

mostlyright.weather.earnings.capture.base

Capture-adapter ABC + the transient audio artifact (Phase 27, 27-03).

The provider-aware webcast capture contract. Each adapter

``

detect“s the webcast URLs it handles and

``

capture“s an earnings event into an AudioArtifact — a TRANSIENT temp file that is DELETED after transcription (D-27.9 legal guardrail: captured audio is never stored-and-served, only text / derived facts survive).

The ABC is structured so the live-during-call seam slots in cleanly: 27-09 (Wave 5) adds a live() override for the Amazon-IVS live path (playback.live-video.netaudio_only rendition). This plan is the VOD/replay path only (the static MP4 exists only AFTER the call), so live() is intentionally NOT on the ABC yet — the shape (detect + capture + AudioArtifact) is the seam it will extend.

AudioArtifact(audio_path, ticker, call_id, …)A captured-audio handle whose backing file is TRANSIENT (D-27.9).
CaptureAdapter()Provider-aware webcast capture contract.
LiveCaptureUnsupported([message, …])A capture adapter has NO live-during-call edge (27-09 W5).

class mostlyright.weather.earnings.capture.base.AudioArtifact(audio_path, ticker, call_id, source_media_url, is_transient=True)

Section titled “class mostlyright.weather.earnings.capture.base.AudioArtifact(audio_path, ticker, call_id, source_media_url, is_transient=True)”

Bases: object

A captured-audio handle whose backing file is TRANSIENT (D-27.9).

audio_path points at a temp file extracted from the captured webcast media. is_transient is True by design — the file MUST be deleted after transcription (never persisted or served). cleanup() unlinks it; the artifact is also a context manager, so with adapter.capture(...) as a guarantees the audio is gone on exit even if transcription raises.

source_media_url / call_id / ticker carry the capture lineage for audit (which asset produced these bytes) without retaining the bytes.

  • Parameters:
    • audio_path (str)
    • ticker (str)
    • call_id (str)
    • source_media_url (str)
    • is_transient (bool)

Delete the transient audio file (idempotent — safe to call twice).

FileNotFoundError (the idempotent double-call, or an already-cleaned artifact) is suppressed SILENTLY. Any OTHER OSError (e.g. PermissionError on a read-only mount) is suppressed but LOGGED at error level: it must NOT propagate out of __exit__ (that would MASK the original exception the with block was raising — the transcription error the caller must see), but it means copyrighted earnings audio has SURVIVED on disk (a D-27.9 violation), so it cannot pass silently — the log line names the path an operator must reap manually.

class mostlyright.weather.earnings.capture.base.CaptureAdapter

Section titled “class mostlyright.weather.earnings.capture.base.CaptureAdapter”

Bases: ABC

Provider-aware webcast capture contract.

Concrete adapters (Q4CaptureAdapter, later Notified / GlobalMeet / ChorusCall / self-hosted) implement detect() (does this adapter handle the URL?) and capture() (event → transient AudioArtifact). A live() override is added to the fleet in 27-09 for the live-during-call IVS path; this VOD/replay contract is the seam it extends.

Capture event to a transient AudioArtifact.

MUST stay on the public path (no auth/DRM/captcha circumvention — D-27.9) and MUST return an artifact whose backing file is transient. Raise CaptureNotAvailable when the provider template exposes no cold-fetchable HTTP media (e.g. a dial-in bridge) rather than emitting empty audio.

Return True iff this adapter handles url’s webcast provider.

  • Return type: bool
  • Parameters: url (str)

Follow the provider’s LIVE edge, yielding (pcm_frame, spoken_at) (27-09 W5).

The live-during-call seam. Concrete adapters with a fetchable live HLS edge (Q4LiveCapture = Amazon IVS playback.live-video.net, ChorusCallLiveCapture) OVERRIDE this to open a LiveHlsCapturer against manifest_url and stream PCM frames into the streaming STT (27-10). Audio is a TRANSIENT frame stream — the generator yields decoded PCM into the consumer; it NEVER writes audio to disk or serves it (D-27.9).

The ABC DEFAULT raises LiveCaptureUnsupported: a provider with no live edge (dial-in bridge, SABR/YouTube, Lumi/VOD-only) has no live path, and the runner routes it to the authoritative post-call batch ledger instead (D-27.12). session carries any header-gated context (Origin/ Referer/UA/token) a provider needs; Q4/IVS needs none.

exception mostlyright.weather.earnings.capture.base.LiveCaptureUnsupported(message=”, , error_code=None, source=None, request_id=None)

Section titled “exception mostlyright.weather.earnings.capture.base.LiveCaptureUnsupported(message=”, , error_code=None, source=None, request_id=None)”

Bases: EarningsError

A capture adapter has NO live-during-call edge (27-09 W5).

ENGINE-LOCAL exception: it subclasses the core EarningsError so callers filter it with the rest of the earnings-pipeline errors, but it is deliberately NOT added to packages/core — the live seam is engine-only and must never touch the parity surface (research() / merge/* / live/_sources.py). Raised by CaptureAdapter.live() (the ABC default) for providers whose template has no fetchable live HLS edge — a dial-in bridge (telephone), a SABR/YouTube stream we have not proven, or a Lumi/VOD-only provider. The runner routes such a provider to the post-call batch path only (D-27.12), never opening a live session.

  • Parameters:
    • message (str)
    • error_code (str)
    • source (str | None)
    • request_id (str | None)
  • Return type: None

default_error_code : str = ‘EARNINGS_LIVE_CAPTURE_UNSUPPORTED’

Section titled “default_error_code : str = ‘EARNINGS_LIVE_CAPTURE_UNSUPPORTED’”

Subclass override — the stable string enum surfaced via error_code.