No description
  • QML 53.1%
  • C++ 46%
  • CMake 0.8%
  • C 0.1%
Find a file
khamley 51f640db04
All checks were successful
Build / build (push) Successful in 48s
Remote API: add /api/albums?starred=1 for liked albums
Mirrors the existing /api/tracks?starred=1; backed by getStarred2 and
mapped through albumToJson. Browse-gated (web or api surface).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-27 21:20:36 -07:00
.forgejo/workflows CI: keep built binary as an artifact 2026-08-16 14:27:43 -07:00
qml Fix click-through on the tune/columns popups (modal with dim:false) 2026-10-07 01:01:43 -07:00
src Remote API: add /api/albums?starred=1 for liked albums 2026-10-27 21:20:36 -07:00
.gitignore Navidrome Qt client: full-featured build (search, playlists, queue, lyrics, artist pages, ratings, MPRIS, crossfade) 2026-08-04 09:08:38 -07:00
CMakeLists.txt Advertise the remote-control server over mDNS 2026-10-12 09:58:52 -07:00
README.md Reconcile AppController with new RemoteServer API; library filters, disc sections, playlist images 2026-09-30 13:19:46 -07:00
TODO.md Make context menus and the library combo modal so events don't leak through 2026-10-06 21:56:56 -07:00

Overtone

A native Qt 6 / QML desktop music client for Navidrome, with a modern, Spotify/Feishin-style dark UI and libmpv audio playback.

It speaks the OpenSubsonic API (the documented, stable protocol Navidrome exposes to clients — the same one Feishin uses in "Navidrome mode").

Status

Indev

Build

Requires Qt 6.5+, libmpv, CMake, Ninja, a C++17 compiler.

On Arch Linux:

sudo pacman -S --needed qt6-base qt6-declarative cmake ninja mpv pkgconf

Then:

cmake -B build -G Ninja
cmake --build build
./build/overtone

Architecture

QML UI (qml/)  -->  C++ controllers & models (src/)  -->  OpenSubsonic + libmpv
Layer Files
API src/api/SubsonicClient (QtNetwork), SubsonicAuth (token+salt md5)
Models src/models/{Album,Artist,Track}ListModel (QAbstractListModel)
Playback src/player/MpvController (headless libmpv), PlayerController (queue + transport)
App src/app/AppController (session/login, exposed to QML as the App singleton)
UI qml/ — Theme singleton, views, and components

Playback is audio-only libmpv: no GL render surface is needed. The client hands mpv an authenticated stream URL and observes time-pos / duration / pause / EOF via property observers bridged onto the Qt event loop.

Remote control & scripting API

Overtone can serve two HTTP surfaces on one port (default 8080), each toggled independently. Both are unauthenticated and intended for a trusted LAN.

Surface Command Setting What it is
Web remote :rc on / off Settings → Remote control A self-contained control page to open in a phone browser
Scripting API :api on / off Settings → Scripting API A JSON API for scripts

The server binds while either is on. The web page (/) is served only when the remote is on; the scripting routes respond only when the API is on. The address is shown in Settings (http://<lan-ip>:<port>).

Endpoints

Playback control (available whenever the server is running):

Method / path Effect
GET /api/state Current track + playback state (JSON)
GET /api/cover Current cover art (JPEG)
GET /api/toggle · play · pause · next · previous Transport
GET /api/shuffle · repeat Cycle shuffle / repeat
GET /api/vol?v=0..100 Set volume
GET /api/seek?s=<seconds> Seek

Scripting (require :api on):

Method / path Effect
GET /api/search?q=&type=song|album|artist|any&count=N Search (default type any, count 20)
GET /api/play?id=<songId> Replace queue with the song and play it
GET /api/queue?id=<songId>&mode=next|last Enqueue a song (default last)
GET /api/tracks?album=<id> Album track list
GET /api/tracks?playlist=<id> Playlist track list
GET /api/tracks?genre=<name>&count=N&offset=N Songs in a genre (default count 50)
GET /api/tracks?starred=1 Starred songs
GET /api/genres Genres with song/album counts
GET /api/playlists Playlists with metadata
GET /api/playlist/create?name=&songId=&songId=… Create a playlist
GET /api/playlist/edit?id=&name=&add=<songId>&remove=<index> Rename / add / remove (remove is a zero-based index)

Responses are JSON with an ok boolean; errors carry an error string and a 4xx/5xx status. Example:

# Play the first Pop song found
id=$(curl -s 'http://192.168.1.10:8080/api/tracks?genre=Pop&count=1' | jq -r '.tracks[0].id')
curl -s "http://192.168.1.10:8080/api/play?id=$id"

Notes

  • Credentials are stored via QSettings in plaintext (needed to regenerate the auth token). A future revision should move this to a secret store (libsecret / KWallet).