Skip to content

v13.2.0-rc.1

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Jul 21:29

Airsonic-Pulse v13.2.0-rc.1

About this release candidate

Release candidate for 13.2.0. This build is what we intend to ship as 13.2.0 final unless testing surfaces a blocker. The two bugs reported since beta.1 (a 500 on the podcast settings page and missing labels on the password-auth toggle) are fixed; no functional changes landed beyond those fixes.

Please test against real clients and report anything broken. If nothing blocking surfaces, this becomes 13.2.0 final.

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 you do not need the alpha or beta notes to understand this build. New in this rc.1 lists only what changed since beta.1.

Upgrade notes

Cumulative since v13.1.0. Everything operationally relevant to upgrading onto this build.

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): sortName on track, album, and artist, plus musicBrainzId on artist, from the corresponding sort and MusicBrainz tags.
  • BPM (#139): integer bpm on tracks.
  • ReplayGain (#143/#250): rg_track_gain, rg_album_gain, rg_track_peak, rg_album_peak from each track's ID3v2 TXXX / Vorbis tags, plus rg_base_gain from the Opus OpusHead header on Opus files.
  • Multi-genre (#134): a packed genres column from all GENRE tag values (split by the existing GenreSeparators setting, default ;). The single genre column 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 on media_file and aggregated onto album.
  • Contributors (#144/#231/#232): a packed contributors column 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, Vorbis PERFORMER, and MP4 freeform performer atoms.
  • Play queue index (#141): a current_index column on play_queue for the indexBasedQueue extension.

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 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.

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 Opus baseGain from the OpusHead header), genres[] (multi-genre), and contributors[]: composer, lyricist, conductor, arranger, producer, engineer, mixer, remixer, djmixer, orchestra, choir, ensemble, and performer-with-instrument credits across ID3v2.4 TMCL, ID3v2.3 IPLS, Vorbis PERFORMER, and MP4 freeform atoms.
  • Album (AlbumID3): sortName, isCompilation, originalReleaseDate, releaseDate, releaseTypes[], recordLabels[], discTitles[], and album-level replayGain.
  • Artist (ArtistID3): sortName, musicBrainzId, and userRating/averageRating.
  • songLyrics extension: unsynced lyrics (tag and .lrc sidecar), plus synced/LRC lyrics with timestamps stored in the database, and embedded-tag lyrics reading.
  • indexBasedQueue extension (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, so 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, so 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). count and offset on the Subsonic search endpoints are clamped, closing a path where an adversarial large count could drive a Lucene TopDocs allocation 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 are not catalogued artists), so caching misses is what makes the win meaningful. Correctly invalidated around library rescans.

New in this rc.1

Changes since beta.1. Both are fixes for bugs reported against beta.1; no new features.

  • podcastSettings 500 fixed (#296). The podcast settings page threw an Internal Server Error on every load (an immutable-list mutation introduced by an earlier refactor, present since before 13.1.0). The page now loads. Pre-existing bug, not a 13.2.0 regression.
  • Missing password-auth toggle labels fixed (#297). The password-auth toggle added in #278 rendered untranslated placeholder text instead of labels for English-locale users (the message keys were present in the base bundle but missing from the English bundle). Labels now render.

Known issues

  • #239: no per-user limit on API key generation. A user can generate unlimited keys; revoking one does not 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 .lrc sidecar, 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, and apiKey= 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
  • #297 Missing i18n labels on credentials and user settings password-auth toggle (#278 follow-up) by @litebito in #298
  • #296 [Bug]: Internal Server Error (500) when trying to view podcastSettings by @litebito in #301

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-rc.1