No description
  • Python 61.4%
  • QML 37.8%
  • C 0.7%
  • Shell 0.1%
Find a file
brian f8da239181
Some checks failed
Build Flatpak / flatpak (push) Failing after 6m13s
deploy to OSTree
2026-06-07 03:11:40 -06:00
.forgejo/workflows deploy to OSTree 2026-06-07 03:11:40 -06:00
abs_htpc feat: sync progress immediately on pause/resume 2026-06-07 02:25:19 -06:00
flatpak ci: make Flatpak workflow node-free for Forgejo 2026-06-07 01:26:18 -06:00
tests feat: home page + sidebar navigation (Home/Library/Series/Authors) 2026-06-07 02:08:00 -06:00
.gitignore Initial commit: Audiobookshelf HTPC client 2026-06-07 01:05:12 -06:00
pyproject.toml Initial commit: Audiobookshelf HTPC client 2026-06-07 01:05:12 -06:00
README.md feat: home page + sidebar navigation (Home/Library/Series/Authors) 2026-06-07 02:08:00 -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). QR phone-login also uses the openssl CLI for an ephemeral cert.

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 two options:

  • Log in with your phone (QR)recommended, no typing on the TV. The app shows a QR code; scan it with your phone, accept the one-time security warning (it uses a self-signed certificate generated per session), and sign in on your phone's keyboard. The phone talks to the app over HTTPS on your LAN; the app exchanges your login for a token and stores only the token — your password is never written to disk. (The HTPC must be reachable on its LAN port from the phone; if you run a firewall, allow inbound on the local network.)
  • 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: ephemeral HTTPS server + segno QR)
              + 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)
  __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).