Phase 2: exhaustive Subsonic 1.16.1 compatibility matrix, README/config/Docker updates
This commit is contained in:
+258
-102
@@ -1,125 +1,281 @@
|
||||
# Subsonic / OpenSubsonic compatibility
|
||||
# Subsonic API compatibility
|
||||
|
||||
VibeSonic aims to eventually implement the Subsonic API (`/rest/*`) and
|
||||
OpenSubsonic extensions. **Phase 1 implements none of this** — it only
|
||||
builds the foundation (database schema, stable IDs, library indexing,
|
||||
streaming, cover art) that the protocol layer will sit on top of in a
|
||||
later phase.
|
||||
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.
|
||||
|
||||
This matrix will be filled in as `internal/subsonic` and
|
||||
`internal/opensubsonic` are implemented. For now, every endpoint is
|
||||
"Not yet implemented."
|
||||
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 | Status |
|
||||
|---|---|
|
||||
| `ping` | Not yet implemented |
|
||||
| `getLicense` | Not yet implemented |
|
||||
| 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. |
|
||||
|
||||
## Browsing (filesystem-style)
|
||||
## Music folders
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getMusicFolders` | Not yet implemented | `music_folders` table and `internal/database` queries exist |
|
||||
| `getIndexes` | Not yet implemented | `internal/library.ListDirectory` provides the foundation |
|
||||
| `getMusicDirectory` | Not yet implemented | same |
|
||||
| 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 (ID3/tag-style)
|
||||
## Browsing
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getArtists` | Not yet implemented | `database.ListArtists` exists |
|
||||
| `getArtist` | Not yet implemented | `database.ListAlbumsByArtist` exists |
|
||||
| `getAlbum` | Not yet implemented | `database.ListSongsByAlbum` exists |
|
||||
| `getSong` | Not yet implemented | `database.GetSong` exists |
|
||||
| `getGenres` | Not yet implemented | `database.ListGenres` exists |
|
||||
| `getAlbumList` / `getAlbumList2` | Not yet implemented | |
|
||||
| `getRandomSongs` | Not yet implemented | |
|
||||
| `getSongsByGenre` | Not yet implemented | |
|
||||
| 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 | ✅ | — | ✅ | — | — | |
|
||||
|
||||
## Search
|
||||
|
||||
| Endpoint | Status |
|
||||
|---|---|
|
||||
| `search3` | Not yet implemented |
|
||||
|
||||
## Streaming / media retrieval
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `stream` | Not yet implemented | `internal/streaming` provides range-request-aware serving; the web UI's `/stream/{id}` route is the non-Subsonic equivalent used today |
|
||||
| `download` | Not yet implemented | |
|
||||
| `getCoverArt` | Not yet implemented | `internal/coverart` provides discovery + resize cache; the web UI's `/cover-art/{id}` route is the non-Subsonic equivalent used today |
|
||||
| `getLyrics` / `getLyricsBySongId` | Not yet implemented | `lyrics`/`lyrics_lines`/`lyrics_cues` tables exist |
|
||||
| `getAvatar` | Not yet implemented | |
|
||||
| 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 | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getPlaylists` | Not yet implemented | `database.ListPlaylistsVisibleToUser` exists; web UI has a playlists page |
|
||||
| `getPlaylist` | Not yet implemented | |
|
||||
| `createPlaylist` | Not yet implemented | |
|
||||
| `updatePlaylist` | Not yet implemented | |
|
||||
| `deletePlaylist` | Not yet implemented | |
|
||||
| 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) | |
|
||||
|
||||
## Favorites / ratings / activity
|
||||
## Media retrieval
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `star` / `unstar` | Not yet implemented | `database.Star`/`Unstar` exist; web UI has favorite buttons |
|
||||
| `setRating` | Not yet implemented | `database.SetRating` exists |
|
||||
| `scrobble` | Not yet implemented | `scrobbles` table exists |
|
||||
| `getStarred` / `getStarred2` | Not yet implemented | |
|
||||
| `getNowPlaying` | Not yet implemented | `now_playing` table + `database.ListNowPlaying` exist |
|
||||
|
||||
## Bookmarks / play queue
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getBookmarks` / `createBookmark` / `deleteBookmark` | Not yet implemented | `database` functions exist |
|
||||
| `getPlayQueue` / `savePlayQueue` | Not yet implemented | `database` functions exist |
|
||||
|
||||
## Sharing
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getShares` / `createShare` / `updateShare` / `deleteShare` | Not yet implemented | `shares`/`share_items` tables exist |
|
||||
|
||||
## Podcasts
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getPodcasts` | Not yet implemented | `podcast_channels`/`podcast_episodes` tables exist |
|
||||
| `createPodcastChannel` / `deletePodcastChannel` | Not yet implemented | |
|
||||
| `downloadPodcastEpisode` / `deletePodcastEpisode` | Not yet implemented | |
|
||||
|
||||
## Internet radio
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getInternetRadioStations` | Not yet implemented | `internet_radio_stations` table exists |
|
||||
| `createInternetRadioStation` / `updateInternetRadioStation` / `deleteInternetRadioStation` | Not yet implemented | |
|
||||
|
||||
## Users / chat
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| `getUser` / `getUsers` | Not yet implemented | native web admin has a users page |
|
||||
| `createUser` / `updateUser` / `deleteUser` | Not yet implemented | |
|
||||
| `addChatMessage` / `getChatMessages` | Not yet implemented | `chat_messages` table exists |
|
||||
| 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
|
||||
|
||||
| Endpoint | Status | Notes |
|
||||
|---|---|---|
|
||||
| Transcoding profiles (`internal/transcoding`) | Not yet implemented | `transcode_profiles` table exists; Phase 1 only supports direct (non-transcoded) streaming |
|
||||
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](#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
|
||||
|
||||
Phase 1's `/rest/*` authentication (token-based `u`/`t`/`s` or API key)
|
||||
is not yet implemented. When it is, it will authenticate independently
|
||||
of the browser session/CSRF/external-auth flow used by the web UI, per
|
||||
the project's design constraint that external auth must never interfere
|
||||
with native Subsonic client 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`](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.
|
||||
|
||||
Reference in New Issue
Block a user