No description
  • Python 58.8%
  • QML 40.4%
  • C 0.6%
  • Shell 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Brian 2193a8e830
All checks were successful
Build Flatpak / flatpak (push) Successful in 9m3s
Release 0.2.0
On-TV SSO sign-in and a reachable phone login (fixed pairing port) are new
behaviour, so this is a minor bump rather than 0.1.3. The metainfo gains a
release entry; it had nothing after 0.1.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-22 22:41:10 -06:00
.forgejo/workflows Install the runtime the manifest asks for 2026-08-22 11:01:32 -06:00
abs_htpc Release 0.2.0 2026-09-22 22:41:10 -06:00
flatpak Release 0.2.0 2026-09-22 22:41:10 -06:00
tests Make phone login reachable and add SSO sign-in on the TV 2026-09-22 22:13:49 -06:00
.gitignore Initial commit: Audiobookshelf HTPC client 2026-06-07 01:05:12 -06:00
pyproject.toml Release 0.2.0 2026-09-22 22:41:10 -06:00
README.md Make phone login reachable and add SSO sign-in on the TV 2026-09-22 22:13:49 -06:00

abs-htpc — Audiobookshelf client for Home Theater PCs

A 10-foot, couch-friendly client for an Audiobookshelf server. Built for Linux HTPCs and designed to be driven entirely by a remote control or game controller — no keyboard or mouse required (though both work).

  • Fullscreen, large-type UI with bold focus rings for D-pad navigation
  • On-screen keyboard for server/login entry (remote-only friendly)
  • Left-sidebar navigation: Home, Library, Series, Authors
  • Home shelves from the server's personalized API: Continue Listening (in-progress), Next in Series (server-computed next book after you finish one), and Recently Added
  • Cover-art grids, Series (books in order) and Authors browsing, book detail with chapters, now-playing transport
  • Audio playback via mpv (libmpv), with chapter jumps and ±30s skip
  • Listening progress synced back to the server (resume where you left off)
  • Game controller support via SDL2; media keys (Play/Pause/Next/Prev/Stop)

Requirements

System packages (install via your distro):

  • libmpv (mpv shared library) — e.g. Arch mpv, Debian/Ubuntu libmpv2/libmpv-dev
  • libSDL2 — e.g. Arch sdl2, Debian/Ubuntu libsdl2-2.0-0
  • Python ≥ 3.10

Python dependencies (installed below): PySide6, python-mpv, httpx, qasync, PySDL2, segno (QR codes). PySide6 includes QtWebEngine, which the on-TV SSO sign-in uses; if it can't load, that option is simply hidden.

Install

python -m venv .venv
.venv/bin/pip install -e .

Run

.venv/bin/python -m abs_htpc      # or: .venv/bin/abs-htpc

On first launch you'll get the Connect screen with three options:

  • Log in with your phone (QR): recommended, no typing on the TV. The app shows a QR code; scan it with your phone and sign in on your phone's keyboard, with a password or with SSO. The phone talks to the app over plain HTTP on your LAN, on a fixed port, 8645 (pairing_port in config.json); the app exchanges your login for a token and stores only the token. Your password is never written to disk.

    Firewall: many distros (CachyOS with ufw, for example) drop inbound connections by default, and the phone's page then never loads. Allow the port once: sudo ufw allow 8645/tcp (or, with firewalld, sudo firewall-cmd --permanent --add-port=8645/tcp && sudo firewall-cmd --reload). The QR screen shows this hint if no phone connects within 20 seconds.

    SSO from the phone needs one redirect URI allowed in Audiobookshelf (Settings → Authentication → Allowed Mobile Redirect URIs): http://<htpc-ip>:8645/oidc/callback. If it's missing, the phone page tells you the exact value to add.

  • Sign in with SSO on the TV (e.g. Login with Authentik): shown when the server has OpenID enabled; the label is the server's own button text. The provider's login page opens in a built-in browser: ▲▼ move between fields and buttons, OK presses a button or opens the on-screen keyboard for a text field, Back cancels. No firewall or server changes are needed: it uses the audiobookshelf://oauth redirect that Audiobookshelf allows by default. Your provider must allow ABS's https://<abs>/auth/openid/mobile-redirect redirect URI, which the ABS docs already require for the mobile apps. Each sign-in uses a fresh private browser profile, so the next person isn't signed in as you.

  • Enter details manually — the on-screen keyboard for server URL / username / password (a bare host like abs.example.com automatically gets https://).

Either way, the saved token means subsequent launches skip straight to the profile picker.

Multiple users / profiles

The app keeps a list of accounts, each a real Audiobookshelf login with its own library and listening progress. On launch you get a "Who's listening?" picker:

  • Choose a profile to sign in instantly (its token is stored on this device).
  • + Add user opens the login screen (QR or keyboard) for another account.
  • Delete on a focused profile removes it.
  • From the library, press Back and choose Switch user to return to the picker (or Exit to quit to the desktop).

Notes on Audiobookshelf's design: there's no "impersonate" API, so each person signs in once (their token is then remembered). Listing all server users isn't possible from a regular account — it requires admin — so profiles are the ones you've added here, not an automatic roster of everyone on the server.

Controls

Action Remote / Keyboard Game controller
Move focus Arrow keys D-pad / left stick
Select / OK Enter A
Back Esc B
Play / Pause Space or media key X
Skip −30s / +30s (player) ←/→ on transport LB / RB
Previous / Next chapter media Prev/Next Left / Right trigger
Scrub focus the seek bar, then ←/→ focus seek bar, then ←/→

In the player, Esc/B returns to the previous screen while playback continues; use the ⏹ Stop button to end the session. From the library, Esc/B opens an Exit to desktop? prompt (with Switch user when you have profiles).

Configuration

Config (including the auth token) is stored at:

$XDG_CONFIG_HOME/abs-htpc/config.json   # default: ~/.config/abs-htpc/config.json

The token is written with 0600 permissions but is not encrypted (see Notes). Delete this file to reset / log out.

Development

.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest                 # unit tests (endpoints, book timeline)
QT_QPA_PLATFORM=offscreen .venv/bin/python tests/smoke_qml.py   # QML load check
QT_QPA_PLATFORM=xcb .venv/bin/python tests/capture.py out.png   # render a screenshot

Architecture

abs_htpc/
  api/        endpoints.py (URL builders) + client.py (async httpx AbsClient)
  config.py   Account + AccountStore: multi-account config (servers, tokens, active)
  backend/    Qt controllers: auth (login + profiles), library, detail,
              player (mpv), input (SDL2),
              pairing (QR phone-login: LAN HTTP server + segno QR),
              sso (on-TV SSO via QtWebEngine), oidc (shared ABS OIDC handshake)
              + timeline.py (pure book-time <-> track mapping, unit-tested)
  qml/        Theme singleton; Main = left-sidebar shell over a content StackView;
              components (Sidebar, FocusCard, …); views (Home, Library, Series,
              Authors, BookDetail, Player, ProfilePicker, ServerSetup, QrLogin,
              SsoLogin)
  __main__.py QApplication + qasync loop, wiring, media-key filter

The networking is async (httpx on the qasync loop). mpv runs its tracks as a playlist; timeline.py presents the multi-file book as one continuous timeline. Controller input is translated by an SDL2 thread into synthetic arrow-key events plus transport signals, so the same QML focus handling serves remote and pad.

Notes / roadmap (v1 scope)

  • Auth token is stored in plaintext; OS keyring integration is a future option.
  • Podcasts: the data layer understands episodes, but a dedicated podcast browse UI is not yet built.
  • Out of scope for v1: offline downloads, multi-user switching, casting, LIRC/IR remotes (most IR remotes already present as keyboard input via the OS).