| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| SoulSync-main.zip | 2026-08-19 | 33.1 MB | |
| README.md | 2026-08-19 | 14.3 kB | |
| Version 3.2.2 source code.tar.gz | 2026-08-19 | 31.5 MB | |
| Version 3.2.2 source code.zip | 2026-08-19 | 33.1 MB | |
| Totals: 4 Items | 97.7 MB | 0 | |
soulsync 3.2.2: dev → main
five features and twenty-one fixes, most of them from reports. seven community PRs merged in as well.
reassign an album to a different artist
TheHomeGuy asked for this: he had albums filed under the wrong artist, usually because a featured artist got picked up as the album artist. there was no way to move them.
⇄ Reassign now sits in the album action row next to Reorganize and Redownload (artist detail → enhanced view → expand an album). you search for the artist it should belong to, pick one of their releases, and see how your tracks line up before anything happens.
you never type an artist name, you pick a real one and then a real release of theirs. that way the identity handed to the import pipeline is one the source can actually resolve.
it doesn't retag or move anything itself. each track is staged with a hint naming the release you picked and the import pipeline re-files it, same as a single-track re-identify. tags, folder and database rows all come from the code that handles a fresh download, so there's no second version to drift.
the review screen matters because an album is many files. it shows every track, what it maps to and why (track number or title), plus anything it couldn't place. if some tracks don't line up it won't proceed without you saying so, because moving half an album leaves the rest under the old artist which is the exact thing you were trying to fix.
collapsible sidebar (#1155)
wishx runs soulsync in a narrow wavebox tab where a fixed 240px sidebar eats most of the width. one toggle in the sidebar header now shrinks it to 68px of icons, with the tab name on hover. persisted, off by default, desktop only.
collapsed, the section headers become divider lines and everything under them is shown. a chevron you can't read is a dead control — you'd click a nameless arrow, items would vanish, and nothing would tell you what you hid. your saved per-section collapse state is untouched and comes back when you expand.
prefer a specific version of a track
eN1gma: spotify often carries only the radio edit, but the extended mix is on soulseek and nothing went looking for it. Settings → Soulseek → "Prefer a version when one exists". off by default.
nothing is ever skipped. if that version isn't out there the normal one downloads exactly as before. picking live, remix, acoustic or instrumental lifts a protection that normally throws those out on sight, and the help text says so.
youtube premium quality (#1154)
lucasssvaz's PR, the biggest community one this release. youtube used to be the source that lied: flat video search has no artist/album and no formats, missing formats got invented as MP3 320, and opus bitrates showed as a dash.
- search can use the Music catalog with cookies.txt auth, so hits carry structured artist/album metadata and matching works the same shape as tidal
- downloads rank the real opus/aac formats against your quality profile. missing formats claim typical opus 160, never invented MP3 320. premium cookies unlock the premium itags (774/141) via the music player, and only a pasted cookies.txt counts — a browser cookie store is never treated as premium
- mixed best-quality pools score per source now: a soulseek peer no longer forces tidal and youtube through the P2P matcher, and a rejected youtube profile no longer wipes the rest of the pool
- opus bitrate is estimated from size/duration on import and scan, and VBR averages show as ~160 in Enhanced view instead of a dash
took a stabilization round after the merge: a config.settings import silently disabled transcode and catalog auth on a clean tree, matcher-rejected catalog hits now fall through to ytsearch, and expired cookies are only dropped on the last download retry.
remove a server playlist from the compare editor
the server tab could edit a playlist every way except delete it. a "remove from server" pill now sits next to export m3u — confirms, deletes the playlist on plex/jellyfin/navidrome (soulsync-made or not), closes the editor and drops back to the refreshed list. same id-first name-fallback shape as the rest of the edit family: plex and jellyfin delete-recreate on edit, so a stale page id gets re-resolved by name instead of 404ing with the live playlist standing.
fixes
- radio edits were being thrown away, including when you asked for one.
\bedit\blived in the remix patterns and remix was checked first, so "radio edit" classified as a remix — and remix is reject-on-sight. the file scored 0.0 and got binned even when spotify itself named the radio edit. ask for "Song - Radio Edit" now and you get it; ask for the plain version and you still get the plain version - #1150 per-item quality profiles were ignored at the soulseek search (Zombiehamser). assigning a profile to a wishlist row changed what got ACCEPTED at import but not what got CONSIDERED at search — the filter read the app-wide default. a strict item under a loose default downloaded lossy files and only failed at the guard
- #1127 navidrome paths whose filename is synthesized (Clover-Moss). the subsonic api builds the whole path from tags, so the filename can be wrong too, not just the album folder. dead file cleaner called every file unreachable and album completeness refused to fix anything
- navidrome's verified-empty check read the wrong folder key, so it could never pass with a music folder selected
- songs stuck on "Processing", blocking everything else (Sokhi). nothing was hung. one pool of 3 threads ran both soulseek searches and post-processing, and a search holds its thread 25-60s and usually finds nothing. a finished download queued behind them while showing "Processing" — his log has one waiting 52 seconds for 3 seconds of work. post-processing has its own pool now. measured 33.6s → 0.0s
- "Concurrent Downloads" did nothing above 3. the setting offers up to 10 and is a per-batch limit, but the worker pool was hardwired to 3, so seven of ten workers just sat in a queue while the help text promised a speedup. it's sized from the setting now (takes a restart)
- a batch wedged in post-processing had no rescue. the 30-minute timeout only ran when the batch had orphaned tasks, and a batch whose remaining tracks are all post-processing produces none. it held its slots forever
- the album stall-guard left zombie transfers in slskd (sassmastawillis). marking a stalled transfer failed only resolved our bundle — slskd kept the enqueue alive, so the file sat at "queued, remotely" for hours after the guard said it was handled, and a retry picking the same peer+file collided with the zombie instead of issuing a fresh request. the guard now cancels and removes each stalled transfer, same as the monitor's retry path
- quality quarantine wedged the task in Processing (Sokhi again). the verification wrapper pops the task id out of the context before the inner pipeline runs, so the quarantine branch's requeue and failed-marking always saw task_id=None and silently did nothing. the task then sat in post_processing until stuck detection reaped it at 30 minutes — one wedged download slot per quarantined file, 8x in one log
- the whole download subsystem logged nowhere app.log could see. twelve modules named their logger outside the hierarchy the file handler is attached to, so every completion check and stuck-task decision was invisible in the debug export users paste into issues. we were diagnosing Sokhi's stall half blind. 49 modules elsewhere are still silent
- #1157 deezer genres came back in the server's language (PfannkuchenWolf). deezer localises genre names by the caller's IP, so a german-hosted server got "Filme/Videospiele" instead of "Films/Games". one library ended up with both spellings and strict genre filtering matched neither. auditing the fallback map against the live list also turned up genre 464 filed as "Rap" when it is Metal
- deezer playlists always said NEVER SYNCED (TheHomeGuy). the endpoint returned a hardcoded literal — the field matched spotify's shape but never carried a value. sync ran, tracks downloaded, the card never updated
- loading a big deezer playlist looked broken (TheHomeGuy). "seems to hang... sit here for several minutes doing nothing". a 1200-track playlist resolves ~900 albums for release dates and real track numbers, and the spinner said nothing. that work can't go away — it's what the download picker uses to choose and verify a candidate — so it narrates now: "release dates 340/877 (39%)"
- every deezer caller now shares one rate budget. there were three numbers and none agreed: a decorator enforcing 1/s on nine methods, two playlist loops obeying nothing and counted by nothing, and an undocumented ceiling. eleven bypassing call sites found, all routed through one throttle at 8/s
- #1159 tracks with punctuation were unfindable in the manual-match search
(AfonsoG6). the fuzzy fallback split on whitespace only, so "Would've,
Could've, Should've" asked the database for
%would've,%— matching only a title that has the comma too. a file tagged without them matched one word and ranked third behind unrelated songs - the other four [#1159] issues, same report (AfonsoG6). cached auto-matches pinned the compare view and shadowed a saved manual match; replace-track didn't persist the pairing, so a corrected bad match reverted on the next compare; jellyfin edits 404'd on stale playlist ids where plex re-resolved by name; and jellyfin thumbs came from a raw unauthenticated url that answered 200 with an empty body — they go through the token-safe image proxy now. all five items in that report are closed
- #1151 a slow prowlarr indexer threw away everyone else's results (Zombiehamser). one search listing every indexer meant a single slow one held the reply past the 75s timeout, and a timed out request returns nothing. it's one request per indexer now, run together, and you keep whatever comes back
- #1091 folders differing only in capitalization made two albums (TomOdellSheetMusic). on linux you got two real folders and jellyfin showed the album twice; on windows and mac the recorded path didn't match the disk
- quality check upgrades always failed with "no matched track in finding" (Lil-Uzi-Chimp and one other). the fix handler was gated on an entity id the scanner never records for an unmatched file, so it never read the finding's own details
- "replace the original file" did nothing on a manual import (Urethra Franklin). the hint was only ever read by the auto-import worker
- recently played showed 12 tracks and neither dashboard rail updated without a reload. 25 now, both refresh on a timer
community PRs
- #1158 gradual file_path corruption during library scans (shkarlsson).
when the subsonic api omits
path, the code fabricated a bare filename and the UPDATE overwrote the real one — while every other nullable column was COALESCE-guarded. both halves were needed: the fabricated value is non-null, so COALESCE alone couldn't see it - #1160 a stale wing-it flag is not an unresolved guess (nstrelow). the flag is only ever set, so a track that wing-it'd once and matched properly later carried it forever. 317 of 445 rows in the pool were already resolved
- #1162 canonicalize youtube titles before matching, not only after (nstrelow). youtube music restates a localized title in latin next to the original, and providers index only the plain form — so the raw string searched for nothing. 29 of 69 wing-it stubs resolve at confidence 1.00
- #1161 resolve artist aliases before giving up on a track (nstrelow). "mgk" and "Machine Gun Kelly" share no substring, so a title matching at 1.00 was discarded on the 0.5 artist-similarity floor and written off as a wing-it stub. on a miss, discovery now asks musicbrainz for the artist's aliases and retries under each — machinery [#442] already built, never offered to discovery. six sampled mgk tracks go 0.00 → 0.99, and 25 of one playlist's 77 stubs were a single artist alias
- #1147 optionally search unverified Wing It guesses (nstrelow). his install had 185 stubs across 22 playlists and 77 were findable at the provider — guesses, but mostly right ones. new setting, off by default; a stub the source left nameless stays out either way. follow-up rounds closed the wing-it batch paths that bypassed the gate and a retry-stamp gap that kept a failed stub at retry_count 0 forever
- #1163 the download query ladder gets a version-stripped last resort (nstrelow). every rung preserved the edition suffix, so "Sweet Dreams [2005 Remaster]" with no peer carrying that exact edition never resolved at all. a stripped query now sits at the END of the ladder, and it strips edition words only — never live/remix/acoustic — so it can change which pressing you get, never which take. measured against the real network: 5 of 11 stuck edition-decorated wishlist tracks rescued at 0.91+
- #1154 youtube premium quality (lucasssvaz) has its own section up top
tested
full backend suite green, ruff clean, frontend 1,656 tests across the sync routes, bundle builds. each fix since that run landed with its own targeted tests; worth one more full-suite pass on the final head before merging.
two caveats worth carrying:
#1151 is still inference. every test stubs the http layer, so prowlarr's real behaviour under a hung indexer is unverified. worth confirming with the reporter before closing.
#1154's premium path rests on one account. the format ranking and catalog auth are covered by tests, but the premium itags (774/141) only exist for a premium subscriber — that leg is verified on lucasssvaz's install, not reproduced here.