github Nezreka/SoulSync 3.4.7
Version 3.4.7

3 hours ago

soulsync 3.4.7: dev → main

candidates explain their rejections, an inspector across every download source, discovery feedback with an inbox and renewable mixes, profiles, requests and issues rebuilt from security up, audiobooks on soulseek with persistent storage, a complete help and docs rewrite, an overhauled enrichment manager, duplicate healing for compilations, and every credited artist on their own page. scope: the 69 commits since the 3.4.6 release commit (0224491e7).

candidate decisions and the inspector

  • every rejected candidate now records why instead of being silently dropped (core/downloads/decisions.py). reasons are structured across duration, identity, quality, and version stages (accepted, preview, duration_mismatch, artist_mismatch, artist_unverified, version_conflict, match_weak, outranked, below_profile, peer_queue, blacklisted, duplicate, quarantined, too_large, below_cutoff).
  • evaluate_candidates and get_valid_candidates share one _select engine. the automatic path records nothing, pinned by parity tests across configs and negative-checked.
  • candidate inspector opens from the redownload modal, from the wishlist ("search manually" opens it for that exact track with its quality profile and album, downloading a pinned one-task batch), and from failed downloads ("see what every source has"). rejected rows show reason pills, wanted-vs-found evidence, and "grab anyway" overrides (bypassing the import quarantine for that file).
  • automatic grabs remember why they took that file: workers write a summary (winner, 10 alternatives, reason counts) to download_decisions (pruned with download history, 5,000 cap, linked to track_downloads). the track detail modal shows "why this file", and failed downloads answer in one line ("nothing passed: 9 wrong length · 4 wrong version · 2 below your profile").
  • clearing download history runs safe against missing decision tables.

discovery: feedback, inbox, blocklists and recipes

  • blocked artists never render on discover: discovery previously never read the profile blocklist at all, and surfaces used a global union of every profile's blocks. core/discovery/blocked.py filters all discover GET routes and shelf caches. daily mixes drop blocked seeds at generation and rebuild when blocks change. the blocked artists modal edits the profile table instead of reviving unblocked artists on restart.
  • unified explanations: {kind, seeds, confidence} schema across listening recs, bylt, daily mixes, stations, hero, and similar artists (core/discovery/explain.py).
  • taste feedback: a ⋯ menu on every recommendation card (shelves, hero, stations, bylt) with more like this, less like this, not now (30-day quiet period), block artist, and save for later (core/discovery/feedback.py). feedback dynamically re-ranks recs and shifts mix/station seeds. "reset taste" in the blocked modal clears feedback without wiping blocks.
  • discovery inbox: new panel at the top of discover with new / saved tabs and a nav badge (discovery_inbox + core/discovery/inbox.py). collects watchlist releases (last 30 days + upcoming), recs saved for later, and ticketmaster concerts. refreshes in the background every 6 hours with fault isolation. releases mark as added once in the library.
  • renewable mixes and custom recipes: "+ build a mix" in made for you shelf header creates recipes based on seeds, genres, year ranges, and library / discovery / trending balance (mix_recipes + core/personalized/recipes.py). mixes follow rules: one song per artist, automatic rebalancing when a source is short, and reserve track swapping. options to generate new tracks, keep this one as a synced playlist (soulsync_mix), or edit the recipe.

quality upgrades surfaced in the library

  • quality findings move out of tools into the everyday library: "could be better (n)" filter on the library page, per-track upgrade button and per-album "upgrade n" on artist pages.
  • opens the candidate inspector in upgrade mode: candidates below the profile cutoff are rejected as below_cutoff.
  • "apply quality upgrades" automation action queues finder-approved upgrades on the wishlist for until_cutoff profiles.

profiles, requests and issues rebuilt

  • security: sessions without a profile no longer get admin rights on multi-profile installs (401 profile_required). pin brute-force limiter keyed per (ip, account) with lockout countdown from retry-after. profile pin reset by service credential locked down. download permissions enforced across wishlist, redownload, music videos, logs and debug info.
  • unique session cookie name (#1322): named soulsync_session so other apps on the same host using Flask's default session cookie cannot overwrite the session and switch profiles to admin.
  • profile features: "who's here?" picker with pin pad, quick-switch menu on avatar, card grid manager with adult/teen/kids/guest presets, avatar upload re-encoded to 512px webp, one-use invite links, admin audit log, "always open as me on this device", per-device sign out via session epoch, and profile disable toggle.
  • kids limits: hide explicit music, movie/tv rating caps. over-cap or unrated titles return 403 restricted and drop from library, dashboard, search, discover, calendar. explicit tracks cannot play and vanish from listings.
  • requests: music requests page (waiting / on the way / available / declined) with admin approval gate for non-downloaders, reason on decline, nav badge, and arrival notifications. video requests grouped by title, multi-season selection, claim-first locking, progress and quotas.
  • issues: shared backend (core/issues) for music and video. threaded comments, status events, followers instead of duplicates, category fix buttons, search, filters, pagination past 100, and bulk triage. open issues survive reporter profile deletion.

audiobooks: soulseek single-files, persistent database and wishlist (#1330)

  • soulseek single-file .m4b audiobooks: soulseek search now includes .m4b files in extra extensions and wraps lone files as standalone releases titled from folder and filename. downloads target the specific file to prevent importing unrelated files from shared folders.
  • persistent database in docker: audiobooks.db default path now honors AUDIOBOOK_DATABASE_PATH and defaults to /app/data/audiobooks.db in Docker, keeping the database in the persistent volume across image updates.
  • wishlist search timeouts: per-book search timeout extended to 5 minutes (was 10s default ky client timeout) and batch search passes have no timeout.
  • instant reconciliation: wanted books reconcile immediately when identified during library scans or after manual match confirmation (PATCH /library/<asin>/match).
  • optional removal of owned books: new audiobooks.remove_owned_from_wishlist setting (off by default) to optionally delete owned books from the wishlist instead of keeping them marked "In library".

multi-artist credits, release types and tagging

  • featured artists and collab albums show under "appears on" on every credited artist's page (track_artist_credits and album_artist_credits).
  • collab albums tagged with every album artist (TXXX:ALBUM ARTISTS on id3, ALBUMARTISTS on vorbis) so navidrome files them under all collaborators.
  • write_multi_artist defaulted on for fresh installs; existing installs retain their configuration.
  • bare 'album' type is not an explicit release-type signal (#1331): SpotipyFree's hardcoded album_type='album' filler is no longer treated as explicit classification; bare 'album' verifies against track count (7+ tracks) so singles and EPs derive their real type and avoid misfiling under Album/. Deezer raw compile maps to compilation.
  • stop trusting batch artist hints over the track's album artist (#1316): poisoned wishlist rows no longer stamp _explicit_artist_context over the true album artist. the track's own album artist resolves first, and album groups cross-check promoted context against member tracks to catch unanimous disagreements.
  • labels and genres written as real multi-values (#1305).

enrichment workers modal redesign (#1317)

  • collapsible coverage: a toggle in the coverage section lets the cards fold away with a smooth animation and persists in localStorage, giving the unmatched browser room to breathe while keeping overall percentage in a slim summary bar.
  • decluttered topbar: global actions and priority tabs move into a ⋯ overflow menu with explicit labels ("Retry all failed — All workers") and a persistent radio group, eliminating mis-clicks with per-worker actions.
  • slim hero: the ~110px header shrinks to a ~52px status strip (logo, name, state pill, current track, error/budget chips, API graph, pause) while keeping all live-update element IDs and animations intact.
  • per-worker action buttons scoped to "Retry failed" with worker-specific tooltips; Escape cleanly closes the open menu before dismissing the modal.

help and docs rebuild (#1310)

  • complete rewrite of help and docs with a dedicated markdown engine (docs.js, docs.css): 3-column layout, mobile drawer, instant search palette (/ or ctrl+k), table of contents with scroll-spy, and deep links.
  • 17 content modules, 32 sections, ~175 pages verified against code.
  • closes coverage gaps for podcasts, audiobooks, chat & jukebox auto-dj, record labels, hydrabase p2p metadata, video automations and repair jobs, and the interactive rest api explorer.
  • readme audited line-by-line against code, fixing stale claims and retiring dead proxy mentions.

matching, search and general fixes

  • duplicate detector compilation healing (#1315): compilation tracks missing track_artist from pre-3.4.6 syncs no longer get stuck matching only 'Various Artists'. the detector now reads the file's embedded artist tag for compilation-marker albums, matches against performer copies, and persists the credit.
  • styled manual-match buttons (#1329): Match buttons in both React manual-match modal and vanilla enrichment manager now use lib-btn primary instead of showing unstyled browser defaults.
  • non-latin matching: japanese, hebrew and cyrillic titles no longer match non-latin tracks at 100% (#1306).
  • watchlist: watched artists show as watched on their artist page; scan stops missing late-listed releases (#1308, #1309).
  • listenbrainz: sync picks up history imported after the initial sync (#1311).
  • search: files results scroll instead of clipping (#1313), and subtitle names the true source.
  • deezer: failed re-logins back off and retry instead of staying disconnected until restart.
  • video: imports survive network share permission errors after the file lands; torrents never move.

validation

  • test coverage: over 20,200 backend tests passing across all suites; vitest frontend suites passing.
  • candidate selection parity: evaluate_candidates and get_valid_candidates verified identical in order and membership across configs and negative-checked.
  • new regressions covered: audiobooks soulseek/database/reconciliation (#1330), bare album release type derivation (#1331), album artist hint poisoning (#1316), duplicate detector compilation tag healing (#1315), unique session cookies (#1322), and 36 jsdom behavior checks for the enrichment manager redesign (#1317).
  • docs verification: node syntax checks, settings label drift guards, and link/id integrity checks all green.
  • discord announcement: drafted in RELEASE_3.4.7_discord.md and verified under discord's 2,000-character limit.

Don't miss a new SoulSync release

NewReleases is sending notifications on new releases.