Commit graph

534 commits

Author SHA1 Message Date
Broque Thomas
99481a0232 Fix Track Match search ignoring Track/Artist fields and low result limit
- Frontend was concatenating Track and Artist inputs into a single
  query string, causing Spotify to return mixed results matching
  either word in any field. Now sends track and artist as separate
  params; backend builds field-filtered query (track:X artist:Y).
- Result limit was silently capped at 10 in spotify_client.search_tracks
  via min(limit, 10). Raised to respect requested limit up to
  Spotify's API max of 50.
- iTunes fallback endpoint updated with same field-specific params.
- Legacy ?query= param still supported for backward compatibility.

Fixes #194
2026-03-19 08:23:19 -07:00
Broque Thomas
0a3cca4f1f Fix Spotify invalid_client and Tidal redirect URI mismatch on auth
- Duplicate `spotify` key in saveSettings() object literal caused
  second definition (embed_tags/tags) to silently overwrite the first
  (client_id/client_secret/redirect_uri), destroying credentials on
  every save. Merged into single key.
- authenticateSpotify() and authenticateTidal() now await saveSettings()
  before opening auth window, ensuring credentials are persisted.
- Tidal auth now dynamically sets redirect_uri from request host for
  LAN/Docker users and stores it in tidal_oauth_state so the callback
  token exchange uses the same URI.

Fixes #191
2026-03-19 08:02:12 -07:00
Broque Thomas
9a6ee0e229 Replace Inspiration templates with tabbed Automation Hub
Swap the flat 8-card Inspiration section for a rich 4-tab Automation Hub
(Recipes, Quick Start, Tips, Reference) with 20 categorized recipe cards,
5 step-by-step guides, 8 power-user tips, and full trigger/action/then
reference tables. Includes category + difficulty filters, chain flow
visuals, accordion expand/collapse, and responsive grid layouts.
2026-03-18 19:03:41 -07:00
Broque Thomas
b9732156cd Add per-tag granular toggle settings for all 47 embedded tags
Replace category-based tag settings (10 toggles) with per-tag controls
grouped by source service in an accordion UI. Each of the 11 service
groups (Spotify, iTunes, MusicBrainz, Deezer, AudioDB, Tidal, Qobuz,
Last.fm, Genius, General) has a master toggle that disables all child
tags, with individual toggles for fine-grained control. ISRC and
copyright fallback chains are now per-source toggleable. Genre merge
contributions from each source are independently controllable. All
tags default to enabled for backward compatibility.
2026-03-18 16:17:20 -07:00
Broque Thomas
d3bff90fd6 Add Picard-compatible MusicBrainz tags and per-category tag settings
Post-processing now writes all 18 MusicBrainz tags that Picard writes:
Release Group ID, Album Artist ID, Release Track ID, Release Type,
Status, Country, Original Date, Media, Barcode, Catalog #, ASIN,
Script, Total Discs (plus the 5 already supported). One cached API
call per album via get_release with recordings include.

New "Tags to Embed" settings section with 10 category toggles (all
enabled by default): MusicBrainz IDs, Release Info, Source IDs, ISRC,
BPM, Mood & Style, Copyright & Label, Genre Merging, URLs, Quality.
Each shows inline description of what it includes.
2026-03-18 15:49:36 -07:00
Broque Thomas
5adfdad9a3 Speed up dashboard polling intervals for more responsive UI
Service status 10s→5s, activity feed 5s→2s, watchlist/wishlist/db stats 30s→10s.
2026-03-18 15:02:00 -07:00
Broque Thomas
4a7f297b2c Add minimum peer upload speed setting and fix quality scoring tiers
New dropdown in Soulseek settings lets users filter out slow peers at
search time (Any/1/2/3/4/5/10 Mbps). Passes minimumPeerUploadSpeed
to slskd API in bytes/sec.

Also fixes quality scoring tiers which were using wrong units — old
thresholds (5000, 1000, 500) treated bytes/sec values as kbps,
making speed scoring effectively meaningless. Now uses correct
bytes/sec thresholds based on real peer data.
2026-03-18 14:07:39 -07:00
Broque Thomas
b4da777bdc Fix automation group dropdown clipped at bottom of page
Dropdown always opened downward, getting cut off for the last row.
Now checks available space and flips upward when near viewport bottom.
2026-03-18 13:08:37 -07:00
Broque Thomas
a42927f369 Add re-download capability to dead file findings (#189)
Dead file Fix button now adds the track to wishlist for re-download
instead of just removing the DB entry. Builds full wishlist-compatible
track data from DB (artist, album, artwork, IDs) so the download
pipeline can process it like any other wishlist item.
2026-03-18 09:49:38 -07:00
Broque Thomas
2656927f79 Beatport enrichment progress, metadata cache fixes, per-source cache clearing
- Fix enrichment progress never updating: remove `continue` that skipped
  progress_callback for successful tracks in enrich_chart_tracks
- Split chart/extract into two-step flow: extract raw tracks, then enrich
  via polling endpoint with live progress overlay updates
- Move Beatport enrichment cache to persistent metadata cache system
- Fix metadata cache detail modal for Beatport (URL entity_ids with slashes)
- Add per-source Clear dropdown (Spotify/iTunes/Deezer/Beatport) to cache browser
- Remove debug logging from enrichment progress tracking
2026-03-18 08:39:02 -07:00
Broque Thomas
a7c7d9e6ed Fix Beatport download bubbles missing for releases and on page navigation
- Add navigation triggers for Beatport bubbles on sync page load, Beatport
  tab switch, and rebuild tab activation (mirroring artist bubble pattern)
- Register download bubbles for Beatport releases (albums, EPs, singles)
  which were only created for chart/playlist downloads
- Extend modal close cleanup to handle beatport_release_ prefix
2026-03-18 07:21:06 -07:00
Broque Thomas
cde5754f0f Add Auto-Fill fix handler for incomplete album findings
When the maintenance worker flags an incomplete album, users can now
click "Auto-Fill" to automatically locate missing tracks in the library,
move/copy them into the album folder, and apply full metadata enhancement
(MusicBrainz, Deezer, cover art, etc.). Singles are moved; tracks from
multi-track albums are copied. Quality gate prevents filling FLAC albums
with lossy files. Tracks not found in library are added to wishlist with
album context for auto-download.
2026-03-17 22:56:24 -07:00
Broque Thomas
fbf44123ec Add Beatport download bubbles, enrichment cache, and batch enrichment
- Persistent download bubble cards on Beatport page and dashboard,
  matching the existing artist/search bubble UX with click-to-reopen,
  green checkmark on completion, and snapshot persistence
- In-memory enrichment cache (2h TTL, thread-safe) skips re-scraping
  when the same chart is clicked twice
- Batch enrichment replaces one-by-one HTTP requests with a single
  POST, using WebSocket progress events for overlay updates
- Fix _beatportModalOpening guard blocking modal open after fast
  cached enrichment by resetting the flag in openBeatportChartAsDownloadModal
- Hide Browse/My Playlists tabs — Browse is now the only Beatport view
2026-03-17 21:41:04 -07:00
Broque Thomas
dc612da9d5 Add Sync to Server button for Beatport playlists in download modal
- "Sync to Server" button appears for Beatport chart/playlist downloads
- Starts media server sync via /api/sync/start with live progress bar
- Progress area renders below all modal buttons with matched/failed counts
- Cancel button to abort sync mid-way, auto-cleanup on completion
2026-03-17 20:54:58 -07:00
Broque Thomas
27a0cf8b81 Add Sync History feature with live re-sync progress and source detection
- New sync_history DB table tracks last 100 syncs with full cached context
- Records history for all sync types: Spotify, Tidal, Deezer, YouTube,
  Beatport, ListenBrainz, Mirrored playlists, and Download Missing flows
- Sync History button on sync page with modal showing entries, source
  filter tabs, stats badges, and pagination
- Re-sync button: server syncs expand card inline with live progress bar,
  matched/failed counts, and cancel button; download syncs open download modal
- Re-syncs update the original entry (moves to top) instead of creating duplicates
- Delete button (x) on each entry with smooth remove animation
- Fix mirrored playlist source detection (youtube_mirrored_ matched youtube_)
- Fix broken server import thumbnails with URL validation
2026-03-17 20:39:41 -07:00
Broque Thomas
f8f113d0e7 Beatport direct download — skip discovery, open download modal directly
Clicking any Beatport item now opens the download modal directly instead of
going through the discovery flow (scrape → chart card → discovery modal →
Spotify/iTunes matching → download).

Releases (albums/EPs) open as album downloads with full context.
Charts/playlists (Top 100, Featured Charts, DJ Charts, Top 10) open as
playlist downloads with per-track enrichment — each track's individual
Beatport page is visited to get release name, duration, artwork, BPM, key,
genre, and label.

Key changes:
- Add get_release_metadata() and enrich_chart_tracks() to scraper
- Add /api/beatport/release-metadata and /api/beatport/enrich-tracks endpoints
- Rewrite all Beatport click handlers to open download modal directly
- Per-track enrichment with live progress overlay (one-by-one fetching)
- Split combined artist strings so folder paths use primary artist only
- Prevent Beatport IDs from being written to Spotify tag fields
- Add beatport_release_ prefix detection for album download mode
- Support enrich=false query param for frontend-driven enrichment
2026-03-17 19:28:11 -07:00
Broque Thomas
171a64005d Fix discovery fix modal layout shift causing accidental clicks (#186)
Pin source info and search inputs at top of modal with independent
results scrolling, increase auto-search delay to 500ms, and add
confirmation dialog before committing a track match.
2026-03-17 14:21:11 -07:00
Broque Thomas
6bccbd9cf2 Fix Hydrabase background comparison to use configured metadata source instead of hardcoded iTunes 2026-03-17 12:09:05 -07:00
Broque Thomas
a9d0607d25 Add hi-res FLAC to CD quality downsampling in post-processing
New toggle under Post-Download Conversion: automatically converts 24-bit
or high sample rate FLAC files to 16-bit/44.1kHz after download, replacing
the original. Uses ffmpeg with temp file + verify + atomic swap for safety.
Runs before lossy copy so MP3s are made from the downsampled version.
Also prevents bit depth strict mode from rejecting files that will be
downsampled anyway.
2026-03-17 11:53:56 -07:00
Broque Thomas
cc62541a65 Add Select All, Fix Selected & Fix All to Library Maintenance findings 2026-03-17 09:25:21 -07:00
Broque Thomas
b9d3c4051e Fix Unknown Album in wishlist: album name lost when raw Spotify data missing
Root cause: discovery searches return (Track, raw_data, confidence) but
raw_data can be None (Strategy 4 extended search) or have mismatched index.
When raw_data is None, album_obj becomes {} — an empty dict that passes
normalization unchanged, so album name is never populated.

Fix: after extracting album_obj from raw_data, fall back to track_obj.album
(always populated from the SpotifyTrack dataclass) when album_obj has no
name. Applied to all 3 discovery paths (Deezer, Tidal, Spotify Public).

Also handle both string and dict album formats in wishlist UI rendering.
2026-03-17 08:12:34 -07:00
Broque Thomas
4c6e2fe1ec Revamp automation page: 2-col grid, duplicate, search/filter, templates, grouping
- Switch user automations to 2-column grid layout (matches system automations)
- Add duplicate button on non-system cards with POST /api/automations/<id>/duplicate
- Add search/filter bar (text search + trigger/action dropdowns) shown at 6+ automations
- Add Inspiration section with 8 starter templates that pre-fill the builder
- Add folder-style automation grouping with group_name DB column, dropdown
  popover for assignment, collapsible group sections, and builder group input
2026-03-17 07:40:14 -07:00
Broque Thomas
7604239b9a Filter refresh playlist dropdown by source and add spotify_public refresh handler
- Exclude file and beatport playlists from refresh (no external API)
- Hide Spotify library playlists from refresh dropdown when not authenticated
- Add spotify_public refresh handler using public embed scraper via stored URL
- Fix YouTube refresh to use stored description URL instead of hash-based source_id
- API returns source and spotify auth status for frontend filtering
2026-03-16 20:37:44 -07:00
Broque Thomas
53ef9fa913 Add Deezer support to watchlist config modal and linked provider section
- Query/update watchlist artists by deezer_artist_id in config endpoint
- Return deezer_artist_id in config response and recent albums response
- Add Deezer provider badge (purple) to linked provider section
- Detect Deezer vs iTunes for provider linking using fallback source setting
- Show "X fans" instead of "Pop: 0" for Deezer artist search results
- Include followers count in match/search artist response
- Add deezer_artist_id matching to library enrichment and recent releases queries
2026-03-16 19:49:54 -07:00
Broque Thomas
46d3309835 Revamp watchlist & wishlist buttons with dark glass design and animated gradient border
- Dark translucent background with backdrop blur instead of bright colored fills
- Animated flowing gradient border using CSS mask-composite technique
- Color-tinted labels and count badges (amber for watchlist, accent for wishlist)
- Shimmer sweep clipped inside button bounds
- Structured HTML: separate icon, label, badge, and shimmer elements
- Badge pulses when count > 0
- Worker orbs: 7s delay before collapsing back after mouse leaves header
2026-03-16 18:10:10 -07:00
Broque Thomas
775307c2b5 Add worker orbs animation to dashboard header
Worker buttons shrink to floating colored orbs with physics-based
movement, spark emissions from active workers, connection lines, and
center gravity. Hovering the header expands orbs back to full buttons
with staggered spring animation. Desktop only, toggleable in Settings
under UI Appearance.
2026-03-16 16:47:37 -07:00
Broque Thomas
42a4285e09 Add clear findings button to library maintenance modal
Per-job and per-status filtering — respects active toolbar filters so
users can clear e.g. only track number findings or only dismissed items.
Confirmation uses the app's styled modal instead of browser confirm().
2026-03-16 15:37:33 -07:00
Broque Thomas
f10beeea0a Add source badges (Spotify/iTunes/Deezer) to watchlist artist cards
Shows which metadata sources each artist is matched to with small
colored badges on the card. Also adds deezer_artist_id to the
watchlist API response and fixes the data-artist-id fallback chain
to include Deezer-only artists.
2026-03-16 13:37:53 -07:00
Broque Thomas
46ac46134b Add Deezer as configurable free metadata fallback source alongside iTunes
Users can now choose between iTunes/Apple Music and Deezer as their free
metadata source in Settings. Spotify always takes priority when authenticated;
the fallback handles all lookups when it's not.

Core changes:
- DeezerClient: full metadata interface (search, albums, artists, tracks)
  matching iTunesClient's API surface with identical dataclass return types
- SpotifyClient: configurable _fallback property switches between iTunes/Deezer
  based on live config reads (no restart needed)
- MetadataService, web_server, watchlist_scanner, api/search, repair_worker,
  seasonal_discovery, personalized_playlists: all direct iTunesClient imports
  replaced with fallback-aware helpers

Database:
- deezer_artist_id on watchlist_artists and similar_artists tables
- deezer_track_id/album_id/artist_id on discovery_pool and discovery_cache
- Full CRUD for Deezer IDs: add, read, update, backfill, metadata enrichment
- Watchlist duplicate detection by artist name prevents re-adding across sources
- SimilarArtist dataclass and all query/insert methods handle Deezer columns

Bug fixes found during review:
- Similar artist backfill was writing Deezer IDs into iTunes columns
- Discover hero was storing resolved Deezer IDs in wrong column
- Status cache not invalidating on settings save (source name lag)
- Watchlist add allowing duplicates when switching metadata sources
2026-03-16 13:12:12 -07:00
Broque Thomas
a02d14a23a Add URL history pills for YouTube, Deezer, and Spotify Link sync tabs
Saves successfully loaded playlist URLs to localStorage and displays
them as clickable pills between the input bar and playlist container.
Clicking a pill re-loads that URL; X button removes it. Max 10 per
source, most recent first. Source-colored hover accents match each
tab's brand styling.

Also fixes duplicate playlist bug — YouTube and Spotify Public now
check for already-loaded playlists before making API calls, preventing
broken duplicate cards when the same URL is entered twice.
2026-03-16 10:35:45 -07:00
Broque Thomas
837c5ff680 Add persistent library history tracking downloads and server imports
New library_history table logs every completed download and every new
track imported from Plex/Jellyfin/Navidrome. A "History" button next
to "Recent Activity" on the dashboard opens a modal with Downloads and
Server Imports tabs, album art thumbnails, quality/source badges, and
pagination.
2026-03-16 09:36:05 -07:00
Broque Thomas
f4af4ea7db Fix missing album cover art in download bubbles for redownload and issue downloads 2026-03-16 08:40:16 -07:00
Broque Thomas
7871f4581c Add cancel button for watchlist scans (manual and automation-triggered) 2026-03-15 23:24:18 -07:00
Broque Thomas
c90fff37f1 Fix service status labels missing HiFi and Qobuz display names 2026-03-15 23:05:56 -07:00
Broque Thomas
ec389c5ae8 Add HiFi as free lossless download source via public hifi-api instances
New download mode alongside Soulseek, YouTube, Tidal, and Qobuz. Uses
community-run REST API instances (no auth required) that serve Tidal CDN
FLAC streams. Features quality fallback chain (hires→lossless→high→low),
automatic instance rotation on failure, and full hybrid mode support.

Also fixes 6 missing streaming source checks for HiFi and Qobuz in the
frontend that were blocking playback with "format not supported" errors.
2026-03-15 22:53:34 -07:00
Broque Thomas
483e45cbc0 Add Spotify Link tab for public playlist/album scraping without API credentials
Scrapes Spotify's embed endpoint to extract track data from any public
playlist or album URL. Full discovery flow with Deezer parity: parse →
card → discovery modal → live progress → sync → download missing.

- New scraper: core/spotify_public_scraper.py (embed endpoint parsing)
- 12 API endpoints mirroring Deezer's discovery/sync/download flow
- WebSocket live discovery updates via spotify_public_discovery_states
- Green-branded tab, cards, and input styling (#1DB954)
- Album vs playlist detection with distinct card icons (💿/🎵)
- Download persistence: card click reopens download modal after close
- Card phase reset on cancel/completion (closeDownloadMissingModal)
- Backend download linking for spotify_public_ and deezer_ prefixes
- Completion handlers (V2 + no-missing-tracks) for both platforms
- Source URLs stored on mirrored playlists for future auto-refresh
2026-03-15 21:25:05 -07:00
Broque Thomas
da2b42b59a Add Redownload button to enhanced library view & fix album download mode
- Redownload button on each album in enhanced view (admin only)
- Uses same flow as artist page: fetches API tracklist, opens Download
  Missing modal with force-download option
- Register dashboard bubbles for library redownload and issue downloads
- Add library_redownload_ prefix to album download whitelist so it uses
  1 worker with source reuse and sends full album context (release_date
  for year in folder name)
2026-03-15 19:15:46 -07:00
Broque Thomas
0742cc45e6 Add hemisphere setting for seasonal playlists on Discover page
Southern hemisphere users now see correct seasons (e.g. March = Autumn,
December = Summer). Holidays (Halloween, Christmas, Valentine's) stay
calendar-fixed regardless of hemisphere.

- Hemisphere dropdown in Discovery Pool Settings
- GET/POST /api/discovery/hemisphere endpoints
- Season detection offsets months by 6 for southern hemisphere
- Stored in metadata table, defaults to northern
2026-03-15 18:49:51 -07:00
Broque Thomas
186671aa2e Add play button to repair findings & increase finding image sizes
- Play button on acoustid_mismatch, acoustid_no_match, track_number_mismatch, fake_lossless, dead_file, orphan_file findings
- Uses playLibraryTrack() for proper media player integration (track info, sidebar, album art)
- Data attributes for safe escaping instead of inline onclick strings
- Finding images increased from 56px to 150px with hover effects
- Improved detail panel spacing and media card layout
2026-03-15 15:46:30 -07:00
Broque Thomas
ab21855af3 Enrich repair findings with album art, artist images & live job progress
- All 9 repair jobs now emit report_progress() for real-time card updates
  (phase, log lines, per-item activity) via WebSocket repair:progress events
- Enrich finding details with album/artist thumb URLs across all repair jobs
  (dead_file, duplicate, metadata_gap, album_completeness, missing_cover_art,
  acoustid_scanner, track_number_repair, fake_lossless, orphan_file)
- Track number repair: return match_score from fuzzy matching, add suffix-based
  DB lookup for album/artist art (handles cross-environment path mismatches)
- Fix Plex/Jellyfin relative thumb URLs in findings endpoint via fix_artist_image_url
- Labeled media cards in finding detail panels (album title + artist name under images)
- Dashboard tooltip shows current job name + per-job progress instead of stale stats
2026-03-15 14:08:26 -07:00
Broque Thomas
f08550140f Add whistle logo to maintenance modal header & harden fix handlers
- Add whisoul.png to modal header with subtitle text and gradient background
- Responsive: smaller logo on mobile, hide subtitle
- Share _resolve_file_path in repair_worker for cross-environment path compat
- Use path resolution in orphan and duplicate file deletion
- Guard directory cleanup against removing the transfer folder itself
- Restore correct button label text on fix error recovery
2026-03-15 13:25:40 -07:00
Broque Thomas
0594673cad Polish finding detail renderers with richer visuals
- Cover art findings: show artwork image thumbnail preview
- Duplicate findings: KEEP/REMOVE badges highlighting best quality copy
- Incomplete album: completion progress bar with percentage
- Fake lossless: spectral analysis bar comparing detected vs expected cutoff
- Resolved findings: show descriptive action label (e.g. "Entry Removed")
- Fix operator precedence bug in fake lossless nyquist calculation
2026-03-15 13:00:44 -07:00
Broque Thomas
7a706e8c11 Add fix actions for maintenance findings (dead files, orphans, duplicates, etc.)
- New fix_finding() dispatcher in repair_worker with per-type handlers:
  dead_file (remove DB entry), orphan_file (delete from disk),
  track_number_mismatch (update DB), missing_cover_art (apply artwork URL),
  metadata_gap (apply found fields), duplicate_tracks (keep best quality)
- New POST /api/repair/findings/<id>/fix endpoint
- Frontend: contextual fix buttons per finding type, bulk "Fix Selected"
- Removed path_mismatch from fixable types (dry-run preview only)
2026-03-15 12:51:18 -07:00
Broque Thomas
61ed4086e0 Redesign maintenance findings/history tabs with dashboard & fix path resolution bugs
- Add findings dashboard with summary stats and per-job clickable filter chips
- Redesign findings cards with expandable detail panels and per-type renderers
- Redesign history tab with status dots, stat pills, and full timestamps
- Fix dead file cleaner false positives by using suffix-based path resolution
- Fix orphan file detector false positives by matching via path suffixes
- Add help text modal for each repair job card
- Enlarge maintenance modal (1100px wide, 90vh tall)
2026-03-15 12:20:02 -07:00
Broque Thomas
4635ea895b include a '?' to include more details for each job. 2026-03-15 11:19:09 -07:00
Broque Thomas
dab94ce65d update soulsync repair worker with new jobs 2026-03-15 10:18:50 -07:00
Broque Thomas
cae2bd0c62 Add granular post-processing toggles for metadata services, cover art, and lyrics 2026-03-14 22:45:50 -07:00
Broque Thomas
8f0b9518bc Add per-profile ListenBrainz support with personal settings modal 2026-03-14 20:12:19 -07:00
Broque Thomas
9a22c49a0e Add FLAC bit depth fallback option to quality profile 2026-03-14 19:24:25 -07:00
Broque Thomas
4a73ef6d24 Optimize enhanced view performance with event delegation and scoped DOM queries 2026-03-14 18:36:05 -07:00