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 omitspath, 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.