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—/streamhonorstimeOffsetaccurately via source-side ffmpeg seek (was a wasteful post-encoding byte skip)songLyrics v1— new/getLyricsBySongIdendpoint returns structured lyrics/getPodcastEpisode— new endpoint for per-episode lookupAlbumID3.playCountwiring (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:
- GitHub Actions SHA pinning (#175) — every workflow
uses:reference is pinned to a full commit SHA with a# v<N>.x.xannotation, eliminating the "moving target" risk of tag-based pins. - 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.
- 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 verifyagainst this repo's release workflow identity. Seedocs/security/image-verification.mdfor 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 audiotranscoding 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 %oimmediately afterffmpeg %Sin 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 %oafterffmpeg %Sto 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
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.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