soulsync/core
Broque Thomas 783c543c3e Auto-import: live per-track progress + in-progress history row
User reported (Mushy / generally) that dropping an album into the
staging folder left the auto-import history blank for the entire
processing window — sometimes 5+ minutes for a full album. Pre-
existing UX gap, not caused by the recent context-builder refactor.

Two root causes:

1. ``_record_result`` only fired AFTER ``_process_matches`` returned.
   For a 14-track album with ~30s/track post-processing, that meant
   ~7 minutes of zero rows in auto_import_history → nothing for
   ``/api/auto-import/results`` to return → empty UI.

2. ``_current_status`` only ever transitioned between 'idle' and
   'scanning' — never 'processing'. ``get_status()`` had no per-
   track index/name fields, so the UI had no way to render
   "Processing track 3/14: Mine" even if it wanted to.

Fix:

- New ``_record_in_progress`` inserts a status='processing' row
  up-front (before the per-track loop starts) so the UI sees the
  import the moment it begins. Returns the row id.
- New ``_finalize_result`` updates that same row with the final
  outcome (completed/failed) when processing finishes. One row per
  album, not per track — keeps the history list clean.
- Both share ``_serialize_match_data`` (extracted from the original
  ``_record_result``) so the in-progress row carries the same match
  payload shape the existing review UI already understands.
- ``_process_matches`` updates ``_current_track_index``,
  ``_current_track_total``, and ``_current_track_name`` BEFORE each
  per-track callback fires, so a polling UI sees consistent
  "processing N/M: <name>" snapshots.
- ``_scan_cycle`` flips ``_current_status`` to 'processing' before
  the per-album loop, resets it + the per-track fields after.
  Defensive ``finally`` clears progress even if the inner code path
  raised.
- ``get_status()`` exposes the new fields so the UI's existing
  /api/auto-import/status polling picks them up.
- Frontend (stats-automations.js): renders the new
  ``current_status='processing'`` state with track index/total/name
  in the existing progress bar element. New 'processing' status
  class for styling parity with 'scanning'.

8 regression tests in tests/imports/test_auto_import_live_progress.py:
- get_status surfaces the new fields with sane defaults
- track_index advances 1, 2, 3 during a 3-track loop
- track_total set BEFORE the first callback fires (no '1/0' flicker)
- _record_in_progress writes status='processing' with no
  processed_at
- _finalize_result updates the same row to completed +
  processed_at, no second insert
- _finalize_result with failed status leaves processed_at NULL
- _finalize_result with row_id=None is a safe no-op
- Per-track fields cleared by _scan_cycle's finally block

Full pytest 1643 passed; ruff clean.
2026-05-02 22:34:09 -07:00
..
artists Lift liked-artist matching to core/artists/liked_match.py 2026-04-29 17:34:28 -07:00
automation Lift /api/automations/blocks static config into core/automation/blocks.py 2026-04-27 18:31:36 -07:00
discovery Preserve artwork in quality scanner wishlist handoff 2026-04-30 21:42:16 +03:00
downloads Lift _process_failed_tracks_to_wishlist_exact to core/downloads/wishlist_failed.py 2026-04-30 11:03:44 -07:00
enrichment Consolidate enrichment bubble routes behind a service registry 2026-05-01 20:14:30 -07:00
imports Persist source IDs at download time + backfill onto tracks on sync 2026-05-02 17:44:10 -07:00
library Make extract_external_ids recognize all source-tagging conventions 2026-05-02 18:26:12 -07:00
metadata Persist source IDs at download time + backfill onto tracks on sync 2026-05-02 17:44:10 -07:00
playlists Lift _prepare_stream_task + playlist_explorer_build_tree to core/ 2026-04-29 11:26:07 -07:00
repair_jobs Add filename-pass safety: require duration agreement or artist match 2026-05-01 13:15:57 -07:00
search Extract wishlist logic into dedicated package 2026-04-28 21:17:24 +03:00
stats Lift /api/stats/* and /api/listening-stats/* into core/stats/ 2026-04-27 14:27:03 -07:00
streaming Lift _prepare_stream_task + playlist_explorer_build_tree to core/ 2026-04-29 11:26:07 -07:00
watchlist Bulk watchlist add: fall back through every source ID, not just active 2026-04-30 20:27:42 -07:00
wishlist Coerce wishlist track counts before category checks 2026-04-30 21:42:43 +03:00
workers Lift WebMetadataUpdateWorker to core/workers/metadata_update.py 2026-04-29 14:20:41 -07:00
acoustid_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
acoustid_verification.py Fix silent wrong-artist track downloads (Maduk/Tom Walker bug) 2026-04-22 10:32:55 -07:00
album_consistency.py Picard-style release preference scoring for MusicBrainz matching 2026-04-16 13:29:04 -07:00
api_call_tracker.py Make ApiCallTracker.save() atomic to prevent corrupt history files 2026-04-22 22:15:09 -07:00
artist_source_detail.py Move metadata helpers into package modules 2026-04-29 11:28:42 +03:00
artist_source_lookup.py Make artist_name Optional in find_library_artist_for_source 2026-04-22 22:15:03 -07:00
audiodb_client.py Add API Rate Monitor dashboard with real-time speedometer gauges 2026-04-02 10:46:43 -07:00
audiodb_worker.py Add genre whitelist for filtering junk tags during enrichment 2026-04-18 20:23:53 -07:00
auto_import_worker.py Auto-import: live per-track progress + in-progress history row 2026-05-02 22:34:09 -07:00
automation_engine.py Surface handler-returned errors in automation last_error 2026-04-30 15:45:28 -07:00
connection_detect.py Lift run_detection to core/connection_detect.py 2026-04-29 22:40:34 -07:00
connection_test.py Lift run_service_test to core/connection_test.py 2026-04-29 21:00:23 -07:00
database_update_worker.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
debug_info.py Make Spotify status updates event-driven 2026-05-02 22:02:01 +03:00
deezer_client.py Move metadata API into package 2026-04-29 08:10:18 +03:00
deezer_download_client.py Move metadata API into package 2026-04-29 08:10:18 +03:00
deezer_worker.py Fix Album Completeness job reporting zero findings for every album 2026-04-24 12:39:41 -07:00
discogs_client.py Move metadata API into package 2026-04-29 08:10:18 +03:00
discogs_worker.py Discogs: count rows with empty type_ as real tracks too 2026-04-25 08:27:44 -07:00
download_orchestrator.py Fix download cleanup after wishlist runs 2026-04-28 21:22:21 +03:00
genius_client.py Add API Rate Monitor dashboard with real-time speedometer gauges 2026-04-02 10:46:43 -07:00
genius_worker.py Improve graceful shutdown and rollback safety 2026-04-12 15:17:18 +03:00
genre_filter.py Expand default genre whitelist from 223 to 272 genres 2026-04-18 20:32:49 -07:00
hifi_client.py get hifi tags during download, without needing to go through the enrichment pipeline 2026-05-02 07:50:12 -06:00
hydrabase_client.py Add missing _get_artist_image_from_albums to HydrabaseClient 2026-03-23 17:54:21 -07:00
hydrabase_worker.py Improve graceful shutdown and rollback safety 2026-04-12 15:17:18 +03:00
itunes_client.py Move metadata API into package 2026-04-29 08:10:18 +03:00
itunes_worker.py Fix Album Completeness job reporting zero findings for every album 2026-04-24 12:39:41 -07:00
jellyfin_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
lastfm_client.py Add Last.fm Track Radio to Discover page 2026-04-14 23:41:36 -07:00
lastfm_worker.py fix: stop enrichment workers from re-processing rows forever 2026-04-19 15:22:24 -07:00
library_reorganize.py Reorganize: fix moved-count + status/total UX issues from PR #377 review 2026-04-25 09:07:44 -07:00
lidarr_download_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
listenbrainz_client.py Remove emojis from all Python log and print statements 2026-04-11 21:11:02 -07:00
listenbrainz_manager.py Add Last.fm Track Radio to Discover page 2026-04-14 23:41:36 -07:00
listening_stats_worker.py fix: batch N+1 queries in listening stats worker 2026-04-19 15:22:25 -07:00
lyrics_client.py Remove emojis from all Python log and print statements 2026-04-11 21:11:02 -07:00
matching_engine.py Replace more print logs with proper logger usage 2026-04-21 14:42:21 +03:00
metadata_service.py Move profile Spotify cache into registry 2026-04-29 12:36:37 +03:00
musicbrainz_client.py MusicBrainz: Resolve release-group MBIDs to a release on album click 2026-04-24 08:48:02 -07:00
musicbrainz_search.py MusicBrainz: Dedupe same-named homonyms in artist search results 2026-04-24 10:27:45 -07:00
musicbrainz_service.py Add metadata cache maintenance and health monitoring 2026-03-30 07:40:20 -07:00
musicbrainz_worker.py fix: stop enrichment workers from re-processing rows forever 2026-04-19 15:22:24 -07:00
navidrome_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
personalized_playlists.py Replace all legacy metadata_service imports with canonical functions 2026-04-10 12:52:36 -07:00
plex_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
qobuz_client.py Sync Qobuz auth to enrichment worker after login 2026-05-02 14:00:04 -07:00
qobuz_worker.py fix: stop enrichment workers from re-processing rows forever 2026-04-19 15:22:24 -07:00
reorganize_queue.py Reorganize queue: race + dedupe fixes from kettui review 2026-04-26 08:40:24 -07:00
reorganize_runner.py Library reorganize: FIFO queue with live status panel 2026-04-25 18:01:32 -07:00
repair_worker.py Move metadata API into package 2026-04-29 08:10:18 +03:00
replaygain.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
runtime_state.py PR4b: lift cancel + clear download routes to core/downloads/cancel.py 2026-04-27 21:41:35 -07:00
seasonal_discovery.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
socketio_cors.py Socket.IO CORS: handle self-review nits 2026-04-26 19:24:43 -07:00
soulid_worker.py Tighten legacy logging output 2026-04-21 18:00:31 +03:00
soulseek_client.py get hifi tags during download, without needing to go through the enrichment pipeline 2026-05-02 07:50:12 -06:00
soulsync_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
spotify_client.py Make Spotify status updates event-driven 2026-05-02 22:02:01 +03:00
spotify_public_scraper.py Add Spotify Link tab for public playlist/album scraping without API credentials 2026-03-15 21:25:05 -07:00
spotify_worker.py fix: pause Spotify worker on non-Spotify primary + cut daily budget to 500 2026-04-29 12:46:08 -07:00
tag_writer.py Separate AcoustID file logging 2026-04-21 18:00:31 +03:00
tidal_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00
tidal_download_client.py get tidal tags during download, without needing to go through the enrichment pipeline 2026-05-02 07:50:12 -06:00
tidal_worker.py fix: stop enrichment workers from re-processing rows forever 2026-04-19 15:22:24 -07:00
watchlist_scanner.py Make extract_external_ids recognize all source-tagging conventions 2026-05-02 18:26:12 -07:00
web_scan_manager.py Add SoulSync standalone library — no media server required 2026-04-17 20:34:55 -07:00
wishlist_service.py Extract wishlist logic into dedicated package 2026-04-28 21:17:24 +03:00
worker_utils.py Fix Album Completeness job reporting zero findings for every album 2026-04-24 12:39:41 -07:00
youtube_client.py Clean up 286 ruff lint errors to unblock CI and fix 10 latent bugs 2026-04-21 13:30:52 -07:00