Commit graph

2186 commits

Author SHA1 Message Date
Broque Thomas
cdcb05892a Expand default genre whitelist from 223 to 272 genres
Added Alternative, Indie, Dance (common Spotify umbrella genres),
modern genres (Phonk, Hyperpop, Cloud Rap, Emo Rap), regional pop,
additional rock/metal/electronic subgenres, world music traditions,
and media genres (Video Game Music, Anime). Intentionally excluded
mood/activity tags (Chill, Workout, Sleep) as non-genres.
2026-04-18 20:32:49 -07:00
Broque Thomas
e8094c2218 Fix genre whitelist settings not saving — add to allowed config keys
The genre_whitelist key was missing from the settings POST handler's
service whitelist, so the config was silently dropped on save.
2026-04-18 20:26:37 -07:00
Broque Thomas
288776a7f3 Add genre whitelist for filtering junk tags during enrichment
New core/genre_filter.py with ~180 curated default genres. When strict
mode is enabled in Settings → Library Preferences → Genre Whitelist,
only whitelisted genres pass through during enrichment. Junk tags from
Last.fm (artist names, radio shows, playlist names) are silently dropped.

Applied at all 10 genre write points: Spotify, Last.fm, AudioDB, Deezer,
Discogs, iTunes, Qobuz enrichment workers + post-processing genre merge
+ initial download artist/album creation.

Strict mode is OFF by default — zero behavior change for existing users.
First enable auto-populates the whitelist with defaults. Users can add,
remove, search, and reset genres via the Settings UI.
2026-04-18 20:23:53 -07:00
Broque Thomas
c6de707f94 Fix single track search results downloading with album context
When clicking a track in enhanced or global search, the download modal
correctly showed SINGLE but the download used is_album_download=true,
causing the file to be organized under the album path template instead
of the singles template. Now enhanced_search_track_ and gsearch_track_
prefixes pass album metadata for tagging but set is_album_download=false.
2026-04-18 19:46:20 -07:00
Broque Thomas
1564d7bd53 Fix Liked Songs playlist misidentified as YouTube source
The source detection chain for playlist hero sections didn't handle
the 'spotify:liked-songs' playlist ID prefix, falling through to the
default 'YouTube' label. Added 'spotify:' prefix check.
2026-04-18 19:21:25 -07:00
Broque Thomas
62251a66bf Improve deep scan logging — show existing track counts, not just new
Per-artist log lines now show the full details string from the worker
(e.g. "5 albums, 0 new tracks (150 existing updated)") instead of
just "5 albums, 0 tracks". Finished message shows "library up to date"
when no new content is found instead of "0 successful, 0 failed".
2026-04-18 19:10:35 -07:00
Broque Thomas
0e8e3e86a0 Fix Duplicate Detector ignoring 'allow duplicate tracks across albums'
The Duplicate Detector repair job had its own ignore_cross_album setting
that was independent of the global allow_duplicate_tracks setting. When
a user enabled 'Allow duplicate tracks across albums', the detector
still flagged same-titled tracks on different albums as duplicates.
Now respects the global setting — if duplicates are allowed, cross-album
matches are always skipped.
2026-04-18 18:55:02 -07:00
Broque Thomas
a02266596a Add Full Refresh support for SoulSync standalone mode
Full Refresh now clears all soulsync library records and rebuilds from
file tags in the output folder. Reads tags via Mutagen, groups by
artist/album, creates DB records with stable IDs. Files stay in place.
Previously Full Refresh did nothing for standalone — just returned.
2026-04-18 18:29:02 -07:00
Broque Thomas
6036e02011 Fix library scan button stuck on 'Stop' and stale count shown as 'failed'
Dashboard scan polling checked for 'completed' but backend sets 'finished'.
Added 'finished' to the completion check so polling stops, button resets,
stats refresh, and toast fires correctly. Also fixed deep scan reporting
stale record removals as 'failed' instead of 'successful'.
2026-04-18 18:19:49 -07:00
Broque Thomas
2c15d50bff Fix metadata enhancement crash when album_info is None
Playlist and single track downloads pass None as album_info to
_enhance_file_metadata. The downstream _extract_spotify_metadata
called .get() on it without a null guard, crashing with AttributeError.
2026-04-18 18:12:39 -07:00
Broque Thomas
f5a2d51d4e Speed up standalone verify button — stop counting after 10 files
Was walking the entire directory tree counting up to 100 audio files,
taking 60+ seconds on large libraries. Now breaks after 10 files.
2026-04-18 17:54:37 -07:00
Broque Thomas
f8e4adde41 Remove redundant output path mirror from standalone config section
The disabled path field on the Connections tab was showing stale data
(always ./Transfer) because it read from the DOM before settings loaded.
Removed it entirely — the output path is configured on the Downloads tab.
Standalone section now just shows description + verify button.
2026-04-18 17:53:07 -07:00
Broque Thomas
db7714c4db Fix settings page showing all tabs on first load
Non-active tab groups were visible during async data loading because
switchSettingsTab ran after the awaits. Moved it before async calls and
added CSS defaults to hide non-connections groups, preventing any flash.
2026-04-18 17:50:05 -07:00
Broque Thomas
efe8280e23 Rebrand folder terminology: Download→Input, Transfer→Output, Staging→Import
All user-facing labels, docs, help text, tooltips, error messages, and debug
info output updated. Backend config keys, variable names, actual path values,
and Docker volume mounts are completely unchanged — zero functional impact.
2026-04-18 17:35:20 -07:00
Broque Thomas
cef1162c5e Update What's New with today's features and fixes 2026-04-18 16:07:33 -07:00
Broque Thomas
b17a6e2dd7 Add per-artist metadata source override for watchlist scans
Users can now override which metadata provider (Spotify, Deezer, Apple Music,
Discogs) is used when scanning a specific watchlist artist for new releases.
The selector appears in the artist config modal and only shows sources the
artist has enrichment IDs for. Default behavior is unchanged — all artists
use the global metadata source unless explicitly overridden.
2026-04-18 16:05:05 -07:00
Broque Thomas
f749bb9604 Fix AcoustID mismatch fix failing with 'uuid' UnboundLocalError
The redownload branch had `import json, uuid` locally inside the function,
which caused Python to treat `uuid` as a local variable for the entire
function scope. When the retag branch ran instead, `uuid` was unbound.
Both modules are already imported at the top of the file.
2026-04-18 15:06:54 -07:00
Broque Thomas
381e37ecf7 Enhance logging, debug info, and add Troubleshooting docs section
- Move Log Level dropdown from Downloads tab to Advanced tab (Settings)
- Fix staging path config key (import.staging_folder → import.staging_path)
- Fix library stats showing 0 (use get_database_info_for_server like dashboard)
- Add Troubleshooting & Support docs section (log files, debug info, common issues, reporting)
- Beef up Copy Debug Info: ffmpeg version, runner type, Discogs status, wishlist count,
  music library paths, music videos dir, log level, metadata source, hybrid priority,
  lossy copy config, auto import, duplicate tracks, replace quality, log file listing
- Add GitHub issue link footer to debug output
- Add discogs to enrichment worker list in debug endpoint
2026-04-18 15:05:19 -07:00
BoulderBadgeDad
c473bf777c
Merge pull request #318 from kettui/feat/artist-discography-refactoring
Centralize metadata lookup for artist discography
2026-04-18 13:15:49 -07:00
Broque Thomas
1527fea5a1 Merge branch 'main' of https://github.com/Nezreka/SoulSync 2026-04-18 12:33:14 -07:00
BoulderBadgeDad
f636014b9a
Merge pull request #316 from kettui/fix/reduce-ui-stalls
Reduce UI stalling during enhanced search, add caching & small concurrency optimizations
2026-04-18 12:32:55 -07:00
Broque Thomas
b516f6e8ce Restore python web_server.py support alongside gunicorn
The gunicorn PR blocked direct Python execution with SystemExit.
Replaced with _DIRECT_RUN flag at top and startup block at bottom
so both paths work:
- python web_server.py (Werkzeug dev server, Windows compatible)
- gunicorn -c gunicorn.conf.py wsgi:application (production)
2026-04-18 12:03:12 -07:00
BoulderBadgeDad
64d87389d6
Merge pull request #315 from kettui/feat/gunicorn
Run SoulSync under Gunicorn
2026-04-18 11:48:48 -07:00
Antti Kettunen
3191f1fe3b Auto-reload static assets in development 2026-04-18 19:22:03 +03:00
Antti Kettunen
bbd736e915 Reduce gunicorn graceful_timeout to speed up app teardown
The app goes through the usual teardown process quite fast on it's own, so keeping the graceful timeout config high only arbitrarily slows things down.
This is especially releavnt in dev mode, since app reloads should feel snappy when changes are made
2026-04-18 19:22:03 +03:00
Antti Kettunen
832bb07787 Prevent running web_server.py directly, enforce gunicorn
Should not support completely different ways of running the server, as that can lead to inconsistencies between environments / runtimes
2026-04-18 19:22:03 +03:00
Antti Kettunen
cb9a4b23b6 Clean up legacy env vars, print a warning log if running web_server directly 2026-04-18 19:22:03 +03:00
Antti Kettunen
8ff89c63a3 Add logging for slow requests 2026-04-18 19:22:03 +03:00
Antti Kettunen
14bc9b6fad Introduce Gunicorn production runner
Switch the web UI from Werkzeug's built-in server to Gunicorn for a more stable production deployment path.

Keep a separate dev config so local runs still reload quickly, while the production path uses a dedicated WSGI entrypoint and cleaner startup behavior.

The main motivation is to reduce the websocket teardown noise and make the server behavior more predictable under the app's mostly background-driven workload.
2026-04-18 19:21:53 +03:00
Broque Thomas
020ce6d765 Hide discovery modal sync buttons in standalone mode
"Sync This Playlist" buttons in YouTube/Tidal/Deezer/Spotify/Beatport/
ListenBrainz discovery modals were not gated by _isSoulsyncStandalone.
Added check to the hasSpotifyMatches condition that generates them.
2026-04-18 09:15:19 -07:00
Broque Thomas
33911a7001 Show Sync page in standalone mode, only hide sync buttons
The Sync page was hidden entirely for standalone users, blocking
access to playlist browsing, discovery, and downloads. Now the page
is accessible — only the sync-to-server buttons are hidden since
there's no server to push playlists to.
2026-04-18 09:00:55 -07:00
Broque Thomas
8df5cf3be0 Fix lossy copy conversion failing on FLACs with embedded cover art
Added -vn flag to all codec ffmpeg commands (MP3, Opus, AAC) to strip
video/image streams during conversion. Embedded cover art in FLAC
files caused ffmpeg to fail when the output muxer couldn't handle
the image stream, producing 0KB output files. Cover art is
re-embedded afterwards by Mutagen.
2026-04-18 08:57:21 -07:00
Broque Thomas
9369924968 Fix discography dedup dropping studio albums for compilations
The dedup key (normalized_title, year) caused different albums from
the same year to collide when title normalization stripped too much.
The "prefer more tracks" logic then kept compilations over studio
albums.

Two fixes:
- Title similarity check: if normalized titles are <85% similar,
  they're different albums, not variants — keep both
- Compilation deprioritization: studio albums win over compilations
  and "best of" collections when they do collide
2026-04-18 08:53:46 -07:00
Antti Kettunen
cca396e7bd Centralize Hydrabase enablement
Move Hydrabase availability checks into metadata_service so source resolution owns the policy. Keep web_server delegating to the centralized helper and add tests for the enabled/disabled cases.
2026-04-18 18:47:54 +03:00
Antti Kettunen
2b575a59ae Refactor artist discography lookup
Move artist discography resolution into core metadata_service, introduce MetadataLookupOptions, and keep web_server focused on request handling. Add focused tests for the new service boundary and preserve current fallback behavior for now.
2026-04-18 18:41:31 +03:00
Broque Thomas
ad9d5817cf Fix 'Loading playlist...' stuck forever on error
openDownloadMissingModal showed loading overlay but didn't hide it
on error paths (playlist not found, fetch failure). The overlay
persisted across page navigation, blocking the entire UI.
2026-04-18 08:37:31 -07:00
Broque Thomas
f4aaab8a66 Reorganize Settings Library tab with collapsible sections
Three collapsible categories, collapsed by default:
- Paths & Organization (file templates + music library paths)
- Post-Processing (metadata, tags, conversion, lyrics)
- Library Preferences (import, content filter, stats, playlists, M3U)

Section headers have data-stg=library so they only appear on the
Library tab. Bolder headers with accent-colored arrows and subtle
border. Collapse state preserved when switching settings tabs.
2026-04-18 08:28:12 -07:00
Antti Kettunen
1905678c7b Refactor get_artist_discography to respect metadata provider priority 2026-04-18 16:24:59 +03:00
Antti Kettunen
e687486561 Add caching for enhanced search, don't attempt searches with 2 characters or less 2026-04-18 15:06:00 +03:00
Antti Kettunen
688e84ce4d Fix MusicBrainz label, primarily show tabs with fetched results only 2026-04-18 14:57:12 +03:00
Antti Kettunen
02f190efc6 Reduce enhanced search stalls
Delay alternate-source fan-out until the primary enhanced-search response arrives, and stagger those follow-up requests so they do not all compete at once. Also parallelize artist, album, and track lookups inside each metadata source request to shorten the time the UI thread spends waiting on remote APIs. This keeps the single-worker web UI more responsive under the app's chatty search flow.
2026-04-18 14:46:50 +03:00
Broque Thomas
841ad42fdd Fix MusicBrainz tab not appearing in enhanced search
Source fetch list was hardcoded without musicbrainz — tabs only showed
for sources that were pre-fetched in the parallel search loop.
2026-04-18 02:10:12 -07:00
Broque Thomas
4f5025d526 Add MusicBrainz search tab, wider global search, bump to v2.32
New MusicBrainz tab in Enhanced and Global search — finds tracks and
albums on MusicBrainz's community database with Cover Art Archive
images. Covers obscure tracks that Spotify/Deezer/iTunes miss.

- core/musicbrainz_search.py: search adapter with Track/Artist/Album
  dataclasses, Cover Art Archive integration, smart query parsing
- Albums deduplicated (keeps best version with date and art)
- No artist results shown (MusicBrainz has no artist images)
- Album detail with full tracklist for download modal
- Smart word-boundary splitting for queries without separators
- Global search results container widened from 620px to 920px
- UI version bumped to 2.32
2026-04-18 02:06:27 -07:00
Broque Thomas
70005968b6 Restructure version modal and What's New for v2.31
SoulSync Standalone Library is now the first section in both the
version modal and What's New popup. Auto-Import section updated with
all improvements (recursive scan, singles, tag preference, AcoustID).
New Downloads & Soulseek section groups download-related improvements.
Recent Fixes cleaned up — feature items moved to proper sections.
2026-04-18 01:28:19 -07:00
Broque Thomas
b78616431a Add SoulSync standalone deep scan and skip incremental scan
Deep scan for standalone mode:
- Scans Transfer folder for all audio files
- Compares against soulsync DB records by file_path
- Moves untracked files to Staging for auto-import processing
- Removes stale DB records where files no longer exist
- Cleans orphaned albums and artists with no tracks

Incremental scan skips for standalone — library updates at download
time, no periodic scanning needed. Both changes are purely additive
and only activate when server_type is 'soulsync'.
2026-04-18 01:17:32 -07:00
Broque Thomas
b4475152a9 Hide all sync buttons in standalone mode, bump UI to v2.31
All sync-related buttons hidden when active server is SoulSync
Standalone. Covers static buttons (querySelectorAll on status update)
and dynamic modal buttons (_isSoulsyncStandalone flag).
UI version bumped to 2.31 (Docker stays at 2.3).
2026-04-18 01:14:54 -07:00
Broque Thomas
a51e1b6651 Update README: SoulSync standalone mode, no media server required
Added standalone mode to What It Does and Library Management sections.
Media server is now optional — Plex, Jellyfin, Navidrome, or Standalone.
2026-04-18 00:57:14 -07:00
Broque Thomas
2d57831e91 Fix Docker build: stop excluding requirements.txt from build context
PR #311 renamed requirements-webui.txt to requirements.txt but the
.dockerignore still excluded requirements.txt (previously the PyQt6
desktop version). Docker COPY failed because the file was ignored.
2026-04-18 00:37:37 -07:00
Broque Thomas
12e9c0034b Hide Sync page in sidebar for SoulSync standalone mode
No media server to sync playlists to — sync page is irrelevant.
M3U generation is still available via settings toggle and download
modal buttons for standalone users who want playlist files.
2026-04-18 00:27:38 -07:00
Broque Thomas
d688e7fa15 Update What's New and version modal with standalone library and import fixes 2026-04-17 23:29:04 -07:00