navidrome/server/subsonic
Deluan Quintão da56df3160
feat(smartplaylist): extend isMissing/isPresent to bpm, bitDepth and many text fields (#5603)
* feat(smartplaylist): support isMissing/isPresent on mbz_* and lyrics fields

Mark the six mbz_* MusicBrainz ID columns and the lyrics column as Nullable
in the criteria field map, then extend missingExpr to handle string columns
where absence is encoded as NULL or empty string (plus '[]' for lyrics). The
Numeric/Boolean path (ReplayGain) is preserved via an explicit type check.

* refactor(model): make MediaFile BPM and BitDepth nullable pointers

Convert BPM and BitDepth fields in model.MediaFile from int to *int so that
'tag absent' is distinguishable from zero. The metadata mapper now uses
NullableFloat for BPM (nil when absent or zero/unparseable) and only sets
BitDepth when the audio property is non-zero (lossy codecs report 0).

All read sites use gg.V() for zero-fallback deref so Subsonic API output and
transcoding behaviour are byte-identical to before. The persistence layer
bridges the existing NOT NULL DB columns by coercing nil to 0 on write and 0
back to nil on read in PostMapArgs/PostScan; a later migration task will drop
those constraints.

Hash upgrade safety is verified by a new MediaFile.Hash describe block: nil
*int hashes identically to the old int(0) default via ZeroNil+IgnoreZeroValue,
so no files will be spuriously re-imported after this change.

Extra files touched beyond the plan's list: core/stream/legacy_client_test.go
(BitDepth in model.MediaFile literals), persistence/mediafile_repository.go
(NOT NULL bridge).

* test(model): pin pre-conversion golden hashes for BPM/BitDepth

* feat(smartplaylist): support isMissing/isPresent on bpm and bitDepth

* feat(db): make bpm and bit_depth columns nullable, backfill 0 to NULL

Drop the NOT NULL constraint on media_file.bpm and bit_depth via a
lossless migration that converts legacy 0-means-absent values to real
NULL. Remove the temporary shim in PostScan/PostMapArgs that was bridging
the old NOT NULL columns to the *int model fields. Add round-trip
persistence tests asserting NULL storage for nil pointers and correct
value round-trip for non-nil pointers.

* test(e2e): verify isMissing/isPresent partition for nullable fields

Add DescribeTable covering bpm, bitdepth, lyrics, and mbz_recording_id:
for each field, isMissing + isPresent song counts must equal the total
library count, proving the nullable-column SQL is exhaustive and correct.

* test(e2e): seed bpm tag so isMissing/isPresent partition is non-trivial

* fix(model): omit bitDepth from JSON when absent instead of emitting null

* feat(smartplaylist): support isMissing/isPresent on more string fields

Enable isMissing/isPresent operators for album, comment, catalognumber,
discsubtitle, albumcomment, sorttitle, sortalbum, sortartist,
sortalbumartist, and explicitstatus by marking them Nullable in fieldMap.

* refactor(smartplaylist): unify missingExpr column logic into one flow

Collapse the numeric/string fork in missingExpr into a single
empties-driven loop (numeric/boolean fields simply have no empties),
and replace the duplicated IsTag/IsRole guard with a three-way switch
that expresses the dispatch model once. No SQL semantics change for
string fields; numeric/boolean fields now emit a single-element Or/And
which squirrel parenthesizes (e.g. `(col IS NULL)` instead of bare
`col IS NULL`) — update the affected test expectations accordingly.
2026-06-13 13:15:20 -04:00
..
filter fix(subsonic): use SQLite RANDOM() sorting in getRandomSongs, for faster results 2026-06-05 08:14:00 -04:00
responses fix(subsonic): mark AlbumID3 songCount and created as required 2026-05-22 18:42:17 -03:00
album_lists.go feat(plugins): add PlaybackReport to scrobbler capability (#5452) 2026-05-02 16:14:53 -04:00
album_lists_test.go feat(subsonic): add sonicSimilarity extension as plugin capability (#5419) 2026-04-27 17:50:09 -04:00
api.go fix(share): enforce per-user ownership on share reads 2026-06-05 15:50:59 -04:00
api_suite_test.go feat:(server): support reading lyrics from filesystem (#2897) 2025-04-30 08:10:19 -04:00
api_test.go fix(share): enforce per-user ownership on share reads 2026-06-05 15:50:59 -04:00
bookmarks.go fix(subsonic): always return required playqueue fields (#5172) 2026-03-12 08:29:37 -04:00
browsing.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
browsing_test.go feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
helpers.go feat(smartplaylist): extend isMissing/isPresent to bpm, bitDepth and many text fields (#5603) 2026-06-13 13:15:20 -04:00
helpers_test.go fix(subsonic): mark AlbumID3 songCount and created as required 2026-05-22 18:42:17 -03:00
jukebox.go refactor: small improvements and clean up (#3423) 2024-10-22 22:54:31 -04:00
library_scanning.go fix(subsonic): require admin access for Subsonic management endpoints (#5510) 2026-05-19 14:23:38 -03:00
library_scanning_test.go fix(subsonic): require admin access for Subsonic management endpoints (#5510) 2026-05-19 14:23:38 -03:00
media_annotation.go feat(subsonic): implement playbackReport OpenSubsonic extension (#5442) 2026-04-30 23:04:05 -04:00
media_annotation_test.go feat(plugins): add PlaybackReport to scrobbler capability (#5452) 2026-05-02 16:14:53 -04:00
media_retrieval.go chore(lint): upgrade golangci-lint, fix/ignore new errors 2026-05-01 14:22:51 -04:00
media_retrieval_test.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
middlewares.go fix(subsonic): require admin access for Subsonic management endpoints (#5510) 2026-05-19 14:23:38 -03:00
middlewares_test.go fix(subsonic): require admin access for Subsonic management endpoints (#5510) 2026-05-19 14:23:38 -03:00
opensubsonic.go feat(subsonic): implement playbackReport OpenSubsonic extension (#5442) 2026-04-30 23:04:05 -04:00
opensubsonic_test.go feat(subsonic): implement playbackReport OpenSubsonic extension (#5442) 2026-04-30 23:04:05 -04:00
playlists.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
playlists_test.go feat(subsonic): add sonicSimilarity extension as plugin capability (#5419) 2026-04-27 17:50:09 -04:00
radio.go fix(subsonic): return proper artwork ID format in getInternetRadioStations 2026-04-02 15:44:20 -04:00
radio_test.go fix(subsonic): return proper artwork ID format in getInternetRadioStations 2026-04-02 15:44:20 -04:00
searching.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
searching_test.go feat(subsonic): add sonicSimilarity extension as plugin capability (#5419) 2026-04-27 17:50:09 -04:00
sharing.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
sonic_similarity.go feat(subsonic): add sonicSimilarity extension as plugin capability (#5419) 2026-04-27 17:50:09 -04:00
stream.go fix(transcoding): cap concurrent transcodes to prevent ffmpeg DoS (#5522) 2026-05-24 00:24:30 -03:00
system.go Some cleanup, adding missing context handling 2022-12-06 19:57:47 -05:00
transcode.go fix(transcode): log warning for invalid or stale transcode tokens 2026-06-06 11:00:27 -04:00
transcode_test.go feat(smartplaylist): extend isMissing/isPresent to bpm, bitDepth and many text fields (#5603) 2026-06-13 13:15:20 -04:00
users.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
users_test.go refactor: rename EnableCoverArtUpload to EnableArtworkUpload 2026-03-27 19:33:46 -04:00