feat: Backend Abstraction — Emby + experimental Jellyfin (Epic 5, #10)#16
Merged
Conversation
…c 5, #10) Introduces a BackendSpec seam capturing the small Emby↔Jellyfin differences as data (client identity, auth request-header name, session token-header name, static=true requirement, verified-live flag). Emby behavior is byte-identical to what shipped; Jellyfin is added as an honest, explicitly-unverified spec. New hyperwall/backends.py: - EMBY (verified_live=True) and JELLYFIN (verified_live=False) specs. - resolve_backend() defaults to Emby, falls back to Emby on unknown names, and loudly warns when an unverified backend is selected — never silently blessed. - auth_request_headers/token_headers/auth_string build the exact header maps. Wiring (all backward-compatible, Emby default): - emby.EmbyClient takes an optional backend spec; auth POST + per-request token header now derive from it (X-Emby-Authorization/X-Emby-Token for Emby; Authorization/X-Emby-Token for Jellyfin). Emby output verified byte-identical. - urls.build_stream_url gains static=True default; wall._build_url passes client.backend.requires_static_true. Default callers unchanged → still emit the load-bearing static=true. - config: new [Login] backend field (emby|jellyfin), template + round-trip; app.py resolves it and carries it through the post-wizard rewrite. Tests (+18, 80 total green): tests/test_backends.py (13) asserts Emby parity (exact auth strings, static=true), Jellyfin's Authorization header, the verified_live=False guard, unknown→emby fallback, and config round-trip; +2 url static=false branch, wired into run_all.py.⚠️ Jellyfin is NOT yet validated against a live server (verified_live=False by design). Auth + playback must be confirmed on a real Jellyfin instance before it's promoted — per the project's "verify each backend live" guardrail. Refs #10
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.
Implements Epic 5: Backend Abstraction (Emby + Jellyfin) (advances #10).
Why
Emby and Jellyfin share nearly their whole REST surface (Jellyfin is an Emby fork), so the differences this app touches are small: client identity, the auth request-header name, the session token-header name, and the
static=truerequirement. Capturing those as data (BackendSpec) gives one clean seam and roughly doubles the addressable audience — without disturbing the proven Emby path.What changed
New
hyperwall/backends.py—EMBYandJELLYFINspecs +resolve_backend(). Emby isverified_live=True; Jellyfin isverified_live=Falseandresolve_backendwarns loudly when an unverified backend is selected, so it's never silently blessed.Wiring (all backward-compatible, Emby default):
EmbyClienttakes an optional backend spec; the auth POST and per-request token header derive from it (X-Emby-Authorization/X-Emby-Tokenfor Emby;Authorization/X-Emby-Tokenfor Jellyfin).urls.build_stream_urlgains astatic=Truedefault;wall._build_urlpassesclient.backend.requires_static_true. Default callers are unchanged → still emit the load-bearingstatic=true.config.py: new[Login] backendfield (emby|jellyfin) with template + round-trip;app.pyresolves it and carries it through the post-wizard rewrite.Validation (Linux) — 80 tests green
test_backends.py(13) asserts Emby parity byte-for-byte (exactMediaBrowser Client="HyperWall"…auth string,X-Emby-Token,static=true), Jellyfin'sAuthorizationheader, theverified_live=Falseguard, unknown→Emby fallback, and config round-trip. +2 cover the newstatic=falseURL branch.py_compileclean;python tests/run_all.py→ OVERALL: PASS.Per the project's own guardrail — validate each backend against a live server before merge — Jellyfin ships as an honest, explicitly-unverified spec (
verified_live=False, warns on selection, docs say "experimental"). Someone must confirm auth + playback against a real Jellyfin instance before promoting it. I did not set the flag to True from code inspection alone — that would be exactly the "don't assume a fix works" mistake the skill warns against.Milestone: v10 (v10.2) · roadmap #5.