Commit graph

366 commits

Author SHA1 Message Date
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
508594c636 Use static colors for enrichment service status chips
Status text and indicators now use fixed Material Design colors
instead of accent-dependent values — green for running/idle, amber
for paused, red for stopped, dim white for not configured. Readable
regardless of the user's chosen accent color.
2026-03-30 19:08:22 -07:00
Broque Thomas
69346ec313 Add Playlist Explorer — visual discovery tree for expanding playlists
New dedicated Explorer page with interactive node graph visualization.
Users select a mirrored playlist, choose Albums or Discographies mode,
and the app builds a branching tree: playlist root → artist nodes →
album nodes → track nodes. Supports all metadata sources (Spotify,
iTunes, Deezer) with source-aware discovery cache integration.

Features:
- Streaming NDJSON builds tree progressively as artist data arrives
- Circular artist nodes with photos, rounded album nodes with art
- SVG bezier connections that draw in on completion, fade on hover
- Click artist to expand albums, double-click album for track listing
- Single-click albums to select, Select All/Deselect for bulk ops
- Wishlist confirmation modal with per-album progress (NDJSON streaming)
- Artist nodes glow when any of their albums are selected
- Playlist picker with source tabs, discovery % gate (50% minimum)
- Zoom (scroll/pinch/buttons), pan (right/middle-drag), fit-to-view
- Metadata cache for discographies and album track listings
- Owned album detection from library database
- Fallback track-name matching when album names are missing
2026-03-30 17:13:30 -07:00
Broque Thomas
c5652b0d4b Add API call counts and Spotify budget to dashboard service chips
Enrichment chips now show live activity: 24h call count for all
services and daily budget usage (used/3,000) with gradient progress
bar for Spotify. Tracking is centralized in _get_enrichment_status
using cumulative stat diffs over a rolling deque — no worker files
modified. Added section header, "Configure →" label for unconfigured
services, and full 1h/24h breakdown in tooltips.
2026-03-30 13:20:22 -07:00
Broque Thomas
32adc66fe3 Show all services on dashboard with click-to-configure (#219)
Dashboard now displays all enrichment services as live-status chips
below the core service cards. Each chip shows Running, Idle, Paused,
Stopped, or Not Configured state with color-coded left border accents.
Unconfigured services appear dimmed with dashed borders — clicking any
configurable chip navigates to Settings → Connections and scrolls to
the relevant service section.

Also fixes the Spotify card always being labeled "Apple Music" when
using iTunes fallback — card now always says "Spotify" with an amber
"Using iTunes/Deezer" indicator when fallback is active.
2026-03-30 10:04:13 -07:00
Broque Thomas
59587162cd Add metadata cache maintenance and health monitoring
Cache maintenance:
- Input validation rejects junk entities (Unknown Artist, empty names)
  from being cached, with exemptions for synthetic entries (_features,
  _tracks suffixes)
- CacheEvictorJob expanded to 4 phases: TTL eviction, junk cleanup,
  orphaned search cleanup, MusicBrainz failed lookup cleanup
- MusicBrainz null results now expire after 30 days (was 90) so failed
  lookups get retried sooner

Cache health UI:
- Polished modal accessible from Dashboard "Cache Health" button and
  repair dashboard health bar
- Shows health status banner (healthy/fair/poor), stat cards, source
  breakdown with colored progress bars, type pills, and metrics table
- Repair dashboard shows compact bar with health dot indicator
2026-03-30 07:40:20 -07:00
Broque Thomas
3c47281ce5 Redesign Watch All Unwatched as polished preview modal
Replaces the fire-and-forget button with a premium modal that shows
exactly which artists will be added before confirming. Features:

- Glassmorphic modal with stat cards, two-column artist grid, search
  filter, collapsible ineligible section, and loading spinner
- Source-aware filtering: only shows artists with the active source's
  ID (Spotify/iTunes/Deezer) as eligible
- Frontend and backend both paginate at 400 to avoid SQLite variable
  limit (SQLITE_MAX_VARIABLE_NUMBER=999) that silently broke queries
  above ~500 artists
- Backend source detection aligned with frontend — uses only the
  active source's ID, falls back to configured metadata source
2026-03-29 18:12:42 -07:00
Broque Thomas
b5b03a2b86 Add Download Discography feature on artist detail page
New "Download Discography" button in artist hero section opens a modal
showing the full catalog — albums, EPs, and singles — with filter
toggles, select/deselect all, and per-album owned/missing indicators.

Modal features:
- Glassmorphic design with artist image blurred background header
- Filter pills for Albums/EPs/Singles with instant grid filtering
- Album cards with cover art, year, track count, and checkbox
- Owned albums dimmed and unchecked by default, missing pre-selected
- Live NDJSON streaming: each album updates in real-time as processed
- "Process Wishlist Now" button after completion
- Albums sorted by track count (Deluxe first) to prevent duplicate
  folder contexts from standard/deluxe edition ordering

Backend: NDJSON streaming endpoint POST /api/artist/<id>/download-discography
- Fetches tracks per album via active metadata client
- Adds to wishlist with dedup (no slow fuzzy matching)
- Streams one JSON line per album as it completes
- Works on both Artists search page and Library artist detail page
2026-03-27 22:15:05 -07:00
Broque Thomas
326bb548ce Add per-artist Sync button on enhanced library view
New "Sync" button in the enhanced view header validates an artist's
library entries against files on disk. Removes stale tracks (missing
files), cleans empty albums, and updates track counts.

- POST /api/library/artist/<id>/sync endpoint
- Checks each track's file_path via _resolve_library_file_path
- Empty album cleanup checks ALL tracks (not just this artist's)
  to avoid deleting albums shared with other artists
- Toast shows results: stale removed, albums cleaned, or "All files
  verified" if everything checks out
- Auto-refreshes enhanced view when changes are made
2026-03-27 15:36:08 -07:00
Broque Thomas
f19db4ecce Add launch PIN lock screen with credential-based recovery
Security:
- Toggle in Settings → Advanced: "Require PIN to access SoulSync"
- Full-screen lock overlay on every page load when enabled
- PIN validated server-side against admin profile (bcrypt hash)
- Inline PIN creation if admin has no PIN set, change PIN button if set
- One-time session flag: verify-launch-pin sets it, /profiles/current
  consumes it — every page load re-requires PIN

Recovery:
- "Forgot PIN?" on lock screen switches to credential verification
- User pastes any configured API key/token/secret (Spotify, Tidal,
  Plex, Jellyfin, Navidrome, ListenBrainz, AcoustID, Last.fm, Genius)
- Server checks against all 9 stored values — any match clears PIN
  and disables lock, with toast guiding to Settings to set a new one

Profile switch integration:
- Entering PIN during profile switch also sets launch_pin_verified
  flag, preventing double-PIN prompt on the subsequent page reload

Updated version modal and helper What's New with this feature.
2026-03-26 13:15:36 -07:00
Broque Thomas
59e258a922 Add idle glow pulse to help button for new users
Subtle accent border glow breathes every 3s on the ? button until
the user opens the menu for the first time, then stops permanently
via localStorage. Helps new users notice the help system exists.
2026-03-26 12:32:03 -07:00
Broque Thomas
c1287f0ec0 Helper V2 complete + enrichment worker fixes
Helper system phases 2-7:
- Setup Progress: onboarding checklist with progress ring, auto-detection
  via /status, /api/settings, /api/library, /api/watchlist, /api/automations
- Quick Actions: accent pill buttons in popovers (service cards get
  "Open Settings" and "View Docs" actions)
- Keyboard Shortcuts: full-screen overlay with key cap styling, grouped
  by scope (Global, Player, Helper, Forms)
- Search: fuzzy search across 200+ help entries, 11 tours, and shortcuts
  with cross-page navigation via _guessPageFromSelector()
- What's New: version-tagged highlights with "Show me" navigation,
  red badge on ? button for unseen versions, older version cycling
- Troubleshoot: scans dashboard service cards for disconnected/error
  states, shows fix steps with action buttons, "All Clear" when healthy
- Contextual menu: page-aware tour suggestion at top of menu
- Ctrl+K / Cmd+K opens helper search globally
- First-launch welcome tooltip with pulsing ? button
- Redesigned floating button (48px, accent gradient, glass effect)
- Redesigned menu (unified card panel, accent left-stripe on contextual)

Enrichment worker fixes:
- AcoustID: individual recording matches downgraded INFO→DEBUG to reduce
  log noise (14 lines for one track → 1 summary line)
- Name normalization: strip " - Suffix" dash format (Spotify) same as
  "(Suffix)" parens format across all 8 workers. Fixes false mismatch
  on tracks like "Electric Eyes (Studio Brussels Remix)" vs
  "Electric Eyes - Studio Brussels Remix" (was 0.54, now matches)
2026-03-26 12:20:58 -07:00
Broque Thomas
1e9abb588c Add clickable artist name link in download modal hero subtitle
The artist name in album download modals is now a clickable link
that navigates to the Artists page with that artist's discography.
Uses the correct source-specific artist ID from the album data.
Works on enhanced search, artists page, and discover page modals.
Excluded from playlist, wishlist, and default contexts where the
subtitle isn't an artist name.
2026-03-26 08:22:52 -07:00
Broque Thomas
77b6d4927c Add View Discography button to watchlist artist detail overlay
Clicking the button closes the watchlist modal and navigates to the
Artists page with the artist's discography loaded. Uses the correct
source ID based on the active metadata source (Spotify/Deezer/iTunes).
2026-03-26 08:01:31 -07:00
Broque Thomas
f6709c7cc3 Add library ownership badges to enhanced search results
Search results now show "In Library" badges on albums and tracks
that already exist in the user's library. Badges appear with a
staggered fade-in animation after results render (non-blocking).

- Backend: /api/enhanced-search/library-check endpoint builds
  owned album/track sets in 2 queries, O(1) lookups per result
- Frontend: async call after render, 30ms stagger per badge
- Tracks in library get play button rewired for direct playback
  from media server instead of searching download sources
- Fixed enhanced search album card text not visible (info div
  now absolute-positioned with gradient overlay)
- Download manager panel hidden by default for more search space
2026-03-25 19:39:19 -07:00
Broque Thomas
6c3b9ddfc2 Optimize watchlist card CSS, backfill missing album covers
Watchlist cards: removed spring-bounce transitions, staggered
animations, and multi-layer hover shadows. Added CSS containment
and will-change for smoother scrolling.

Recent releases: backfill missing album cover art on page load
via metadata source lookup. Persists found covers to database.
2026-03-25 18:36:45 -07:00
Broque Thomas
38ba6ddbc1 Add interactive contextual help system with floating helper button
New feature: click the floating ? button (bottom-right corner) to
enter help mode. Click any UI element to see a popover explaining
what it is and how to use it. Covers Dashboard + Sidebar + Watchlist.

- Floating button always visible above modals (z-index 999999)
- Click interception via capture phase prevents accidental actions
- Popover with smart positioning (right/left/below fallback)
- Arrow pointing to target element with accent highlight pulse
- "View full documentation" links navigate to the correct docs section
- Escape key dismisses popover or exits help mode
- Works inside modals (watchlist, artist config, global settings)
- 45+ contextual help entries covering sidebar nav, service cards,
  stat cards, all 9 tool cards, watchlist modal buttons, artist
  config options, content filters, and activity feed
- Separate helper.js file for maintainability
2026-03-25 17:34:43 -07:00
Broque Thomas
d248c36da1 Redesign Artists page: rich hero section, full-bleed cards, multi-source genres
Artists page hero section:
- Large portrait artist photo (400x480px, rounded rectangle)
- Blurred saturated background from artist image
- 2.6em bold name with text shadow
- Real service logo badges (Spotify, MusicBrainz, Deezer, iTunes,
  Last.fm, Genius, Tidal, Qobuz) — matching library page
- Genre pills merged from metadata cache + Last.fm tags
- Last.fm bio with read more/show less toggle
- Last.fm listener count + playcount stats (large bold numbers)
- Backend enriches discography response with artist_info from
  metadata cache + library (all service IDs, Last.fm data, genres)

Album/Single/EP cards:
- Full-bleed cover art filling entire card with gradient overlay
- Album name + year overlaid at bottom over dark gradient
- Image zoom on hover, accent glow for dynamic-glow cards
- Responsive grid (220px desktop, 170px tablet, 140px mobile)

Similar artist cards:
- Full-bleed image cards matching library artist card style
- Gradient overlay with name at bottom, aspect-ratio 0.8
- Grid-controlled sizing via existing responsive breakpoints

Genre explorer (multi-source):
- Queries all allowed sources (iTunes+Deezer always, Spotify when
  authed) via _get_genre_allowed_sources() helper
- Deezer genre support: genre_id mapping from search results,
  one-time backfill from stored raw_json, album-to-artist propagation
- Genre deep dive deduplicates artists across sources
- Source dots on artists/tracks in deep dive modal
- Artist clicks route through source-specific client
- Album endpoint falls back across sources when IDs don't match
- Genre explorer cached 24hr in-memory, positioned at top of
  Discover page below hero slider

All changes mobile responsive with proper breakpoints.
2026-03-25 11:49:51 -07:00
Broque Thomas
e08462a002 Multi-source genre explorer with Deezer genre support and cross-source routing
Genre explorer and deep dive modal now combine data from all available
metadata sources (iTunes + Deezer always, Spotify when authenticated).
Artists are deduplicated by name across sources, preferring entries
with images. Source dots (green/red/purple) indicate data origin.

Deezer genre support:
- Extract genre_id from Deezer album search responses via ID-to-name
  mapping table (26 Deezer genre categories)
- Extract full genre names from Deezer get_album responses
- One-time backfill updates existing cached albums from stored raw_json
- Propagate album genres to Deezer artist entities

Cross-source album routing:
- /api/discover/album endpoint uses source-specific client (iTunes or
  Deezer) based on the item's source, not just the active fallback
- Spotify path falls back to active fallback when album not found
- Track clicks use album_id directly instead of name-based resolution
- resolve-cache-album adds partial match and live search fallback

Other fixes:
- Genre explorer positioned at top of Discover page (below hero)
- Genre explorer results cached 24hr in-memory for fast reload
- Related genres computed from all albums by matched artists
- Artist clicks open Artists page with discography (not library detail)
- Discovery pool genre queries restored to source-filtered (Browse by
  Genre tabs stay source-isolated as designed)
2026-03-24 19:36:11 -07:00
Broque Thomas
6101832ee1 Add search filter and rematch to discovery pool modal
Discovery pool lists (matched and failed) now have a search input
that filters tracks client-side by name, artist, or playlist.

Matched tracks get a "Rematch" button that opens the fix modal in
cache-only mode — deletes the old cache entry and saves the new
match directly to the discovery cache via /api/discovery-pool/rematch.
This works regardless of whether a mirrored playlist track exists.

Failed tracks retain the existing "Fix Match" flow unchanged.
2026-03-24 13:07:04 -07:00
Broque Thomas
e28aeccfd1 Redesign pool fix match modal: fixed height, no layout shift (#186)
Modal now uses a fixed 600px height from open — results scroll within
a dedicated area instead of growing the modal and pushing inputs up.
This eliminates the layout shift that caused accidental result clicks.

Other fixes:
- Input fields now have labels (Track, Artist)
- Overlay dismiss uses mousedown with stopPropagation to prevent
  accidental close when clicking near inputs
- Reduced results from 50 to 20 for faster response
- Clean minimal design matching app style
- Mobile: full-screen modal, stacked inputs with 44px touch targets
2026-03-24 11:16:37 -07:00
Broque Thomas
7b615a9534 Redesign enhanced search results: modern flat layout with responsive cards
- Search bar: stripped heavy purple chrome, minimal dark input style
- Dropdown: inline flow instead of overlay, hides page header when active
- Section labels: flat uppercase text, no bordered glass boxes
- Artist cards: full-bleed photo with gradient overlay and name at bottom
  (matches library page style), flexbox wrap layout with fixed dimensions
- Album cards: discover-style dark cards in horizontal scroll on desktop,
  wrap to 2-per-row on mobile
- Track rows: clean flat list, subtle hover, smaller cover art
- Source tabs: compact pills with per-source accent colors
- Renamed grid classes (enh-artists-grid, enh-albums-grid, enh-tracks-list)
  to avoid collision with generic .artists-grid rule
- Mobile: downloads-main-panel min-width:0 fix for 1190px overflow,
  cards use calc(50% - 8px) for 2-per-row fill, touch-friendly targets
2026-03-24 10:45:14 -07:00
Broque Thomas
96fa3fe388 Add SoulSync logo to sidebar header next to app name 2026-03-23 21:24:39 -07:00
Broque Thomas
e652726c22 Invert Tidal/Qobuz hero badges, add Artist Radio button and Last.fm play buttons
- Tidal and Qobuz SVG logos inverted on artist detail hero badges
- New Artist Radio button: clears queue, plays random artist track, enables radio
- Play buttons on Last.fm top tracks (hover reveal, resolves from library)
- Fixed inline JS escaping with data attribute delegation
2026-03-23 20:42:33 -07:00
Broque Thomas
4c3375745c Invert Tidal and Qobuz badge logos on library artist cards for visibility 2026-03-23 20:16:03 -07:00
Broque Thomas
cb32be4922 Refine settings page: glass buttons, accent section borders, softer inputs 2026-03-23 19:32:00 -07:00
Broque Thomas
2ae5050ef1 Add Deezer download source: client, settings UI, ARL authentication
- New core/deezer_download_client.py: full download client with ARL auth,
  Blowfish decryption, quality fallback (FLAC/MP3 320/MP3 128), search,
  thread-safe download tracking. Not yet integrated into orchestrator.
- Settings UI: Deezer download quality selector, ARL token input, test
  connection button. Appears in download source dropdown and hybrid list.
- Test endpoint: /api/deezer-download/test verifies ARL and returns tier.
- Added deezer_download to settings save whitelist and sensitive paths.
- Fixed Spotify enrichment worker default to unpaused (like other workers).
2026-03-23 15:06:40 -07:00
Broque Thomas
203f317236 Redesign download missing and wishlist modals with unified glass style 2026-03-22 22:16:25 -07:00
Broque Thomas
8d3d623f10 Redesign sync page playlist cards: unified glass style across all sources and mirrored playlists 2026-03-22 22:10:42 -07:00
Broque Thomas
1ac16ce0d4 Redesign dashboard cards: tool cards, service status, and system stats with unified glass style 2026-03-22 21:52:47 -07:00
Broque Thomas
e3d70da55a Add DB storage visualization + cache-powered discovery sections + Genre Deep Dive
- Stats page: database storage donut chart with per-table breakdown and total size
- Discover page: 5 new sections mined from metadata cache (zero API calls):
  Undiscovered Albums, New In Your Genres, From Your Labels, Deep Cuts, Genre Explorer
- Genre Deep Dive modal: artists (clickable → artist page), popular tracks,
  albums with download flow, related genre pills, in-library badges
- All cache queries filtered by active metadata source (Spotify/iTunes/Deezer)
- Stale cache entries (404) gracefully fall back to name+artist resolution
- Album cards show "In Library" badge, artist avatars scaled by prominence
2026-03-22 21:35:18 -07:00
Broque Thomas
c937045192 Mobile responsive overhaul: stats, artist detail, enhanced library, automations, hydrabase, docs
- Stats page: full mobile layout with compact cards, charts, ranked lists
- Artist hero: stacked layout, compact image/name/badges, top tracks below
- Enhanced library: meta header/expanded header stack vertically, track table
  collapses action columns into bottom sheet popover on mobile
- Automations: builder sidebar collapses, inputs go full width
- Hydrabase/Issues/Help: responsive stacking and compact layouts
- Fix grid blowout: add min-width:0 to stats grid children and overflow:hidden
2026-03-22 19:32:58 -07:00
Broque Thomas
93005298ee Cap Opus bitrate at 256kbps, fix lossy copy flavor text, redesign artist action buttons 2026-03-22 16:34:36 -07:00
Broque Thomas
21d7e65986 Speed up library page: split DB query, innerHTML rendering, staggered card animation 2026-03-22 16:14:28 -07:00
Broque Thomas
8c84189121 Add per-artist enrichment coverage rings to artist hero section 2026-03-22 15:56:12 -07:00
Broque Thomas
b59a0eaf95 Add play buttons to stats page with cover art support 2026-03-22 15:24:38 -07:00
Broque Thomas
232481fd13 Personalize discovery playlists using listening stats
Integrates play history data into the discovery algorithm:

- Listening profile: _get_listening_profile() builds user's top artists,
  genres, play counts, and listening velocity from the last 30 days
- Artist genre cache: pre-built from local DB for O(1) genre lookups
- Release Radar: +10 genre affinity, +15 artist familiarity, -10 overplay
  penalty. Weights rebalanced to 45% recency + 25% popularity + bonuses
- Discovery Weekly: serendipity scoring within tiers — boosts unheard
  artists in preferred genres, penalizes overplayed artists
- Recent Albums: adaptive time window (21-60 days) based on listening
  velocity — heavy listeners get fresher content, casual listeners more
- New "Because You Listen To" sections: personalized carousels based on
  user's top 3 played artists via similar artists + genre fallback
- New endpoint: /api/discover/because-you-listen-to with artist images
- Frontend: BYLT sections with artist photo headers on discover page
- All changes gracefully fall back when no listening data exists
2026-03-22 13:54:37 -07:00
Broque Thomas
cfb0e85564 Add Listening Stats page with media server play data integration
Full stats dashboard that polls Plex/Jellyfin/Navidrome for play
history and presents it with Chart.js visualizations:

Backend:
- ListeningStatsWorker polls active server every 30 min
- listening_history DB table with dedup, play_count/last_played on tracks
- get_play_history() and get_track_play_counts() for all 3 servers
- Pre-computed cache for all time ranges (7d/30d/12m/all) rebuilt each sync
- Single cached endpoint serves all stats data instantly
- Stats query methods: top artists/albums/tracks, timeline, genres, health

Frontend:
- New Stats nav page with glassmorphic container matching dashboard style
- Overview cards (plays, time, artists, albums, tracks) with accent hover
- Listening timeline bar chart (Chart.js)
- Genre breakdown doughnut chart with legend
- Top artists visual bubbles with profile pictures + ranked list
- Top albums and tracks ranked lists with album art
- Library health: format breakdown bar, unplayed count, enrichment coverage
- Recently played timeline with relative timestamps
- Time range pills with instant switching via cache
- Sync Now button with spinner, last synced timestamp
- Clickable artist names navigate to library artist detail
- Last.fm global listeners shown alongside personal play counts
- SoulID badges on matched artists
- Empty state when no data synced yet
- Mobile responsive layout

DB migrations: listening_history table, play_count/last_played columns,
all with idempotent CREATE IF NOT EXISTS / PRAGMA checks.
2026-03-22 13:18:14 -07:00
Broque Thomas
491b89a1d2 Redesign library artist hero with Last.fm integration
- Add get_artist_top_tracks to Last.fm client (up to 100 tracks)
- Include lastfm_listeners, lastfm_playcount, lastfm_tags, lastfm_bio,
  and soul_id in artist detail API response
- New endpoint: /api/artist/<id>/lastfm-top-tracks for lazy loading
- Hero layout: image (160px) | center (name, badges, genres, bio,
  listener/play stats, progress bars) | right card (scrollable top
  100 tracks from Last.fm)
- Badges 36px with hover lift, bio in subtle card with Read More
  toggle, Last.fm tags merged with existing genres
- Numbers formatted: 1234567 → 1.2M
- Graceful degradation: sections hidden when Last.fm data unavailable
2026-03-21 23:04:34 -07:00
Broque Thomas
2d511d0a16 Add SoulID worker with API-based debut year disambiguation
SoulID worker generates deterministic soul IDs for all library entities:
  - Artists: hash(name + debut_year) — searches iTunes + Deezer APIs,
    verifies correct artist by matching discography against local DB
    albums via MusicMatchingEngine, pools years from both sources and
    picks the earliest. Falls back to hash(name) if no match found.
  - Albums: hash(artist + album)
  - Tracks: song ID hash(artist + track) + album ID hash(artist + album + track)

Dashboard button with trans2.png logo, rainbow spinner, hover tooltip.
Worker orb with rainbow effect. SoulSync badge on library artist cards.
DB migration adds soul_id columns with indexes to artists/albums/tracks.
Migration version flag auto-resets artist soul IDs when algorithm changes.
2026-03-21 10:21:06 -07:00
Broque Thomas
2709f0fb37 Add hover animations and interaction polish to settings page
Tabs, cards, form rows, buttons, toggles, and inputs all get
tactile hover/focus/active states with lifts, glows, and accent
highlights to clarify which section the user is editing.
2026-03-20 16:31:55 -07:00
Broque Thomas
eab527224f Add min-width: fit-content to settings page buttons 2026-03-20 14:19:37 -07:00
Broque Thomas
272b1cd278 Redesign personal settings modal with tabs and library dropdowns
Non-admin: 3-tab layout (Music Services | Server | Scrobbling).
Admin: just ListenBrainz, no tabs (unchanged).

Server tab auto-detects active server (Plex/Jellyfin) and shows
library name dropdowns instead of raw ID inputs. Modal has max-height
with scroll, tab bar with accent underline indicator.
2026-03-20 12:29:13 -07:00
Broque Thomas
e8c26fb015 Revamp Automation Hub with one-click pipeline deployment
Add Pipelines tab with 7 pre-built automation groups that deploy
multiple linked automations in one click. Visual pipeline cards with
accent-colored gradients, connected flow nodes, and deploy buttons.

- Release Radar Sync, Discovery Weekly Sync, Playlist Auto-Sync:
  4-stage pipelines (refresh → discover → sync → download)
- New Music Pipeline: watchlist scan → download → cleanup → notify
- Nightly Operations: staggered time-based (1AM-4AM)
- Download Monitor: 3 notification automations for failures/quarantine/completion
- Library Guardian: quality scan → notify chain

Pipeline detail modal shows full automation breakdown with WHEN/DO/THEN
tags. Deploy prompts for notification config when pipeline includes
alert steps. All signal chains use unique prefixes to avoid collisions.
2026-03-20 00:17:30 -07:00
Broque Thomas
fc4e16337a Redesign hybrid mode with N-source priority ordering
Replace fixed primary/secondary hybrid dropdowns with an ordered list
of all 5 download sources. Users enable/disable each source and reorder
with up/down arrows to set download priority. Sources are tried in
order until one returns results.

- New hybrid_order config field (backward compat with legacy primary/secondary)
- Download orchestrator loops ordered list with per-source error handling
- Sortable source list UI with icons, toggle switches, priority numbers
- Source-specific settings shown for all enabled hybrid sources
- Seamless migration from legacy 2-source to N-source format
2026-03-19 18:51:52 -07:00
Broque Thomas
42b457f9d6 Redesign settings page with modern tabbed single-column layout
Replace 3-column glassmorphic card wall with centered single-column
tabbed interface. Horizontal pill tab bar (Connections, Downloads,
Library, Appearance, Advanced) with category switching.

- Kill glassmorphic cards, accent gradient bars, and box shadows
- Clean section headers with subtle dividers
- Horizontal setting rows (label left, control right)
- Custom styled select dropdowns with SVG arrow
- Quality Profile moved into Downloads tab (conditionally visible)
- Help text wraps to new line below controls
- Path inputs and template inputs properly styled
- Mobile responsive (rows stack, tab bar scrolls)
- Zero functional changes — all element IDs and JS logic preserved
2026-03-19 15:59:55 -07:00
Broque Thomas
e715ceef3e Add multi-source search tabs for enhanced search (Spotify/iTunes/Deezer)
Search results now show switchable tabs for alternate metadata sources.
Primary source renders immediately, alternate sources load in parallel
and tabs appear progressively as each completes.

- New /api/enhanced-search/source/<name> endpoint for per-source queries
- Source-aware routing via ?source= param on discography, album tracks,
  album detail, and artist image endpoints (prevents numeric ID
  misrouting between iTunes and Deezer)
- Source override stored on artistsPageState for consistent navigation
- Tabs styled with source brand colors, show result counts
- All additive — users who ignore tabs see zero behavioral change
2026-03-19 13:23:43 -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