Commit graph

24 commits

Author SHA1 Message Date
Broque Thomas
d9217237d2 Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs
PR #340 added ruff to the build-and-test.yml CI gate, which surfaced
286 pre-existing lint errors. Left unfixed, every feature branch push
fails CI. This commit resolves all of them so CI goes green and
contributors can actually land work.

Auto-fixes (248 of 286): removed unused f-string prefixes (F541),
renamed unused loop control variables with underscore prefix (B007),
removed duplicate imports (F811).

Manually fixed 10 latent bugs ruff caught (all wrapped in try/except
today, silently failing):

- music_database.py: _add_discovery_tables() called undefined
  conn.commit() — would have crashed the iTunes-support migration
  for existing databases. Now uses cursor.connection.commit().
- web_server.py settings GET: referenced undefined download_orchestrator
  when it should be soulseek_client. Feature (_source_status on the
  settings payload) was silently missing for UI auto-disable logic.
- web_server.py _process_wishlist_automatically: active_server
  undefined in track-ownership check. Auto-wishlist was falling
  through to the error handler and re-downloading owned tracks.
- web_server.py start_wishlist_missing_downloads: same active_server
  bug in the manual wishlist path.
- web_server.py _process_failed_tracks_to_wishlist_exact: emitted
  wishlist_item_added automation event with undefined artist_name
  and track. Automation event silently never fired correctly.
- web_server.py discovery metadata enrichment: referenced cache
  without calling get_metadata_cache() first. Track enrichment from
  cached API responses was silently skipped.
- web_server.py Beatport discovery worker: wing-it fallback branch
  used undefined successful_discoveries variable. Wing-it counter
  never incremented correctly. Now uses state['spotify_matches']
  consistently with the rest of the function.
- web_server.py _run_full_missing_tracks_process: stale import json
  mid-function shadowed the module-level import, making an earlier
  json.dumps() call reference an unbound local (F823).
- web_server.py discovery loop: platform loop variable shadowed
  the module-level platform import (F402).
- core/watchlist_scanner.py: 7 lambda captures of loop variables
  (B023 classic Python closure-in-loop bug) now bind at creation.

No existing tests had to change. Full suite stays at 263 passed.
2026-04-21 13:30:52 -07:00
Antti Kettunen
71e114b6fe
Tighten legacy logging output
- collapse old multi-line debug bursts into single structured rows
- remove leftover DEBUG-style prefixes from message text
- keep the app log readable without losing useful trace detail
2026-04-21 18:00:31 +03:00
Broque Thomas
5bb36412be Fix Jellyfin playlist track fetch truncating at default API limit
get_playlist_tracks() had no Limit or StartIndex params, so Jellyfin
defaulted to ~100 items per response. This caused the Server Playlists
comparison to show most tracks as missing even though they were present.

Now paginates in batches of 1000 until a partial batch signals the last page.
2026-04-13 15:17:52 -07:00
Broque Thomas
71e4df65e3 Remove emojis from all Python log and print statements
Stripped 4,200+ emoji characters from print(), logger calls across
39 Python files. Logs are now clean text — easier to grep, more
professional, no encoding issues on terminals without Unicode support.

Seasonal config icons preserved for UI display.
2026-04-11 21:11:02 -07:00
Broque Thomas
963a003ca0 Set playlist poster image on Plex/Jellyfin/Emby after sync
After a successful playlist sync, if the source playlist has cover
art (Spotify, Tidal, Deezer, etc.), the image is downloaded and
uploaded as the playlist poster on the media server. Plex uses
uploadPoster(), Jellyfin/Emby uses POST /Items/{id}/Images/Primary.
Navidrome skipped (no playlist image API). Failure is silent — sync
result unchanged. Automation-triggered syncs and playlists without
images are unaffected.
2026-04-09 10:08:18 -07:00
Broque Thomas
08a7408d8b Fix playlist sync failing on Emby due to integer ID validation
The _is_valid_guid method only accepted 32-char hex GUIDs (Jellyfin
format) but Emby uses plain integer IDs like "12345". All matched
tracks were rejected as "invalid/empty IDs" causing playlist creation
to fail with zero tracks. Now accepts both numeric strings (Emby)
and hex GUIDs (Jellyfin).
2026-04-07 13:12:51 -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
7de84c535c Add file path and bitrate support for Jellyfin tracks 2026-03-08 13:08:26 -07:00
Broque Thomas
7b854baba8 Detect and remove deleted content during incremental database updates
Incremental database updates now detect when artists or albums have been removed from your media server (Plex, Jellyfin, or Navidrome) and automatically clean them up from SoulSync's database. Previously, deleted content would persist as ghost entries until you ran a full refresh. Removal counts are reported in the scan results. Includes safety checks to prevent accidental mass deletion if the server is unreachable or returns incomplete data.
2026-03-03 13:13:38 -08:00
Broque Thomas
c4bf6ff0f3 jellyfin server connection fix 2026-02-27 08:47:50 -08:00
Broque Thomas
477615593e Increase Jellyfin API timeout default to 120
Raise the default Jellyfin API timeout for bulk operations from 30s to 120s to better handle slow servers and large database syncs. Updated core/jellyfin_client.py to set bulk_timeout to 120, webui/index.html to show 120s in the settings input, and webui/static/script.js to use 120 as the fallback when loading settings. Aligns UI and client behavior to reduce timeout errors during heavy syncs.
2026-02-26 08:28:08 -08:00
Broque Thomas
70156bdb98 Configurable Jellyfin API timeout and retries
Add a configurable API timeout for Jellyfin bulk requests and improve fetch retry behavior. UI: adds an "API Timeout (seconds)" field (15–300s, default 30) in webui/index.html and persists it via webui/static/script.js (load/save). Client: jellyfin_client.py now reads api_timeout from config_manager, uses it as the bulk timeout and computes a sensible non-bulk timeout (max(5, bulk_timeout//6)). Fetch loops for tracks and albums were hardened: reducing batch size now resets consecutive failure counters, log messages were clarified, and the stopping/retry thresholds were adjusted to avoid premature aborts at minimum batch sizes.
2026-02-26 08:21:20 -08:00
Broque Thomas
0d66f884e6 fix - jellyfin servers can not select a user when determing which library to scan. 2026-02-15 14:09:44 -08:00
Broque Thomas
b42d0252a1 Add Jellyfin music library selection support
Introduces backend and frontend functionality to list and select Jellyfin music libraries. Adds API endpoints, updates the client logic, and provides a UI selector for users to choose their preferred Jellyfin music library.
2025-12-08 10:20:15 -08:00
Broque Thomas
b7b76b378d Fix: Emby sync issue 2025-11-23 11:34:38 -08:00
Broque Thomas
ec6a9163a8 Fix: Jellyfin multiple users could break functionality. 2025-11-22 14:41:44 -08:00
Broque Thomas
8712209bb9 emby support? 2025-11-21 16:19:58 -08:00
Broque Thomas
22db9ba04d Update jellyfin_client.py 2025-09-25 14:17:57 -07:00
Broque Thomas
3e8b469adb library page updates 2025-09-23 23:47:58 -07:00
Broque Thomas
329e665db9 library page update 2025-09-23 20:45:58 -07:00
Broque Thomas
87a1427b1e bug fix 2025-08-18 21:19:52 -07:00
Broque Thomas
ceeb4c22dc remove metadata updater tool for jellyfin since most of the support is lost. 2025-08-18 18:00:58 -07:00
Broque Thomas
0c90f4f327 multi-server source 2025-08-18 15:32:58 -07:00
Broque Thomas
620c78766b better 2025-08-18 10:36:57 -07:00