Prioritize image.small (230px) over image.large (600px) for all
AlbumCard artwork and queue track thumbnails. Hero/detail views
retain 600px via getQobuzImageForSize(image, 'large').
- Metadata cache (artist location/genres, 30-day TTL)
- Scene cache (full discovery response for first page, 30-day TTL)
- Qobuz validation cache (per-artist name match, 30-day TTL)
Second visit to same scene loads instantly from cache.
When loading more results, the button is replaced by a compact
progress bar with percentage, reusing the same backend progress
events as the initial load.
Backend emits 'scene-discovery-progress' events with phase, progress
percentage, and detail. Frontend listens and shows a real progress bar
(0-40% MB search, 40-95% Qobuz validation, 100% done).
Loading state: hides header, cycles icons (Music/Globe/Network) at
1.5s intervals in a 96px pulse circle, shows progress bar with
percentage text.
- Genre filter buttons now hidden behind a Filter icon toggle in
the nav bar, with badge showing active filter count
- Revert to 100 results with load-more pagination (200 was too slow)
All text-based .back-btn now use margin-top: 24px, margin-bottom: 24px,
color: var(--text-muted), padding: 0. Fixes inconsistent spacing when
navigating between views. Also change genre filter pills from pill
shape (border-radius: 16px) to rounded rectangle (8px).
13 views fixed + ADR-004 created.
- Match back button margin-top/bottom (24px) with ArtistDetailView
- Add explicit padding-top/bottom: 0 to scene-view container
- Remove load-more pagination, load 200 artists at once
- Remove unused loadMore function and load-more CSS
Extract unique genres from MB tags across all results and display
as clickable pills. Selecting genres filters artists client-side
(AND logic). Shows count ratio when filters active.
When navigating from scene discovery to artist detail, pass the
MBID we already have from MusicBrainz instead of resolving by
name. Fixes wrong metadata for homonymous artists (e.g. The
Warning MX vs The Warning US).
Also improve Qobuz validation to request 5 results and pick
the best match by albums_count for name collisions.
Tags like "latin", "rock", "pop", "electronic" return the entire
country's catalog as noise. These are now excluded from MB search
queries but kept for affinity scoring. If ALL genres are broad,
falls back to using them to avoid empty results.
- Country flag header from circle-flags CDN
- Client-side search filtering
- A-Z grouping toggle
- Grid/sidepanel view toggle with album browser
- Back button matching FavoritesView style
- Proper margins and layout matching existing views
- Scene label shows country name only
- Pass countryCode through location context for flag display
Store ISO 3166-1 alpha-2 country code (lowercase) in ArtistLocation
for flag display. Scene label now shows just country name instead of
"Country, City scene".
Restore "Born in: United Kingdom, Brixton" / "Founded in: United
Kingdom, Leyton" in the artist sidebar. The scene discovery search
uses country-level queries regardless, so the sidebar should show
the precise MB location data.
MB Lucene is literal text match only - no hierarchy. Searching by
subdivision (England, California) misses most artists. Now search
directly by country: tag:"heavy metal" AND area:"United Kingdom".
Scoring handles regional relevance.
MB artists have beginarea as specific city (London, Birmingham) and
area as country (United Kingdom), never the subdivision (England).
Now the Lucene query includes the country: tag:"heavy metal" AND
(beginarea:"England" OR area:"England" OR area:"United Kingdom").
This finds all UK heavy metal artists instead of just the 9 that
literally have "England" as their area.
Resolve city to top-level subdivision at metadata time so the artist
sidebar shows "United Kingdom, England" instead of "United Kingdom,
Brixton". Label changed from "Born in"/"Founded in" to "From" to
match the broader regional context of scene discovery.
Rewrite resolve_parent_subdivision to iteratively walk the hierarchy
until finding the subdivision whose parent is the country.
Brixton -> Lambeth -> Greater London -> England (under UK).
Los Angeles -> California (under US).
Max 5 hops to prevent infinite loops.
Also: bump per_genre_limit to 200, display limit to 50,
fix misleading log (showed original city instead of resolved name).
Resolve cities UP to subdivision level via MB area relations API:
- Leyton -> England, Los Angeles -> California, Brixton -> England
- Uses direction:"forward" to walk "part of" chain upward
- Two-level inline lookup for districts/boroughs (avoids recursive async)
- Bump per_genre_limit to 100 for broader coverage
Was searching beginarea:"United States, Los Angeles" which returns 0
results. Now passes just "Los Angeles" for the search query and builds
"United States, Los Angeles" display separately for the UI header.
The browse API returns ALL artists from an area alphabetically with no
genre filtering. For LA that's 15K+ random artists. Now we search
tag:"thrash metal" AND beginarea:"Los Angeles" for each genre, giving
genre-relevant results directly from MusicBrainz.
Also adds dynamic loading messages that cycle through discovery steps
and temporarily disables all scene/validation caching for tuning.
Full scene discovery pipeline:
1. Resolve area MBID (if not provided) via MB area search
2. Check 30-day scene cache
3. Browse MB artists by area with tags
4. Score candidates with genre affinity algorithm
5. Validate top results against Qobuz catalog
6. Cache validation results individually (30-day TTL)
7. Cache full scene response for offset=0
The Rust ArtistType enum uses serde(rename_all = "lowercase"), so
"Person" serializes as "person". Frontend was comparing against "Person"
causing all artists (including David Bowie) to show "Founded" instead
of "Born".
Create the artists-by-location view that displays scene discovery
results using VirtualizedFavoritesArtistGrid. Wire onLocationClick
from ArtistDetailView through +page.svelte to navigate to the new view.
Shows Founded/Born date and Founded in/Born in location from
MusicBrainz metadata. Location is a clickable link that will
navigate to the artistsByLocation scene discovery view.
Metadata fetched in parallel with relationships, cached 30 days.
Section hidden when MusicBrainz is disabled. Translations added
for en, es, de, fr.
Exposes artist location, life span, and affinity seeds to the
frontend. Caches results for 30 days. Reuses the existing
get_artist_with_relations API call, extracting area fields
that were previously ignored.
- models.rs: Area, ArtistMetadata, ArtistLocation, LocationPrecision,
AffinitySeeds, LocationCandidate, LocationDiscoveryResponse,
ArtistBrowseResponse, AreaSearchResponse structs. Added area and
begin_area fields to ArtistFullResponse and ArtistResult.
- client.rs: browse_artists_by_area (MB browse API by area MBID),
search_area (resolve area name to MBID).
- cache.rs: mb_artist_metadata (30d), mb_scene_cache (30d),
mb_qobuz_validation (30d) tables with get/set methods.
- genre_normalization.rs: normalize_genre, noisy tag filtering,
extract_affinity_seeds, genre_summary. Covers rock, metal,
electronic, hip-hop, jazz, and other genre families.
- location_discovery.rs: extract_metadata from ArtistFullResponse,
resolve_location with city/state/country precision, country code
to name mapping, affinity scoring, scene cache key builder,
MB date formatting.
ArtistDetailView sort preferences and PlaylistDetailView copied
playlists now use getUserItem/setUserItem for per-user scoping.
Added migrateLocalStorageV2 to handle qbz_ prefixed keys that
the original migration missed (only covered qbz- prefix). Runs
once per user independently from v1 migration.
Replace per-track loops in queueAlbumNextById,
queuePlaylistNextById, and handleAddAlbumToQueueNext with a
single addTracksToQueueNext() call that sends all tracks in one
invoke to v2_add_tracks_to_queue_next. Backend already reverses
order internally.
Use Courier New/system monospace for --font-mono instead of
bundling JetBrains Mono (270KB). Only used for log output
and duration displays where a custom font adds no value.
TrackRow instances now subscribe with their trackId, so toggling
one track's favorite only fires that row's callback instead of
all ~25 visible rows. Global listeners (FavoritesView, QueuePanel,
etc.) still receive all notifications.
Add 80px padding-bottom inside the virtual scroll containers for
tracks, albums, and artists tabs. This is scrollable space (not fixed)
that prevents the navigate-to-top button from covering the last item.
Remove @import for fonts.googleapis.com. Self-host JetBrains Mono,
Oswald, and LINE Seed JP with @font-face declarations. App now works
fully offline without external font requests.
Removes will-change: transform from .virtual-item in all 4 virtualized
list components to avoid promoting 10-20 items to individual compositor
layers permanently.
Replace transition: all across 63 files with explicit property lists
(color, background-color, border-color, opacity). Special cases:
- NowPlayingBar control-btn: includes transform for :active scale
- CoverflowPanel: transform + opacity for 3D carousel
- ImmersiveHeader: max-width + opacity for expand animation
- Remove aur-source and aur-git PKGBUILDs (non-functional, unmaintained)
- Only qbz-bin (prebuilt binary) is maintained on AUR
- Update maintainer email in aur/PKGBUILD
- Extract darkest saturated pixel from artwork (clamped to very dark)
- Render radial gradients at all 4 corners with source-over blending
- Bottom corners slightly stronger for track info text readability
- Bass-driven radius pulse and per-corner position drift for organic feel
- Dark color lerps smoothly on track change like the main palette
- Extract dominant hues from album artwork via canvas sampling
- Replace all hardcoded hue values with palette-derived colors
- Smooth palette transitions when track changes (lerp on hue circle)
- Add time-based mutation for continuous color evolution
- Wisps, rings, and clouds fade to near-invisible when silent
- Black hole shrinks significantly when no audio energy
- Remove shared dropdownFadeIn from submenu (caused translateX jump)
- Add dedicated submenuFadeIn animation (slide from left)
- Anchor submenu at bottom instead of top (no position jump)
- Reduce gap to 2px and add invisible hover bridge
- Match submenu width to parent dropdown width
Reduce frontend smoothing (0.72->0.52) for faster audio response.
Lower beat detection thresholds and cooldown for more frequent spawns.
Increase bass/high multipliers on wisp speed, width, alpha, tunnel ring
radius, ring stroke width, feedback zoom, and phase advance. Wisps now
visibly surge on beats, rings pulse with bass, and the whole scene
breathes with the music instead of appearing randomly animated.
Reset all sidebar data (relationships, discovery, MB state) immediately
on artist change to prevent stale content flash. Add subtle fade-in
animation on sidebar sections for smooth content appearance.
Add dismissal system for "You May Also Like" discovery artists. Dismissals
are scoped to the genre tag used for the search, so dismissing AC/DC from
electronic recommendations won't affect metal recommendations.
- New discovery_dismissals table in reco DB (tag + normalized artist name)
- v2_dismiss_discovery_artist command persists dismissal per tag
- Discovery pipeline filters dismissed artists for both primary and secondary tags
- Backend returns 8 results (frontend shows 6, keeps 2 reserves)
- Thumbs-down button appears on hover, instantly replaces with reserve
- Returns primaryTag in DiscoveryResponse for frontend dismiss context
- Sidebar opens by default with localStorage persistence
- Simplified header: "Artist Network" title + PanelRightClose icon
- Hide RELATIONSHIPS section when no data (no empty placeholder)
- Restore sidebar after queue/lyrics panel closes
- Clean up sidebar state on artist view destroy
- TrackRow: show track number instead of frozen bars when paused
Replace toast notification with a proper modal dialog when the
performance watchdog degrades background mode. Modal includes a
"Don't show again" checkbox that persists to localStorage.
Threshold: < 15 FPS sustained for 5 seconds (after 3s warmup).
User can always re-enable effects from Settings > Appearance.
Add performance watchdog to ImmersiveRenderer that monitors FPS after
a 3-second warmup period. If FPS stays below 15 for 5 consecutive
seconds, automatically degrades background mode (Full -> Lite -> Off),
persists the setting, and shows a toast notification.
ImmersiveBackground listens for the degradation event and switches
renderer in-place without requiring a full remount.