Skip to content

feat: Backend Abstraction — Emby + experimental Jellyfin (Epic 5, #10)#16

Merged
tcconnally merged 1 commit into
mainfrom
feat/epic5-backend-abstraction
Jul 2, 2026
Merged

feat: Backend Abstraction — Emby + experimental Jellyfin (Epic 5, #10)#16
tcconnally merged 1 commit into
mainfrom
feat/epic5-backend-abstraction

Conversation

@tcconnally

Copy link
Copy Markdown
Owner

Implements Epic 5: Backend Abstraction (Emby + Jellyfin) (advances #10).

Stacked on #15 (Epic 4) → #14#13#12. Base branch is feat/epic4-multi-source, so this diff shows only Epic 5. Merge order: #12#13#14#15 → this.

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=true requirement. 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.pyEMBY and JELLYFIN specs + resolve_backend(). Emby is verified_live=True; Jellyfin is verified_live=False and resolve_backend warns loudly when an unverified backend is selected, so it's never silently blessed.

Wiring (all backward-compatible, Emby default):

  • EmbyClient takes an optional backend spec; the auth POST and per-request token header derive from it (X-Emby-Authorization/X-Emby-Token for Emby; Authorization/X-Emby-Token for Jellyfin).
  • urls.build_stream_url gains a static=True default; wall._build_url passes client.backend.requires_static_true. Default callers are unchanged → still emit the load-bearing static=true.
  • config.py: new [Login] backend field (emby|jellyfin) with template + round-trip; app.py resolves it and carries it through the post-wizard rewrite.

Validation (Linux) — 80 tests green

  • +18 tests. test_backends.py (13) asserts Emby parity byte-for-byte (exact MediaBrowser Client="HyperWall"… auth string, X-Emby-Token, static=true), Jellyfin's Authorization header, the verified_live=False guard, unknown→Emby fallback, and config round-trip. +2 cover the new static=false URL branch.
  • Emby auth header string confirmed identical to the pre-abstraction code.
  • py_compile clean; python tests/run_all.pyOVERALL: PASS.

⚠️ Jellyfin is deliberately NOT blessed yet

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.

…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
@tcconnally tcconnally added this to the v10 milestone Jul 2, 2026
@tcconnally tcconnally changed the base branch from feat/epic4-multi-source to main July 2, 2026 14:23
@tcconnally tcconnally merged commit 96ad385 into main Jul 2, 2026
1 check passed
@tcconnally tcconnally deleted the feat/epic5-backend-abstraction branch July 2, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants