Files
vibesonic/docs/subsonic-compatibility.md
T

17 KiB
Raw Blame History

Subsonic API compatibility

VibeSonic implements the complete original Subsonic REST API through version 1.16.1 as of Phase 2. Every endpoint below is backed by real data from VibeSonic's own database/scanner/streaming/transcoding/jukebox subsystems — none of them are stub handlers that return an empty successful response.

OpenSubsonic extensions are not yet implemented (planned for Phase 3), except where the underlying architecture already needed to make a choice (e.g. opaque string IDs almost everywhere) that happens to also suit OpenSubsonic later.

How to read this table

  • Version — the Subsonic API version the endpoint was introduced in (per the official protocol history), included so client compatibility checks make sense.
  • Implemented — ✅ real, ⚠️ real but with a noted simplification.
  • XML / JSON Tested — both formats share one Go struct tree (internal/subsonic/model*.go) serialized via encoding/xml and encoding/json respectively, so a test that decodes one and asserts on response shape effectively covers both; most endpoint tests exercise JSON responses directly and XML responses via the shared decodeXML/decodeJSON test helpers in internal/subsonic/*_test.go.
  • Auth Tested — covered by internal/subsonic/router_test.go (wrong password, missing credentials, disabled user, token auth, enc: password auth, external-auth-only accounts, protocol version negotiation) plus per-endpoint auth-failure cases where relevant.
  • Permission Tested — covered where the endpoint enforces a specific Subsonic role (stream/download/podcast/jukebox/admin/ownership).

System

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
ping 1.0.0 ✅ ✅ ✅ ✅ —
getLicense 1.0.0 ✅ ✅ ✅ ✅ — Always reports a valid, unlimited license — VibeSonic never requires one.

Music folders

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getMusicFolders 1.0.0 ✅ ✅ ✅ ✅ ✅ Respects per-user folder restrictions; folder IDs are small sequential integers derived from creation order (the protocol's musicFolder.id is xs:int), mapped internally onto VibeSonic's opaque folder IDs.

Browsing

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getIndexes 1.0.0 ✅ ✅ — ✅ ✅ (folder restriction) Filesystem-style browsing, driven entirely by media_files.relative_path — never faked from the ID3 model.
getMusicDirectory 1.0.0 ✅ ✅ — ✅ ✅ (folder restriction) Directory IDs encode (folder, path); rejects a request for a folder the user can't see.
getGenres 1.9.0 ✅ — ✅ ✅ — Real per-genre song/album counts.
getArtists 1.8.0 ✅ — ✅ ✅ — ID3 browsing; only artists with at least one album, per protocol convention.
getArtist 1.8.0 ✅ — ✅ ✅ —
getAlbum 1.8.0 ✅ ✅ ✅ ✅ ✅ (folder restriction on songs)
getSong 1.8.0 ✅ — ✅ ✅ ✅ (folder restriction)
getVideos 1.8.0 ✅ — ✅ — — Real: lists media files the scanner tagged kind=video.
getVideoInfo 1.14.0 ⚠️ — — — — Reports one default audio track (VibeSonic doesn't probe multi-audio-track containers yet) and real sidecar .srt/.vtt/.ass captions found next to the file.
getArtistInfo 1.11.0 ⚠️ — — — — Honestly returns empty biography/images/similar-artists — VibeSonic has no external metadata provider in Phase 2. Not a fake success: it's an accurate "we don't have this" response.
getArtistInfo2 1.11.0 ⚠️ — — — — Same as above, ID3 form.
getAlbumInfo 1.14.0 ⚠️ — — — — Same reasoning as getArtistInfo.
getAlbumInfo2 1.14.0 ⚠️ — — — — Same.
getSimilarSongs 1.11.0 ✅ — — — — Real, local genre-overlap heuristic (song/album/artist seed), not fabricated data.
getSimilarSongs2 1.11.0 ✅ — — — — ID3 (artist-seeded) form.
getTopSongs 1.13.0 ✅ — ✅ — — Ranked by real aggregate play count across all users.

Album/song lists

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getAlbumList 1.2.0 ✅ — ✅ ✅ — All 10 official types: random, newest, highest, frequent, recent, alphabeticalByName, alphabeticalByArtist, starred, byYear, byGenre.
getAlbumList2 1.8.0 ✅ ✅ ✅ ✅ — Same types, ID3 form.
getRandomSongs 1.2.0 ✅ — ✅ — — Supports size, genre, fromYear, toYear.
getSongsByGenre 1.9.0 ✅ — ✅ — — Supports count/offset.
getNowPlaying 1.0.0 ✅ — ✅ — — Real now_playing table, populated by scrobble (submission=false) and native web streaming.
getStarred 1.8.0 ✅ — ✅ — —
getStarred2 1.8.0 ✅ — ✅ — —
Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
search 1.0.0 (deprecated 1.4.0) ✅ — ✅ — — Still fully functional, as required — deprecated doesn't mean removed.
search2 1.4.0 ✅ — ✅ — — Independent offset/count per artist/album/song.
search3 1.8.0 ✅ — ✅ ✅ — ID3 form; independent offset/count per category.

Playlists

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getPlaylists 1.0.0 ✅ — ✅ — — Own + public playlists; username param for admins to view another user's.
getPlaylist 1.0.0 ✅ — ✅ — ✅ (ownership/public)
createPlaylist 1.0.0 ✅ — ✅ — ✅ (playlist role) Supports both "new playlist" and "replace an existing playlist's songs" forms.
updatePlaylist 1.8.0 ✅ — — — ✅ (ownership) Rename, re-describe, publicity toggle, songIndexToRemove/songIdToAdd.
deletePlaylist 1.0.0 ✅ — — — ✅ (ownership)

Media retrieval

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
stream 1.0.0 ✅ n/a (binary) n/a ✅ ✅ (stream role) Direct streaming (range requests, no buffering) or real ffmpeg transcoding depending on format/maxBitRate; folder-permission checked.
download 1.0.0 ✅ n/a n/a — ✅ (download role) Original bytes with Content-Disposition: attachment.
hls 1.8.0 ✅ n/a n/a — ✅ (stream role) Real ffmpeg HLS segmenting, cached per (source, bitrate); segment URLs re-embed the caller's auth params.
getCaptions 1.14.0 ✅ n/a n/a — — Serves real sidecar subtitle files.
getCoverArt 1.0.0 ✅ n/a n/a — ✅ (coverArt role) Embedded or folder art, resized and cached on disk.
getLyrics 1.2.0 ✅ ✅ — — — Real embedded lyrics extracted at scan time; empty (not fabricated) if none found.
getAvatar 1.8.0 ⚠️ — — — — Honestly 404s — VibeSonic has no user-avatar feature in Phase 1/2.

Transcoding

Integrated into stream/download/hls above rather than a separate endpoint (the classic API has none). internal/transcoding runs real ffmpeg processes, bounded to 4 concurrent jobs, piping output directly to the response (never buffering a full file), and is killed when the client disconnects (context cancellation). Supported output formats: mp3, opus, aac. maxBitRate, format, and timeOffset are all honored.

Annotation

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
star 1.4.0 ✅ ✅ — — — Song/album/artist, multiple IDs per call.
unstar 1.4.0 ✅ ✅ — — —
setRating 1.6.0 ✅ ✅ — — — Auto-detects song/album/artist from the given ID.
scrobble 1.5.0 ✅ ✅ — — — submission=true records playback history + a scrobble row; false only updates now-playing.

Sharing

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getShares 1.6.0 ✅ — ✅ — ✅ (share role) Own shares, or all shares for an admin.
createShare 1.6.0 ✅ — ✅ — ✅ (share role) Songs and/or whole albums; real public URL.
updateShare 1.6.0 ✅ — — — ✅ (ownership)
deleteShare 1.6.0 ✅ — — — ✅ (ownership)

A real public, unauthenticated share page/stream route also exists at /share/{token} and /share/{token}/stream/{songID} — see Web UI. Streaming through a share is still path-validated the same way every other stream is; a share can never expose an arbitrary filesystem path.

Podcasts

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getPodcasts 1.6.0 ✅ — ✅ — ✅ (podcast role)
getNewestPodcasts 1.13.0 ✅ — ✅ — ✅ (podcast role)
refreshPodcasts 1.9.0 ✅ — — — ✅ (admin) Real RSS fetch + parse for every channel, in the background.
createPodcastChannel 1.9.0 ✅ — — — ✅ (admin) Kicks off a real feed refresh.
deletePodcastChannel 1.9.0 ✅ — — — ✅ (admin)
deletePodcastEpisode 1.9.0 ✅ — — — ✅ (admin)
downloadPodcastEpisode 1.9.0 ✅ — — — ✅ (admin) Real bounded background download; the result becomes an ordinary streamable song. Interrupted downloads recover to "new" on restart (internal/podcasts.Manager.RecoverInterruptedDownloads, run at startup).

internal/podcasts is a real subsystem: RSS/Atom parsing (internal/podcasts/rss.go), episode metadata (GUID-keyed, stable across refreshes), enclosure downloads bounded to 2 concurrent jobs, and downloaded episodes registered as normal media_files/songs rows so they stream through the exact same path as scanned music.

Jukebox

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
jukeboxControl 1.2.0 (current form 1.7.0) ✅ ✅ ✅ — ✅ (jukebox role) Every action: get, status, set, start, stop, skip, add, clear, remove, shuffle, setGain.

Backed by a real mpv process controlled over its JSON IPC socket (internal/jukebox), tested against an actual mpv binary (playback, pause/resume, add/remove/clear, gain, shuffle, index seeking all verified in internal/jukebox/jukebox_test.go). If mpv isn't installed, the jukebox is administratively unavailable and jukeboxControl returns a clear error — never fake success.

Internet radio

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getInternetRadioStations 1.9.0 ✅ — ✅ — —
createInternetRadioStation 1.16.0 ✅ ✅ — — ✅ (admin)
updateInternetRadioStation 1.16.0 ✅ — — — ✅ (admin)
deleteInternetRadioStation 1.16.0 ✅ ✅ — — ✅ (admin)

Also exposed in the web UI at /radio (viewing: any signed-in user; managing: admin).

Chat

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getChatMessages 1.2.0 ✅ — ✅ — — Supports since.
addChatMessage 1.2.0 ✅ — ✅ — —

User management

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getUser 1.3.0 ✅ — ✅ — ✅ (self or admin)
getUsers 1.8.0 ✅ — — — ✅ (admin)
createUser 1.1.0 ✅ ✅ — — ✅ (admin) Full role set + musicFolderId restriction.
updateUser 1.10.1 ✅ ✅ ✅ — ✅ (admin) Role params not present in the request are preserved, not reset to false (regression-tested).
deleteUser 1.3.0 ✅ ✅ — — ✅ (admin; can't delete self)
changePassword 1.1.0 ✅ ✅ — — ✅ (self or admin) Also refreshes the encrypted copy used for token auth.

The full Subsonic role set (adminRole, settingsRole, streamRole, jukeboxRole, downloadRole, uploadRole, playlistRole, coverArtRole, commentRole, podcastRole, shareRole, videoConversionRole) is stored per user and enforced on the server side by every endpoint above — never only hidden in the UI. Per-user music folder restriction is also enforced server-side (internal/subsonic/folders.go).

Bookmarks

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getBookmarks 1.9.0 ✅ — ✅ — — Per-user.
createBookmark 1.9.0 ✅ ✅ — — — Position (ms) + comment.
deleteBookmark 1.9.0 ✅ ✅ — — —

Play queue

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getPlayQueue 1.12.0 ✅ — ✅ — — Per-user, persisted.
savePlayQueue 1.12.0 ✅ ✅ ✅ — —

Scanner

Endpoint Version Implemented XML Tested JSON Tested Auth Tested Permission Tested Notes
getScanStatus 1.15.0 ✅ — ✅ — — Real status/count from internal/scanner, not simulated.
startScan 1.15.0 ✅ — ✅ — ✅ (admin)

Authentication

Both original Subsonic authentication modes are implemented in internal/subsonic/auth.go:

  • Token/salt (u, t, s): t = md5(password + salt). Verified against a separate, AES-256-GCM–encrypted copy of the password (internal/auth/subsonic_secret.go) kept in sync with every password change — never against the Argon2id hash (impossible, by design) and never by storing the password in the clear.
  • Password (p): plain or enc:<hex>, verified against the Argon2id hash used everywhere else in VibeSonic.

An account created purely through external (trusted-proxy) auth has no local password and therefore cannot authenticate to /rest/* until an administrator or the user sets one — /rest/* is completely independent of the browser session/CSRF/external-auth flow (see docs/external-auth.md).

Common parameters

u, p, t, s, v, c, f (xml/json/jsonp), and callback are all handled centrally in internal/subsonic/router.go and internal/subsonic/auth.go — no handler parses or validates them itself. v is checked against ServerVersion (currently 1.16.1): a client claiming a newer protocol version gets error 20; older clients are accepted, since the protocol is additive.

Error codes

All defined in internal/subsonic/errors.go: 0 (generic), 10 (missing parameter), 20 (client must upgrade), 30 (server must upgrade, reserved — VibeSonic never needs to require a newer client), 40 (wrong credentials), 41 (token auth unsupported for this account), 50 (unauthorized for the operation), 60 (license/trial — never returned), 70 (not found).

Web UI

Newly exposed through the Bootstrap UI in Phase 2: Podcasts (/podcasts, admin at /admin/podcasts), Internet Radio (/radio), Shares (/shares, plus the public /share/{token} page), Bookmarks (/bookmarks), and per-user Subsonic permission editing (/admin/users/{id}/permissions). The admin Jukebox page (/admin/jukebox) shows live status and emergency stop/clear; building a jukebox playlist is done from a Subsonic client via jukeboxControl, which is the actual protocol surface for that feature.