github Nezreka/SoulSync 2.6.4
Version 2.6.4

3 hours ago

SoulSync 2.6.4 — Merge devmain

Patch release on top of 2.5.2 (the last tag on main). The 2.6.x line was bumped on dev but never tagged / published, so this is the first time all of it reaches users. Big patch — bug fixes across downloads / wishlist / library, a chunk of internal hardening, and some UI consistency work.

Version constant: 2.6.4 (web_server.py:_SOULSYNC_BASE_VERSION).


Headline bug fixes

#745 — Every download saved with the first 4 chars of the album name where $year should be.
The $year template variable was a blind release_date[:4] slice. When something upstream put the album NAME into release_date, that slice emitted garbage — "Mantras (Deluxe)"[:4]"Mant", so files landed in Mantras (Deluxe) (Mant) [Album]/ instead of (2026). Added _extract_year_from_release_date(): returns the leading 4 chars only when they're a plausible year (isdigit, 1900 < y ≤ 2100), else empty — matching the guard soulid_worker._extract_year already uses. A non-year now resolves to empty and the template's bracket-cleanup drops the empty (). Fixed at the shared post-process path builder (core/imports/paths.build_final_path_for_track) that downloads, reorganize, AND imports all route through, so it's covered everywhere at once. Defensive layer — stops the symptom regardless of which upstream poisons release_date; the exact upstream (Soulseek + AcoustID + a future-dated album) is still being chased separately.

#746 — Library Reorganize pulling files back out of the /deleted folder.
The Duplicate Cleaner quarantines de-duped files into <transfer>/deleted/. If a user's media server scans the transfer folder (e.g. a /music root holding both the library and the transfer dir), those quarantined files get real DB rows — and Reorganize, being purely DB-driven, would dutifully move them back OUT of /deleted to the template location. We can't stop the rows existing (they come from the server), so the fix is bounded to Reorganize as the reporter asked: skip any track whose resolved path is under <transfer>/deleted. Anchored to the prefix (not a substring) so a real album like "Deleted Scenes" is kept; surfaced as a skip in the preview, mirrored on apply.

#740 — Wishlist album-bundle downloads jamming the whole queue.
Per-album wishlist bundles were blocking the shared 3-worker missing-tracks pool (a 2.6.3 regression). Moved album-bundle downloads onto a dedicated album_bundle_executor so a big bundle can't starve the per-track pool. User-validated on a 73-track run.

#747 — Soulseek album poll hangs on a stalled/dead peer.
A peer that stalls (stuck InProgress/Queued, dropped transfers, or "Completed, Aborted" at 0 bytes) used to spin the bundle poll to its full ~6h timeout. Three fixes: a 180s bundle-level stall guard (resolves with whatever completed when nothing progresses); "Aborted"/"Cancelled" correctly classified as failed (the "Completed, Aborted" string was misread as a completed-but-missing file); and when the chosen folder yields nothing usable, fall back to the proven per-track flow instead of hard-failing the batch. failed batches now also age out of the stuck-batch cleanup.

#732 — Search results disappear when interacting with the media player.
The outside-click allow-lists didn't include the player popup, so clicking into the mini-player counted as a click-away and tore down the results. Added the player to the allow-lists.

#735 — Import overwriting the album-artist tag to "Unknown Artist".
#736 — Spotify playlist sync showing only the first 100 tracks (pagination cutoff).
#722 — Duplicate tracks in albums with Japanese / CJK titles (similarity normalisation didn't fold CJK width/forms).
#721 — Usenet album bundles stuck on "downloading release" / 99–100%. Two-stage SAB History handoff: 2.6.4 covers the gap where SAB flips status to Completed before its post-processing writes the final storage field, plus writable staging dir + client→local path resolution.
#728 — Usenet album progress fetch from SAB (contributor PR, @IamGroot60).

Also: enhanced artist view no longer 404s for library artists opened via source ID; MusicBrainz artist discography no longer capped at 25 releases; album art now embeds at highest available resolution across all art paths (HiFi/MB cover art uses the CAA 1200px thumbnail instead of the flaky /front original).


Internal hardening (no user-facing change, lots of safety)

Wishlist orchestration unified. Manual wishlist runs now route through the same shared engine as auto (_run_wishlist_cycle), with batch-row construction centralized in make_wishlist_batch_row. Auto behavior proven unchanged; manual is now identical to auto instead of a parallel reimplementation.

DB schema hardening. Added a schema_migrations ledger + PRAGMA user_version backstop; stopped watchlist_artists rebuilds from silently dropping amazon_artist_id; normalized legacy comma-separated genres to canonical JSON; introduced a canonical source-ID registry (core/source_ids.py) and adopted it at the highest-value sites.

Discovery endpoints lift (10 commits). Collapsed ~10 families of near-duplicate per-source discovery endpoints (get_*_sync_status, reset_*_playlist, start_*_sync, etc.) into shared helpers.

Exception surfacing. ~70 silent except: pass sites across remaining modules now log instead of swallowing.


UI consistency

  • New shared .btn primitive + .btn--sm/.btn--block/.btn--warning tiers; migrated config-modal, wishlist, watchlist, and sync-history button families onto it.
  • Shared .page-shell card primitive extracted; automations, playlist-explorer, settings, sync, and exception pages adopted it. .tab / .card primitives added.
  • Downloads moved above Automations in the sidebar.
  • Tools → Database Updater: added a Deep Scan option.
  • Basic search visual overhaul + per-source picker in hybrid mode; standardized artist-detail hero action buttons.

Test plan

  • Full suite green on dev (4400+ tests)
  • #745: import-path tests — differential-verified the poisoned release_date produces the exact (Mant) folder without the fix, clean folder with it; real years preserved
  • #746: 8 unit + 2 integration reorganize tests, differential-verified; 128 adjacent reorganize tests green
  • #747: 6 poll-stall + 2 fallback tests (fake-clock), differential-verified; live Slipknot run no longer wedges the queue
  • #740: dedicated-pool fix user-validated on a 73-track run

Don't miss a new SoulSync release

NewReleases is sending notifications on new releases.