github Nezreka/SoulSync 2.7.3
Version 2.7.3

9 hours ago

SoulSync 2.7.3 — Merge devmain

patch release on top of 2.7.2. one new feature (Quality Upgrade Finder), a Tidal-discovery completeness fix, and a stack of import / wishlist / quarantine bug fixes.

Headline:

  • Quality Upgrade Finder — new findings-based job that finds tracks you own in worse quality than is available and upgrades them (ISRC-first, tiered matching, dedup-skip, duration guard). replaces the old auto-acting Quality Scanner.
  • #867 — Tidal playlist discovery capped at ~21 tracks → now walks the whole playlist, and the modal opens instantly instead of freezing ~10s. Reported against 2.7.2.
  • #880 — Tidal "Favorite Tracks" mirror truncating to ~98 of 500+ on a transient 429. Reported by @Yug1900.
  • #879 — opening Settings reset the whole config to defaults after a failed GET /api/settings. Reported by @Lysticity (2.7.0–2.7.2).
  • #877 — Download Discography filters didn't match Artist Detail (dead EPs toggle; no Live/Compilations/Featured). Reported by @HolyFredy.
  • #876 — Quarantine duplicates + stale count. Reported workflow pain: many failed attempts at one song, and a stale tab count on open.
  • #874 — wishlist re-downloads a removed/cancelled track forever. New TTL'd ignore-list.
  • "Track 01" bug — single tracks (esp. Deezer) imported as 01 regardless of real album position.

New

Quality Upgrade Finder

Replaces the old auto-acting Quality Scanner with a findings-based repair job: it scans the library for tracks you own in worse quality than a source can provide, surfaces them as findings, and you choose what to upgrade — nothing acts on your library behind your back.

  • Tiered structured matching: ISRC-first exact match (using the IDs enrichment already embedded) → album→track → artist+title.
  • Best-in-class picker: a direct track-ID tier for exact source matches, a dedup-skip so the same upgrade isn't re-found, and a duration guard so a wrong-but-similarly-named track can never be swapped in.
  • Old auto-acting Quality Scanner tool removed.

Fixes

#867 — Tidal playlist discovery only showed ~21 tracks

The discovery walk was capped; it now pages the full playlist. Plus a UX pass: the discovery modal renders before the blocking discovery-start POST and opens in its discovering state, so it no longer freezes the UI for ~10s on a track pre-fetch or leave an empty/loading modal interactable.

#880 — Tidal "Favorite Tracks" mirror captured ~98 of a 500+ collection

The cursor paginator (_iter_collection_resource_ids) bailed on any non-200 — including a transient 429 mid-walk — truncating a 524-track collection to 98. It now retries the same cursor page with backoff (5/10/15/20s, up to 4 attempts), mirroring the playlist paginator. 401/403 still bail + set the reconnect flag; other non-200s still break. 3 regression tests.

#879 — opening Settings reset the entire config to defaults

loadSettingsData() called response.json() without checking response.ok, so a failed GET /api/settings (its error body) was treated as settings → every field fell back to blank → autosave wrote the blanks over the real config. Fix (settings.js): bail before touching any field on a non-ok/error load, set window._settingsLoadFailed, and guard both save paths on it (cleared on next good load). Any load failure now leaves the saved config untouched. Regression test pins redacted_config stays a callable method.

#877 — Download Discography filters didn't match Artist Detail

The modal used the base get_artist_discography (which lumps EPs into singles) and only read {albums, singles} — so its EPs bucket was always empty (dead toggle), with no Live/Compilation/Featured classification. Now uses get_artist_detail_discography (same split Artist Detail uses) + a shared _classifyReleaseContent() adopted by both surfaces so they can't drift; adds Live/Compilations/Featured filters; the download payload is built from visible checked cards. Regression test pins EPs land in their own bucket.

#876 — Quarantine workflow: duplicates + stale count

  • Group duplicates + auto-clear: failed attempts at the same song group by their intended target track-id (isrc → source id → uri, name fallback for legacy sidecars) and render as a collapsible group; approving one auto-deletes the sibling alternatives. Hooked so the success-cleanup path is never mistaken for a duplicate.
  • Stale count: the quarantine tab badge now shows the real count the moment the modal opens, not a stale 0 until you click the tab.
  • 13 tests incl. the capture-before-approve ordering regression.

#874 — wishlist re-downloads a removed/cancelled track forever

A removed or user-cancelled wishlist track used to be re-added on the next auto cycle (watchlist scan, failed-capture, cancel re-add), so the same release downloaded → failed → re-queued endlessly. New TTL'd ignore-list (core/wishlist/ignore.py + wishlist_ignore table) gates the single add_to_wishlist funnel: softer than the blocklist (expires after 30 days, never blocks a manual force-download), fail-open throughout, manual add bypasses + clears. New "Ignored" view to see/undo. 13 tests incl. the success-cleanup-does-not-ignore regression.

"Track 01" bug — single tracks imported as 01

A single Deezer track is matched via /search/track, which omits track_position, so the import context never carried the real number — then service.py/context.py fabricated a confident track_number=1 that beat the real source. Fix: stop fabricating 1 (default to the 0 "unknown" sentinel), and recover the position from the downloaded file's own embedded tag (mutagen, no network), consulted last — only when metadata + filename both come up empty, so it can never override a value the old resolver produced (strictly additive, no regression for albums). 13 resolver tests incl. the no-regression precedence guards.

Smaller fixes

  • #870 — Deezer ARL "resets itself": the connection test ran against the redacted mask instead of the saved token, so a valid ARL read as broken. Tests the saved token now.
  • #868 — same-name artist mis-enriched: an artist sharing a name with another got the wrong Standard discography. Disambiguated by owned-catalog overlap during enrichment; a re-match clears the stored id.
  • Find & Add: a Title - Remix search now matches the base-titled track in your library.
  • Normalization: a colon in a title (T:T) now matches an underscore variant (T_T).
  • Sidebar UI: frosted-glass header blur, vertically-centered nav count badges, and the My-Accounts / Personal-Settings buttons hidden for admins (who use the global app account).

Earlier in 2.7.x (carried, already shipped)

  • 2.7.2 — playlist-folder mirroring, server-playlist M3U export, follow-only watchlist, SoundCloud-link + better YouTube imports, ReplayGain Filler + Empty Folder Cleaner maintenance jobs, HiFi restore-defaults.
  • 2.7.1 — download verification (AcoustID fingerprint-checks every download against what you asked for) + an unverified review queue; closed the websocket login-bypass (#852).
  • 2.7.0 — multi-user for real: per-profile streaming accounts (My Accounts), opt-in username/password login with recovery, reverse-proxy support.

Test plan

  • full imports + wishlist suites green (no regressions from the Track-01 de-poison or the ignore-list)
  • 13 quarantine ignore-list tests (#874) incl. success-cleanup regression
  • 13 quarantine-grouping tests (#876) incl. capture-before-approve ordering
  • 13 track-number resolver tests incl. embedded-tag precedence / no-regression guards
  • 3 Tidal 429-retry regressions (#880); settings-redaction regressions (#879); EP-split regression (#877)
  • ruff check . clean
  • Live: re-mirror Tidal Favorites grabs the full collection (#880)
  • Live: Deezer single-track import lands at its real album position (Track-01)
  • Live: Quality Upgrade Finder surfaces + upgrades a known lower-quality track

Post-merge checklist

  • Tag v2.7.3 on main
  • Trigger docker-publish.yml with version_tag: 2.7.3 (default already bumped) → boulderbadgedad/soulsync:2.7.3 + ghcr.io/nezreka/soulsync:2.7.3
  • Discord release announcement (auto-fired by the workflow)
  • Reply on #867 / #880 / #879 / #877 / #876 / #874 / #870 / #868 with the 2.7.3 release link

Don't miss a new SoulSync release

NewReleases is sending notifications on new releases.