Commit graph

38 commits

Author SHA1 Message Date
Broque Thomas
9fcbd323a5 Add stream source setting, auto-update yt-dlp on container start
Stream source:
- New setting in Settings → Downloads: "Stream / Preview Source"
- Options: YouTube (instant, default) or Active Download Source
- YouTube streams require no auth and are instant
- If active source is Soulseek, automatically falls back to YouTube
- Uses direct client search (bypasses orchestrator's download mode)
- Config key: download_source.stream_source

Docker:
- entrypoint.sh now runs pip install -U yt-dlp on every container
  start, so Docker users always have the latest yt-dlp without
  rebuilding the image
2026-03-26 19:27:35 -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
a48018f4ca Fix config save 'database is locked': add 30s timeout, WAL mode, and retry
Settings DB connections had no timeout (default 5s), causing lock failures when
enrichment workers hold concurrent write locks. Added 30s timeout, WAL journal
mode for better concurrency, retry-once before falling back to config.json.
2026-03-23 08:54:53 -07:00
Broque Thomas
9e75731f6c Add scrobbling to Last.fm/ListenBrainz + update What's New
Scrobbling:
- Last.fm: API signing, web auth flow, batch scrobble (50/request)
- ListenBrainz: submit_listens (batch 1000, listen_type=import)
- Worker hook: scrobbles unscrobbled events after each poll
- DB: scrobbled_lastfm/scrobbled_listenbrainz tracking columns
- Settings: API secret input, authorize button, scrobble toggles
- Config: lastfm.api_secret, lastfm.session_key, *.scrobble_enabled

What's New: added all features from this session (scrobbling,
personalized discovery, stats page, SoulID, lossy codecs, import,
hero redesign, Hydrabase, orphan fixes, year collection).
2026-03-22 14:38:53 -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
f9fc95c9f5 Add Opus and AAC codec options to lossy copy (Blasphemy Mode)
Lossy copy now supports MP3, Opus, and AAC (M4A) codecs with a
configurable dropdown in settings. Each codec uses the appropriate
ffmpeg encoder (libmp3lame/libopus/aac) and Mutagen tag writer
(ID3/Vorbis/MP4). Quality tag, filename substitution, and Blasphemy
Mode file cleanup all work per-codec. Backward compatible — existing
configs default to MP3.
2026-03-21 20:19:36 -07:00
Broque Thomas
d7b9b3ba26 Refactor post-processing metadata lookups + fix Hydrabase as fallback source
Post-processing: Extract 7 inline source lookup blocks into standalone
_pp_lookup_* functions called in configurable order via
metadata_enhancement.post_process_order config. Default order matches
original hardcoded sequence — zero behavioral change.

Hydrabase: Fix connected Hydrabase incorrectly becoming primary source.
_is_hydrabase_active() now only returns True in dev_mode (legacy).
Auto-connect no longer forces dev_mode. Hydrabase as fallback works
through normal _get_metadata_fallback_client path like iTunes/Deezer.
Settings button min-width fix.
2026-03-20 15:48:48 -07:00
Broque Thomas
2f9491c71b Expose Hydrabase as a configurable metadata source (no dev mode needed)
Add Hydrabase section to Settings → Connections with enable toggle,
WebSocket URL, API key, auto-connect, and connect/disconnect button.
_is_hydrabase_active() now checks hydrabase.enabled config in addition
to dev_mode — either path activates it. Default disabled, zero change
for existing users. Dev admin page stays behind dev mode password.
2026-03-20 13:41:02 -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
b9c83a50fa Add Soulseek peer queue filtering and configurable download timeout
- Add max_peer_queue setting to skip peers with long queues (soft filter
  with fallback to unfiltered if all results removed)
- Add download_timeout setting replacing hardcoded 10-minute limit
- Include quality_score (peer health: upload speed, free slots, queue
  length) in result ranking — was calculated but never used in sort key
- New UI controls in Soulseek settings section
2026-03-19 11:47:37 -07:00
Broque Thomas
a9d0607d25 Add hi-res FLAC to CD quality downsampling in post-processing
New toggle under Post-Download Conversion: automatically converts 24-bit
or high sample rate FLAC files to 16-bit/44.1kHz after download, replacing
the original. Uses ffmpeg with temp file + verify + atomic swap for safety.
Runs before lossy copy so MP3s are made from the downsampled version.
Also prevents bit depth strict mode from rejecting files that will be
downsampled anyway.
2026-03-17 11:53:56 -07:00
Broque Thomas
ec389c5ae8 Add HiFi as free lossless download source via public hifi-api instances
New download mode alongside Soulseek, YouTube, Tidal, and Qobuz. Uses
community-run REST API instances (no auth required) that serve Tidal CDN
FLAC streams. Features quality fallback chain (hires→lossless→high→low),
automatic instance rotation on failure, and full hybrid mode support.

Also fixes 6 missing streaming source checks for HiFi and Qobuz in the
frontend that were blocking playback with "format not supported" errors.
2026-03-15 22:53:34 -07:00
Broque Thomas
8a4672e2eb Encrypt sensitive config values at rest with Fernet — transparent migration, zero breaking changes 2026-03-12 09:25:25 -07:00
Broque Thomas
70c32aa640 Hybrid Mode Redesign 2026-03-11 18:10:35 -07:00
Broque Thomas
fb04d0f4bc Full qobuz support 2026-03-11 17:43:27 -07:00
Broque Thomas
f8d23ec37c Add Last.fm and Genius API clients with settings integration 2026-03-09 22:35:10 -07:00
Broque Thomas
aa93458ed3 Configurable ListenBrainz API endpoint for self-hosted instances 2026-03-09 20:52:33 -07:00
Broque Thomas
bbccd3524f Explicit content filter with configurable toggle to skip explicit tracks during downloads 2026-03-09 20:39:40 -07:00
Broque Thomas
75f9b7364a User configurable youtube rate limiting and optional cookies for bot detection 2026-03-05 10:50:06 -08:00
Broque Thomas
acfc26a4bd Add Tidal as a download source with full pipeline integration
Adds Tidal as a third download source alongside Soulseek and YouTube. Uses the tidalapi library with device-flow authentication to search and download tracks in configurable quality (Low/High/Lossless/HiRes) with automatic fallback. Integrates into the download orchestrator for all modes (Tidal Only, Hybrid with fallback chain), the transfer monitor, post-processing pipeline, and file finder. Frontend includes download settings with quality selector, device auth flow, and dynamic sidebar/dashboard labels that reflect the active download source. No breaking changes for existing users.
2026-02-28 21:47:19 -08:00
Broque Thomas
1283041836 Integrate Hydrabase P2P metadata client
Add Hydrabase support as an optional/dev metadata source and comparison tool.

- Add core/hydrabase_client.py: synchronous Hydrabase WebSocket client that normalizes results to Track/Artist/Album types and exposes raw access.
- Update config/settings.py: add hydrabase settings (url, api_key, auto_connect) and getter.
- Update web_server.py: integrate HydrabaseClient, initialize client alongside the existing HydrabaseWorker, add auto-reconnect using saved config, persist credentials on connect/disconnect, add endpoints for status and stored comparisons, background comparison runner (Hydrabase vs Spotify vs iTunes), and adapt multiple search endpoints to optionally use Hydrabase as the primary metadata source with fallbacks.
- Update web UI (webui/index.html, webui/static/script.js, webui/static/style.css): add network stats and source comparison UI, pre-fill saved credentials, show peer count, load/display comparisons, update disconnect behavior to disable dev mode, and add Hydrabase badge styling.

Behavioral notes: when dev mode + Hydrabase are active, searches can be served from Hydrabase and comparisons to Spotify/iTunes are run in background; when Hydrabase fails the code falls back to Spotify/iTunes. Saved Hydrabase credentials are persisted for auto-reconnect; disconnect disables dev mode and auto_connect.

Files touched: config/settings.py, core/hydrabase_client.py, web_server.py, webui/index.html, webui/static/script.js, webui/static/style.css.
2026-02-24 08:11:48 -08:00
Broque Thomas
2e6550ec53 Disable M3U export by default
Set m3u_export.enabled default to false and update the UI so the M3U auto-save checkbox is unchecked unless explicitly enabled. Changes: config/settings.py flips the default to false, webui/index.html removes the checked attribute from the checkbox, and webui/static/script.js adjusts the logic to only check the box when settings.m3u_export.enabled === true. This prevents automatic M3U exports for users who don't explicitly opt in.
2026-02-23 12:14:53 -08:00
Broque Thomas
2ba48f917d Add server-side M3U export and UI setting
Introduce M3U export feature with UI control and server-side saving. Adds a new m3u_export config option (enabled flag) and a checkbox in the settings UI. The web endpoint /api/save-playlist-m3u now checks the m3u_export setting (unless force=true), builds the target folder using a new _compute_m3u_folder() helper (leveraging existing template logic with sensible fallbacks), sanitizes filenames, and writes .m3u files into the computed folder. Frontend JS loads/saves the new setting, supplies album/artist metadata when auto-saving, and both autoSave and manual export now POST M3U data to the server (manual export uses force=true). Also changed browser download filename extension to .m3u and added minor logging/response behavior.
2026-02-23 11:59:16 -08:00
Broque Thomas
129f69fce9 Add Blasphemy Mode to delete FLAC after MP3
Introduce an optional "Blasphemy Mode" that deletes the original FLAC after a verified MP3 copy is created.

- config: add lossy_copy.delete_original (default: false).
- webui/index.html & static script: add checkbox and warning in settings UI and persist the setting.
- web_server.py: make _create_lossy_copy return the MP3 path when it deletes the FLAC (otherwise None); validate the MP3 using mutagen before removing the FLAC; rename associated .lrc files if present; update post-processing to use the final processed path in logs and wishlist checks and to consider .mp3 variants when FLAC may have been removed.

Behavior is off by default and includes safety checks and logging to avoid accidental deletion of originals.
2026-02-22 23:23:52 -08:00
Broque Thomas
d2adf17ca5 Add lossy MP3 copy of downloaded FLACs
Introduce a configurable "lossy_copy" feature that creates an MP3 copy alongside downloaded FLAC files. Adds default config (example and runtime) and UI controls for enabling the feature and selecting an MP3 bitrate. Implements _create_lossy_copy in web_server.py which checks the FLAC extension, respects the configured bitrate (default 320 kbps), locates ffmpeg (including a local tools/ffmpeg fallback), performs conversion, and attempts to update the QUALITY tag via mutagen. The feature is invoked after post-processing/moving downloads. Logs and graceful failures (missing ffmpeg, timeouts, tag errors) are included.
2026-02-20 22:16:00 -08:00
Broque Thomas
d674b999e5 feat: Import Music from local staging folder
Added an Import feature that lets users process local audio files through the existing post-processing pipeline (metadata enrichment, cover art, lyrics, library organization). Files are placed in a configurable Staging folder and imported via two modes: Album mode (search/match files to a Spotify tracklist) and Singles mode (select individual files for processing). Includes auto-suggested albums based on staging folder contents and real-time per-track progress tracking.
2026-02-06 23:29:05 -08:00
Broque Thomas
d08a2e91a2 feat: embed MusicBrainz, Spotify/iTunes IDs, ISRC, and merged genres into audio file tags
Enrich downloaded audio files with external identifiers and improved genre metadata in a single post-processing write. During metadata enhancement, the app now looks up the MusicBrainz recording and artist MBIDs, retrieves the ISRC and MusicBrainz genres from a follow-up detail lookup, merges them with Spotify's artist-level genres (deduplicated, capped at 5), and embeds everything alongside the Spotify/iTunes track, artist, and album IDs. All MusicBrainz API calls are serialized through the existing global rate limiter, making concurrent download workers safe without needing to pause the background worker. Includes a database migration adding Spotify/iTunes ID columns to the library tables.
2026-02-05 21:26:19 -08:00
Broque Thomas
d9efcbdf99 feat: AcoustID audio verification, MusicBrainz enrichment UI, v1.5
Add optional post-download audio fingerprint verification using AcoustID.
  Downloads are verified against expected track/artist using fuzzy string
  matching on AcoustID results. Mismatched files are quarantined and
  automatically added to the wishlist for retry.

  - AcoustID verification with title/artist fuzzy matching (not MBID comparison)
  - Quarantine system with JSON metadata sidecars for failed verifications
  - fpcalc binary auto-download for Windows, macOS (universal), and Linux
  - MusicBrainz enrichment worker with live status UI and track badges
  - Settings page AcoustID section with real-fingerprint connection test
  - Source reuse for album downloads to keep tracks from same Soulseek user
  - Enhanced search queries for better track matching
  - Bug fixes: wishlist tracking, album splitting, regex & handling, log rotation
2026-02-05 16:33:07 -08:00
Rafael Richard
3e733ab459 gracefully try to load configs from file into DB if the DB is fresh 2026-01-18 12:04:39 -05:00
Broque Thomas
9d275488e2 Add config reload support and improve config loading
Introduces a reload_config method to SpotifyClient and refactors ConfigManager to support reloading configuration from a file. Updates web_server.py to use the new config loading mechanism, ensuring configuration is loaded into the existing singleton instance and SpotifyClient is properly re-initialized after settings changes.
2026-01-06 09:29:11 -08:00
Broque Thomas
c94b1d2f5b Add hybrid Soulseek/YouTube download orchestration - TESTING
Introduces a DownloadOrchestrator class to route downloads between Soulseek and YouTube based on user-configurable modes (Soulseek only, YouTube only, Hybrid with fallback). Updates web server and UI to support new download source settings, including hybrid mode options and YouTube confidence threshold. Refactors YouTube client for thread-safe download management and bot detection bypass. Ensures quality filtering is skipped for YouTube results and improves file matching and post-processing logic for YouTube downloads.
2026-01-03 22:20:36 -08:00
Broque Thomas
61a698aefa Move database files to /app/data and use env var for path
Updated Dockerfile, entrypoint.sh, and Python code to store database files in /app/data instead of /app/database, avoiding conflicts with the Python package. The database path is now configurable via the DATABASE_PATH environment variable, improving flexibility for container deployments.
2026-01-01 09:53:39 -08:00
Broque Thomas
36418e05b4 Migrate config storage to SQLite database
ConfigManager now stores configuration in a SQLite database instead of a JSON file. The class supports migration from existing config.json files and falls back to defaults if no configuration is found. This change improves reliability and centralizes configuration management.
2025-12-04 11:08:38 -08:00
Broque Thomas
a3f98f29d3 utilize quality profile over preferred. 2025-11-17 14:11:14 -08:00
Broque Thomas
0f64d17bf6 add navidrome to settings 2025-09-21 14:10:17 -07:00
Broque Thomas
eb33e25805 Update settings.py 2025-08-20 14:02:07 -07:00
Broque Thomas
ed91099a1c enable quality pref. 2025-08-07 21:43:03 -07:00
Broque Thomas
7d43bda3e5 Initial commit 2025-07-09 12:07:41 -07:00