github Nezreka/SoulSync 3.4.2
Version 3.4.2

17 hours ago

SoulSync 3.4.2: devmain

This release rebuilds chat around shareable music cards, upgrades search, tightens playlist discovery and sync correctness, makes the library and artist pages fast on large installs, plays music videos on the artist page, and fixes reported Jellyfin, Navidrome, tagging and torrent client problems. Scope: commits since the 3.4.1 tag.

Performance

Measured on a 300k-track / 70k-album library, every number from the slow-request log on a live install.

  • Ownership checks no longer scan the whole library through Python. Normalized name and title columns (indexed, backfilled in the background on first boot) replace a per-row Python function inside SQLite: 3-10 s per track down to milliseconds, which is what made the app halt during wishlist cleanup, download cleanup and watchlist scans.
  • The artist page's completion check loads an artist's albums by index instead of five substring scans (5.4 s to 42 ms), reads the album's stored track count before fetching a tracklist, remembers a pinned release's track count instead of asking MusicBrainz on every visit, and searches title-only album matches through a trigram index (1-2.5 s per unowned card to instant).
  • WAL mode is set once per process, not on every connection. Each new connection was waiting ~250 ms behind the enrichment workers just to run the pragma; that was the 1-2 s on every request that touched the database.
  • The library artist list stops scanning the artists table per click (1.9-2.4 s to 4 ms with a covering index), and the stats and recently-added calls it fires stop scanning per load (2 s warm / 21 s cold to an index lookup).
  • The dashboard's recent plays joined listening history to tracks across a TEXT/INTEGER type mismatch that defeated the primary key: 54 s (407 s cold) to 26 ms, with a query-plan test so the scan cannot come back.
  • Image cache: a cached hit no longer writes or waits on the process lock (75 cards each took ~1.1 s of wall on 0 ms of cpu), and a page registers its image urls in one write instead of one per card. Slow requests now report thread cpu next to wall time so a slow page can be traced to what actually cost it.

Chat

  • Now Playing cards (/np) with artwork, bitrate, a live equalizer and direct actions: preview, download the missing tracks, open the artist page, search. Wanted / In Search Of cards (/want, /iso) search Spotify, Deezer, Apple Music, Discogs and MusicBrainz for the release, check the local library automatically and offer a one-click PM to whoever has it. Both cards keep their full metadata and artwork, resolve the album tracklist for wishlist and download, and are SoulSync-only in room mode so vanilla Soulseek clients see plain text.
  • Links stand out with inline players for audio and video, YouTube embeds (the Error 153 referrer case is fixed) and unfurled preview cards for web links.
  • Friends, block list and peer bookmarks in a slide-over Social & Lists drawer, DM conversations can be closed, and the peer file explorer is a collapsible folder tree with one-click downloads.
  • Smart format detection with filter pills, drag-and-drop file uploads onto the chat, an expanded slash command autocomplete, a verified developer badge, and a cleaner composer row. Plain-mode chat no longer sends typing noise or empty beacons.

Search and discovery

  • Search has an explore hub on the idle page, a hero result, a sticky jump bar with counts, hover play on album and playlist cards, shimmer skeletons and a clearable search history.
  • Deezer playlist search, with cover art in the playlist preview and track actions from the preview modal. Deezer requests now retry and rate-limit themselves.
  • Discovery scoring penalizes duration mismatches and rejects tribute, karaoke and preview false positives. Manual matches and provider metadata survive a mirrored playlist re-discovery, cancelling a sync actually stops the background work, unmatch works on every source, and resetting a mirrored playlist clears both discovery caches.

Artist page

  • Music videos play in the page. Any video plays in place through the YouTube player with the rail as the queue: previous/next, autoplay next, the playing row marked, Esc to close. Starting a video pauses the app player and vice versa. Scroll away mid-video and the player docks to the corner. Videos an owner has blocked from embedding get an open-on-YouTube card instead of a grey box. Show more loads past the first eight (up to sixty), and the rail scrolls inside the stage instead of stretching the page.
  • Artist card images ease in from a dark tile instead of popping.

Sync and library

  • Mirrored playlists have a select mode with batch delete. Pick the cards, or search a name and Select all visible, and delete them in one confirm (#1219).
  • A sync that finds no library matches no longer empties the existing server playlist; the missing tracks still go to the wishlist. Artist agreement is enforced in the second matching pass so a long shared title cannot override an artist mismatch.
  • Library ownership checks fold accents, punctuation and multi-artist strings, so a label or search release is recognized as owned when the tags differ only in form. The completeness cache is correct across artists and keeps full artist identities.
  • Discography completion skips upstream API calls for releases you do not own, and release cards on the artist page are clickable again.
  • The MusicBrainz release you picked is kept through import and album completion, the same release MBID is written to every track of an album across restarts, and tagging writes Picard's native frames with multi-value fields preserved.
  • Stale Navidrome track IDs are no longer reused when updating playlists, failed updates are not reported as successful, and confirmed duplicate library entries are cleaned up (#1248).

Downloads

  • Optional music size limit per minute of audio, as a pre-download candidate filter. Unknown sizes and durations stay eligible.
  • qBittorrent 5.0+ returns JSON from the add call; it is parsed, with 4.x still supported. Transmission URLs copied from the web UI normalize to the RPC endpoint.

Video

  • Jellyfin 12 rejected every video-side request with 401 while the same key worked for music. The 3.4.0 fix for the modern Authorization header only reached the music client; the video connection test, user picker, library refresh, poster and collection calls and server activity now send the same header pair (#1250).
  • Fresh Releases board overhaul: widescreen layout, revamped cards with title clamping, season/episode context and release chips, a centered detail dialog, and instant poster loading through the site's clearance cookies.
  • Watchlist is responsive on mobile with a bottom sheet drawer.

Fixes

Tags and album splits

  • An album built one track at a time stays one album. A single downloaded track (a search pick, a wishlist track, the one missing song) now adopts the album-level tags already in its folder, the same step full album downloads have had, so Navidrome does not see a second release id and split the album. Gated: the track's own album tag must match the folder's (editions stay separate), a pinned release is never overwritten, siblings are never touched.
  • The Album Tag Consistency job now catches albums your server has already split: rows sharing an artist and title are compared as one album, and a track missing the release id the others carry counts as a mismatch. Two real albums with one title (self-titled records) are not merged: differing release groups or years keep them apart.

Artwork

  • Jellyfin/Emby gave every artist an image url whether or not the artist had one; the dead url then blocked enrichment from ever filling in a real photo, so the library grid showed a note while the artist page showed release art (#1253). Only real images are stored, a sweep after every scan clears existing phantoms, and the library grid falls back to the artist's Deezer photo like the artist page does.
  • The album art picker gets the artist picker's treatment: text album ids no longer break the cover.jpg write on Jellyfin/Navidrome, the chosen cover is pushed to the media server's poster, a pasted url that is not an image is refused, and the toast says what else was updated.
  • Watchlist artists whose source had no photo (Deezer's empty-hash placeholder urls) showed a mic icon. Placeholders are recognized, never stored, and the list falls back to the library artist's own thumb.
  • A corrupt image cache index heals itself: it is moved aside and rebuilt from the files on disk (nothing re-downloads), with a startup check and a retry for Windows file handles. The test suite is isolated from the live cache so a test run can no longer write into it, which is how the index got corrupted.

Matching and discovery

  • Band names containing commas, slashes, ampersands or "and" (Earth, Wind & Fire; AC/DC) are no longer split into separate artists during matching. Separate credits arrive as artist list entries, and only explicit featured-artist credits are split when there is no artist ID.
  • Duration mismatches are penalized, and tribute, karaoke and preview copies are rejected as false positives.
  • A release with an unknown track count uses release ownership instead of being assumed to be a one-track single.
  • Cancelling a sync now signals the sync service and keeps the worker handle until it has actually exited, so a new sync cannot start on top of a worker that is still running. A cancel that cannot identify its playlist returns an error instead of pretending.
  • Manual matches and provider metadata survive a mirrored playlist re-discovery. Rediscover from the modal runs immediately without crashing or closing the modal, and a reset clears both the SQLite discovery cache and the match cache.
  • Unmatch works on every source endpoint and match counts stay in step.
  • Playlist preview rows can be played without the selection controls being on.

Sync and library

  • A sync that finds no library matches keeps the existing server playlist and still wishlists the missing tracks, instead of emptying the playlist (zero-match wishlisting).
  • Artist agreement is enforced in the second matching pass, so a long shared title cannot override an artist mismatch from the first pass.
  • Library ownership checks match SQLite's LOWER exactly, fold accents and punctuation, index albums under both the full credit and the primary artist, and try the full credit before a featured-artist fallback.
  • The completeness cache is keyed correctly across artists and no longer collapses full artist identities.
  • Discography completion skips upstream API calls for releases you do not own, and release cards open immediately while ownership checking is still running in the background.
  • A release-group is no longer treated as an edition, and an explicit edition never assigns a different song by track position alone.
  • The MusicBrainz release MBID resolved for an album is persisted, so every track of that album gets the same MUSICBRAINZ_ALBUMID even across restarts and cache eviction.

Navidrome (#1248)

  • Stale Navidrome track IDs were being reused when updating playlists. IDs are validated against a fresh, complete OpenSubsonic inventory (never partial; keeps paging when the server caps page size), same-path rekeys are repaired with foreign-key references preserved, and a selected folder cannot make other live IDs look obsolete.
  • A refused or failed Navidrome write is reported as a failure and never triggers a second destructive attempt.
  • Cover art keeps its identity instead of a newly salted auth URL on every serialization, so artwork stops churning.

Chat

  • The Download Missing Tracks modal opened from a card showed Unknown Artist and zero durations; artists, duration and cover art are now populated, with an enhanced-search fallback when the card lacks them.
  • Wanted cards keep their full metadata and artwork, and resolve the full album tracklist before wishlist or download. Wishlisting from a card uses the standard Add to Wishlist modal.
  • Rich cards are never sent as plain text; in room mode they are SoulSync-only so vanilla Soulseek clients see readable text.
  • The /want drawer animates properly, Now Playing cards probe for missing artwork before sending, and YouTube embeds no longer fail with Error 153.
  • Plain-mode chat no longer sends typing noise or empty beacons, while avatar caching is preserved.

Downloads and clients

  • qBittorrent 5.0+ returns a JSON body from the add call, which was being ignored; it is parsed now, with 4.x still supported and tested.
  • Transmission URLs copied from the web UI (/transmission/web, /web, bare host) normalize to the RPC endpoint.
  • Deezer requests retry on transient failures and rate-limit themselves during playlist searches.

Video

  • Jellyfin 12: every video-side request was rejected with 401 while the same key worked on the music tab (#1250).

Don't miss a new SoulSync release

NewReleases is sending notifications on new releases.