Commit graph

1795 commits

Author SHA1 Message Date
Broque Thomas
0f0ec3acb8 Bump version to 2.2, update changelogs with all new features
Version 2.2 includes: Wing It mode, Server Playlist Manager, Track
Redownload with source info, redesigned notifications, Spotify API
caching improvements, hybrid download fix, discovery progress fix,
YouTube Topic suffix fix, CAA art toggle, Genius search improvements,
auto-pause enrichment during downloads, download blacklist system,
and download provenance tracking.

Updated: SOULSYNC_VERSION, version button, helper.js WHATS_NEW key,
docker-publish.yml default version.
2026-04-01 20:46:42 -07:00
Broque Thomas
0e3a217591 Polish Wing It UX — dropdown on LB cards, position detection, better toasts
1. LB Discover page cards now use the dropdown (Download/Sync) instead
   of the old single button with full choice dialog
2. Dropdown position auto-flips downward when button is near viewport top
3. Dropdown centered on button instead of right-aligned
4. Sync completion toast now includes playlist name and  indicator
5. Download modal already shows  prefix on playlist name as indicator

All changes purely additive — existing flows unaffected.
2026-04-01 20:30:34 -07:00
Broque Thomas
a9dd93b176 Fix Wing It sync live status and button persistence across all modals
Live status: updateYouTubeModalSyncProgress was hardcoded to youtube-*
element IDs but each source uses its own prefix (listenbrainz-*, tidal-*,
deezer-*, etc). Now tries all prefixes to find the correct elements.
Fixes Wing It sync progress AND a pre-existing bug where normal LB/Tidal
sync from the modal wouldn't show live progress.

Wing It button added to sync_complete phase so it persists after sync.
Fixed tracks lookup with state.playlist?.tracks fallback. Increased
button size in modal to match other action buttons.
2026-04-01 20:10:30 -07:00
Broque Thomas
3c19cc085b Fix Wing It downloads not creating download bubble
The discoverMetadata (needed for bubble creation) was only set for
playlist IDs starting with discover_lb_, listenbrainz_, or source
SoulSync. Wing It uses wing_it_ prefix which wasn't matched.
Added wing_it_ to the condition so bubbles appear on dashboard
and sidebar during Wing It downloads.
2026-04-01 19:31:05 -07:00
Broque Thomas
9556fc9b5c Add Wing It mode — download or sync without metadata discovery
Wing It bypasses Spotify/iTunes/Deezer matching and uses raw track
names directly. User chooses Download or Sync from a choice dialog.

Download: opens Download Missing modal with force-download-all
pre-checked. wing_it flag skips wishlist for failed tracks.

Sync: new POST /api/wing-it/sync endpoint runs _run_sync_task with
raw track dicts. Live inline sync status display on the LB card
using the same progress elements as normal sync. Unmatched tracks
skip wishlist via _skip_wishlist flag on sync_service.

Button in three places:
- Next to "Start Discovery" in all discovery modals (fresh phase)
- Next to "Download Missing"/"Sync" after discovery (discovered phase)
- Next to "Download" on ListenBrainz cards (Discover page)

Fixed force-download toggle ID, sync progress field names
(total_tracks/matched_tracks not total/matched). All changes
purely additive — normal flows unaffected.
2026-04-01 19:16:51 -07:00
Broque Thomas
f58be8f05c Fix discovery progress not updating in modal (ListenBrainz, YouTube, etc.)
Discovery status polling was skipped when WebSocket was connected
(8 places), assuming socket events would push updates. But no
WebSocket events exist for discovery progress — the table stayed
on "Pending..." forever. Now always polls the status endpoint.
Pre-existing bug, not caused by recent changes.
2026-04-01 16:45:51 -07:00
Broque Thomas
119840fa5d Redesign toast notification system with bell button and history panel
Complete replacement of the old bottom-center stacking toast system:

Compact Toasts: Single toast at a time, bottom-right above buttons.
Pill shape with type-colored left border stripe, icon, message, and
optional "Learn more" link. Slides in, fades out after 3.5s. Click
to dismiss. New toasts replace the current one smoothly.

Notification Bell: 44px circle button next to the helper (?), with
red badge counter for unread notifications. Click opens panel.

Notification Panel: Glass popover above bell button showing history
of last 50 notifications. Each entry has type icon, message, relative
timestamp, and optional help link. Unread dot indicator. Clear All
button. Marks all as read when panel opens.

Same showToast(message, type, helpSection) signature — all 842
callers unchanged. Deduplication preserved. Updated version modal
and helper What's New.
2026-04-01 16:41:41 -07:00
Broque Thomas
89b5178838 Fix Download Discography on library page
Three issues fixed:
1. Stale discography data from a previous Artists page search was used
   instead of fetching for the current library artist. Now detects
   library page context and forces fresh fetch when names differ.
2. Enhanced view may not be loaded, so metadata IDs (spotify/itunes/
   deezer) are fetched from the enhanced endpoint on demand.
3. Fixed undefined spotifyId reference — now uses properly scoped
   metadataArtistId variable.

Falls back to name-based search when no metadata IDs are available.
Better error message directs users to Artists page as alternative.
2026-04-01 16:26:25 -07:00
Broque Thomas
77e4671236 Fix Download Discography button on library artist page
The button called openDiscographyModal() which expected discography
data in artistsPageState — but the library page never populated it.
Now fetches discography on-demand from /api/artist/<id>/discography
when called from the library page, using the artist's DB ID and name.
2026-04-01 15:57:26 -07:00
Broque Thomas
ce9ba42a91 Fix hybrid download mode not trying fallback sources (#235)
The download orchestrator's hybrid search stops at the first source
that returns ANY results, even if all those results fail quality
filtering. This meant Soulseek returning 100 low-quality results
would prevent HiFi/Tidal/YouTube from ever being tried.

Added hybrid fallback in the download worker: when all queries exhaust
with no valid candidates and hybrid mode is active, remaining sources
in the hybrid order are searched directly, bypassing the orchestrator's
stop-at-first-hit logic. Each fallback source gets the first 2 queries
with full quality filtering and candidate validation.

Fixed guard condition from convoluted diagnostic string parsing to a
simple mode check. Uses getattr for safe attribute access.
2026-04-01 15:49:51 -07:00
Broque Thomas
b4ed200633 Filter sync history to only show playlist syncs, not album downloads
Dashboard Recent Syncs and Sync page history were showing album
downloads, wishlist processing, and redownloads alongside actual
playlist syncs. Now filters to only show entries with sync_type
'playlist' (or no type for legacy entries).
2026-04-01 15:41:28 -07:00
Broque Thomas
8105f589ce Update version modal and helper What's New with all recent features
Added: Track Redownload with source info and provenance tracking,
Spotify API rate limit improvements (caching, auto-pause workers),
YouTube Topic fix, CAA art toggle, Genius search improvements,
Genius rate limit interval increase. Updated existing redownload
entry to reflect final implementation (removed old blacklist-from-delete
reference, added streaming source search and provenance tracking).
2026-04-01 15:39:23 -07:00
Broque Thomas
27aca53339 Add blacklist viewer on Settings tools page
New tool card shows blocked source count. "View Blacklist" opens a
modal listing all blacklisted sources with track name, filename,
username, service icon, and time ago. Each entry has a remove button
to unblock. Empty state explains how to blacklist from Source Info.
2026-04-01 15:35:07 -07:00
Broque Thomas
b8006d93c3 Refine track number filename extraction — separator required
_extract_track_number_from_filename now requires a separator after
digits (dash, dot, bracket) to prevent parsing artist names like
"50 Cent" as track number 50. Also handles disc-track format "1-03".

This is a last-resort fallback only — standard downloads get track
numbers from Spotify/iTunes/Deezer metadata. Only affects files with
no metadata where the filename starts with digits.
2026-04-01 15:24:03 -07:00
Broque Thomas
04bf2c5c4f Fix redownload progress bar visibility
Bar was using var(--accent) which can be dark/invisible against the
modal background. Now uses bright green gradient with glow shadow.
Also thicker (8px) and queries DOM fresh each poll tick to prevent
stale references.
2026-04-01 15:19:29 -07:00
Broque Thomas
19538233fd Group track action buttons (info, redownload, delete) into single cell
Three separate table cells with fixed widths replaced by one compact
cell with a flex group. Buttons are 24px each, 2px gap, fade in on
row hover. Removes ~70px of wasted horizontal space per track row.
2026-04-01 15:12:28 -07:00
Broque Thomas
45129263b4 Move Artist Radio and Enhance buttons to artist info area
Artist Radio and Enhance Quality buttons moved from the page header
into the artist hero section (after badges, before genres). Add to
Watchlist stays in the top-right header where it was.
2026-04-01 15:08:16 -07:00
Broque Thomas
0493f566df Fix redownload pipeline — full parity, stuck batch, button timing
Pipeline parity: redownload/start now fetches full track details from
the selected metadata source (Spotify/iTunes/Deezer) for real
track_number, disc_number, and album context. Sets explicit album
context flags so post-processing uses the standard album download path.

Stuck batch fix: active_count was 0, decremented to -1 on completion,
so batch never detected as complete. Now initializes active_count=1
and queue_index=1 since we submit the worker directly.

Button timing: Download Selected handler wired up immediately before
streaming starts, reads from window._redownloadCandidates which
updates live as results arrive. No longer blocked by slow Soulseek.

Track number: _extract_track_number_from_filename requires separator
after digits so "50 Cent" is not parsed as track 50.

Progress: real download stats from /api/downloads/status. Handles
streaming sources showing "Processing..." when no transfer found.
2026-04-01 14:59:30 -07:00
Broque Thomas
af98cb54c4 Fix redownload pipeline — Track object, confidence, lock safety
Three bugs that would crash when clicking Download Selected:

1. Passed raw dict to _attempt_download_with_candidates which accesses
   track.artists/track.album as attributes — now constructs Track object
2. TrackResult missing confidence attr — sort would crash — now set
   from candidate data
3. Redownload hook (old file delete + DB update) was inside tasks_lock
   blocking all task state changes — moved outside lock with proper
   None initialization
2026-04-01 14:14:50 -07:00
Broque Thomas
76a76c206e Make redownload modal buttons sticky at bottom
Step 1 and Step 2 action buttons (Cancel, Search/Download) now render
in a sticky footer outside the scrollable body — always visible
regardless of how many results are shown. Footer has backdrop blur
and top border separator matching the modal glass theme.
2026-04-01 14:01:51 -07:00
Broque Thomas
0fd6be3239 Stream download source results progressively via NDJSON
Step 2 of the redownload modal now streams results as each download
source responds instead of waiting for all sources to finish. Tidal/
YouTube/Qobuz columns appear instantly while Soulseek searches.

Backend: search-sources endpoint uses ThreadPoolExecutor + NDJSON
streaming — one JSON line per source as it completes.

Frontend: reads the NDJSON stream, appends columns with fade-in
animation as each source responds. Download button enables as soon
as any results arrive.

Each source gets its own column with results grouped and sorted by
confidence. Visual confidence bars, format badges, and source-specific
metadata (Soulseek username/slots). Best overall match auto-selected.
2026-04-01 13:53:57 -07:00
Broque Thomas
40f3621c48 Redesign redownload modal — columns, glass blur, Deezer fix, cover art
Major redesign:
- All metadata sources shown as side-by-side columns (not tabs)
- Frosted glass modal background with blur(40px) saturate(1.4)
- Album cover art in header from DB thumb_url (resolved for Plex)
- 1100px width, all elements scaled up, white text on accent buttons

Bug fixes:
- Deezer: use global singleton client, title-only fallback search,
  strip version suffixes from query
- Track.__init__: added missing popularity=0 parameter
- Overlay: dedicated .redownload-overlay class avoids CSS conflicts
2026-04-01 12:33:14 -07:00
Broque Thomas
04f01d36e1 Add track download provenance tracking and source info UI (#234)
New track_downloads table records every download with full source data:
service type (soulseek/youtube/tidal/etc), username, remote filename,
file size, and audio quality. Recorded at all 3 post-processing
completion points.

Source Info button (ℹ) on each track in the enhanced library view shows
a popover with download provenance: service, username, original filename,
size, quality, download date. Includes "Blacklist This Source" button
that stores the real username+filename (not guessed local filenames).

Removed broken "Delete & Blacklist" option from Smart Delete since it
had no access to real source data. Blacklisting now done exclusively
from the Source Info popover where actual provenance data exists.

Added blacklist CRUD API endpoints (GET/POST/DELETE /api/library/blacklist).
2026-04-01 11:37:21 -07:00
Broque Thomas
f8f87e0e38 Update version modal and helper What's New with redownload feature 2026-04-01 10:40:07 -07:00
Broque Thomas
35dd0546d1 Add Track Redownload modal with manual source selection (#234)
Three-step redownload flow in the enhanced library view:
1. Metadata Source — searches Spotify/iTunes/Deezer simultaneously,
   shows results with match scores, flags current match
2. Download Source — searches all active download sources (Soulseek,
   YouTube, Tidal, etc.), shows candidates with format/bitrate/size/
   confidence, flags blacklisted sources
3. Download — starts download, polls for progress, deletes old file
   on success, updates DB path

Also integrates the download blacklist into the download pipeline —
_attempt_download_with_candidates now skips blacklisted sources
automatically during all downloads (wishlist, playlist sync, etc.).

New redownload button (↻) on each track row in enhanced library view.
Post-processing hook deletes old file and updates DB track path after
successful redownload.
2026-04-01 10:36:51 -07:00
Broque Thomas
8d6486bee3 Add Smart Delete with file removal and download blacklist (#234)
Track delete in the enhanced library now shows three options:
- Remove from Library: DB record only (existing behavior)
- Delete File Too: DB + os.remove() the file from disk
- Delete & Blacklist: DB + file removal + add source to blacklist

New download_blacklist table stores rejected sources (username + filename)
with CRUD methods. Blacklist will be checked by the download pipeline
and the upcoming track redownload modal.

Smart delete modal styled with the same glass/dark theme as other
SoulSync modals, with color-coded destructive options.
2026-04-01 10:29:07 -07:00
Broque Thomas
660221d86a Show 'Yielding for downloads' on auto-paused enrichment workers
Dashboard enrichment chips show 'Yielding' instead of 'Paused' when
workers are auto-paused during downloads. Tooltips show 'Yielding for
downloads' for full context. Distinguishes user-paused from auto-paused.

Also handles edge case where user manually resumes a worker during
downloads — adds to override set so the loop doesn't re-pause it.
Override resets when downloads finish so next download session re-pauses.
2026-04-01 09:09:33 -07:00
Broque Thomas
c73df05fd9 Auto-pause rate-limited enrichment workers during downloads
Spotify, Last.fm, and Genius enrichment workers are now automatically
paused while any download batch is active. This prevents enrichment
API calls from competing with post-processing metadata lookups for
rate limit headroom, especially during heavy download scenarios
(3 playlist workers + wishlist downloads simultaneously).

Workers resume automatically when all downloads finish. Only workers
that were auto-paused are resumed — manually paused workers stay paused.
Piggybacks on the existing 2-second enrichment status loop with no new
threads or timers.
2026-04-01 08:55:38 -07:00
Broque Thomas
5d2215c1d2 Increase Genius API interval from 1.5s to 2s to reduce 429 rate limits
Genius rate limits are undocumented but users hit 429s at ~40 req/min.
Bumping interval from 1.5s to 2s drops throughput to ~30 req/min which
stays under the threshold. No functional change — just slower enrichment.
2026-04-01 08:42:55 -07:00
Broque Thomas
778e68a844 Improve Genius artist search for manual matching (#233)
The manual match modal for Genius only returned 0 or 1 artist result
because search_artist() searched songs (per_page=5), extracted the
primary artist, and returned the first match or None.

Added search_artists() that returns multiple unique artists extracted
from song results with broader search (per_page=20). The manual match
endpoint now shows up to 8 artist candidates and multiple track results
instead of one-or-nothing. Also shows the Genius URL as extra info.
2026-04-01 08:31:17 -07:00
Broque Thomas
982ca77501 Make Cover Art Archive album art opt-in instead of default (#232)
CAA art can be higher resolution (1200x1200+) but quality is
inconsistent — some releases have cellophane-wrapped photos or
low-quality scans. Spotify/iTunes/Deezer art is lower res (640x640)
but consistently clean and official.

New toggle: Settings → Post-Processing → "Use MusicBrainz Cover Art
Archive for album art" (off by default). Applies to both embedded
art and cover.jpg downloads.
2026-04-01 07:59:40 -07:00
Broque Thomas
f608331867 Ensure all watchlist scanner album fetches bypass cache
The previous commit only added skip_cache=True to one call site in
web_server.py. The watchlist scanner in core/watchlist_scanner.py has
6 Spotify get_artist_albums calls that also need fresh data to detect
new releases. All now bypass cache. iTunes/Deezer calls are unaffected
(they don't have the skip_cache param, detected via hasattr check).
2026-03-31 22:25:25 -07:00
Broque Thomas
89ef5f931f Route all Spotify search calls through cached methods
Five places in web_server.py called spotify_client.sp.search() directly,
bypassing the cached search_tracks()/search_artists() methods. Each
discovery worker (Tidal, YouTube, ListenBrainz, Beatport) was also
doubling API calls — sp.search() for raw data then search_tracks() for
Track objects.

Now all use cached methods only. Raw track data for album art is
retrieved from the metadata cache by track ID after matching. Also fixed
a pre-existing bug where Tidal discovery could pair stale Spotify raw
data with a newer iTunes match.

Bumped is_spotify_authenticated() probe cache TTL from 5 to 15 minutes
to reduce /v1/me calls (~288/day → ~96/day). Manual disconnect still
takes effect immediately via _invalidate_auth_cache().
2026-03-31 22:18:02 -07:00
Broque Thomas
b5c2878533 Fix get_artist_albums cache not actually storing data
The cache stores raw_data through _extract_fields which expects a dict
with a 'name' field. Storing a raw list caused silent AttributeError,
and storing a dict without 'name' triggered junk entity rejection
(empty string is in _JUNK_NAMES). Now wraps the albums list in a dict
with a valid name field so it passes validation and persists correctly.
2026-03-31 22:05:01 -07:00
Broque Thomas
54f927320d Fix skip_cache crash when watchlist scan uses non-Spotify client
iTunes and Deezer clients don't accept skip_cache param — only pass
it when the active client is the Spotify client (detected via sp attr).
2026-03-31 22:01:20 -07:00
Broque Thomas
a7ebde8c01 Add skip_cache param to get_artist_albums for watchlist scans
The watchlist auto-scan needs fresh data from Spotify to detect new
releases, so it bypasses the cache added in the previous commit.
All other callers (UI browsing, completion badges, discography views)
continue to benefit from cached results.
2026-03-31 21:59:46 -07:00
Broque Thomas
62da959889 Cache get_artist_albums to reduce Spotify API rate limiting
get_artist_albums was making fresh API calls on every invocation with
no cache check, despite being one of the most called methods (discography
views, completion badges, watchlist scans). The method already cached
individual albums opportunistically but never checked for a cached result
before hitting the API.

Now follows the same check-then-fetch-then-cache pattern used by
get_album_tracks and get_artist. Cache key includes album_type param
so different queries (album,single vs compilation) are cached separately.
2026-03-31 21:51:10 -07:00
Broque Thomas
87f17a1318 Fix cover.jpg not using Cover Art Archive high-res source
The MusicBrainz release ID found by _embed_source_ids was stored in the
metadata dict but never propagated to album_info. The old code tried to
write album_info['musicbrainz_release_id'] inside _embed_source_ids, but
album_info wasn't in that function's scope — causing a silent NameError.

Fix: copy the MBID from metadata to album_info in _enhance_file_metadata
(where both are in scope) right after _embed_source_ids returns. This
makes _download_cover_art see the MBID and use Cover Art Archive for
cover.jpg instead of falling back to the smaller Spotify/iTunes thumbnail.
2026-03-31 21:40:19 -07:00
Broque Thomas
f275a9831e Strip '- Topic' suffix from YouTube auto-generated channel names (#231)
YouTube's auto-generated artist channels use the format "Artist - Topic"
as the channel name. This suffix was not being stripped during playlist
parsing, causing metadata discovery to fail (e.g., searching for
"Koven - Topic" instead of "Koven" on iTunes/Deezer).

Fixed in all three places where YouTube artist names are cleaned:
- web_server.py clean_youtube_artist() — playlist parsing
- ui/pages/sync.py clean_youtube_artist() — UI-side parsing
- core/youtube_client.py — search result fallback artist extraction
2026-03-31 21:06:05 -07:00
Broque Thomas
34c8b1bb50 Add Server Playlist Manager with dual-column compare editor
New "Server Playlists" tab (default on Sync page) lets users compare
mirrored playlists against their media server and fix match issues.

- Dual-column comparison: source tracks (left) vs server tracks (right)
- Smart matching: exact title first, then fuzzy artist+title (≥75%)
- Find & Add: search library to fill missing slots at correct position
- Swap: replace matched tracks with different versions
- Remove: delete tracks from server playlists with confirmation
- Title similarity percentage badge on each match
- Disambiguation modal when multiple mirrored playlists share a name
- Album art on source tracks, server tracks, and search results
- Cross-column click-to-scroll highlighting
- Filter buttons (All/Matched/Missing/Extra) with live counts
- Escape key and backdrop click to close modals
- Mobile responsive (stacked columns under 768px)
- Works with Plex, Jellyfin, and Navidrome
2026-03-31 20:58:27 -07:00
Broque Thomas
de3fba3f37 Add Sync History dashboard with per-track match caching and detail modal
New dashboard section shows recent syncs as scrolling cards with
playlist art, source badge, match percentage bar, and health color.
Click any card to open a detail modal showing every track's match
status, confidence score, album art, and download/wishlist status.

Per-track data is now cached in sync_history.track_results for all
sync paths: server-sync (playlist→media server), download missing
tracks, and wishlist processing. SyncResult carries match_details
from the sync service. Both image URLs and matched track info are
preserved for review.

Features:
- Staggered card entrance animation, delete button on hover
- Filter bar: All/Matched/Unmatched/Downloaded
- Color-coded confidence badges (green/amber/red)
- Unmatched tracks show "→ Wishlist" status
- 32px album art thumbnails per track row
- Auto-refreshes every 30 seconds on dashboard
- Falls back gracefully for old syncs without track_results
2026-03-31 15:26:46 -07:00
Broque Thomas
ad262822a4 Cache per-track results in sync history + fix config_manager import
Sync operations now store per-track data (name, artist, match status,
confidence, download status) in a new track_results column on
sync_history. Also fixed missing config_manager import in
add_to_wishlist that crashed the duplicate tracks toggle.
2026-03-31 14:21:37 -07:00
Broque Thomas
cfe2ab7dec Add toggle to disable auto-clearing slskd search history
Users who keep manual searches in slskd as reminders were losing
them when SoulSync auto-cleaned at 200+ entries. New toggle in
Settings → Downloads → Soulseek: "Auto-clear slskd search history"
(on by default, preserving current behavior). When disabled, both
the hourly cleanup automation and the full cleanup step skip the
search history maintenance.
2026-03-31 13:03:37 -07:00
Broque Thomas
d1397722e2 Increase Navidrome API timeout from 10s to 60s
Large libraries (first import) can take longer than 10 seconds for
getArtists to respond. The short timeout caused the library fetch
to fail with 0 artists returned.
2026-03-31 12:44:40 -07:00
Broque Thomas
f4407490c1 Skip slskd connection check when Soulseek is not an active source
The dashboard status poll and hybrid connection check were pinging
slskd every 2 minutes regardless of download source, flooding logs
with connection errors when slskd wasn't running. Now only checks
slskd when the download mode is 'soulseek' or when 'soulseek' is
in the hybrid order. Hybrid mode also only checks sources in the
configured priority order instead of all six.
2026-03-31 12:37:14 -07:00
Broque Thomas
0bc6abd683 Allow duplicate tracks across albums with settings toggle
Same song from different albums was blocked from entering the
wishlist by a name+artist dedup check. Added toggle in Settings →
Library → File Organization: "Allow duplicate tracks across albums"
(on by default). When enabled, the dedup is skipped — different
album versions of the same song can coexist in the wishlist for
complete discography downloads. The UNIQUE constraint on track ID
still prevents the exact same track from being added twice.
2026-03-31 12:16:57 -07:00
Broque Thomas
2f4ff8213f Fix artist sync endpoint returning 404 for non-integer IDs (#230)
Route used <int:artist_id> which rejected Spotify/iTunes/Deezer
artist IDs. Changed to accept any string — tries as DB integer ID
first (verified against DB), then falls back to checking
spotify_artist_id, itunes_artist_id, deezer_artist_id columns.
Handles numeric iTunes/Deezer IDs correctly by verifying the DB
row exists before assuming it's a DB ID.
2026-03-31 11:33:46 -07:00
Broque Thomas
8369109ea0 Fix crypto copy buttons and correct ETH address
ETH address was wrong in the support modal. Also fixed clipboard
copy failing on HTTP (Docker) — navigator.clipboard requires HTTPS.
Added textarea fallback for insecure contexts, and shows the address
in a toast as last resort if both methods fail.
2026-03-31 11:13:09 -07:00
Broque Thomas
9189378364 Fetch Tidal tracks on click with loading overlay before discovery modal
With metadata-only listing, tracks aren't pre-loaded. Now shows
loading overlay while fetching tracks via /api/tidal/playlist/<id>,
then dismisses overlay and opens discovery modal. Overlay is hidden
at every exit point (error, empty, success) to prevent it from
blocking the modal.
2026-03-31 11:03:13 -07:00
Broque Thomas
c50eee1563 Fix discovery sync running outside Flask context + pass profile ID
_sync_discovery_results_to_mirrored was calling get_current_profile_id()
which requires Flask's g context, but runs in a background thread.
Now captures the profile ID in the Flask endpoint (where context exists)
and passes it through the discovery state to the background worker.
Also added debug logging to trace mirrored playlist matching.
2026-03-31 10:54:11 -07:00