diff --git a/database/music_database.py b/database/music_database.py index 7b2be55f..88b96945 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -636,7 +636,8 @@ class MusicDatabase: playlist_folder_mode INTEGER DEFAULT 0, started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP, - track_results TEXT + track_results TEXT, + server_push_status TEXT ) """) cursor.execute("CREATE INDEX IF NOT EXISTS idx_sh_started_at ON sync_history (started_at DESC)") @@ -662,6 +663,16 @@ class MusicDatabase: except Exception: pass + # Migration: add server_push_status column to sync_history + try: + cursor.execute("SELECT server_push_status FROM sync_history LIMIT 1") + except Exception: + try: + cursor.execute("ALTER TABLE sync_history ADD COLUMN server_push_status TEXT") + logger.info("Added server_push_status column to sync_history table") + except Exception: + pass + # Migration: add track_artist column for per-track artist on compilations/DJ mixes try: cursor.execute("SELECT track_artist FROM tracks LIMIT 1") @@ -10497,6 +10508,20 @@ class MusicDatabase: logger.debug(f"Error updating sync history track results: {e}") return False + def update_sync_history_push_status(self, batch_id, status): + """Update the server push status for a sync_history entry.""" + try: + conn = self._get_connection() + cursor = conn.cursor() + cursor.execute(""" + UPDATE sync_history SET server_push_status = ? WHERE batch_id = ? + """, (status, batch_id)) + conn.commit() + return cursor.rowcount > 0 + except Exception as e: + logger.debug(f"Error updating sync history push status: {e}") + return False + def refresh_sync_history_entry(self, entry_id, tracks_found=0, tracks_downloaded=0, tracks_failed=0): """Update an existing sync_history entry with new stats and reset timestamps to move it to the top.""" try: @@ -10611,7 +10636,8 @@ class MusicDatabase: cursor.execute(""" SELECT id, batch_id, playlist_name, source, sync_type, source_page, total_tracks, tracks_found, tracks_downloaded, tracks_failed, - thumb_url, is_album_download, started_at, completed_at + thumb_url, is_album_download, started_at, completed_at, + server_push_status FROM sync_history WHERE completed_at IS NOT NULL AND started_at >= datetime('now', ? || ' days') diff --git a/web_server.py b/web_server.py index ef27e463..45ce32a8 100644 --- a/web_server.py +++ b/web_server.py @@ -28652,11 +28652,17 @@ def _on_download_completed(batch_id, task_id, success=True): except Exception: pass - # Push discover playlists to media server after downloads complete + # Push playlists to media server after downloads complete playlist_id = batch.get('playlist_id') - if playlist_id and playlist_id.startswith('discover_'): + _push_prefixes = ( + 'discover_', 'auto_mirror_', 'youtube_mirrored_', + 'youtube_', 'tidal_', 'deezer_', 'spotify_public_', + 'listenbrainz_', 'beatport_', + ) + if playlist_id and playlist_id.startswith(_push_prefixes): + database.update_sync_history_push_status(batch_id, 'pending') threading.Thread( - target=_push_discover_playlist_to_server, + target=_push_playlist_to_server, args=(batch_id, batch), daemon=True ).start() @@ -31912,11 +31918,17 @@ def _check_batch_completion_v2(batch_id): except Exception: pass - # Push discover playlists to media server after downloads complete + # Push playlists to media server after downloads complete playlist_id = batch.get('playlist_id') - if playlist_id and playlist_id.startswith('discover_'): + _push_prefixes = ( + 'discover_', 'auto_mirror_', 'youtube_mirrored_', + 'youtube_', 'tidal_', 'deezer_', 'spotify_public_', + 'listenbrainz_', 'beatport_', + ) + if playlist_id and playlist_id.startswith(_push_prefixes): + database.update_sync_history_push_status(batch_id, 'pending') threading.Thread( - target=_push_discover_playlist_to_server, + target=_push_playlist_to_server, args=(batch_id, batch), daemon=True ).start() @@ -32464,18 +32476,23 @@ def _record_sync_history_completion(batch_id, batch): traceback.print_exc() -def _push_discover_playlist_to_server(batch_id, batch): - """After a discover batch completes, push the playlist to the media server. - Runs in a background thread. Triggers a scan, waits, then searches for tracks and creates/updates the playlist.""" +def _push_playlist_to_server(batch_id, batch): + """After a batch completes, push the playlist to the media server. + Runs in a background thread. Triggers a scan, waits, then searches for tracks and creates/updates the playlist. + Supports discover, mirrored, and auto-mirror playlists.""" + database = get_database() try: playlist_id = batch.get('playlist_id', '') playlist_name = batch.get('playlist_name', '') if not playlist_name: return + database.update_sync_history_push_status(batch_id, 'pushing') + analysis_results = batch.get('analysis_results', []) if not analysis_results: - logger.info(f"[DiscoverPush] No analysis results for {playlist_name} - skipping server push") + logger.info(f"[PlaylistPush] No analysis results for {playlist_name} - skipping server push") + database.update_sync_history_push_status(batch_id, 'skipped') return # Build list of tracks that should be in the playlist (found in library OR successfully downloaded) @@ -32508,16 +32525,17 @@ def _push_discover_playlist_to_server(batch_id, batch): }) if not tracks_to_find: - logger.info(f"[DiscoverPush] No tracks to push for {playlist_name}") + logger.info(f"[PlaylistPush] No tracks to push for {playlist_name}") + database.update_sync_history_push_status(batch_id, 'skipped') return - logger.info(f"[DiscoverPush] {playlist_name}: {len(tracks_to_find)} tracks to push to server, triggering scan first") + logger.info(f"[PlaylistPush] {playlist_name}: {len(tracks_to_find)} tracks to push to server, triggering scan first") # Trigger a library scan so newly downloaded tracks are indexed if navidrome_client and navidrome_client.is_connected(): navidrome_client.trigger_library_scan() elif hasattr(web_scan_manager, 'request_scan'): - web_scan_manager.request_scan(f"Discover playlist push: {playlist_name}") + web_scan_manager.request_scan(f"Playlist push: {playlist_name}") # Wait for scan to finish (poll every 5s, up to 90s) if navidrome_client and navidrome_client.is_connected(): @@ -32525,7 +32543,7 @@ def _push_discover_playlist_to_server(batch_id, batch): time.sleep(5) if not navidrome_client.is_library_scanning(): break - logger.info(f"[DiscoverPush] Scan complete, searching for tracks") + logger.info(f"[PlaylistPush] Scan complete, searching for tracks") else: time.sleep(30) @@ -32540,27 +32558,34 @@ def _push_discover_playlist_to_server(batch_id, batch): nav_track = getattr(best, '_original_navidrome_track', None) if nav_track: matched_server_tracks.append(nav_track) - logger.debug(f"[DiscoverPush] Matched: '{t['title']}' by '{t['artist']}' → {best.id}") + logger.debug(f"[PlaylistPush] Matched: '{t['title']}' by '{t['artist']}' → {best.id}") else: matched_server_tracks.append(best) else: - logger.info(f"[DiscoverPush] No match for: '{t['title']}' by '{t['artist']}'") + logger.info(f"[PlaylistPush] No match for: '{t['title']}' by '{t['artist']}'") if not matched_server_tracks: - logger.warning(f"[DiscoverPush] No tracks matched on server for {playlist_name}") + logger.warning(f"[PlaylistPush] No tracks matched on server for {playlist_name}") + database.update_sync_history_push_status(batch_id, 'failed') return - logger.info(f"[DiscoverPush] Pushing {len(matched_server_tracks)}/{len(tracks_to_find)} tracks to '{playlist_name}' on server") + logger.info(f"[PlaylistPush] Pushing {len(matched_server_tracks)}/{len(tracks_to_find)} tracks to '{playlist_name}' on server") success = navidrome_client.update_playlist(playlist_name, matched_server_tracks) if success: - logger.info(f"[DiscoverPush] Successfully pushed '{playlist_name}' to server with {len(matched_server_tracks)} tracks") + logger.info(f"[PlaylistPush] Successfully pushed '{playlist_name}' to server with {len(matched_server_tracks)} tracks") + database.update_sync_history_push_status(batch_id, 'success') else: - logger.warning(f"[DiscoverPush] Failed to push '{playlist_name}' to server") + logger.warning(f"[PlaylistPush] Failed to push '{playlist_name}' to server") + database.update_sync_history_push_status(batch_id, 'failed') except Exception as e: - logger.error(f"[DiscoverPush] Error pushing playlist to server: {e}") + logger.error(f"[PlaylistPush] Error pushing playlist to server: {e}") import traceback traceback.print_exc() + try: + database.update_sync_history_push_status(batch_id, 'failed') + except Exception: + pass # ===============================