From 931167f19798ecd38fa2945d6ef90670f79f7e96 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Tue, 9 Jun 2026 23:03:49 -0700 Subject: [PATCH] Release 2.6.9: version bump + docker-publish default + What's New changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps _SOULSYNC_BASE_VERSION 2.6.8 → 2.6.9, the docker-publish workflow's default version tag, and adds the 2.6.9 What's New entry (15 items, security fixes first: #832 launch-PIN enforcement and the settings-secret leak, then #833/#831/#830/#829/#828/#827/#825/#824/#823/#740, Spotify (no auth), multi- artist tags, decimal-volume dedup). --- .github/workflows/docker-publish.yml | 4 ++-- web_server.py | 2 +- webui/static/helper.js | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 70344823..34e38beb 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -9,9 +9,9 @@ on: workflow_dispatch: inputs: version_tag: - description: 'Version tag (e.g. 2.6.8)' + description: 'Version tag (e.g. 2.6.9)' required: true - default: '2.6.8' + default: '2.6.9' jobs: build-and-push: diff --git a/web_server.py b/web_server.py index c2cc2017..59bf8074 100644 --- a/web_server.py +++ b/web_server.py @@ -40,7 +40,7 @@ logger = setup_logging(_log_level, _log_path) # App version — single source of truth for backup metadata, system-info, update check, etc. # Semver: MAJOR.MINOR.PATCH. Bump at each dev→main release. -_SOULSYNC_BASE_VERSION = "2.6.8" +_SOULSYNC_BASE_VERSION = "2.6.9" def _build_version_string(): """Append short commit hash to version when available (e.g. 2.35+abc1234).""" diff --git a/webui/static/helper.js b/webui/static/helper.js index b512605c..98c9fece 100644 --- a/webui/static/helper.js +++ b/webui/static/helper.js @@ -3413,6 +3413,24 @@ function closeHelperSearch() { // projects that span multiple commits before shipping. Strip the flag at // release time and add a real `date:` line at the top of the version block. const WHATS_NEW = { + '2.6.9': [ + { date: 'June 9, 2026 — 2.6.9 release' }, + { title: 'Security: the admin launch PIN is now actually enforced (#832)', desc: 'the "require PIN on launch" lock used to be a client-side overlay only — the server never checked it, so removing the overlay (Safari\'s "Hide Distracting Items", browser devtools, or any non-browser client like curl) gave full access to every API. If you expose SoulSync publicly through a reverse proxy, that was wide open. There is now a real server-side gate: while the launch PIN is on, every request from an unverified session is rejected until the PIN is entered — data, settings, profile management, even websockets. A side door was closed too (the internal API-key endpoints were "no auth required", so a key could be minted to bypass the lock). The public REST API still works for valid API-key holders, and a deep link / refresh while locked now lands on the lock screen instead of raw JSON. Worth saying plainly: this makes the launch PIN do its job — for public exposure, keep your reverse proxy\'s own auth in front of it too.', page: 'settings' }, + { title: 'Security: your saved secrets stop being sent to the browser', desc: 'the settings API returned the full decrypted config to the browser — every API key, OAuth secret, Plex/Jellyfin token, and service password in cleartext. They are encrypted at rest, but that does nothing once the API hands the plaintext to the client (devtools, HAR captures, an XSS, a screen share). Configured secrets are now masked before they leave the server (shown as dots in the settings fields); saving an untouched form keeps the real value, editing replaces it, and clearing a field still removes it. No secret reaches the browser anymore.', page: 'settings' }, + { title: 'Delete now works on tracks with curly apostrophes (#833, the-hang-man)', desc: 'a track like "I\'m Upset" could delete its database row but leave the file behind. The library stored the title with a curly apostrophe (what Spotify/Apple metadata uses) while the file was written to disk with a straight one, so the delete compared the wrong characters and missed. File resolution now folds typographic look-alikes (curly vs straight quotes, en/em dashes, ellipsis) when matching the on-disk file — it never renames, just finds the file that\'s actually there. Fixes existing mismatched files with no re-import, and covers sidecar cleanup and dead-file checks too, not just delete.', page: 'library' }, + { title: 'Watchlist live scan — a full revamp with a per-run History (#831)', desc: 'the watchlist scan display was rebuilt. A bespoke live deck shows the artist being scanned with a large portrait, a real progress bar, found/added counters, and a live feed of exactly which tracks a scan found and added to your wishlist — with zero layout shift as data arrives. A new History button opens a modal of every past scan and the tracks each one added (not downloaded — added to the wishlist by the watchlist), persisted per run. The Download Origins view also groups its entries by action, and the whole page got the house-style chip buttons and a reskinned Global Settings modal.', page: 'watchlist' }, + { title: 'Spotify (no auth): renamed, default for enrichment, and fuller search', desc: '"Spotify Free" is now "Spotify (no auth)" — a clearer name for the credential-free metadata source. A new always-visible toggle in the Spotify settings lets the background enrichment worker use it (on by default); if you have Spotify auth, enrichment works either way, and the toggle overrides. The no-auth source also gained album search (via the artist\'s discography, closing a gap where it could only do tracks), and the daily-budget → free bridge now actually diverts to the free source when your real-API budget is spent instead of stalling.', page: 'settings' }, + { title: 'Owned tracks stop coming back to the wishlist (#825, carlosjfcasero)', desc: 'two fixes so tracks you already own stop reappearing as wishlist items. Manually adding an album to the wishlist now checks ownership and skips tracks you have. And the track matcher no longer reads a bracketed subtitle (e.g. a "(Bonus Track)" or remix qualifier) as a different song, so a track you own isn\'t treated as missing — version markers (live / remix / acoustic) are still respected and not collapsed.', page: 'watchlist' }, + { title: 'Playlist sync append: no more duplicates, and it honors your sync mode (#823)', desc: 'append mode no longer re-adds every track on each run (which turned playlists into N copies of themselves) — it now dedupes against what\'s already there. Automated syncs also honor the per-playlist sync mode you configured instead of always running "replace".', page: 'sync' }, + { title: 'Download Discography: explains skips + credits collaborations correctly (#830, Vicky)', desc: 'downloading an artist\'s discography no longer silently skips tracks with a flat "No new tracks". It now shows why each was skipped — already owned, by a different artist, or filtered out. And collaboration tracks credited as one combined string (e.g. "A, B & C") are matched correctly instead of being dropped as an artist mismatch.', page: 'library' }, + { title: 'Album downloads reuse an existing folder (#829)', desc: 'when an album already has a folder on disk, a new batch download drops into it instead of creating a split second folder — so an album stays in one place.', page: 'downloads' }, + { title: 'Dead File Cleaner stops flagging your whole library (#828)', desc: 'the Dead File detector no longer marks an entire library as dead when the files are simply unreachable (an unmounted or temporarily missing path). When an implausibly large share of tracks come back unresolvable, it now treats that as an environment problem and aborts with zero findings instead of proposing to delete everything.', page: 'dashboard' }, + { title: 'Settings stop flooding the log (#827)', desc: 'sitting on the Logs tab no longer triggers settings auto-saves that spam app.log with save lines — auto-save is suppressed while you\'re viewing logs.', page: 'settings' }, + { title: 'Wishlist album bundles no longer jam the download pool (#740, Sokhi)', desc: 'per-album wishlist bundles are serialized so they stop flooding the shared search/download workers and blocking everything behind them.', page: 'downloads' }, + { title: 'Multi-artist tags apply on Search → Download Now (Netti93)', desc: 'a track downloaded straight from Search now carries its real metadata source through, so your multi-artist tagging settings actually take effect instead of being lost on the way to the downloader.', page: 'search' }, + { title: 'Full release dates written to your tags (#824)', desc: 'the tag writer stores and writes full yyyy-mm-dd release dates end to end instead of downgrading them to just the year.', page: 'library' }, + { title: 'Watchlist stops re-flagging decimal-volume albums as duplicates (Sokhi)', desc: 'different decimal-volume editions of an album (e.g. "Vol. 4" vs "Vol. 4.5") are no longer treated as the same album, so the watchlist stops collapsing or skipping one of them.', page: 'watchlist' }, + ], '2.6.8': [ { date: 'June 7, 2026 — 2.6.8 release' }, { title: 'Blocklist — never download an artist, album, or track again', desc: 'a proper artist / album / track blocklist, reachable from the Blocklist button on the Watchlist page. Add an entry by pasting a Spotify / iTunes / Deezer / MusicBrainz link or ID; SoulSync resolves it and, in the background, matches it across your other sources so a ban added by Spotify ID also blocks the same thing arriving via Deezer or Tidal. Bans are enforced everywhere it matters: the wishlist never re-adds a blocked item, the download queue (playlist sync / album / discography) skips them, and starting a manual download of a blocked artist asks for a "download anyway?" confirm first. Profile-scoped, and your old Discovery blacklist is migrated in automatically.', page: 'watchlist' },