Skip to content

Releases: Airsonic-Pulse/airsonic-pulse

v13.2.0-rc.1

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

Read more

v13.2.0-beta.1

v13.2.0-beta.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Jun 21:56

Airsonic-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)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 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 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 — 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). 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 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 songLyrics with 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 baseGain from OpusHead (#250), closing the #205 umbrella.
  • Multi-frame genre browsing (#213/#255/#256) — genre browse and getSongsByGenre now 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 .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 ...
Read more

v13.2.0-alpha.202605301821

Pre-release

Choose a tag to compare

About this alpha

First public preview of 13.2.0, intended for exercising the new OpenSubsonic surface and the API key authentication path against real clients. Not for production use — please report anything that doesn't behave as documented.

Legacy u/p and t+s Subsonic authentication continues to work unchanged. The operator log will now emit one throttled WARN per (user, client) pair per 24 hours noting that legacy auth is deprecated — this is intentional behavior, the 13.2.x precursor to the removal targeted for 13.3.x as part of #56.

Highlights

API key authentication available (#145)

The OpenSubsonic apiKeyAuthentication extension is now implemented end-to-end. Each user manages their own keys under Personal Settings → API Keys (generate, disable, revoke). A generated key is shown exactly once at creation; only its HMAC-SHA-256 hash (peppered with a server-side secret) is stored. Clients authenticate by sending the key as Authorization: Bearer <key> or as an apiKey= query parameter — the legacy u/p and t/s parameters continue to work unchanged.

This is the underlying primitive, not an automatic security upgrade. Existing deployments stay on whatever authentication their clients use today; switching an individual client to API keys is a per-client opt-in. The benefit is that long-lived passwords and the deprecated MD5-salted-token derivative stop being part of every API call, and a leaked key can be revoked from the UI without forcing a password change.

To try it: sign in as the client's owner, navigate to Personal Settings → API Keys, generate a key with a descriptive name (e.g. the client + device), copy it immediately (it's shown exactly once), then configure the client to use Authorization: Bearer <key> or apiKey=<key> instead of u/p or t/s. There is no per-user cap on the number of keys in this alpha — please be sensible with key generation; a cap and a generation rate-limit are tracked in #239.

Legacy authentication deprecated (#145)

The Subsonic u/p (username + plaintext password) and t/s (MD5-salted token) authentication methods are deprecated in favor of the API key authentication described above. Both keep working in 13.2.x with no behavior change. Removal is targeted for 13.3.x as part of #56 — that target is 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: it names the method (legacy username/password or legacy token+salt), points operators at API keys as the replacement, and references #56. The log line intentionally contains no password, token, salt, IP, or User-Agent. Subsequent requests within the throttle window are silent. The warning fires only for legacy authentication — successful API-key requests do not trigger it.

OpenSubsonic field and extension expansion

In addition to apiKeyAuthentication, this release expands OpenSubsonic coverage across track-, album-, and artist-level response fields. Notable additions:

  • Track-level (Child): sortName, bpm, replayGain (track gain/peak from ID3v2 TXXX and Vorbis comments), genres[] (multi-genre array), contributors[] covering composer, lyricist, conductor, arranger, producer, engineer, mixer, remixer, djmixer, orchestra, choir, and ensemble, plus performer-with-instrument credits from ID3v2.4 TMCL and Vorbis PERFORMER.
  • Album-level (AlbumID3): sortName, isCompilation, originalReleaseDate, releaseDate, releaseTypes[], recordLabels[], discTitles[].
  • Artist-level (ArtistID3): sortName, musicBrainzId.
  • indexBasedQueue extension (v1): getPlayQueueByIndex and savePlayQueueByIndex, disambiguating the current track in queues that contain the same track more than once.

Some less-common tag-format coverage remains gapped — see Known issues below.

Faster artist resolution (#228)

artistService.getArtist(name), previously an uncached repository query called per-album, per-item, and per-contributor in the response-construction loops, is now JCache-backed (10000 entries, 24-hour TTL) and caches 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. The cache is correctly invalidated around library rescans.

Known issues

  • #215 — playlist folder watcher exhausts the DB connection pool on large library syncs. Pre-existing bug, not introduced in 13.2.x. A large initial scan or a watcher-triggered re-sync of a large library can drain the connection pool via unbounded virtual-thread fan-out, blocking other requests until the pool recovers. If you have a large library (tens of thousands of tracks or more), prefer the scheduled-scan path over watcher-triggered re-syncs, or split the library into multiple folders. Fix in flight.
  • #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. Be sensible with key generation.
  • OpenSubsonic field coverage gaps in less-common tag formats: ReplayGain from MP4 atoms and Opus R128 (#205), genre-browse count table missing secondary-frame genres (#213), ratings on ArtistID3 (#203), album-level ReplayGain on AlbumID3 (#206), MP4 freeform performer atoms (#232), and ID3v2.3 IPLS contributor paths (#231). Each is tracked individually for completion before 13.2.0 final.
  • #140songLyrics extension currently supports unsynced lyrics only. Synced / LRC support with DB storage is tracked for completion before 13.2.0 final.
  • #211 — multifile CUE sheets attach all tracks to the first FILE entry, and CUE frame offsets are truncated to whole seconds. CUE-sheet users are affected; non-CUE libraries are unaffected.
  • #178 — Lucene query injection in the search endpoint is under investigation. No known exploit, no public PoC; tracked separately.

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
  • #142 [OpenSubsonic]: AlbumID3 isCompilation + original/release dates (#142 batch A) by @litebito in #218
  • #142 [OpenSubsonic]: AlbumID3 releaseTypes + recordLabels (#142 batch B) by @litebito in #219

Bug Fixes

  • #145 [Bug]: ApiKeyRepositoryTest Postgres portability + logback analyze-only (#145 follow-up) by @litebito in #236

Hardening & Security

Full Changelog: v13.1.0...v13.2.0-alpha.202605301821

v13.1.0

Choose a tag to compare

@github-actions github-actions released this 18 May 21:53
d9fbf16

Airsonic-Pulse v13.1.0

Highlights

Subsonic / OpenSubsonic API surface

OpenSubsonic API support. Airsonic-Pulse 13.1.0 is the first kagemomiji-descended fork to implement the OpenSubsonic API specification. Five OpenSubsonic extensions and endpoints are now supported:

  • transcodeOffset v1/stream honors timeOffset accurately via source-side ffmpeg seek (was a wasteful post-encoding byte skip)
  • songLyrics v1 — new /getLyricsBySongId endpoint returns structured lyrics
  • /getPodcastEpisode — new endpoint for per-episode lookup
  • AlbumID3.playCount wiring (existing field, never previously populated)
  • New optional fields on Child/AlbumID3/ArtistID3: played, musicBrainzId, displayArtist, displayAlbumArtist, mediaType

OpenSubsonic-compliant clients (Symfonium, Tempo, others) will now see Pulse declare its supported extensions via getOpenSubsonicExtensions and use the extended response shapes automatically.

SubsonicRESTController refactor. The monolithic SubsonicRESTController has been split into 13 domain-specific controllers (system, browsing, ID3, search, annotation, play queue, bookmark, share, podcast, radio, jukebox, playlist, artist info, user, media). Shared utilities are lifted into a new AbstractSubsonicController base class, and exception handling is consolidated via a @ControllerAdvice. Result: ~1000 fewer lines of duplicated boilerplate, clearer ownership, and easier future maintenance. No API contract changes for clients.

Subsonic API 1.16.1. The server now declares itself as Subsonic API version 1.16.1 (was 1.15.0). Internet radio CRUD endpoints from Subsonic 1.16.0 are implemented (createInternetRadioStation, updateInternetRadioStation, deleteInternetRadioStation). Modern clients sending v=1.16.1 are no longer rejected.

Supply-chain hardening (new in 13.1.0)

13.1.0 establishes a stronger supply-chain posture for Airsonic-Pulse. Three controls land together:

  1. GitHub Actions SHA pinning (#175) — every workflow uses: reference is pinned to a full commit SHA with a # v<N>.x.x annotation, eliminating the "moving target" risk of tag-based pins.
  2. GitHub Actions major-version bumps (#184) — checkout v6, the three Docker actions on current majors (setup-buildx v4, login v4, build-push v7), all SHA-pinned per the #175 convention.
  3. Cosign keyless container image signing (#183) — every official image published from 13.1.0 onward is signed via GitHub Actions OIDC + Sigstore Fulcio + Rekor. Downstream users can verify image provenance with cosign verify against this repo's release workflow identity. See docs/security/image-verification.md for the full verification recipe and trust model.

These controls are also enforced at the platform level via repo settings:
"Allow Airsonic-Pulse, and select non-Airsonic-Pulse, actions" with an explicit allowlist, and "Require actions to be pinned to a full-length commit SHA."

Build / CI

  • Checkstyle 11.0.0 → 13.4.2 (#125)
  • Checkstyle configLocation fix so CLI runs match plugin-bound runs (#185)

Upgrade notes

Audio transcoding offsets (admins of customized transcoding profiles).

13.1.0 plumbs the user's timeOffset directly into the ffmpeg command via the -ss flag, producing a fast, accurate source-side seek for transcoded audio. Previously, audio offsets were applied via a byte-level skip on the transcoded output — wasteful (ffmpeg encoded audio that was then discarded) and imprecise (especially for VBR sources).

Two settings power this behavior:

  • The mp3 audio transcoding profile — updated automatically on upgrade. A Liquibase precondition preserves admin customizations: if you previously changed this profile, your changes are kept intact. To benefit from accurate seek in customized configurations, manually add -ss %o immediately after ffmpeg %S in your profile's Step 1.
  • The system Downsample command — reset to the new default on upgrade. Any customization of the Downsample command will be lost (consistent with how prior Airsonic versions handled forced default upgrades). If you had a custom Downsample command, recreate it post-upgrade and add -ss %o after ffmpeg %S to benefit from accurate seek.

What's Changed

Features & Enhancements

  • #64 Add OpenSubsonic response envelope fields by @litebito in #65
  • #66 Add getOpenSubsonicExtensions endpoint by @litebito in #68
  • #98 Refactor: extract APIException @ExceptionHandler into @ControllerAdvice by @litebito in #118
  • #112 Refactor: extract AbstractSubsonicController base class for shared utilities by @litebito in #119
  • #67 Clean up version display for non-SNAPSHOT releases by @litebito in #121
  • #123 Implement Subsonic 1.16.0 internet radio CRUD endpoints by @litebito in #128
  • #129 [OpenSubsonic] Wire already-in-XSD response fields (Child.played, AlbumID3.playCount/played) by @litebito in #146
  • #130 [OpenSubsonic] Add musicBrainzId, displayArtist, displayAlbumArtist, mediaType to XSD and wire by @litebito in #147
  • #132 [OpenSubsonic] Verify transcodeOffset extension and declare support by @litebito in #149
  • #133 [OpenSubsonic] Implement getPodcastEpisode endpoint by @litebito in #155
  • #131 [OpenSubsonic] Implement songLyrics extension (unsynced, tag-read-at-request-time) by @litebito in #156

Documentation

  • docs: correct Full Changelog URL in v13.1.0-rc.2 notes by @litebito in #173

Bug Fixes

  • #57 [Bug]: updateUser REST endpoint maps streamRole from downloadRole by @litebito in #58
  • #59 [Bug]: getVideoInfo and getCaptions return plain text 501 instead of JAXB error envelope by @litebito in #61
  • #115 [Bug]: /getAvatar returns non-standard Content-Type 'img/png' instead of 'image/png' by @litebito in #120
  • #117 [Bug]: /stream returns empty 200 with application/octet-stream for non-existent media ID by @litebito in #122
  • #113 [Bug]: Server reports Subsonic API version 1.15.0 — modern clients sending v=1.16.1 get rejected by @litebito in #124
  • #126 [Bug]: PodcastRepositoryTest is non-deterministic on HSQLDB due to missing ORDER BY in repository queries by @litebito in #127

Hardening & Security

  • #183 [Enhancement]: Sign container images with Cosign on release (keyless OIDC) by @litebito in #195

Infrastructure & CI

  • ci: bump docker/setup-qemu-action from 3 to 4 by @app/dependabot in #74
  • chore: rename Trivy job display name to 'Trivy scan' for clarity by @litebito in #148
  • chore: release workflow uses committed release-notes file by @litebito in #159
  • chore: update release workflow by @litebito in #160
  • chore: add gitleaks baseline for inherited findings by @litebito in #161
  • ci: update runner in pr_ci.yml by @litebito in #170
  • docs: release notes for v13.1.0-rc.2 by @litebito in #171
  • docs: release notes for v13.1.0-rc.2 by @litebito in #172
  • ci:update Trivy Scan scope and rename CodeQL workflow file by @litebito in #174
  • ci: SHA-pin third-party actions and finish paths-ignore cleanup by @litebito in #175

Maintenance

  • #62 [Chore]: Make JAXBWriter a Spring @component by @litebito in #63
  • #69 [Chore]: Extract SubsonicSystemController from SubsonicRESTController by @litebito in #70
  • #71 [Chore]: Extract SubsonicBrowsingController from SubsonicRESTController by @litebito in #72
  • deps: bump org.codehaus.mojo:buildnumber-maven-plugin from 3.2.1 to 3.3.0 by @app/dependabot in #77
  • deps: bump com.twelvemonkeys.imageio:imageio-webp from 3.12.0 to 3.13.1 by @app/dependabot in #78
  • deps: bump ch.qos.logback:logback-core from 1.5.18 to 1.5.25 in the maven group across 1 directory by @app/dependabot in #81
  • Bump com.github.junrar:junrar from 7.5.5 to 7.5.10 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #82
  • Bump org.bouncycastle:bcprov-jdk18on from 1.81 to 1.84 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #83
  • #80 [Chore]: Extract SubsonicID3Controller from SubsonicRESTController by @litebito in #84
  • #85 [Chore]: Extract SubsonicSearchController from SubsonicRESTController by @litebito in #86
  • #87 [Chore]: Ignore liquibase-core major version updates in Dependabot by @litebito in #88
  • #89 [Chore]: Extract SubsonicAnnotationController from SubsonicRESTController by @litebito in #90
  • #91 [Chore]: Extract SubsonicPlayQueueController from SubsonicRESTController by @litebito in #92
  • #93 [Chore]: Extract SubsonicBookmarkController from SubsonicRESTController by @litebito in #94
  • #95 [Chore]: Extract SubsonicShareController from SubsonicRESTController by @litebito in #96
  • #97 [Chore]: Extract SubsonicPodcastController from SubsonicRESTController by @litebito in #99
  • #100 [Chore]: Extract SubsonicRadioController from SubsonicRESTController by @litebito in #101
  • #102 [Chore]: Extract SubsonicJukeboxController from SubsonicRESTController by @litebito in #103
  • #104 [Chore]: Extract SubsonicPlaylistController from SubsonicRESTController by @litebito in #105
  • #106 [Chore]: Extract SubsonicArtistInfoController from SubsonicRESTController by @litebito in #107
  • #108 [Chore]: Extract SubsonicUserController from SubsonicRESTController by @litebito in #109
  • #110 [Chore]: Extract SubsonicMediaController from SubsonicRESTController by @litebito in #111
  • deps: bump com.google.guava:guava from 33.4.8-jre to 33.6.0-jre by @app/dependabot in #150
  • deps: bump org.apache.commons:commons-configuration2 from 2.12.0 to 2.14.0 by @app/dependabot in #151
  • deps: bump com.google.errorprone:error_prone_annotations from 2.41.0 to 2.49.0 by @app/dependabot in #152
  • deps: bump net.java.dev.jna:jna from 5.17.0 to 5.18.1 by @app/dependabot in #153
  • deps: bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0 by @app/dependabot in #154
  • #162 chore: rename in-repo references in preparation for org transfer by @litebito in #163
  • deps: bump org.eclipse.persistence:org.eclipse.persistence.moxy from 4.0.7 to 5.0.0 by @app/dependabot in #165
  • deps: bump lucene.ver...
Read more

v13.1.0-rc.3

v13.1.0-rc.3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 May 18:17
55f1d13

Airsonic-Pulse v13.1.0-rc.3

Highlights

No functional / feature changes since v13.1.0-rc.1.

OpenSubsonic API support. Airsonic-Pulse 13.1.0 is the first kagemomiji-descended fork to implement the OpenSubsonic API specification. Five OpenSubsonic extensions and endpoints are now supported:

  • transcodeOffset v1/stream honors timeOffset accurately via source-side ffmpeg seek (was a wasteful post-encoding byte skip)
  • songLyrics v1 — new /getLyricsBySongId endpoint returns structured lyrics
  • /getPodcastEpisode — new endpoint for per-episode lookup
  • AlbumID3.playCount wiring (existing field, never previously populated)
  • New optional fields on Child/AlbumID3/ArtistID3: played, musicBrainzId, displayArtist, displayAlbumArtist, mediaType

OpenSubsonic-compliant clients (Symfonium, Tempo, others) will now see Pulse declare its supported extensions via getOpenSubsonicExtensions and use the extended response shapes automatically.

SubsonicRESTController refactor. The monolithic SubsonicRESTController has been split into 13 domain-specific controllers (system, browsing, ID3, search, annotation, play queue, bookmark, share, podcast, radio, jukebox, playlist, artist info, user, media). Shared utilities are lifted into a new AbstractSubsonicController base class, and exception handling is consolidated via a @ControllerAdvice. Result: ~1000 fewer lines of duplicated boilerplate, clearer ownership, and easier future maintenance. No API contract changes for clients.

Subsonic API 1.16.1. The server now declares itself as Subsonic API version 1.16.1 (was 1.15.0). Internet radio CRUD endpoints from Subsonic 1.16.0 are implemented (createInternetRadioStation, updateInternetRadioStation, deleteInternetRadioStation). Modern clients sending v=1.16.1 are no longer rejected.

Upgrade notes

Audio transcoding offsets (admins of customized transcoding profiles).

13.1.0 plumbs the user's timeOffset directly into the ffmpeg command via the -ss flag, producing a fast, accurate source-side seek for transcoded audio. Previously, audio offsets were applied via a byte-level skip on the transcoded output — wasteful (ffmpeg encoded audio that was then discarded) and imprecise (especially for VBR sources).

Two settings power this behavior:

  • The mp3 audio transcoding profile — updated automatically on upgrade. A Liquibase precondition preserves admin customizations: if you previously changed this profile, your changes are kept intact. To benefit from accurate seek in customized configurations, manually add -ss %o immediately after ffmpeg %S in your profile's Step 1.
  • The system Downsample command — reset to the new default on upgrade. Any customization of the Downsample command will be lost (consistent with how prior Airsonic versions handled forced default upgrades). If you had a custom Downsample command, recreate it post-upgrade and add -ss %o after ffmpeg %S to benefit from accurate seek.

What's Changed

Features & Enhancements

  • #64 Add OpenSubsonic response envelope fields by @litebito in #65
  • #66 Add getOpenSubsonicExtensions endpoint by @litebito in #68
  • #98 Refactor: extract APIException @ExceptionHandler into @ControllerAdvice by @litebito in #118
  • #112 Refactor: extract AbstractSubsonicController base class for shared utilities by @litebito in #119
  • #67 Clean up version display for non-SNAPSHOT releases by @litebito in #121
  • #123 Implement Subsonic 1.16.0 internet radio CRUD endpoints by @litebito in #128
  • #129 [OpenSubsonic] Wire already-in-XSD response fields (Child.played, AlbumID3.playCount/played) by @litebito in #146
  • #130 [OpenSubsonic] Add musicBrainzId, displayArtist, displayAlbumArtist, mediaType to XSD and wire by @litebito in #147
  • #132 [OpenSubsonic] Verify transcodeOffset extension and declare support by @litebito in #149
  • #133 [OpenSubsonic] Implement getPodcastEpisode endpoint by @litebito in #155
  • #131 [OpenSubsonic] Implement songLyrics extension (unsynced, tag-read-at-request-time) by @litebito in #156

Documentation

  • docs: correct Full Changelog URL in v13.1.0-rc.2 notes by @litebito in #173

Bug Fixes

  • #57 [Bug]: updateUser REST endpoint maps streamRole from downloadRole by @litebito in #58
  • #59 [Bug]: getVideoInfo and getCaptions return plain text 501 instead of JAXB error envelope by @litebito in #61
  • #115 [Bug]: /getAvatar returns non-standard Content-Type 'img/png' instead of 'image/png' by @litebito in #120
  • #117 [Bug]: /stream returns empty 200 with application/octet-stream for non-existent media ID by @litebito in #122
  • #113 [Bug]: Server reports Subsonic API version 1.15.0 — modern clients sending v=1.16.1 get rejected by @litebito in #124
  • #126 [Bug]: PodcastRepositoryTest is non-deterministic on HSQLDB due to missing ORDER BY in repository queries by @litebito in #127

Infrastructure & CI

  • ci: bump docker/setup-qemu-action from 3 to 4 by @app/dependabot in #74
  • chore: rename Trivy job display name to 'Trivy scan' for clarity by @litebito in #148
  • chore: release workflow uses committed release-notes file by @litebito in #159
  • chore: update release workflow by @litebito in #160
  • chore: add gitleaks baseline for inherited findings by @litebito in #161
  • ci: update runner in pr_ci.yml by @litebito in #170
  • docs: release notes for v13.1.0-rc.2 by @litebito in #171
  • docs: release notes for v13.1.0-rc.2 by @litebito in #172
  • ci:update Trivy Scan scope and rename CodeQL workflow file by @litebito in #174
  • ci: SHA-pin third-party actions and finish paths-ignore cleanup by @litebito in #175

Maintenance

  • #62 [Chore]: Make JAXBWriter a Spring @component by @litebito in #63
  • #69 [Chore]: Extract SubsonicSystemController from SubsonicRESTController by @litebito in #70
  • #71 [Chore]: Extract SubsonicBrowsingController from SubsonicRESTController by @litebito in #72
  • deps: bump org.codehaus.mojo:buildnumber-maven-plugin from 3.2.1 to 3.3.0 by @app/dependabot in #77
  • deps: bump com.twelvemonkeys.imageio:imageio-webp from 3.12.0 to 3.13.1 by @app/dependabot in #78
  • deps: bump ch.qos.logback:logback-core from 1.5.18 to 1.5.25 in the maven group across 1 directory by @app/dependabot in #81
  • Bump com.github.junrar:junrar from 7.5.5 to 7.5.10 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #82
  • Bump org.bouncycastle:bcprov-jdk18on from 1.81 to 1.84 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #83
  • #80 [Chore]: Extract SubsonicID3Controller from SubsonicRESTController by @litebito in #84
  • #85 [Chore]: Extract SubsonicSearchController from SubsonicRESTController by @litebito in #86
  • #87 [Chore]: Ignore liquibase-core major version updates in Dependabot by @litebito in #88
  • #89 [Chore]: Extract SubsonicAnnotationController from SubsonicRESTController by @litebito in #90
  • #91 [Chore]: Extract SubsonicPlayQueueController from SubsonicRESTController by @litebito in #92
  • #93 [Chore]: Extract SubsonicBookmarkController from SubsonicRESTController by @litebito in #94
  • #95 [Chore]: Extract SubsonicShareController from SubsonicRESTController by @litebito in #96
  • #97 [Chore]: Extract SubsonicPodcastController from SubsonicRESTController by @litebito in #99
  • #100 [Chore]: Extract SubsonicRadioController from SubsonicRESTController by @litebito in #101
  • #102 [Chore]: Extract SubsonicJukeboxController from SubsonicRESTController by @litebito in #103
  • #104 [Chore]: Extract SubsonicPlaylistController from SubsonicRESTController by @litebito in #105
  • #106 [Chore]: Extract SubsonicArtistInfoController from SubsonicRESTController by @litebito in #107
  • #108 [Chore]: Extract SubsonicUserController from SubsonicRESTController by @litebito in #109
  • #110 [Chore]: Extract SubsonicMediaController from SubsonicRESTController by @litebito in #111
  • deps: bump com.google.guava:guava from 33.4.8-jre to 33.6.0-jre by @app/dependabot in #150
  • deps: bump org.apache.commons:commons-configuration2 from 2.12.0 to 2.14.0 by @app/dependabot in #151
  • deps: bump com.google.errorprone:error_prone_annotations from 2.41.0 to 2.49.0 by @app/dependabot in #152
  • deps: bump net.java.dev.jna:jna from 5.17.0 to 5.18.1 by @app/dependabot in #153
  • deps: bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0 by @app/dependabot in #154
  • #162 chore: rename in-repo references in preparation for org transfer by @litebito in #163
  • deps: bump org.eclipse.persistence:org.eclipse.persistence.moxy from 4.0.7 to 5.0.0 by @app/dependabot in #165
  • deps: bump lucene.version from 10.2.2 to 10.4.0 by @app/dependabot in #166
  • deps: bump org.apache.commons:commons-configuration2 from 2.14.0 to 2.15.0 by @app/dependabot in #167
  • deps: bump org.apache.maven.plugins:maven-dependency-plugin from 3.8.1 to 3.10.0 by @app/dependabot in #168
  • #125 Update Checkstyle dependency from 11.0.0 to 13.4.2 by @litebito in #186
  • #185 [Chore]: Move maven-checkstyle-plugin configLocation to plugin-level configuration (sun_checks.xml fallback bug) by @litebito in #187
  • #176 [Chore]: Controller hygiene sweep — error handling, helpers, and consistency follow-ups by @litebito in #188
  • #184 [Chore]: Major-version bumps for GitHub Actions held back from #175 SHA-pinning sweep by @litebito in #189

Full Changelog: v13.0.0...v13.1.0-rc.3

v13.1.0-rc.2

v13.1.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 May 23:26
745204f

Airsonic-Pulse v13.1.0-rc.2

Highlights

This is the first release under the new Airsonic-Pulse GitHub organization. The repository was transferred from the private repository to Airsonic-Pulse/airsonic-pulse during this release cycle, and the container image namespace moved from the private repository to ghcr.io/airsonic-pulse/airsonic-pulse.

The historical container tags (v12.0.0, v13.0.0, v13.1.0-rc.1) have been mirrored to the new namespace. The old ghcr.io/litebito/airsonic-pulse package remains as an archive and will not receive new pushes.

No functional / feature changes since v13.1.0-rc.1.

OpenSubsonic API support. Airsonic-Pulse 13.1.0 is the first kagemomiji-descended fork to implement the OpenSubsonic API specification. Five OpenSubsonic extensions and endpoints are now supported:

  • transcodeOffset v1/stream honors timeOffset accurately via source-side ffmpeg seek (was a wasteful post-encoding byte skip)
  • songLyrics v1 — new /getLyricsBySongId endpoint returns structured lyrics
  • /getPodcastEpisode — new endpoint for per-episode lookup
  • AlbumID3.playCount wiring (existing field, never previously populated)
  • New optional fields on Child/AlbumID3/ArtistID3: played, musicBrainzId, displayArtist, displayAlbumArtist, mediaType

OpenSubsonic-compliant clients (Symfonium, Tempo, others) will now see Pulse declare its supported extensions via getOpenSubsonicExtensions and use the extended response shapes automatically.

SubsonicRESTController refactor. The monolithic SubsonicRESTController has been split into 13 domain-specific controllers (system, browsing, ID3, search, annotation, play queue, bookmark, share, podcast, radio, jukebox, playlist, artist info, user, media). Shared utilities are lifted into a new AbstractSubsonicController base class, and exception handling is consolidated via a @ControllerAdvice. Result: ~1000 fewer lines of duplicated boilerplate, clearer ownership, and easier future maintenance. No API contract changes for clients.

Subsonic API 1.16.1. The server now declares itself as Subsonic API version 1.16.1 (was 1.15.0). Internet radio CRUD endpoints from Subsonic 1.16.0 are implemented (createInternetRadioStation, updateInternetRadioStation, deleteInternetRadioStation). Modern clients sending v=1.16.1 are no longer rejected.

Upgrade notes

Audio transcoding offsets (admins of customized transcoding profiles).

13.1.0 plumbs the user's timeOffset directly into the ffmpeg command via the -ss flag, producing a fast, accurate source-side seek for transcoded audio. Previously, audio offsets were applied via a byte-level skip on the transcoded output — wasteful (ffmpeg encoded audio that was then discarded) and imprecise (especially for VBR sources).

Two settings power this behavior:

  • The mp3 audio transcoding profile — updated automatically on upgrade. A Liquibase precondition preserves admin customizations: if you previously changed this profile, your changes are kept intact. To benefit from accurate seek in customized configurations, manually add -ss %o immediately after ffmpeg %S in your profile's Step 1.
  • The system Downsample command — reset to the new default on upgrade. Any customization of the Downsample command will be lost (consistent with how prior Airsonic versions handled forced default upgrades). If you had a custom Downsample command, recreate it post-upgrade and add -ss %o after ffmpeg %S to benefit from accurate seek.

What's Changed

Features & Enhancements

  • #64 Add OpenSubsonic response envelope fields by @litebito in #65
  • #66 Add getOpenSubsonicExtensions endpoint by @litebito in #68
  • #98 Refactor: extract APIException @ExceptionHandler into @ControllerAdvice by @litebito in #118
  • #112 Refactor: extract AbstractSubsonicController base class for shared utilities by @litebito in #119
  • #67 Clean up version display for non-SNAPSHOT releases by @litebito in #121
  • #123 Implement Subsonic 1.16.0 internet radio CRUD endpoints by @litebito in #128
  • #129 [OpenSubsonic] Wire already-in-XSD response fields (Child.played, AlbumID3.playCount/played) by @litebito in #146
  • #130 [OpenSubsonic] Add musicBrainzId, displayArtist, displayAlbumArtist, mediaType to XSD and wire by @litebito in #147
  • #132 [OpenSubsonic] Verify transcodeOffset extension and declare support by @litebito in #149
  • #133 [OpenSubsonic] Implement getPodcastEpisode endpoint by @litebito in #155
  • #131 [OpenSubsonic] Implement songLyrics extension (unsynced, tag-read-at-request-time) by @litebito in #156

Bug Fixes

  • #57 [Bug]: updateUser REST endpoint maps streamRole from downloadRole by @litebito in #58
  • #59 [Bug]: getVideoInfo and getCaptions return plain text 501 instead of JAXB error envelope by @litebito in #61
  • #115 [Bug]: /getAvatar returns non-standard Content-Type 'img/png' instead of 'image/png' by @litebito in #120
  • #117 [Bug]: /stream returns empty 200 with application/octet-stream for non-existent media ID by @litebito in #122
  • #113 [Bug]: Server reports Subsonic API version 1.15.0 — modern clients sending v=1.16.1 get rejected by @litebito in #124
  • #126 [Bug]: PodcastRepositoryTest is non-deterministic on HSQLDB due to missing ORDER BY in repository queries by @litebito in #127

Infrastructure & CI

  • ci: bump docker/setup-qemu-action from 3 to 4 by @app/dependabot in #74
  • chore: rename Trivy job display name to 'Trivy scan' for clarity by @litebito in #148

Maintenance

  • #62 [Chore]: Make JAXBWriter a Spring @component by @litebito in #63
  • #69 [Chore]: Extract SubsonicSystemController from SubsonicRESTController by @litebito in #70
  • #71 [Chore]: Extract SubsonicBrowsingController from SubsonicRESTController by @litebito in #72
  • deps: bump org.codehaus.mojo:buildnumber-maven-plugin from 3.2.1 to 3.3.0 by @app/dependabot in #77
  • deps: bump com.twelvemonkeys.imageio:imageio-webp from 3.12.0 to 3.13.1 by @app/dependabot in #78
  • deps: bump ch.qos.logback:logback-core from 1.5.18 to 1.5.25 in the maven group across 1 directory by @app/dependabot in #81
  • Bump com.github.junrar:junrar from 7.5.5 to 7.5.10 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #82
  • Bump org.bouncycastle:bcprov-jdk18on from 1.81 to 1.84 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #83
  • #80 [Chore]: Extract SubsonicID3Controller from SubsonicRESTController by @litebito in #84
  • #85 [Chore]: Extract SubsonicSearchController from SubsonicRESTController by @litebito in #86
  • #87 [Chore]: Ignore liquibase-core major version updates in Dependabot by @litebito in #88
  • #89 [Chore]: Extract SubsonicAnnotationController from SubsonicRESTController by @litebito in #90
  • #91 [Chore]: Extract SubsonicPlayQueueController from SubsonicRESTController by @litebito in #92
  • #93 [Chore]: Extract SubsonicBookmarkController from SubsonicRESTController by @litebito in #94
  • #95 [Chore]: Extract SubsonicShareController from SubsonicRESTController by @litebito in #96
  • #97 [Chore]: Extract SubsonicPodcastController from SubsonicRESTController by @litebito in #99
  • #100 [Chore]: Extract SubsonicRadioController from SubsonicRESTController by @litebito in #101
  • #102 [Chore]: Extract SubsonicJukeboxController from SubsonicRESTController by @litebito in #103
  • #104 [Chore]: Extract SubsonicPlaylistController from SubsonicRESTController by @litebito in #105
  • #106 [Chore]: Extract SubsonicArtistInfoController from SubsonicRESTController by @litebito in #107
  • #108 [Chore]: Extract SubsonicUserController from SubsonicRESTController by @litebito in #109
  • #110 [Chore]: Extract SubsonicMediaController from SubsonicRESTController by @litebito in #111
  • deps: bump com.google.guava:guava from 33.4.8-jre to 33.6.0-jre by @app/dependabot in #150
  • deps: bump org.apache.commons:commons-configuration2 from 2.12.0 to 2.14.0 by @app/dependabot in #151
  • deps: bump com.google.errorprone:error_prone_annotations from 2.41.0 to 2.49.0 by @app/dependabot in #152
  • deps: bump net.java.dev.jna:jna from 5.17.0 to 5.18.1 by @app/dependabot in #153
  • deps: bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0 by @app/dependabot in #154

Full Changelog: v13.0.0...v13.1.0-rc.2

v13.1.0-rc.1

v13.1.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 13 May 11:31
bac4bfe

Airsonic-Pulse v13.1.0-rc.1

Highlights

OpenSubsonic API support. Airsonic-Pulse 13.1.0 is the first kagemomiji-descended fork to implement the OpenSubsonic API specification. Five OpenSubsonic extensions and endpoints are now supported:

  • transcodeOffset v1/stream honors timeOffset accurately via source-side ffmpeg seek (was a wasteful post-encoding byte skip)
  • songLyrics v1 — new /getLyricsBySongId endpoint returns structured lyrics
  • /getPodcastEpisode — new endpoint for per-episode lookup
  • AlbumID3.playCount wiring (existing field, never previously populated)
  • New optional fields on Child/AlbumID3/ArtistID3: played, musicBrainzId, displayArtist, displayAlbumArtist, mediaType

OpenSubsonic-compliant clients (Symfonium, Tempo, others) will now see Pulse declare its supported extensions via getOpenSubsonicExtensions and use the extended response shapes automatically.

SubsonicRESTController refactor. The monolithic SubsonicRESTController has been split into 13 domain-specific controllers (system, browsing, ID3, search, annotation, play queue, bookmark, share, podcast, radio, jukebox, playlist, artist info, user, media). Shared utilities are lifted into a new AbstractSubsonicController base class, and exception handling is consolidated via a @ControllerAdvice. Result: ~1000 fewer lines of duplicated boilerplate, clearer ownership, and easier future maintenance. No API contract changes for clients.

Subsonic API 1.16.1. The server now declares itself as Subsonic API version 1.16.1 (was 1.15.0). Internet radio CRUD endpoints from Subsonic 1.16.0 are implemented (createInternetRadioStation, updateInternetRadioStation, deleteInternetRadioStation). Modern clients sending v=1.16.1 are no longer rejected.

Upgrade notes

Audio transcoding offsets (admins of customized transcoding profiles).

13.1.0 plumbs the user's timeOffset directly into the ffmpeg command via the -ss flag, producing a fast, accurate source-side seek for transcoded audio. Previously, audio offsets were applied via a byte-level skip on the transcoded output — wasteful (ffmpeg encoded audio that was then discarded) and imprecise (especially for VBR sources).

Two settings power this behavior:

  • The mp3 audio transcoding profile — updated automatically on upgrade. A Liquibase precondition preserves admin customizations: if you previously changed this profile, your changes are kept intact. To benefit from accurate seek in customized configurations, manually add -ss %o immediately after ffmpeg %S in your profile's Step 1.
  • The system Downsample command — reset to the new default on upgrade. Any customization of the Downsample command will be lost (consistent with how prior Airsonic versions handled forced default upgrades). If you had a custom Downsample command, recreate it post-upgrade and add -ss %o after ffmpeg %S to benefit from accurate seek.

What's Changed

Features & Enhancements

  • #64 Add OpenSubsonic response envelope fields by @litebito in #65
  • #66 Add getOpenSubsonicExtensions endpoint by @litebito in #68
  • #98 Refactor: extract APIException @ExceptionHandler into @ControllerAdvice by @litebito in #118
  • #112 Refactor: extract AbstractSubsonicController base class for shared utilities by @litebito in #119
  • #67 Clean up version display for non-SNAPSHOT releases by @litebito in #121
  • #123 Implement Subsonic 1.16.0 internet radio CRUD endpoints by @litebito in #128
  • #129 [OpenSubsonic] Wire already-in-XSD response fields (Child.played, AlbumID3.playCount/played) by @litebito in #146
  • #130 [OpenSubsonic] Add musicBrainzId, displayArtist, displayAlbumArtist, mediaType to XSD and wire by @litebito in #147
  • #132 [OpenSubsonic] Verify transcodeOffset extension and declare support by @litebito in #149
  • #133 [OpenSubsonic] Implement getPodcastEpisode endpoint by @litebito in #155
  • #131 [OpenSubsonic] Implement songLyrics extension (unsynced, tag-read-at-request-time) by @litebito in #156

Bug Fixes

  • #57 [Bug]: updateUser REST endpoint maps streamRole from downloadRole by @litebito in #58
  • #59 [Bug]: getVideoInfo and getCaptions return plain text 501 instead of JAXB error envelope by @litebito in #61
  • #115 [Bug]: /getAvatar returns non-standard Content-Type 'img/png' instead of 'image/png' by @litebito in #120
  • #117 [Bug]: /stream returns empty 200 with application/octet-stream for non-existent media ID by @litebito in #122
  • #113 [Bug]: Server reports Subsonic API version 1.15.0 — modern clients sending v=1.16.1 get rejected by @litebito in #124
  • #126 [Bug]: PodcastRepositoryTest is non-deterministic on HSQLDB due to missing ORDER BY in repository queries by @litebito in #127

Infrastructure & CI

  • ci: bump docker/setup-qemu-action from 3 to 4 by @app/dependabot in #74
  • chore: rename Trivy job display name to 'Trivy scan' for clarity by @litebito in #148

Maintenance

  • #62 [Chore]: Make JAXBWriter a Spring @component by @litebito in #63
  • #69 [Chore]: Extract SubsonicSystemController from SubsonicRESTController by @litebito in #70
  • #71 [Chore]: Extract SubsonicBrowsingController from SubsonicRESTController by @litebito in #72
  • deps: bump org.codehaus.mojo:buildnumber-maven-plugin from 3.2.1 to 3.3.0 by @app/dependabot in #77
  • deps: bump com.twelvemonkeys.imageio:imageio-webp from 3.12.0 to 3.13.1 by @app/dependabot in #78
  • deps: bump ch.qos.logback:logback-core from 1.5.18 to 1.5.25 in the maven group across 1 directory by @app/dependabot in #81
  • Bump com.github.junrar:junrar from 7.5.5 to 7.5.10 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #82
  • Bump org.bouncycastle:bcprov-jdk18on from 1.81 to 1.84 in /airsonic-main in the maven group across 1 directory by @app/dependabot in #83
  • #80 [Chore]: Extract SubsonicID3Controller from SubsonicRESTController by @litebito in #84
  • #85 [Chore]: Extract SubsonicSearchController from SubsonicRESTController by @litebito in #86
  • #87 [Chore]: Ignore liquibase-core major version updates in Dependabot by @litebito in #88
  • #89 [Chore]: Extract SubsonicAnnotationController from SubsonicRESTController by @litebito in #90
  • #91 [Chore]: Extract SubsonicPlayQueueController from SubsonicRESTController by @litebito in #92
  • #93 [Chore]: Extract SubsonicBookmarkController from SubsonicRESTController by @litebito in #94
  • #95 [Chore]: Extract SubsonicShareController from SubsonicRESTController by @litebito in #96
  • #97 [Chore]: Extract SubsonicPodcastController from SubsonicRESTController by @litebito in #99
  • #100 [Chore]: Extract SubsonicRadioController from SubsonicRESTController by @litebito in #101
  • #102 [Chore]: Extract SubsonicJukeboxController from SubsonicRESTController by @litebito in #103
  • #104 [Chore]: Extract SubsonicPlaylistController from SubsonicRESTController by @litebito in #105
  • #106 [Chore]: Extract SubsonicArtistInfoController from SubsonicRESTController by @litebito in #107
  • #108 [Chore]: Extract SubsonicUserController from SubsonicRESTController by @litebito in #109
  • #110 [Chore]: Extract SubsonicMediaController from SubsonicRESTController by @litebito in #111
  • deps: bump com.google.guava:guava from 33.4.8-jre to 33.6.0-jre by @app/dependabot in #150
  • deps: bump org.apache.commons:commons-configuration2 from 2.12.0 to 2.14.0 by @app/dependabot in #151
  • deps: bump com.google.errorprone:error_prone_annotations from 2.41.0 to 2.49.0 by @app/dependabot in #152
  • deps: bump net.java.dev.jna:jna from 5.17.0 to 5.18.1 by @app/dependabot in #153
  • deps: bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0 by @app/dependabot in #154

Full Changelog: v13.0.0...v13.1.0-rc.1

v13.0.0

Choose a tag to compare

@github-actions github-actions released this 29 Apr 19:50

What's Changed

Bug Fixes

  • #21 [Bug]: VersionService logs warning on GitHub API 404 when paginating releases by @litebito in #23
  • #30 CodeQL workflow fails with OutOfMemoryError during analysis by @litebito in #31
  • #40 Rebrand Subsonic API response type from Airsonic-Advanced to Airsonic-Pulse by @litebito in #41
  • #42 Default transcoding command missing %S seek parameter on MariaDB by @litebito in #43
  • #44 SecurityServiceTest.recoverCredentialWithExistingUser fails on CI — expects hex but gets bcrypt by @litebito in #45
  • #47 VersionServiceTest fails — version check returns false for new versions available by @litebito in #48

Hardening & Security

  • #15 Hardening: audit records conversion for null safety by @litebito in #18
  • #16 Hardening: audit synchronized-to-ReentrantLock migration for lock safety by @litebito in #19
  • #17 Hardening: audit for resource leaks (streams, connections, processes) by @litebito in #20

Infrastructure & CI

  • #26 Add Debian-style EnvironmentFile path to systemd unit by @litebito in #27
  • #28 Add installation documentation and Linux install scripts by @litebito in #29
  • #36 Split ci.yml into pr_ci.yml and pm_ci.yml by @litebito in #37
  • #38 Add multi-database test matrix to pr_ci.yml by @litebito in #39
  • Workflow maintenance: fix Trivy, disable CodeQL by @litebito in #46
  • #49 Update Docker containerization for Java 21 and rebrand by @litebito in #50
  • #51 Update release workflow: fix runner compatibility, add Docker image publish by @litebito in #52

Full Changelog: v12.0.0...v13.0.0

v13.0.0-beta.1

v13.0.0-beta.1 Pre-release
Pre-release

Choose a tag to compare

@litebito litebito released this 19 Apr 18:29
691a03a

What's Changed

Bug Fixes

  • #21 [Bug]: VersionService logs warning on GitHub API 404 when paginating releases by @litebito in #23

Hardening & Security

  • #15 Hardening: audit records conversion for null safety by @litebito in #18
  • #16 Hardening: audit synchronized-to-ReentrantLock migration for lock safety by @litebito in #19
  • #17 Hardening: audit for resource leaks (streams, connections, processes) by @litebito in #20

Full Changelog: v12.0.0...v13.0.0-beta.1

v13.0.0-alpha.202604191539

Pre-release

Choose a tag to compare

@litebito litebito released this 19 Apr 13:40
691a03a

What's Changed

  • hardening: audit records for null safety (fixes #15) by @litebito in #18
  • hardening: audit ReentrantLock conversions for lock safety (fixes #16) by @litebito in #19
  • hardening: audit and fix resource leaks (fixes #17) by @litebito in #20
  • fix: treat GitHub API 404 as end-of-pagination in VersionService (fixes #21) by @litebito in #23

New Contributors

Full Changelog: v12.0.0...v13.0.0-alpha.202604191539