Handoff audit: fix upstream-failure 500s + Safari/live bugs, add self-enforcing guardrails - #8
Closed
betmoar wants to merge 2 commits into
Closed
Handoff audit: fix upstream-failure 500s + Safari/live bugs, add self-enforcing guardrails#8betmoar wants to merge 2 commits into
betmoar wants to merge 2 commits into
Conversation
… check, stale hlsUrl Backend: - Map pytubefix exceptions to YouTubeError in the adapter and handle YouTubeError/UpstreamHostError/UpstreamStatusError/httpx.HTTPError globally: dead videos, expired itags, and YouTube outages now return 404/502/503 JSON instead of bare 500s (replaces the dead HumError). - sidx parser: return None (-> 415 -> direct-stream fallback) when the sidx box extends past the fetched head instead of raising struct.error. - Live tail-trim rewriter: re-emit #EXT-X-ENDLIST after the kept segments so players stop polling finished broadcasts. - Enforce the upstream host allowlist on every redirect hop via an httpx request event hook (SSRF hardening). - Compare bearer tokens as bytes so non-ASCII header values 401, not 500. - Evict expired live master-cache entries on write (unbounded growth). - Gate /api/debug/live/* on DEBUG=true (it exposes raw CDN URLs). Frontend: - Strip signed hlsUrl from the persisted queue on flush AND rehydrate; Safari no longer replays an expired HLS URL after reload. - Sequence search requests so a slow early query can't overwrite a newer one. - Catch play() promise rejections (autoplay policy) via safePlay(). - Gate the 20-listener audio event logger behind import.meta.env.DEV. Regression tests for each fix in tests/unit/test_audit_regressions.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAANTDpjvHuz7DXt8M8dhZ
- tests/unit/test_invariants.py: the three architecture invariants (pytubefix only in the adapter, single httpx.AsyncClient, every route bearer-authed or signature-verified, adapter never emits CDN URLs) enforced as tests. - scripts/check.sh: single pre-push gate mirroring CI (backend ruff + mypy --strict + pytest, frontend svelte-check + vitest + build). - Remove stale StreamTube-era scripts (dev-setup.sh installed a nonexistent extra; test.sh duplicated pytest). - Lint tests in CI (ruff check . with test-appropriate ignores); fix the existing violations. - Dockerfile: run as non-root user; pre-create pytubefix's token cache dir so enabling po_token later doesn't crash. - CLAUDE.md, docs/PLAYBOOKS.md, docs/BACKLOG.md: maintainer handoff — mental model, landmines, couplings, step-by-step procedures for the recurring jobs, prioritized backlog + residual risk register. Fix doc drift (repo links, routes.ts name, stale test counts, CI non-goal). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAANTDpjvHuz7DXt8M8dhZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full-codebase audit with remediation and permanent guardrails. Every fix has a regression test; the architecture invariants are now enforced by tests instead of prose.
Bug fixes (each verified with a reproducing test first)
Backend
YouTubeErrorin the adapter and handled globally: dead/private video →404 VIDEO_UNAVAILABLE, anti-bot wall →503 YOUTUBE_BLOCKED, pytubefix breakage →502 UPSTREAM_FAILURE, YouTube unreachable →502 UPSTREAM_UNREACHABLE. Previously/api/video,/proxy/audio|stream, and/api/hlsall returned bare 500s on the most common runtime failures (expired cached itag, removed video). Replaces the deadHumErrorclass.struct.error→ 500. Now returnsNone→ 415 → clean direct-stream fallback.#EXT-X-ENDLIST(it was queued as a per-segment tag with no following segment), so players polled a dead stream forever. It's now re-emitted after the kept segments.follow_redirects=Trueonly allowlist-checked the initial URL; an httpx request event hook now enforces the YouTube host allowlist on every hop.compare_digeston bytes)./api/debug/live/*(leaks raw CDN URLs) now requiresDEBUG=true, not just the bearer.Frontend
hlsUrlsurvived queue persistence whileaudioUrlwas stripped, sopickVodSrc()returned a dead URL and the rehydrate refetch never ran. Now stripped on flush and rehydrate.play()promise rejections caught (safePlay()); 20-listener audio event logger gated behindimport.meta.env.DEV.Guardrails & tooling
tests/unit/test_invariants.py— the three load-bearing rules enforced as tests: pytubefix imported only in the adapter, singlehttpx.AsyncClient, every route bearer-authed or signature-verified (route-table introspection), adapter never emits CDN URLs.tests/unit/test_audit_regressions.py— one test per fix above, each commented with the failure it locks out.scripts/check.sh— single pre-push gate mirroring CI exactly; removed the stale StreamTube-eradev-setup.sh(installed a nonexistent extra) andtest.sh.ruff check ., with test-appropriate ignores; existing violations fixed).po_tokenlater (the usual YouTube-block mitigation) doesn't crash.Handoff docs
CLAUDE.md— mental model, load-bearing map, landmines, change-X-update-Y couplings.docs/PLAYBOOKS.md— step-by-step procedures: "pytubefix broke", adding endpoints, adding signed-URL types, touching the stream cache or Player.docs/BACKLOG.md— prioritized residual risks and debt (top item: CSP to neutralize the localStorage-token XSS exposure).betmoar/streamtube, stale test counts,routes.tsnaming, CI listed as a non-goal despite existing.Verification
./scripts/check.shfully green: ruff clean (app + tests),mypy --strictclean, 157 backend tests (was 143), 148 frontend tests (was 146), svelte-check 0 errors, vite build OK.🤖 Generated with Claude Code
https://claude.ai/code/session_01EAANTDpjvHuz7DXt8M8dhZ
Generated by Claude Code