Commit Graph
1578 Commits
Author SHA1 Message Date
vicrodh 589e6071c4 feat(genre): show parent genres by default with sub-genre toggle
- Default view shows only parent genres (less cluttered like Qobuz)
- When selecting a parent, filtering includes all its sub-genres
- Added "Show all sub-genres" toggle for granular control
- Store now tracks parent/child relationships for proper filtering
- getFilterGenreNames() returns selected + children for filtering
2026-02-02 10:26:21 -06:00
vicrodh d2544dceb9 fix(genre): load all genres including sub-genres
Fetch parent genres first, then fetch sub-genres for each parent in
parallel. Combine into a flat list sorted alphabetically. This fixes
the issue where albums with sub-genres (like Alternative & Indie,
Opera, Concertos) were filtered out even when all genres were selected.
2026-02-02 10:18:57 -06:00
vicrodh df7a7e2cb5 feat(favorites): add playlist overlay with action buttons
Add FavoritePlaylistCard component with hover overlay containing:
- Heart button (remove from favorites)
- Play button (start playlist playback)
- More menu (play next, add to queue, share, remove)

Card click navigates to playlist page. Updates FavoritesView to use
the new component with all action handlers wired through +page.svelte.
2026-02-02 10:15:02 -06:00
vicrodh befe36ce30 style(ui): genre button variants and dropdown alignment
- Add 'control' variant for Favorites style matching other dropdowns
- Add 'align' prop: 'left' (Home) or 'right' (Favorites)
- FavoritesView uses control variant with right alignment
- Control variant shows "Genre: X" with chevron icon
2026-02-02 10:03:55 -06:00
vicrodh 2c886fae7a feat(genre): separate persistence for Home and Favorites contexts
- Refactor genreFilterStore to support multiple contexts
- Each context (home, favorites) has independent selection state
- Separate localStorage keys: qbz_genre_filter_home, qbz_genre_filter_favorites
- GenreFilterButton/Popup accept context prop
- FavoritesView uses 'favorites' context
2026-02-02 09:55:08 -06:00
vicrodh 65b42295e9 feat(favorites): add genre filter to Albums tab
- Add GenreFilterButton to Favorites > Albums toolbar
- Filter albums by genre name (multi-select supported)
- Use genreFilterVersion for reactivity tracking

Note: Artists tab doesn't have genre filter since artist
objects don't contain genre info directly.
2026-02-02 09:49:06 -06:00
vicrodh 6e4f48ff2e style(ui): clear button always visible, popup 530px wide 2026-02-02 09:43:00 -06:00
vicrodh 8dc1efec5d feat(genre): support multi-select genre filter
- Allow multiple genre selection (checkbox behavior)
- Add getSelectedGenreNames() for multiple name retrieval
- Merge results from multiple API calls for featured albums
- Clear button now closes the popup
- Button shows "N genres" when multiple selected
2026-02-02 09:37:43 -06:00
vicrodh 22ba76a8d1 feat(home): filter user sections by genre
- Add getSelectedGenreName() to genre filter store
- Add filterAlbumsByGenre() helper in HomeView
- Apply genre filter to Recently Played and Favorite Albums
- Fetch 3x more items when filtering to ensure enough results
2026-02-02 09:29:55 -06:00
vicrodh eee364e29c style(ui): genre popup extends left, 480px wide, 3 columns 2026-02-02 09:26:17 -06:00
vicrodh e61891bfc2 style(ui): widen genre filter popup to 420px with 4 columns 2026-02-02 09:25:04 -06:00
vicrodh 1b68b85402 style(ui): make genre filter cards smaller and more neutral
- Reduce card height from aspect-ratio to fixed 36px
- Use neutral bg-tertiary background instead of colored gradients
- Horizontal layout with name and check circle
- Selected state uses accent-primary
- Better spacing to prevent overlap
2026-02-02 09:24:37 -06:00
vicrodh d37b4ef7ff fix(ui): prevent infinite loop in GenreFilterButton
Use onMount instead of $effect to load genres once.
Add initialized flag to prevent onFilterChange from firing
during initial subscription setup, which was causing
loadHome -> re-render -> loadGenres -> notify -> loadHome loop.
2026-02-02 09:11:18 -06:00
vicrodh 733a12d702 fix(reco): fix migration for existing users without genre_id column
Remove genre_id from base schema and let migration add it.
This ensures existing databases don't fail when trying to create
index on non-existent column during initial schema execution.
2026-02-02 09:07:47 -06:00
vicrodh 8428fafaa5 feat(home): integrate genre filter into HomeView
- Add GenreFilterButton to home header
- Pass genre_id to fetchFeaturedAlbums API calls
- Reload home content when genre filter changes
- Featured sections now filter by selected genre
2026-02-02 08:53:13 -06:00
vicrodh 0d0da7c5fc feat(ui): add genre filter store and components
- Add genreFilterStore.ts with localStorage persistence
- Add GenreFilterButton.svelte for filter trigger
- Add GenreFilterPopup.svelte with colored genre cards
- Support "Remember my selection" toggle
- Single-genre selection (API limitation)
2026-02-02 08:51:37 -06:00
vicrodh d0dd42db65 feat(reco): add genre backfill commands
- Add reco_backfill_genres command to populate genre_id for existing events
- Add reco_needs_genre_backfill command to check if backfill is needed
- Fetches album details from Qobuz API and updates events in batches
- Includes rate limiting to avoid API throttling
2026-02-02 08:49:02 -06:00
vicrodh b4d7f7f4c3 feat(reco): add genre_id to reco_store schema
- Add genre_id column to reco_events table
- Add migration to add column to existing databases
- Update RecoEventInput and RecoEventRecord structs
- Update insert_event and get_events_since to handle genre_id
- Add get_album_ids_without_genre() for backfill
- Add update_genre_for_album() for backfill
2026-02-02 08:47:43 -06:00
vicrodh c2d61b46dc feat(api): add genre_id filter to get_featured_albums
- Add optional genre_id parameter to client method
- Add optional genre_id parameter to Tauri command
- Qobuz API filters featured albums by genre when provided
2026-02-02 08:45:44 -06:00
vicrodh 8e31717908 feat(api): add genre list endpoint
- Add GENRE_LIST endpoint constant
- Add GenreInfo model with id, name, color, slug, path
- Add get_genres() method to client
- Add get_genres Tauri command
2026-02-02 08:44:42 -06:00
vicrodh b6c723d3df fix(search): use 4-quadrant collage for playlist cards 2026-02-02 08:11:48 -06:00
vicrodh de0b820230 fix(search): use standard menu styles for playlist card dropdown 2026-02-02 08:09:17 -06:00
vicrodh d27ae32aee feat(search): wire up playlist handlers in main page
- Add playPlaylistById to set queue and play first track
- Add queuePlaylistNextById to add playlist tracks next
- Add queuePlaylistLaterById to add playlist tracks later
- Add copyPlaylistToLibraryById using subscribe_playlist
- Add sharePlaylistQobuzLinkById to copy link to clipboard
- Connect all handlers to SearchView component
2026-02-02 08:06:27 -06:00
vicrodh 61dd071326 feat(search): integrate playlists into search page
- Add playlists to unified search results (SearchAllResults)
- Add dedicated Playlists tab in jump-nav
- Create SearchPlaylistCard with album-like overlay (heart/play/menu)
- Add playlist menu: Play next, Add to queue, Copy to library, Share
- Add offset parameter to search_playlists command
- Add PlaylistGenre and extra fields to Playlist model
- Update searchState with Playlist type and playlistResults

UI changes:
- Remove glyphs from search tabs
- Reduce tab spacing (gap 8px→4px, padding 16px→12px)
- Reduce filter font size (13px→12px)
2026-02-02 07:57:16 -06:00
vicrodh f6d2a6bddd feat(immersive): default to coverflow view when opening immersive mode 2026-02-01 19:43:55 -06:00
vicrodh 2431a66545 refactor(settings): move limit quality toggle below streaming quality
- Repositioned toggle to appear right after Streaming Quality dropdown
- Updated description to clarify it overrides quality setting when device
  doesn't support the selected sample rate
2026-02-01 19:40:19 -06:00
vicrodh 39e8814848 feat(settings): add limit quality to device toggle and max sample rate tracking
Frontend changes for quality limiting feature:
- Add limitQualityToDevice state and handler
- Add AudioSettings interface fields for limit_quality_to_device and device_max_sample_rate
- Update handleBackendDeviceChange to send max_sample_rate when device changes
- Update handleOutputDeviceChange to find max_sample_rate from backend devices
- Add Settings UI toggle for "Limit Quality to Device"

This completes the quality limiting feature that prevents requesting tracks
that exceed the device's sample rate capabilities, ensuring bit-perfect playback.
2026-02-01 19:29:15 -06:00
vicrodh 6d53907233 feat(audio): add quality limiting based on device capabilities
- Add limit_quality_to_device setting (enabled by default)
- Add device_max_sample_rate field to store selected device's max rate
- Implement limit_quality_for_device() to restrict quality based on device
- Apply quality limiting in play_track and prefetch_track commands
- Add set_audio_device_max_sample_rate Tauri command

This ensures bit-perfect playback by not requesting tracks that
exceed the device's sample rate capabilities. When enabled:
- Devices <= 48kHz: limited to Lossless (44.1kHz)
- Devices <= 96kHz: limited to HiRes (up to 96kHz)
- Devices > 96kHz: allow UltraHiRes (up to 192kHz)
2026-02-01 19:21:02 -06:00
vicrodh be4786e6d3 feat(settings): add limit_quality_to_device setting
Add new audio setting to limit streaming quality based on device
capabilities. When enabled (default: true), QBZ will avoid requesting
tracks that exceed the device's max supported sample rate.

This ensures bit-perfect playback by preventing the need to resample
high-res tracks on devices that don't support them.

- Add limit_quality_to_device field to AudioSettings
- Add database migration for new column
- Add Tauri command for frontend integration

cc
2026-02-01 19:15:11 -06:00
vicrodh da5ec2abcb feat(audio): implement controlled hw→plughw fallback for ALSA Direct
- Add AlsaDirectError enum for structured error classification
- Add BitPerfectMode enum to track runtime bit-perfect status
- Implement automatic fallback: hw fails with format error → try plughw
- Abort on non-recoverable errors (busy, permissions, invalid params)
- Respect ALSA plugin selection from UI settings
- Log runtime mode for debugging

This allows SMSL-class DACs that fail on hw: to work via plughw:
while maintaining bit-perfect guarantees (format conversion only,
no resampling).

cc
2026-02-01 19:13:11 -06:00
vicrodh 4670b1548a feat(audio): add S24_3LE format support for SMSL-class USB DACs
- Add S24_3LE (S243LE) to format priority list as first choice
- Implement S24_3LE write handler with 3-byte packed conversion
- Add S24LE write handler (was missing explicit implementation)
- Reorder format priority: S24_3LE > S32LE > S24LE > S16LE > FloatLE

This fixes bit-perfect playback on USB DACs that only support
the S24_3LE format (SMSL, Topping, Fosi with TAS1020B chip).

Closes #30

cc
2026-02-01 19:07:17 -06:00
vicrodh bb4fcfd6b8 Optimize visualizer: 16 bins mirrored, Material You colors
- Reduce FFT to 30fps with 16 bins (mirrored to 32 visual bars)
- Use 1024 FFT size for faster processing
- Extract dominant colors from album artwork
- Center artwork with track info and quality badge
- Bars render from edges inward with color gradient
2026-02-01 18:44:52 -06:00
vicrodh 4b74e1c490 Update visualizer track info: larger art, artist/album/track order
- Increase artwork to 108px (20% larger)
- Reorder info: Artist (bold) > Album (italic) > Track
- Add album prop to VisualizerPanel
- Adjust text styling hierarchy
2026-02-01 15:42:43 -06:00
vicrodh a46936da53 Move track info to bottom-right, text aligned left of artwork
- Position track info at bottom-right with margin
- Reduce artwork to 90x90px
- Text aligned right, to the left of artwork
- Compact layout that doesn't obstruct visualization
2026-02-01 15:40:48 -06:00
vicrodh 55c3e80bbc Switch to Canvas 2D wave visualizer for lower CPU usage
- Replace WebGL2 instanced bars with simple Canvas 2D waves
- Mirror wave from center (top + bottom)
- Quadratic bezier curves for smooth lines
- Gradient coloring: cyan -> blue -> purple
- Black #000000 background
- Much lighter than 128 WebGL instances
2026-02-01 15:38:44 -06:00
vicrodh 283d17b5b8 Use black background for visualizer view (skip ImmersiveBackground entirely) 2026-02-01 15:36:35 -06:00
vicrodh 587e204c7b Disable ambient background animation in visualizer view
Reduces CPU usage by disabling the WebGL ambient motion when
the visualizer tab is active - both are GPU intensive.
2026-02-01 15:35:31 -06:00
vicrodh 337d4866d6 Add Visualizer tab to Focus mode with mirror visualization
- Add 'visualizer' to FocusTab type
- Add Visualizer tab with Activity icon in ImmersiveHeader
- Update VisualizerPanel with mirror mode (bars up and down from center)
- Integrate VisualizerPanel in ImmersivePlayer focus view
- Three-color gradient: teal -> purple -> pink
- Track info overlay with artwork, title, artist
2026-02-01 15:33:47 -06:00
vicrodh 06d3de0d73 Add WebGL2 VisualizerPanel frontend component
- Instanced bar rendering with vertex/fragment shaders
- Receives binary frequency data via Tauri events
- R32F texture for efficient frequency data upload
- Cyan-to-magenta gradient coloring
- Auto-enables/disables backend when mounted/unmounted
- 64 frequency bars with logarithmic mapping from backend
2026-02-01 15:11:40 -06:00
vicrodh 7bc429744b Integrate visualizer with audio thread
- Add VisualizerTap to Player for sample capture
- Create wrap_source helper in audio thread
- Wrap all audio sources with TappedSource at 4 append points
- Player now captures samples for visualization when enabled
- Zero overhead when disabled (fast atomic check)

Audio samples flow: Source -> TappedSource -> RingBuffer -> FFT Thread -> Frontend
2026-02-01 15:10:51 -06:00
vicrodh b8cfcfb34a Add audio visualizer backend infrastructure
- Add spectrum-analyzer crate for FFT processing
- Create visualizer module with:
  - RingBuffer: lockless SPSC buffer for audio samples
  - FFT processor: dedicated thread for spectrum analysis
  - TappedSource: rodio Source wrapper for sample capture
  - VisualizerTap: shared state for audio thread integration
- Add Tauri commands: set_visualizer_enabled, is_visualizer_enabled
- Register visualizer in AppState and lib.rs

Backend compiles. Next: integrate with audio thread to capture samples.
2026-02-01 15:07:21 -06:00
vicrodh 5a93ac509d Fix volume spam: deduplicate set_volume calls in backend
- Skip setting volume if already at same value (0.001 tolerance)
- Skip logging when volume unchanged
- Prevents MPRIS/PipeWire feedback loop spam
2026-02-01 14:33:56 -06:00
vicrodh 43444951ba Hypersensitive sleeve parallax: ±120px H, ±35px V with power curve
- Doubled horizontal range to ±120px
- Doubled vertical range to ±35px
- Added power curve (0.7) for hypersensitivity near center
- Small mouse movements now produce dramatic response
2026-02-01 14:27:57 -06:00
vicrodh 33dc0ccafb Increase sleeve parallax range: 120px horizontal, 35px vertical 2026-02-01 14:26:16 -06:00
vicrodh 1a4fa3cc78 Remove all parallax from vinyl - only sleeve moves
- Vinyl stays perfectly centered, only rotates
- Removed X and Y parallax from vinyl position
- Removed parallax uniform from shader
- Only the cover art (sleeve) responds to mouse movement
2026-02-01 14:24:36 -06:00
vicrodh 2fe0efc3d4 Fix vinyl rotation: 33⅓ RPM, concentric, no Y-axis movement
- Set rotation speed to 33.333 RPM (0.0582 rad/frame at 60fps)
- Remove Y-axis parallax from vinyl position (stays centered)
- Remove Y parallax from shader for perfectly concentric rotation
- Add mipmaps and trilinear filtering for anti-aliasing
- Enable anisotropic filtering when available
2026-02-01 14:22:56 -06:00
vicrodh 81232457d1 Fix parallax values to prevent artifacts 2026-02-01 14:15:37 -06:00
vicrodh 68b863ea64 Extreme horizontal parallax and use PNG texture
- Use PNG instead of SVG for better WebGL compatibility
- Extreme horizontal parallax: cover 200px, vinyl 120px
- Reduced vertical parallax for subtle depth
- Shader parallax: 0.15 horizontal, 0.05 vertical
- Disc appears to slide out of sleeve with minimal mouse movement
2026-02-01 14:14:02 -06:00
vicrodh fc85d66f5c Fix VinylPanel: add padding and aggressive parallax
- Add padding around vinyl (scale UV by 1.15x)
- Discard pixels outside bounds to avoid edge artifacts
- Much more aggressive parallax: cover 80/50px, vinyl 50/35px
- Shader parallax offset increased to 0.08
- Vinyl size reduced to 85% for more breathing room
2026-02-01 14:10:55 -06:00
vicrodh 6265e685ea Update logo and regenerate all app icons
- Updated qbz-logo.svg and qbz-logo.png
- Added qbz-vinyl-v3.svg for VinylPanel
- Regenerated all Tauri icons from new logo
- Updated Android icons and favicon
2026-02-01 14:09:57 -06:00