mostlyright.weather.earnings.capture.q4
mostlyright.weather.earnings.capture.q4
Section titled “mostlyright.weather.earnings.capture.q4”Q4 browser-webcast capture adapter (Phase 27, 27-03; RESEARCH §2).
Q4 (events.q4inc.com / *.q4cdn.com) is the widest large-cap coverage
provider (~50% of the S&P 500). Its browser-webcast template exposes a STATIC
progressive MP4 at
https://static.events.q4inc.com/edited-recordings/{eventId}/{uuid}.mp4 that
answers a plain ranged GET with no cookies, any/no referer → 206 — the guest
form gates the PAGE, not the asset (RESEARCH §2 “Fetch that worked”).
Capture path (public, no circumvention — D-27.9):
: 1. cold ranged-GET the static static.events.q4inc.com/.../*.mp4 directly
with a UA only — NO cookie / token / Authorization header. For the 27-03
VOD/replay path NO guest-page navigation is required: the static replay MP4
is cold-fetchable directly because the guest form gates the PAGE, not the
asset (RESEARCH §2 “Fetch that worked”). A guest-form navigation seam is
kept for later GATED providers (Notified MX-gate, etc.), but the 27-03
static asset does not need it.
2. extract audio to a TRANSIENT temp file and return an
AudioArtifact whose cleanup() deletes it (never persisted).
The analyst template (Q4 dial-in) is a telephone bridge with zero HTTP media —
CaptureNotAvailable is raised so the caller routes to dial-in / skip
(RESEARCH §2 CHWY), never silently producing empty audio.
NOTE: this is the VOD/replay path — the static MP4 exists only AFTER the call.
The live-during-call Q4 edge (Amazon IVS playback.live-video.net) is the
live() seam added in 27-09.
Classes
Section titled “Classes”Q4CaptureAdapter() | Q4 browser-webcast VOD capture over the public guest path (RESEARCH §2). |
|---|
class mostlyright.weather.earnings.capture.q4.Q4CaptureAdapter
Section titled “class mostlyright.weather.earnings.capture.q4.Q4CaptureAdapter”Bases: CaptureAdapter
Q4 browser-webcast VOD capture over the public guest path (RESEARCH §2).
capture(event, , client=None, transport=None, tmp_dir=None, **_)
Section titled “capture(event, , client=None, transport=None, tmp_dir=None, **_)”Cold-fetch the Q4 static MP4 and extract transient audio.
event carries ticker / call_id / media_url (the sniffed
static.events.q4inc.com/.../*.mp4 currentSrc). When media_url is
absent the event is the analyst dial-in template (no HTTP media) —
CaptureNotAvailable is raised.
client may be an injected httpx.Client (a guest-flow session for
later gated providers). transport is the explicit transport the cold
MEDIA fetch rides on — tests pass an httpx.MockTransport directly.
The cold MEDIA fetch is ALWAYS issued through a FRESH UA-only client
built here (from transport when given) — it deliberately does NOT
inherit the injected client’s cookies or default headers, so no
cookie / token / Authorization can ever leak onto the asset GET (the
guest form gates the page, not the asset — RESEARCH §2). When only
client is given (no explicit transport) the media fetch reuses
that client’s transport via its public httpx.Client._transport
seam so the mock host still serves the request, but starts from a clean
header set and empty cookie jar.
media_url is validated (https + Q4 media host) BEFORE any fetch — an
unvalidated cold GET would be an SSRF sink (D-27.9). The media fetch rides
a GENEROUS timeout (not httpx’s flat 5s default, which would abort a full
MP4 stream at the read deadline). A BORROWED transport (from an injected
client or the explicit transport param) is NEVER closed here —
only a transport this method created itself is; closing a borrowed shared
transport would break the caller’s injected client.
A 2xx/206 that carries a ZERO-byte body (an expired / edited replay that
answers the range but serves no media) raises CaptureNotAvailable
instead of writing an empty MP4 and flowing to STT — the byte count is
tracked during the stream loop and an empty result fails loud.
The fetch is also bounded on SIZE and TOTAL TIME (D-27.9 resource-safety):
media_url is external event data and the SSRF allow-list bounds only the
host, not the object size, so an over-_MAX_MEDIA_BYTES asset is
rejected up front from its declared Content-Range/Content-Length and,
as a backstop, aborted mid-stream once the written bytes exceed the cap; a
slow-drip source that never trips the per-op read timeout is aborted at the
_MEDIA_TOTAL_TIMEOUT wall-clock deadline. All abort paths unlink the
partial MP4 via the outer finally — an oversized/slow asset can never
fill the ingest host’s temp disk or flow to STT.
- Return type:
AudioArtifact - Parameters:
detect(url)
Section titled “detect(url)”Return True when url is a Q4 webcast/asset host.