navidrome/persistence
Deluan Quintão 2e03766a9d
fix(playlist): preserve smart playlist song count on re-import (#5907) (#5908)
* fix(playlist): preserve smart playlist counters on re-import (#5907)

* perf(playlist): skip re-importing unchanged NSP files (#5907)

* feat(playlist): also store content hash for M3U imports (unused for now)

* fix(playlist): return stored record when skipping unchanged NSP import

Skipping before copying the stored identity broke the ImportFile(sync=false)
contract: callers received an ID-less playlist and the requested Sync change
was silently dropped.

* refactor(playlist): hash imports once at the caller; protect smart counters in Put

Move content hashing out of both parsers into the code that owns the file
(parsePlaylist and ImportFile), removing the NSP double-buffer and the
duplicated hashing idiom. Put now drops song_count/duration/size for smart
playlists (PostMapArgs), disarming the counter-zeroing trap for all callers.

* fix(playlist): invalidate imported hash when rules are edited via API

Without this, a rules edit through the REST API kept the stored file hash,
so every scan skipped the unchanged file and never restored the file-backed
rules while sync was on.

* test(playlist): verify smart counters survive a re-import, end to end

The existing Put test seeds the stored counters with a raw SQL update, so it
pins the guard in PostMapArgs but not the pipeline around it. This test drives
the counters through a real evaluation instead: it saves a smart playlist, reads
it with GetWithTracks to populate song_count/duration/size, then saves the
playlist the way the scanner rebuilds it after parsing the .nsp file, with the
counters back at zero. Both routes fail without the guard, and the new one
covers the exact sequence reported in #5907.

Test taken from #5970, which diagnosed the same root cause independently.

Co-authored-by: Junker der Provinz <133605895+junkerderprovinz@users.noreply.github.com>

* test(playlist): build the service with artwork.NewUploader

The artwork pipeline in #5847 replaced core.NewImageUploadService() with
artwork.NewUploader(ds) and updated every call site it could see. The five call
sites this branch adds were written against the old constructor, so the merge
applied cleanly but left the package uncompilable.

* fix(db): re-stamp the imported_hash migration after the master merge

Master gained three migrations while this branch was open, the newest being
20260816180040. The original 20260808200333 stamp now sorts before them, so any
database already upgraded past that point would skip this migration entirely and
never get the imported_hash column. Same SQL, current timestamp.

* refactor(playlist): hash imported playlists with xxh3 and the id encoding

ImportedHash is a change detector, not a security boundary, so it does not need
a cryptographic digest. xxh3 is already a direct dependency and is used the same
way to fingerprint files in the artwork image store. Encoding the 128-bit digest
with id.Encode stores it in the same 22-char base62 form as every other id in the
schema, down from 64 hex chars.

No migration is needed: the imported_hash column has not shipped in a release, so
no database holds a value in the old format.

* refactor(playlist): extract the imported-playlist fingerprint helper

Both import paths encoded the hash inline, so how a playlist file is fingerprinted
lived in two places. A third import path that encoded it differently would silently
never match the stored value, turning the unchanged-file skip into a no-op.

---------

Co-authored-by: Junker der Provinz <133605895+junkerderprovinz@users.noreply.github.com>
2026-08-18 20:58:55 -04:00
..
e2e feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
album_repository.go fix(db): keep album created_at in the driver's timestamp format when copying (#5867) 2026-08-16 14:14:41 -04:00
album_repository_test.go fix(db): keep album created_at in the driver's timestamp format when copying (#5867) 2026-08-16 14:14:41 -04:00
artist_repository.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
artist_repository_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
artwork_hydration.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
artwork_hydration_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
artwork_queue_repository.go feat(cli): add an artwork command group for diagnosing and re-driving artwork (#5957) 2026-08-14 21:07:56 -04:00
artwork_queue_repository_test.go feat(cli): add an artwork command group for diagnosing and re-driving artwork (#5957) 2026-08-14 21:07:56 -04:00
artwork_repository.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
artwork_repository_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
collation_test.go perf(db): index media_file album/artist sort orders (#5706) 2026-07-03 08:58:55 -04:00
criteria_sql.go feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00
criteria_sql_benchmark_test.go feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00
criteria_sql_test.go feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00
export_test.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
folder_repository.go fix(artwork): re-resolve artwork when image files change on disk (#5965) 2026-08-16 13:24:07 -04:00
folder_repository_test.go fix(artwork): re-resolve artwork when image files change on disk (#5965) 2026-08-16 13:24:07 -04:00
genre_repository.go fix(jellyfin): resolve genre id as a MusicGenre item 2026-08-10 21:37:57 -04:00
genre_repository_test.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
helpers.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
helpers_test.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
item_tags_test.go perf(genre): index genre filtering via join tables across all APIs (#5940) 2026-08-11 08:00:50 -04:00
library_repository.go perf(db): keep query planner statistics trustworthy with full ANALYZE (#5740) 2026-07-13 12:04:29 -04:00
library_repository_test.go perf(subsonic): speed up artist search3 deep-offset pagination (#5620) 2026-06-16 21:47:15 -04:00
mediafile_repository.go fix(artwork): re-resolve artwork when image files change on disk (#5965) 2026-08-16 13:24:07 -04:00
mediafile_repository_test.go fix(artwork): re-resolve artwork when image files change on disk (#5965) 2026-08-16 13:24:07 -04:00
persistence.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
persistence_suite_test.go fix(jellyfin): stream collection responses to prevent OOM on large libraries (#5783) 2026-07-15 14:07:00 -04:00
persistence_test.go fix(server): play queue should not return empty entries for deleted tracks 2024-09-20 11:22:37 -04:00
player_repository.go fix(share): enforce per-user ownership on share reads 2026-06-05 15:50:59 -04:00
player_repository_test.go fix(share): enforce per-user ownership on share reads 2026-06-05 15:50:59 -04:00
playlist_repository.go fix(playlist): preserve smart playlist song count on re-import (#5907) (#5908) 2026-08-18 20:58:55 -04:00
playlist_repository_test.go fix(playlist): preserve smart playlist song count on re-import (#5907) (#5908) 2026-08-18 20:58:55 -04:00
playlist_track_repository.go fix(playlists): chunk track deletes to stay under the SQLite variable limit (#5977) 2026-08-17 15:47:41 -04:00
playlist_track_repository_test.go fix(playlists): chunk track deletes to stay under the SQLite variable limit (#5977) 2026-08-17 15:47:41 -04:00
playqueue_repository.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
playqueue_repository_test.go fix(server): ensure single record per user by reusing existing playqueue ID 2025-06-11 17:26:13 -04:00
plugin_cleanup.go feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
plugin_cleanup_test.go feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
plugin_repository.go fix(plugins): clear plugin errors on startup to allow retrying 2026-03-02 08:56:56 -05:00
plugin_repository_test.go fix(plugins): clear plugin errors on startup to allow retrying 2026-03-02 08:56:56 -05:00
property_repository.go Replace beego/orm with dbx (#2693) 2023-12-09 13:52:17 -05:00
property_repository_test.go revert: separation of write and read DBs 2024-11-19 18:41:50 -05:00
radio_repository.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
radio_repository_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
scrobble_buffer_repository.go fix(plugins): discard buffered scrobbles when a plugin is removed (#5737) 2026-07-08 12:37:17 -04:00
scrobble_buffer_repository_test.go fix(plugins): discard buffered scrobbles when a plugin is removed (#5737) 2026-07-08 12:37:17 -04:00
scrobble_repository.go feat(server): add scrobble history Native API (#5761) 2026-07-13 11:32:03 -04:00
scrobble_repository_test.go feat(server): add scrobble history Native API (#5761) 2026-07-13 11:32:03 -04:00
share_repository.go feat(artist): add Share and Download actions to the Artist detail page (#5944) 2026-08-12 11:59:56 -04:00
share_repository_test.go feat(artist): add Share and Download actions to the Artist detail page (#5944) 2026-08-12 11:59:56 -04:00
smart_playlist_repository.go feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00
smart_playlist_repository_test.go fix(playlist): preserve smart playlist song count on re-import (#5907) (#5908) 2026-08-18 20:58:55 -04:00
sort_index_coverage_test.go perf(db): index media_file album/artist sort orders (#5706) 2026-07-03 08:58:55 -04:00
sql_annotations.go feat(playlists): per-user starred/rating annotations (backend) (#5749) 2026-07-14 07:38:25 -04:00
sql_annotations_test.go perf(db): skip annotation join in CountAll when unused (#5694) 2026-06-30 22:50:43 -04:00
sql_base_repository.go feat(instant-mix): support album, playlist and genre sources (#5948) 2026-08-12 23:02:13 -04:00
sql_base_repository_test.go test(db): fix flaky applyLibraryFilter specs on shared DB (#5697) 2026-07-01 13:58:33 -04:00
sql_bookmarks.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
sql_bookmarks_test.go revert: separation of write and read DBs 2024-11-19 18:41:50 -05:00
sql_participations.go perf(persistence): use *_artists join tables for artist participant filters (#5930) 2026-08-10 11:42:27 -04:00
sql_restful.go fix(nativeapi): make /api/song path filter work and use startsWith (#5566) 2026-06-05 14:00:08 -04:00
sql_restful_test.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
sql_search.go perf(subsonic): speed up artist search3 deep-offset pagination (#5620) 2026-06-16 21:47:15 -04:00
sql_search_fts.go feat(search): rank exact matches above prefix matches (#5704) 2026-07-02 15:51:03 -04:00
sql_search_fts_test.go feat(search): rank exact matches above prefix matches (#5704) 2026-07-02 15:51:03 -04:00
sql_search_like.go chore: go fix 2026-05-28 22:13:05 -03:00
sql_search_like_test.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
sql_search_test.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
sql_tags.go perf(genre): index genre filtering via join tables across all APIs (#5940) 2026-08-11 08:00:50 -04:00
tag_library_filtering_test.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
tag_repository.go feat(jellyfin): filter items by year and record label (#5817) 2026-07-19 12:39:31 -04:00
tag_repository_test.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
transcoding_repository.go fix: restrict transcoding config reads to admins (#5564) 2026-06-04 23:07:13 -04:00
transcoding_repository_test.go fix: restrict transcoding config reads to admins (#5564) 2026-06-04 23:07:13 -04:00
user_props_repository.go Replace beego/orm with dbx (#2693) 2023-12-09 13:52:17 -05:00
user_repository.go feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00
user_repository_test.go feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00