- Python 58.8%
- QML 40.4%
- C 0.6%
- Shell 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Build Flatpak / flatpak (push) Successful in 9m3s
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> |
||
| .forgejo/workflows | ||
| abs_htpc | ||
| flatpak | ||
| tests | ||
| .gitignore | ||
| pyproject.toml | ||
| README.md | ||
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. Archmpv, Debian/Ubuntulibmpv2/libmpv-devlibSDL2— e.g. Archsdl2, Debian/Ubuntulibsdl2-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_portinconfig.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://oauthredirect that Audiobookshelf allows by default. Your provider must allow ABS'shttps://<abs>/auth/openid/mobile-redirectredirect 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.comautomatically getshttps://).
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).