Commit Graph
441 Commits
Author SHA1 Message Date
vicrodh 49a0a4223e fix(db): Move folder_id index creation to after migrations
The index was being created during schema execution, but for existing
databases the column doesn't exist until the migration runs. Now the
index is created after all migrations have completed.
2026-01-24 23:38:57 -06:00
vicrodh e5779ce6cf feat(backend): Add playlist folders Phase 1 - database and Tauri commands
- Add uuid crate dependency for folder ID generation
- Add PlaylistFolder struct and database schema
- Add folder_id field to PlaylistSettings
- Add database migration for existing installations
- Add CRUD methods for playlist folders:
  - create_playlist_folder
  - get_all_playlist_folders
  - get_playlist_folder
  - update_playlist_folder
  - delete_playlist_folder
  - reorder_playlist_folders
  - move_playlist_to_folder
  - get_playlists_in_folder
- Register Tauri commands for frontend integration
2026-01-24 23:15:48 -06:00
vicrodh 270465e822 fix(share): implement proper album.link resolution via iTunes lookup
- Add UPC field to Album models (Rust and TypeScript)
- Enhance share_album_songlink command to use iTunes lookup:
  - First tries UPC lookup via iTunes API
  - Falls back to Odesli API with UPC
  - Then tries album title/artist search on iTunes
  - Generates direct album.link/i/{id} URLs like tracks do
- Update frontend to use the backend command instead of
  generating non-functional URL-based links

This mirrors the track sharing approach that uses ISRC/iTunes
lookup to generate working song.link URLs.
2026-01-24 12:54:59 -06:00
vicrodh e86734c9a7 fix(tray): restore window properly from tray menu
Add unminimize() call to show_hide menu handler to match left-click
behavior. Also add logging for debugging visibility state.
2026-01-23 16:49:33 -06:00
vicrodh 077aa01356 feat(settings): add system tray settings
Add tray icon configuration options:
- Enable/disable tray icon (requires restart)
- Minimize to tray: hide window when clicking minimize
- Close to tray: hide window instead of quitting

Rust backend: TraySettingsState with SQLite persistence
Frontend: SettingsView tray subsection with toggles

Related to #15 (title bar settings)
2026-01-23 16:38:17 -06:00
vicrodh 6d5d264778 refactor(backend): rename module and APIs for compliance
- Renamed download_cache module to offline_cache
- Updated Tauri command names (e.g., cache_track_for_offline)
- Updated event names (offline:caching_started, etc.)
- Updated library database function names
- Updated frontend invoke calls to use new command names
2026-01-23 12:14:39 -06:00
vicrodh 712c7be4f7 fix(player): add delay after engine stop to prevent sink contention
Add 50ms delay after stopping the previous playback engine to allow
the rodio Sink to fully release its reference to the OutputStreamHandle.
This prevents "resource busy" errors when rapidly switching tracks,
especially when selecting from search while radio is playing.
2026-01-23 10:17:58 -06:00
vicrodh 1684df7752 fix(queue): complete album_id and artist_id propagation for track navigation
Ensure album_id and artist_id are passed through all queue track creation paths:
- trackActions.ts build functions (Qobuz, album, playlist, local tracks)
- Session persistence (both frontend and Rust backend)
- buildAlbumQueueTrack wrapper function
- Session restoration
2026-01-23 03:56:14 -06:00
vicrodh 2ea767f37c feat(radio): implement 50-track initial queue and auto-refill support
Radio improvements:
- Increase initial tracks from 15 to 50
- Add refill_radio_queue command (generates 20 more when queue low)
- Add get_queue_remaining command to check queue size
- Add create_infinite_radio command for "Keep Playing" feature
- Add InfiniteRadio autoplay mode based on last 5 tracks

Backend infrastructure ready for:
- Auto-refill when queue drops below 10 tracks
- Infinite playback when normal queue ends
2026-01-23 03:45:51 -06:00
vicrodh aabbe8ea67 fix(radio): pass album_id and artist_id for track navigation
Add album_id and artist_id fields to QueueTrack struct so that
radio tracks have proper navigation metadata. This enables
clicking artist/album in the main player to navigate correctly
when playing tracks from artist or track radio.

Changes:
- Add album_id/artist_id to Rust QueueTrack struct
- Update track_to_queue_track to extract IDs from Qobuz Track
- Add fields to frontend BackendQueueTrack interface
- Pass IDs in ArtistDetailView radio playback
- Pass IDs in playQueueTrack to playTrack service
2026-01-23 03:14:42 -06:00
vicrodh 9504294907 fix: remove track limit completely from library search
- Backend now supports limit=0 meaning "no limit"
- Frontend passes limit=0 to fetch all tracks
- Search now works across entire library regardless of size
- Virtualization handles rendering efficiently
2026-01-23 01:53:37 -06:00
vicrodh 58badcc64b fix: improve app identification in task managers
Set GTK_APPLICATION_ID for proper WM_CLASS and add desktop template
with StartupWMClass for correct process identification on Linux.
Also add window label for Tauri window configuration.
2026-01-23 01:50:27 -06:00
vicrodh 66d6ffb6ed feat: add canonical artist name normalization
- Add canonical_name column to artist_images table (migration)
- Store canonical names from Qobuz when fetching artist images
- Display canonical names (properly capitalized) in artist views
- Group and sort artists by canonical name when available
- Source priority: Qobuz -> Discogs -> Metadata

This ensures "Alice in chains" displays as "Alice In Chains" based on
Qobuz's authoritative naming.
2026-01-23 01:35:55 -06:00
vicrodh eec512a3f9 fix: address UI and performance issues in LocalLibrary
- Fix asset protocol scope for thumbnails in tauri.conf.json
- Homologate artist view controls to match album controls pattern
- Make artist grouping optional with dropdown (Off, A-Z)
- Change grid/list to single toggle button for artists
- Allow 3 lines for artist names in grid view
- Fix album card width to prevent title overflow
- Add throttling to artist image fetch to avoid rate limiting
2026-01-23 01:20:55 -06:00
vicrodh fc03bee9b1 feat(thumbnails): add backend thumbnail generation system 2026-01-23 01:07:16 -06:00
vicrodh b86effe4e9 perf(library): optimize artists and search queries
Extend SQL-based filtering to artists and search:
- Add get_artists_with_filter() method that filters source and
  network folders directly in SQL
- Replace library_get_artists N+1 loop that was calling
  db.search(artist.name, 10000) for EACH artist
- Add search_with_filter() method for optimized track search
- Replace library_search post-query filtering with SQL predicates

These changes eliminate thousands of redundant database queries
when browsing large libraries with network folders or Qobuz downloads.

Part of #13 - LocalLibrary performance optimization
2026-01-22 23:56:42 -06:00
vicrodh befe93e619 perf(library): fix N+1 query and reduce logging overhead
Backend performance improvements for large libraries:
- Change per-album INFO logging to DEBUG level (was logging every
  album during deserialization, causing 10k+ log lines)
- Add get_albums_with_full_filter() method that filters network
  folders directly in SQL instead of N+1 query pattern
- Replace library_get_albums N+1 loop with single SQL query
  (was doing 1 + N queries for N albums when excluding network folders)

Frontend infrastructure:
- Add libraryPerformanceStore.ts with feature flags for:
  - virtualizationEnabled (default: true)
  - thumbnailsEnabled (default: false, for future use)
  - optimizedNetworkFilter (default: true)
  - albumSearchDebounceMs (default: 150)
  - performanceModeThreshold (default: 500 albums)
  - forcePerformanceMode (default: false)

Part of #13 - LocalLibrary performance optimization
2026-01-22 23:55:16 -06:00
vicrodh 2a8b7c6d55 feat: Qobuz™ compliance, subscription gating, and settings cleanup
cx
2026-01-22 10:33:45 -06:00
vicrodh 52def4b7e8 fix: Restore Music category in tauri.conf.json (AudioVideo is invalid for Tauri) 2026-01-20 22:15:19 -06:00
vicrodh 6260b2f66c chore: Bump version to 1.1.6 for release 2026-01-20 22:00:40 -06:00
vicrodh 43ed44daf7 chore: Improve desktop integration and AppImage metadata for release
- Add Comment field to all .desktop files for app launcher descriptions
- Change category from Music to AudioVideo for better categorization
- Add Keywords for improved searchability (audiophile, hifi, streaming)
- Enhance metainfo.xml with detailed feature list and links
- Configure AppImage bundle settings in tauri.conf.json
- Update workflows to generate improved .desktop files

AppImage now includes embedded icon via Tauri icon configuration.
App will now appear in Multimedia category with proper description in all Linux app launchers.
2026-01-20 21:03:07 -06:00
vicrodh 7a99ac6e92 feat: Force first track to be from seed artist/track in radio
Artist radio: Generate 20 tracks, find first from seed artist (distance 0), move to position 0
Track radio: Generate 20 tracks, find seed track itself, move to position 0
This ensures radio always starts with a relevant track
2026-01-20 20:51:51 -06:00
vicrodh b2c413b3fd Revert: Remove first track forcing logic
Keep only the weight and pool size improvements
2026-01-20 20:22:05 -06:00
vicrodh 9dcc23c478 fix: Correct track radio first track logic
Use RadioEngine selection for all tracks instead of manually prepending seed track
2026-01-20 20:19:38 -06:00
vicrodh 484c27417a feat: Improve radio algorithm to favor seed artist
- Increase weight for seed artist tracks from 3 to 6 (doubles probability)
- Reduce similar artists pool: 12 -> 8 artists, 40 -> 30 tracks per artist
- Force first track in artist radio to always be from the seed artist
- Force first track in track radio to always be the seed track itself
2026-01-20 20:16:37 -06:00
vicrodh 40eba46e60 fix: Resolve Radio Engine compilation errors
- Add Clone impl for QobuzClient to enable thread-safe usage
- Use spawn_blocking for RadioDb operations to avoid Send trait issues
- Fix track.hires type mismatch (bool vs Option<bool>)
- Compilation now succeeds with no errors
2026-01-20 17:59:56 -06:00
vicrodh 4bceda90c8 wip: Fix Radio Engine Tauri commands compilation errors
Still has Send trait issues due to RadioDb not being thread-safe
2026-01-20 17:51:17 -06:00
vicrodh ba4f7af909 feat: Add Radio context type to backend playback context system 2026-01-20 17:41:17 -06:00
vicrodh ed3a9cf8cb feat: Add Radio Engine Tauri commands for artist and track radio 2026-01-20 17:39:37 -06:00
vicrodh 4748cef7df chore: Bump version to 1.1.6-rc and add ARM64 workflow
- Update version to 1.1.6-rc for release candidate testing
- Add Linux ARM64/aarch64 build workflow
- Uses ubuntu-24.04-arm GitHub runner (free for public repos)
- Builds .deb, .rpm, .AppImage, and AUR tarball for ARM64
- Workflow isolated with continue-on-error to not block x86_64 builds
2026-01-20 16:04:35 -06:00
vicrodh f86bc826b8 test(radio): add spacing and long-run stability tests
cx
2026-01-20 15:07:32 -06:00
vicrodh baade9949c test(radio): add deterministic selection tests
cx
2026-01-20 15:07:09 -06:00
vicrodh b8d78a9003 feat(radio): expand pool with playlists and similar artists
cx
2026-01-20 15:06:39 -06:00
vicrodh 029e4a07df feat(radio): add pool builder and artist tracks endpoint
cx
2026-01-20 15:05:52 -06:00
vicrodh da1384fbcc feat(radio): add zero-weight selection fallback
cx
2026-01-20 15:05:02 -06:00
vicrodh eab57dc4cd feat(radio): add deterministic next-track selection
cx
2026-01-20 15:04:38 -06:00
vicrodh fab21cbf8e feat(radio): add pool/history counters
cx
2026-01-20 15:04:12 -06:00
vicrodh 334dc4f1b5 feat(radio): add radio DB schema and sessions
cx
2026-01-20 15:03:52 -06:00
vicrodh c749b7d932 chore: Update tauri-plugin-dialog to 2.6.0
Fixes version mismatch warning between Rust crate (2.5.0) and
NPM package (@tauri-apps/plugin-dialog 2.6.0)
2026-01-20 14:34:18 -06:00
vicrodh b0d70b390a Security: Remove email from credential logs and add live smoke test
credentials/mod.rs:
- Remove email from log messages (privacy/security best practice)
- Logs still indicate success without exposing PII

tests/qobuz_live_smoke.rs:
- Add ignored live smoke test for Qobuz API
- Requires explicit opt-in via QBZ_RUN_QOBUZ_LIVE_TESTS=1
- Uses local credentials from keyring (no hardcoded secrets)
- Basic test: login + get_favorites endpoint
- Safe for repository (no credentials exposed)
2026-01-20 14:07:52 -06:00
vicrodh c1d32b4181 Rewrite offline playlist system to use file paths instead of IDs
Problem: Pending playlists created offline used auto-increment track IDs,
which change on library re-scans, causing FOREIGN KEY errors and data loss.

Solution: Store file_path instead of IDs for local tracks. Paths are stable
identifiers that only change if files are moved/renamed.

Backend changes:
- Add local_track_paths TEXT column to pending_playlist_sync table
- Update PendingPlaylist struct to include local_track_paths field
- Modify create/add functions to accept paths instead of IDs
- Add get_track_by_path command to resolve paths to current IDs
- Keep local_track_ids for backward compatibility

Frontend changes:
- Update PendingPlaylist interface with localTrackPaths field
- Fetch file paths from track IDs before creating pending playlists
- Implement path-to-ID resolution in sync function
- Gracefully handle missing files (log warning and skip)
- Backward compatible with old ID-based pending playlists

Testing:
- Simulated playlist creation with 3 local tracks (IDs 197-199)
- Simulated re-scan by changing IDs to 1500-1502
- Verified path resolution finds tracks with new IDs correctly
- Verified missing files are handled gracefully

This makes the offline playlist system robust and production-ready.
2026-01-20 13:30:10 -06:00
vicrodh 59cf94e7ca Support adding tracks to pending playlists in offline mode
Backend:
- Add add_tracks_to_pending_playlist method to OfflineStore
- Reads current track lists, appends new tracks, updates DB
- Add corresponding Tauri command and register in lib.rs

Frontend:
- Detect pending playlists by negative ID in handleAddToPlaylist
- Call add_tracks_to_pending_playlist for pending playlists
- Properly separate Qobuz and local tracks before adding
- Fixes error trying to call playlist_add_local_track with negative ID
2026-01-20 10:11:07 -06:00
vicrodh ea03553718 Add local track support to pending playlist sync
- Add local_track_ids column to pending_playlist_sync table
- Update PendingPlaylist struct to include localTrackIds field
- Modify create_pending_playlist to accept and store local track IDs
- Update get_pending_playlists to read local track IDs
- Allows creating playlists with both Qobuz and local tracks in offline mode
2026-01-20 08:23:52 -06:00
vicrodh 2270d80139 Add source/qobuz_track_id to LocalTrack model and implement filtering for Artists/Tracks
- Added source and qobuz_track_id fields to LocalTrack model for proper filtering
- Updated all database row mapping functions to include new fields
- Implemented network folder and download filtering for library_get_artists command
- Implemented network folder and download filtering for library_search (Tracks) command
- Made tab counters (Albums, Artists, Tracks) reactive to filtered content
- Counters now update based on current filtered data instead of total stats

Fixes offline mode filtering for Artists and Tracks tabs. When offline or when
downloads are hidden, Artists/Tracks now properly filter out inaccessible content,
matching the behavior already implemented for Albums.
2026-01-20 01:26:27 -06:00
vicrodh 6387fd598e Fix download repair to handle scanner-damaged tracks
Problem: Repair was failing for 42 tracks because it only searched
by qobuz_track_id, which was lost when scanner replaced downloads.

Root cause: Scanner's INSERT OR REPLACE doesn't include qobuz_track_id
in its column list, so it gets set to NULL during replacement. Previous
repair logic couldn't find tracks without qobuz_track_id.

Solution: Multi-strategy repair approach:
1. Search by qobuz_track_id first (intact downloads)
2. If not found, search by file_path (scanner-damaged downloads)
3. When found by path, restore BOTH qobuz_track_id AND source

New database functions:
- repair_qobuz_download_by_path(): Restores both ID and source
- track_exists_by_path(): Check existence by file path

Updated backfill logic:
- Try match by ID (for intact downloads with lost source)
- Try match by path (for scanner-damaged downloads)
- Insert as new only if not found by either method

This should successfully repair all 42 damaged downloads by matching
them via file_path from download_cache.

Files changed:
- src-tauri/src/library/database.rs
- src-tauri/src/library/commands.rs
2026-01-20 01:05:32 -06:00
vicrodh ac77a31b06 Add repair functionality for damaged Qobuz downloads
Problem: Qobuz downloads that were already scanned lost their
source = 'qobuz_download' marker. The previous fix only prevents
future damage, but doesn't repair existing tracks.

Solution:
- Added repair_qobuz_download_source() to restore source marker
- Modified library_backfill_downloads to repair existing tracks:
  - When a track exists, try to repair its source marker
  - Report shows: added, repaired, and skipped counts
  - This fixes downloads that lost their marker during scans

User can now run the backfill command to:
1. Restore source markers for damaged downloads
2. Make "Show in Local Library" setting work correctly
3. Separate Qobuz downloads from scanned music again

Files changed:
- src-tauri/src/library/database.rs
- src-tauri/src/library/commands.rs
2026-01-20 00:49:57 -06:00
vicrodh 6073cb96c1 Fix Qobuz downloads behavior and network filtering in offline mode
Problem 1: "Show in Local Library" setting was being ignored
- Qobuz downloads appeared in library even when setting was disabled
- Root cause: Scanner was overwriting Qobuz downloads with INSERT OR REPLACE
- This lost the 'source = qobuz_download' marker needed for filtering

Problem 2: Network music visible in offline mode
- Music from network folders remained visible when offline
- User expected network content to be completely hidden offline
- Only local disk music should appear

Solution 1: Protect Qobuz downloads from scanner
- Added is_qobuz_download_by_path() to check if file is already a download
- Modified insert_track() to skip files with source = 'qobuz_download'
- Now downloads and scanned music coexist as separate "joins" in DB

Solution 2: Filter network folders when offline
- Added get_network_folder_paths() to get list of network folder paths
- Modified library_get_albums to accept excludeNetworkFolders parameter
- When offline, filter out albums whose tracks are in network folders
- Frontend passes excludeNetworkFolders: isOffline to backend
- Added reactive effect to reload library when offline state changes

This implements the "join between tables" approach - Qobuz downloads
don't interfere with scanned music, and network content is properly
hidden when offline without being deleted.

Files changed:
- src-tauri/src/library/database.rs
- src-tauri/src/library/commands.rs
- src/lib/components/views/LocalLibraryView.svelte
2026-01-20 00:42:47 -06:00
vicrodh b450accb56 Fix LocalLibrary infinite loading in offline mode
Problem: When app is in offline mode, LocalLibrary gets stuck in
infinite loading screen and becomes unusable.

Root cause: loadFolders() was calling checkFolderAccessibility()
for each network folder. The backend command library_check_folder_accessible
uses blocking std::fs::read_dir which can hang for 20-30 seconds per
unreachable network path.

Solution:
Frontend (LocalLibraryView.svelte):
- Skip network folder accessibility checks when offline
- Assume network folders are inaccessible in offline mode
- Only check accessibility when online

Backend (library/commands.rs):
- Add 2-second timeout to library_check_folder_accessible
- Use tokio::task::spawn_blocking to avoid blocking async runtime
- Log timeout warnings for unreachable network paths

This allows LocalLibrary to load instantly when offline, showing
only local disk content as intended.

Files changed:
- src/lib/components/views/LocalLibraryView.svelte
- src-tauri/src/library/commands.rs
2026-01-20 00:27:28 -06:00
vicrodh 0d147a41b0 revert: restore previous offline detection behavior
cx
2026-01-20 00:08:55 -06:00
vicrodh a43ec7b7fb feat(offline): harden connectivity checks
cx
2026-01-19 22:53:47 -06:00