feat(api): deliver loudness gains to every client (M464 #4997)
release / web (push) Successful in 1m44s
release / go (push) Successful in 2m12s
release / govulncheck (push) Successful in 40s
release / android (push) Successful in 5m28s
release / Build signed APK (releases and dev) (push) Successful in 4m42s
release / integration (push) Successful in 15m33s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m21s
release / Verify release artifacts (tag releases only) (push) Skipped
release / web (push) Successful in 1m44s
release / go (push) Successful in 2m12s
release / govulncheck (push) Successful in 40s
release / android (push) Successful in 5m28s
release / Build signed APK (releases and dev) (push) Successful in 4m42s
release / integration (push) Successful in 15m33s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m21s
release / Verify release artifacts (tag releases only) (push) Skipped
ReplayGain 2.0 values (gain to -18 LUFS, linear peak) derived from the stored track and album loudness: - Web: GET /api/tracks/replay-gain?ids=... (up to 200), a lookup the player calls for its queue, rather than a field on every TrackRef surface. - Android: track_gain/track_peak and album_gain/album_peak on the sync views, so cached tracks level offline. Storing a measurement logs a track change, and an album's values moving logs an album change, both before the write (#2704), so caches pick the gains up. - OpenSubsonic: replayGain on every song (album, getSong, search3, starred), as a JSON object and an XML element. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -185,9 +185,10 @@ func (b *browseHandlers) getAlbum(w http.ResponseWriter, r *http.Request) {
|
||||
WriteFail(w, r, ErrGeneric, "Failed to load tracks")
|
||||
return
|
||||
}
|
||||
gains := replayGains(r.Context(), q, tracks)
|
||||
songs := make([]SongRef, 0, len(tracks))
|
||||
for _, t := range tracks {
|
||||
songs = append(songs, songRef(t, album.Title, artist.Name))
|
||||
songs = append(songs, songRef(t, album.Title, artist.Name, gains[t.ID]))
|
||||
}
|
||||
Write(w, r, AlbumResponse{
|
||||
Envelope: NewEnvelope("ok"),
|
||||
@@ -230,7 +231,7 @@ func (b *browseHandlers) getSong(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
Write(w, r, SongResponse{
|
||||
Envelope: NewEnvelope("ok"),
|
||||
Song: songRef(track, album.Title, artist.Name),
|
||||
Song: songRef(track, album.Title, artist.Name, replayGains(r.Context(), q, []dbq.Track{track})[track.ID]),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -386,6 +387,7 @@ func (b *browseHandlers) search3(w http.ResponseWriter, r *http.Request) {
|
||||
WriteFail(w, r, ErrGeneric, "Song search failed")
|
||||
return
|
||||
}
|
||||
gains := replayGains(r.Context(), q, tracks)
|
||||
for _, t := range tracks {
|
||||
album, aerr := q.GetAlbumByID(r.Context(), t.AlbumID)
|
||||
if aerr != nil {
|
||||
@@ -397,7 +399,7 @@ func (b *browseHandlers) search3(w http.ResponseWriter, r *http.Request) {
|
||||
WriteFail(w, r, ErrGeneric, "Song search failed")
|
||||
return
|
||||
}
|
||||
result.Songs = append(result.Songs, songRef(t, album.Title, artist.Name))
|
||||
result.Songs = append(result.Songs, songRef(t, album.Title, artist.Name, gains[t.ID]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user