v13.2.0-beta.1
Pre-releaseAirsonic-Pulse v13.2.0-beta.1
About this beta
Second public preview of 13.2.0, following the alpha. This milestone brings OpenSubsonic API support and API key authentication to Airsonic-Pulse; the alpha delivered the core surface, and this beta closes the tag-format gaps the alpha shipped with. The OpenSubsonic metadata surface is now substantially complete.
This is a preview — not for production use. Please exercise it against real clients and report anything that doesn't behave as documented.
The notes below are cumulative against the last final release (v13.1.0): Upgrade notes and Summary of changes cover everything in 13.2.0 so far, so you don't need to read the alpha notes to understand this build. New in this beta lists only what changed since the alpha.
Upgrade notes
Cumulative since v13.1.0. Everything operationally relevant to upgrading onto this build, regardless of which preview you came from.
Library rescan runs automatically on first start
13.2.0 adds a range of new metadata columns, populated by a one-time rescan triggered by a MediaFile.VERSION bump (#135). The rescan runs automatically on first start and backfills every new column below in a single pass — there is no separate rescan per feature, and no manual action is required.
- Sort names (#135/#136/#137/#138) —
sortNameon track, album, and artist, plusmusicBrainzIdon artist, from the corresponding sort and MusicBrainz tags. - BPM (#139) — integer
bpmon tracks. - ReplayGain (#143/#250) —
rg_track_gain,rg_album_gain,rg_track_peak,rg_album_peakfrom each track's ID3v2 TXXX / Vorbis tags, plusrg_base_gainfrom the OpusOpusHeadheader on Opus files. - Multi-genre (#134) — a packed
genrescolumn from allGENREtag values (split by the existingGenreSeparatorssetting, default;). The singlegenrecolumn and genre browsing are unchanged in shape. - Album metadata (#142) —
is_compilation,original_release_date,release_date,release_types,record_labels, and per-disc subtitles, carried onmedia_fileand aggregated ontoalbum. - Contributors (#144/#231/#232) — a packed
contributorscolumn covering composer, lyricist, conductor, arranger, producer, engineer, mixer, remixer, djmixer, orchestra, choir, ensemble, and performer-with-instrument credits, read from ID3v2.4 TMCL, ID3v2.3 IPLS, VorbisPERFORMER, and MP4 freeform performer atoms. - Play queue index (#141) — a
current_indexcolumn onplay_queuefor theindexBasedQueueextension.
CUE indexed tracks re-key once (#211)
If you have CUE-sheet libraries: existing CUE indexed tracks were stored with start positions truncated to whole seconds. The fix carries the correct sub-second (frame) offsets, so the rescan re-materialises each CUE track once, replacing the old whole-second rows. playCount, lastPlayed, and comments are preserved across the refresh. One-time and automatic.
Legacy authentication is deprecated (#145, removal #56)
Legacy u/p (username + password) and t/s (MD5-salted token) Subsonic authentication continue to work unchanged in 13.2.x. They are deprecated in favor of API key authentication (see Summary below). Removal is targeted for 13.3.x under #56 — movable based on client-ecosystem readiness.
The server now emits a single throttled WARN to the operator log the first time each (user, client) pair authenticates via a legacy method within a 24-hour window, naming the method and pointing at API keys as the replacement. The log line contains no password, token, salt, IP, or User-Agent. Successful API-key requests never trigger it.
Summary of changes since v13.1.0
Cumulative — the full set of what 13.2.0 delivers so far.
API key authentication (#145)
The OpenSubsonic apiKeyAuthentication extension is implemented end-to-end. Each user manages their own keys under Personal Settings → API Keys (generate, disable, revoke). A key is shown exactly once at creation; only its HMAC-SHA-256 hash (peppered with a server-side secret) is stored. Clients authenticate with Authorization: Bearer <key> or an apiKey= query parameter; the legacy parameters keep working.
This is the underlying primitive, not an automatic security upgrade. Switching a client to API keys is a per-client opt-in. The benefit is that long-lived passwords and the deprecated salted-token derivative stop being part of every API call, and a leaked key can be revoked from the UI without a password change.
Per-account legacy-auth migration path (#233, #278)
Each account carries a password_auth_enabled flag, toggleable from the credentials settings page (self-service) and the admin user settings page. A user who has moved a client to API keys can disable legacy password/token auth for their own account; an admin can manage it for others. The auth gate is REST-only, so disabling it breaks legacy Subsonic clients for that account but leaves web-UI login working. This is the per-account runway toward the 13.3.x legacy-auth removal (#56).
OpenSubsonic metadata surface
13.2.0 expands OpenSubsonic coverage across track-, album-, and artist-level response fields:
- Track (
Child):sortName,bpm,replayGain(track/album gain and peak, plus OpusbaseGainfrom theOpusHeadheader),genres[](multi-genre), andcontributors[]— composer, lyricist, conductor, arranger, producer, engineer, mixer, remixer, djmixer, orchestra, choir, ensemble, and performer-with-instrument credits across ID3v2.4 TMCL, ID3v2.3 IPLS, VorbisPERFORMER, and MP4 freeform atoms. - Album (
AlbumID3):sortName,isCompilation,originalReleaseDate,releaseDate,releaseTypes[],recordLabels[],discTitles[], and album-levelreplayGain. - Artist (
ArtistID3):sortName,musicBrainzId, anduserRating/averageRating. songLyricsextension: unsynced lyrics (tag and.lrcsidecar), plus synced/LRC lyrics with timestamps stored in the database, and embedded-tag lyrics reading.indexBasedQueueextension (v1):getPlayQueueByIndex/savePlayQueueByIndex, disambiguating the current track in queues that contain the same track more than once.- Genre browsing now sees secondary-frame genres, not just the primary genre — albums and songs with multiple genre frames are no longer under-counted or missed.
The advertised OpenSubsonic extension set is formPost, transcodeOffset, songLyrics, indexBasedQueue, apiKeyAuthentication, and getPodcastEpisode. Two higher-effort Phase 3 extensions — transcoding negotiation and playback reporting — are deferred to a later release.
Reliability and hardening
- Playlist watcher pool exhaustion fixed (#215/#242). A large watcher-triggered re-sync could drain the DB connection pool via unbounded virtual-thread fan-out and block other requests. Fixed, with event debounce added — large-library syncs no longer need to avoid the watcher path.
- CUE indexed-track correctness (#211). Multifile CUE sheets now map each track to its correct file, and frame offsets that were truncated to whole seconds now carry their sub-second component (every indexed track was starting up to ~1 second early).
- Search endpoint hardening (#262).
countandoffseton the Subsonic search endpoints are clamped, closing a path where an adversarial largecountcould drive a LuceneTopDocsallocation toward out-of-memory. - apiKey operator audit log (#237). Authentication failures on the apiKey path emit an operator-forensics log line keyed on the key id, never the raw key.
Performance
- Contributor artist resolution cached (#228).
artistService.getArtist(name)— called per-album, per-item, and per-contributor in the response-construction loops — is now cache-backed (10000 entries, 24h TTL), caching both hits and misses. Most contributor names miss (composers and producers usually aren't catalogued artists), so caching misses is what makes the win meaningful. Correctly invalidated around library rescans.
New in this beta
Changes since the alpha. See the Summary above for full detail on each.
- Synced lyrics (#140) and embedded-tag lyrics reading (#158) — the alpha shipped
songLyricswith unsynced lyrics only. - Contributor extraction completed — ID3v2.3 IPLS (#231) and MP4 freeform performer atoms (#232), on top of the alpha's TMCL and Vorbis paths.
- ReplayGain coverage completed — MP4 atoms and album-level ReplayGain (#206), and Opus
baseGainfromOpusHead(#250), closing the #205 umbrella. - Multi-frame genre browsing (#213/#255/#256) — genre browse and
getSongsByGenrenow see secondary-frame genres. - Artist ratings on
ArtistID3(#203). - Per-account legacy-auth toggle + settings UI (#233/#278).
- Search endpoint clamp (#262) and apiKey audit log (#237).
- CUE multifile + frame-offset fix (#211).
- Playlist watcher pool-exhaustion fix (#215/#242) — this was the alpha's headline known issue; it is now fixed.
- getPodcastEpisode advertised (#294) — the endpoint shipped earlier but wasn't discoverable via
getOpenSubsonicExtensions.
Known issues
- #239 — no per-user limit on API key generation. A user can generate unlimited keys; revoking one doesn't lock out anyone who has minted others. A cap and a generation rate-limit are tracked for a later release. Be sensible with key generation.
- #281 — lyrics cache has no staleness invalidation. Once lyrics for a track are cached (from an
.lrcsidecar, an embedded tag, or the synced store), re-editing the source does not refresh the cached copy until the cache entry is otherwise cleared. Tracked for a fix.
Reporting issues
Please file issues at https://github.com/Airsonic-Pulse/airsonic-pulse/issues. Useful context to include:
- A short description of what you were doing and what went wrong.
- Relevant server log output, with
p=,t=,s=,Authorization: Bearer, andapiKey=values redacted. - Client name and version.
- The Subsonic / OpenSubsonic endpoint(s) involved if known.
What's Changed
Features & Enhancements
- #135 [OpenSubsonic] Child.sortName — track sort title by @litebito in #197
- #136 [OpenSubsonic] AlbumID3.sortName — album sort name by @litebito in #198
- #137 [OpenSubsonic] ArtistID3.musicBrainzId — artist MB ID by @litebito in #199
- #139 [OpenSubsonic] Child.bpm — beats per minute by @litebito in #200
- #179 [OpenSubsonic] Add userRating/averageRating to JaxbContentService.createJaxbArtist(MediaFile) by @litebito in #201
- #143 [OpenSubsonic] Child.replayGain — ReplayGain metadata extraction by @litebito in #204
- #134 [OpenSubsonic] Multi-genre support — Child.genres[] by @litebito in #212
- #141 [OpenSubsonic] indexBasedQueue extension — getPlayQueueByIndex / savePlayQueueByIndex by @litebito in #217
- #142 [OpenSubsonic] AlbumID3 metadata bundle — isCompilation, releaseTypes, originalReleaseDate, discTitles, recordLabels by @litebito in #225
- #144 [OpenSubsonic] contributors[] — multi-role contributor extraction by @litebito in #227
- #228 [OpenSubsonic] Cache contributor artist resolution on the hot path (createJaxbChild) by @litebito in #229
- #144 [OpenSubsonic] contributors[] — multi-role contributor extraction by @litebito in #230
- apiKey storage and service layer (Part of #145) by @litebito in #235
- Add apiKey filter, provider, and extension advert (#145 PR-B) by @litebito in #238
- Add apiKey self-service settings page (#145 PR-C) by @litebito in #240
- #203 [OpenSubsonic] userRating/averageRating on ArtistID3 by @litebito in #247
- OpenSubsonic: ReplayGain coverage — MP4 atoms, Opus R128, fallbackGain (#205 PR-A) by @litebito in #249
- #206 [OpenSubsonic] AlbumID3.replayGain (album-level ReplayGain) by @litebito in #253
- #213 [OpenSubsonic] Genre browsing misses multi-frame secondary genres — feed the count table from media_file.genres by @litebito in #254
- #226 [OpenSubsonic] Secondary (FFmpeg) parser doesn't populate the new 13.2.x OpenSubsonic fields by @litebito in #260
- #255 [OpenSubsonic] Album-folder rows miss multi-frame genres; album_count under-counts vs song_count by @litebito in #273
- #256 [OpenSubsonic] getSongsByGenre filters on scalar genre column; misses multi-frame matches by @litebito in #274
- #232 [OpenSubsonic] contributors: MP4/M4A freeform performer atoms by @litebito in #275
- #231 [OpenSubsonic] contributors: ID3v2.3 IPLS performer/involved-people extraction by @litebito in #276
- #158 [OpenSubsonic] Add embedded-tag lyrics reading to LyricsService (FieldKey.LYRICS via jaudiotagger) by @litebito in #279
- #140 [OpenSubsonic] songLyrics — synced/LRC support with DB storage by @litebito in #282
- #250 [OpenSubsonic] ReplayGain baseGain — Opus output_gain from OpusHead (PR-B of #205) by @litebito in #290
- #278 Settings UI for per-user password_auth_enabled toggle by @litebito in #291
Bug Fixes
- fix: ApiKeyRepositoryTest Postgres portability + logback analyze-only (#145 follow-up) by @litebito in #236
- #215 Playlist folder watcher exhausts the DB connection pool under a large sync (unbounded virtual-thread fan-out, no event debounce) by @litebito in #242
- #257 .opus and .m4b files route to FFmpegParser despite Jaudiotagger 3.0.1 supporting both — #205 Opus R128 work is dead code in production by @litebito in #259
- fix: IndexManagerTestCase.testExpunge isolation under matrix DBs (post-#55) by @litebito in #265
- #261 FFmpegParser album_artist lookup misses Vorbis-canonical ALBUMARTIST key by @litebito in #268
- #270 SearchServiceTestCase leaks music_folder rows: getTestMusicFolders() returns id-less instances, @AfterEach deletes with null id by @litebito in #271
- #211 Multifile CUE sheets attach all tracks to the first FILE; CUE frame offsets truncated to whole seconds by @litebito in #288
- #294 [OpenSubsonic] Advertise getPodcastEpisode in getOpenSubsonicExtensions by @litebito in #295
Hardening & Security
- #145 [OpenSubsonic] apiKeyAuthentication extension by @litebito in #241
- #233 [OpenSubsonic] Per-user password_auth_enabled toggle (apiKey migration path) by @litebito in #277
- #237 [OpenSubsonic] apiKey authentication audit log for operator forensics by @litebito in #283
- #262 Clamp count/offset in Subsonic search endpoints to prevent TopDocs OOM by @litebito in #285
Infrastructure & CI
- OpenSubsonic: AlbumID3 isCompilation + original/release dates (#142 batch A) by @litebito in #218
- OpenSubsonic: AlbumID3 releaseTypes + recordLabels (#142 batch B) by @litebito in #219
Maintenance
- #55 Investigate and fix excluded search test cases by @litebito in #264
- #245 PlaylistFileService logger is bound to PlaylistService.class instead of itself by @litebito in #267
- #181 Use Double directly for offsetSeconds in stream/transcode paths, eliminate int truncation by @litebito in #272
Full Changelog: v13.1.0...v13.2.0-beta.1