From b0c58a0f91e90ff84c2fbbc58fb1eeb0985dcb42 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Thu, 7 May 2026 09:09:17 -0700 Subject: [PATCH] =?UTF-8?q?Surface=20silent=20exceptions=20in=20web=5Fserv?= =?UTF-8?q?er.py=20=E2=80=94=2081=20sites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces `except Exception: pass` blocks with `except Exception as e: logger.debug(...)` so failures are inspectable in the log instead of disappearing silently. Per JohnBaumb's request in #369. - Pattern is consistent: `logger.debug(": %s", e)` with lazy formatter and 2-6 word context describing the operation. - 2 atexit handlers (lines 2977, 2983) intentionally left silent — the log file handles can be torn down before atexit fires, and a separate `_atexit_silence_shutdown_logger_errors` already exists for this exact reason. - No behavior changes; control flow is unchanged. Test suite green (2188 passed). Refs #369 --- web_server.py | 324 +++++++++++++++++++++++++------------------------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/web_server.py b/web_server.py index 1f8a093d..f2fe8d57 100644 --- a/web_server.py +++ b/web_server.py @@ -51,8 +51,8 @@ def _build_version_string(): result = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True, text=True, cwd=os.path.dirname(__file__) or '.') if result.returncode == 0: sha = result.stdout.strip() - except Exception: - pass + except Exception as e: + logger.debug("git rev-parse failed: %s", e) if sha: return f"{_SOULSYNC_BASE_VERSION}+{sha[:7]}" return _SOULSYNC_BASE_VERSION @@ -415,8 +415,8 @@ def _log_slow_request(response): response.status_code, elapsed_ms, ) - except Exception: - pass + except Exception as e: + logger.debug("slow request log failed: %s", e) return response @@ -1249,8 +1249,8 @@ def _register_automation_handlers(): 'added': str(added_count), 'removed': str(removed_count), }) - except Exception: - pass + except Exception as e: + logger.debug("playlist_synced automation emit failed: %s", e) else: logger.warning(f"[AUTOMATION] No changes: '{pl.get('name', '')}' (tracks={len(old_ids)})") _update_automation_progress(auto_id, @@ -1855,8 +1855,8 @@ def _register_automation_handlers(): elif os.path.isdir(fp): _shutil.rmtree(fp) removed += 1 - except Exception: - pass + except Exception as e: + logger.debug("quarantine entry purge failed: %s", e) _update_automation_progress(automation_id, log_line=f'Removed {removed} quarantined items', log_type='success' if removed > 0 else 'info') return {'status': 'completed', 'removed': str(removed)} @@ -1961,8 +1961,8 @@ def _register_automation_handlers(): while len(existing) > max_backups: try: os.remove(existing.pop(0)) - except Exception: - pass + except Exception as e: + logger.debug("rolling backup cleanup failed: %s", e) _update_automation_progress(automation_id, log_line=f'Backup created: {size_mb}MB ({os.path.basename(backup_path)})', log_type='success') return {'status': 'completed', 'backup_path': backup_path, 'size_mb': str(size_mb)} @@ -2101,8 +2101,8 @@ def _register_automation_handlers(): elif os.path.isdir(fp): _shutil.rmtree(fp) q_removed += 1 - except Exception: - pass + except Exception as e: + logger.debug("quarantine entry purge failed: %s", e) steps.append(f'Quarantine: removed {q_removed} items') _update_automation_progress(automation_id, log_line=f'Quarantine: removed {q_removed} items', log_type='success' if q_removed else 'info') @@ -2166,8 +2166,8 @@ def _register_automation_handlers(): for hidden in entries: try: os.remove(os.path.join(dirpath, hidden)) - except Exception: - pass + except Exception as e: + logger.debug("hidden file cleanup failed: %s", e) try: os.rmdir(dirpath) s_removed += 1 @@ -2515,8 +2515,8 @@ def get_cached_transfer_data(): all_downloads = run_async( download_orchestrator.engine.get_all_downloads(exclude=('soulseek',)) ) - except Exception: - pass + except Exception as e: + logger.debug("get_all_downloads failed: %s", e) for download in all_downloads: key = _make_context_key(download.username, download.filename) # Convert DownloadStatus to transfer dict format @@ -3446,8 +3446,8 @@ def _get_enrichment_status(): if key == 'spotify_enrichment': try: svc_data['daily_budget'] = worker._get_daily_budget_info() - except Exception: - pass + except Exception as e: + logger.debug("spotify daily budget read failed: %s", e) services[key] = svc_data else: @@ -4205,8 +4205,8 @@ def handle_settings(): # Include which download sources are configured so the UI can auto-disable unconfigured ones try: data['_source_status'] = download_orchestrator.get_source_status() - except Exception: - pass + except Exception as e: + logger.debug("download source status read failed: %s", e) return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -6118,8 +6118,8 @@ def deezer_callback(): try: json_data = resp.json() access_token = json_data.get('access_token') - except Exception: - pass + except Exception as e: + logger.debug("deezer token json parse failed: %s", e) if not access_token: return f"

No Access Token

Deezer response: {resp.text[:200]}

", 400 @@ -7451,8 +7451,8 @@ def get_download_status(): if transfer_id: try: run_async(download_orchestrator.cancel_download(str(transfer_id), username, remove=True)) - except Exception: - pass + except Exception as e: + logger.debug("orphan transfer cancel failed: %s", e) _orphaned_download_keys.discard(context_key) continue # Skip normal post-processing either way @@ -8475,8 +8475,8 @@ def get_artist_detail(artist_id): except Exception: enrichment_coverage[svc] = 0 enrichment_coverage['total_tracks'] = total - except Exception: - pass + except Exception as e: + logger.debug("enrichment coverage build failed: %s", e) response_data = { "success": True, @@ -8780,8 +8780,8 @@ def get_artist_discography(artist_id): if not artist_info.get('genres') and lib['genres']: try: artist_info['genres'] = json.loads(lib['genres']) - except Exception: - pass + except Exception as e: + logger.debug("genres json parse failed: %s", e) # Last.fm enrichment if lib.get('lastfm_bio'): artist_info['lastfm_bio'] = lib['lastfm_bio'] @@ -8792,8 +8792,8 @@ def get_artist_discography(artist_id): if lib.get('lastfm_tags'): try: artist_info['lastfm_tags'] = json.loads(lib['lastfm_tags']) if isinstance(lib['lastfm_tags'], str) else lib['lastfm_tags'] - except Exception: - pass + except Exception as e: + logger.debug("lastfm_tags json parse failed: %s", e) if lib.get('lastfm_url'): artist_info['lastfm_url'] = lib['lastfm_url'] if lib.get('genius_url'): @@ -9475,25 +9475,25 @@ def _build_artist_quality_deps(): sources.append(('spotify', spotify_client)) try: sources.append(('itunes', _get_itunes_client())) - except Exception: - pass + except Exception as e: + logger.debug("itunes client init failed: %s", e) try: sources.append(('deezer', _get_deezer_client())) - except Exception: - pass + except Exception as e: + logger.debug("deezer client init failed: %s", e) # Discogs needs an explicit token; only include when configured. try: _discogs_token = config_manager.get('discogs.token', '') if _discogs_token: sources.append(('discogs', _get_discogs_client(_discogs_token))) - except Exception: - pass + except Exception as e: + logger.debug("discogs client init failed: %s", e) # Hydrabase only when connected (dev-mode + active client). try: if hydrabase_client and hydrabase_client.is_connected(): sources.append(('hydrabase', hydrabase_client)) - except Exception: - pass + except Exception as e: + logger.debug("hydrabase client check failed: %s", e) return sources return _artists_quality.ArtistQualityDeps( @@ -10958,8 +10958,8 @@ def _resolve_library_file_path(file_path): if plex and plex.server: for loc in plex.get_music_library_locations(): library_dirs.add(loc) - except Exception: - pass + except Exception as e: + logger.debug("plex library locations lookup failed: %s", e) # Check user-configured music library paths (Settings > Library) try: @@ -10970,8 +10970,8 @@ def _resolve_library_file_path(file_path): resolved_p = docker_resolve_path(p.strip()) if resolved_p: library_dirs.add(resolved_p) - except Exception: - pass + except Exception as e: + logger.debug("library music paths read failed: %s", e) path_parts = file_path.replace('\\', '/').split('/') @@ -11462,8 +11462,8 @@ def library_delete_track(track_id): try: os.remove(sidecar) logger.info(f"Deleted sidecar file: {sidecar}") - except Exception: - pass + except Exception as e: + logger.debug("sidecar removal failed: %s", e) except Exception as e: logger.warning(f"Failed to delete file: {e}") file_error = str(e) @@ -12023,8 +12023,8 @@ def library_delete_album(album_id): if os.path.exists(sidecar): try: os.remove(sidecar) - except Exception: - pass + except Exception as e: + logger.debug("sidecar removal failed: %s", e) except Exception as e: logger.warning(f"Failed to delete track file: {e}") files_failed += 1 @@ -12042,8 +12042,8 @@ def library_delete_album(album_id): if os.path.isdir(album_dir) and not os.listdir(album_dir): os.rmdir(album_dir) logger.info(f"Removed empty album directory: {album_dir}") - except Exception: - pass + except Exception as e: + logger.debug("empty album dir cleanup failed: %s", e) # Delete all tracks belonging to this album cursor.execute("DELETE FROM tracks WHERE album_id = ?", (album_id,)) @@ -13197,8 +13197,8 @@ def _sweep_empty_download_directories(): for hidden in entries: try: os.remove(os.path.join(dirpath, hidden)) - except Exception: - pass + except Exception as e: + logger.debug("hidden file cleanup failed: %s", e) os.rmdir(dirpath) removed += 1 except OSError: @@ -14010,8 +14010,8 @@ def _apply_path_template(template: str, context: dict) -> str: resolved = _get_itunes_client().resolve_primary_artist(itunes_artist_id) if resolved and resolved != album_artist_value: album_artist_value = resolved - except Exception: - pass + except Exception as e: + logger.debug("itunes primary artist resolve failed: %s", e) # $cdnum — smart CD label for multi-disc filenames. Produces "CD01" / # "CD02" etc. when the album has 2+ discs, empty string otherwise. # Empty output collapses gracefully via the trailing double-dash cleanup @@ -14491,8 +14491,8 @@ def _get_current_commit_sha(): result = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True, text=True, cwd=os.path.dirname(__file__) or '.') if result.returncode == 0: return result.stdout.strip() - except Exception: - pass + except Exception as e: + logger.debug("git rev-parse failed: %s", e) return None _current_commit_sha = _get_current_commit_sha() @@ -14809,8 +14809,8 @@ def _db_update_finished_callback(total_artists, total_albums, total_tracks, succ 'total_albums': str(total_albums), 'total_tracks': str(total_tracks), }) - except Exception: - pass + except Exception as e: + logger.debug("library_updated automation emit failed: %s", e) # Invalidate sync match cache (track IDs may have changed) try: @@ -14818,8 +14818,8 @@ def _db_update_finished_callback(total_artists, total_albums, total_tracks, succ cleared = inv_db.invalidate_sync_match_cache() if cleared: logger.info(f"Cleared {cleared} sync match cache entries after database update") - except Exception: - pass + except Exception as e: + logger.debug("sync match cache invalidation failed: %s", e) # WISHLIST CLEANUP: Automatically clean up wishlist after database update try: @@ -14950,8 +14950,8 @@ def _run_soulsync_full_refresh(): INSERT OR IGNORE INTO artists (id, name, server_source, created_at, updated_at) VALUES (?, ?, 'soulsync', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) """, (artist_id, artist_name)) - except Exception: - pass + except Exception as e: + logger.debug("soulsync artist insert failed: %s", e) for album_name, tracks in albums.items(): album_key = f"{artist_name.lower()}::{album_name.lower()}" @@ -14970,8 +14970,8 @@ def _run_soulsync_full_refresh(): INSERT OR IGNORE INTO albums (id, artist_id, title, year, track_count, server_source, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 'soulsync', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) """, (album_id, artist_id, album_name, year, len(tracks))) - except Exception: - pass + except Exception as e: + logger.debug("soulsync album insert failed: %s", e) # Insert tracks for file_path, tags in tracks: @@ -15774,8 +15774,8 @@ def backup_database_endpoint(): # Also remove sidecar if present if os.path.exists(removed + '.meta.json'): os.remove(removed + '.meta.json') - except Exception: - pass + except Exception as e: + logger.debug("rolling backup cleanup failed: %s", e) return jsonify({"success": True, "backup_path": backup_path, "size_mb": size_mb, "version": SOULSYNC_VERSION}) except Exception as e: return jsonify({"success": False, "error": str(e)}), 500 @@ -15809,8 +15809,8 @@ def list_backups_endpoint(): with open(meta_path, 'r') as mf: meta = json.load(mf) entry['version'] = meta.get('version') - except Exception: - pass + except Exception as e: + logger.debug("backup metadata read failed: %s", e) backups.append(entry) db_size_mb = round(os.path.getsize(db_path) / (1024 * 1024), 2) if os.path.exists(db_path) else 0 return jsonify({ @@ -15838,8 +15838,8 @@ def delete_backup_endpoint(filename): if os.path.exists(meta_path): try: os.remove(meta_path) - except Exception: - pass + except Exception as e: + logger.debug("backup sidecar removal failed: %s", e) return jsonify({"success": True, "deleted": filename}) except Exception as e: return jsonify({"success": False, "error": str(e)}), 500 @@ -15865,8 +15865,8 @@ def restore_backup_endpoint(filename): with open(meta_path, 'r') as mf: meta = json.load(mf) backup_version = meta.get('version') - except Exception: - pass + except Exception as e: + logger.debug("backup version metadata read failed: %s", e) version_warning = None # Compare base versions only (strip +commit suffix) to avoid false mismatches @@ -15898,8 +15898,8 @@ def restore_backup_endpoint(filename): try: with open(safety_path + '.meta.json', 'w') as mf: json.dump({"version": SOULSYNC_VERSION, "created": safety_ts}, mf) - except Exception: - pass + except Exception as e: + logger.debug("safety backup metadata write failed: %s", e) # Restore using SQLite backup API (handles concurrent access safely) from database.music_database import close_database, get_database @@ -18140,13 +18140,13 @@ def get_server_playlist_tracks(playlist_id): raw_playlist = None try: raw_playlist = media_server_engine.client('plex').server.fetchItem(int(playlist_id)) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist fetchItem failed: %s", e) if not raw_playlist and playlist_name: try: raw_playlist = media_server_engine.client('plex').server.playlist(playlist_name) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist by-name lookup failed: %s", e) if not raw_playlist: logger.warning( f"[ServerPlaylistTracks] Plex playlist not found by " @@ -18417,13 +18417,13 @@ def server_playlist_replace_track(playlist_id): raw_playlist = None try: raw_playlist = plex_server.fetchItem(int(playlist_id)) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist fetchItem failed: %s", e) if not raw_playlist and playlist_name: try: raw_playlist = plex_server.playlist(playlist_name) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist by-name lookup failed: %s", e) if not raw_playlist: logger.warning(f"[ServerPlaylist] replace-track: playlist not found by id={playlist_id} or name='{playlist_name}'") return jsonify({"success": False, "error": "Playlist not found on server"}), 404 @@ -18517,13 +18517,13 @@ def server_playlist_add_track(playlist_id): raw_playlist = None try: raw_playlist = plex_server.fetchItem(int(playlist_id)) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist fetchItem failed: %s", e) if not raw_playlist and playlist_name: try: raw_playlist = plex_server.playlist(playlist_name) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist by-name lookup failed: %s", e) if not raw_playlist: logger.warning(f"[ServerPlaylist] add-track: playlist not found by id={playlist_id} or name='{playlist_name}'") return jsonify({"success": False, "error": "Playlist not found"}), 404 @@ -18601,13 +18601,13 @@ def server_playlist_remove_track(playlist_id): raw_playlist = None try: raw_playlist = plex_server.fetchItem(int(playlist_id)) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist fetchItem failed: %s", e) if not raw_playlist and playlist_name: try: raw_playlist = plex_server.playlist(playlist_name) - except Exception: - pass + except Exception as e: + logger.debug("plex playlist by-name lookup failed: %s", e) if not raw_playlist: logger.warning(f"[ServerPlaylist] remove-track: playlist not found by id={playlist_id} or name='{playlist_name}'") return jsonify({"success": False, "error": "Playlist not found"}), 404 @@ -20747,8 +20747,8 @@ def _pause_enrichment_workers(label='discovery'): worker.pause() was_running[name] = True logger.warning(f"Paused {name} enrichment worker during {label}") - except Exception: - pass + except Exception as e: + logger.debug("enrichment worker pause failed: %s", e) return was_running @@ -20765,8 +20765,8 @@ def _resume_enrichment_workers(was_running, label='discovery'): if was_running.get(name) and worker: worker.resume() logger.info(f"Resumed {name} enrichment worker after {label}") - except Exception: - pass + except Exception as e: + logger.debug("enrichment worker resume failed: %s", e) def _sync_discovery_results_to_mirrored(source_type, source_playlist_id, discovery_results, discovery_source, profile_id=1): @@ -24764,8 +24764,8 @@ def add_to_watchlist(): elif row['discogs_id']: artist_id = row['discogs_id'] source = 'discogs' - except Exception: - pass + except Exception as e: + logger.debug("watchlist artist source lookup failed: %s", e) if not source: fallback_source = _get_metadata_fallback_source() source = fallback_source if is_numeric_id else 'spotify' @@ -24845,16 +24845,16 @@ def add_to_watchlist(): try: pid = get_current_profile_id() socketio.emit('watchlist:count', _build_watchlist_count_payload(profile_id=pid), room=f'profile:{pid}') - except Exception: - pass + except Exception as e: + logger.debug("watchlist count emit failed: %s", e) try: if automation_engine: automation_engine.emit('watchlist_artist_added', { 'artist': artist_name, 'artist_id': str(artist_id), }) - except Exception: - pass + except Exception as e: + logger.debug("watchlist_artist_added emit failed: %s", e) _artmap_cache_invalidate(get_current_profile_id()) return jsonify({"success": True, "message": f"Added {artist_name} to watchlist"}) else: @@ -24882,16 +24882,16 @@ def remove_from_watchlist(): try: pid = get_current_profile_id() socketio.emit('watchlist:count', _build_watchlist_count_payload(profile_id=pid), room=f'profile:{pid}') - except Exception: - pass + except Exception as e: + logger.debug("watchlist count emit failed: %s", e) try: if automation_engine: automation_engine.emit('watchlist_artist_removed', { 'artist': data.get('artist_name', str(artist_id)), 'artist_id': str(artist_id), }) - except Exception: - pass + except Exception as e: + logger.debug("watchlist_artist_removed emit failed: %s", e) _artmap_cache_invalidate(get_current_profile_id()) return jsonify({"success": True, "message": "Removed artist from watchlist"}) else: @@ -25033,8 +25033,8 @@ def watchlist_all_unwatched_library_artists(): if artist.get('image_url'): try: database.update_watchlist_artist_image(artist_id, artist['image_url']) - except Exception: - pass + except Exception as e: + logger.debug("watchlist artist image update failed: %s", e) total_unwatched = len(unwatched_artists) message_parts = [f"Added {added} artist{'s' if added != 1 else ''} to watchlist"] @@ -25193,8 +25193,8 @@ def start_watchlist_scan(): try: if config_manager.get('discogs.token', ''): providers_to_backfill.append('discogs') - except Exception: - pass + except Exception as e: + logger.debug("discogs token backfill check failed: %s", e) for _bf_provider in providers_to_backfill: try: logger.debug(f"Checking for missing {_bf_provider} IDs in watchlist...") @@ -25357,8 +25357,8 @@ def start_watchlist_scan(): # Clear one-time rescan cutoff after full scan cycle try: scanner._clear_rescan_cutoff() - except Exception: - pass + except Exception as e: + logger.debug("scanner rescan cutoff clear failed: %s", e) # Always reset flag when scan completes (success or error) with watchlist_timer_lock: @@ -26195,8 +26195,8 @@ def enrich_similar_artists(): aid, fallback_source, image_url=img_url, genres=genres, popularity=0 ) - except Exception: - pass + except Exception as e: + logger.debug("similar artist enrichment failed: %s", e) cached_count = len(enriched) - len([aid for aid in uncached_ids if aid in enriched]) api_count = len([aid for aid in uncached_ids if aid in enriched]) @@ -26363,10 +26363,10 @@ def get_discover_recent_releases(): if album_id: try: database.update_discovery_recent_album_cover(album_id, cover) - except Exception: - pass - except Exception: - pass + except Exception as e: + logger.debug("recent album cover update failed: %s", e) + except Exception as e: + logger.debug("recent album cover fetch failed: %s", e) # Filter out blacklisted artists blacklisted = database.get_discovery_blacklist_names() @@ -26500,8 +26500,8 @@ def get_discover_because_you_listen_to(): if row and row[0]: artist_image = fix_artist_image_url(row[0]) conn.close() - except Exception: - pass + except Exception as e: + logger.debug("artist image lookup failed: %s", e) sections.append({ 'artist_name': artist_name, @@ -26686,8 +26686,8 @@ def resolve_cache_album(): if results: r = results[0] return jsonify({'success': True, 'entity_id': str(r.id), 'source': _get_metadata_fallback_source()}) - except Exception: - pass + except Exception as e: + logger.debug("fallback album search failed: %s", e) return jsonify({'success': False, 'error': 'Album not found in cache'}) except Exception as e: @@ -27435,8 +27435,8 @@ def get_your_artists_sources(): try: if tidal_client and hasattr(tidal_client, '_ensure_valid_token') and tidal_client._ensure_valid_token(): connected.append('tidal') - except Exception: - pass + except Exception as e: + logger.debug("tidal auth check failed: %s", e) # Last.fm if config_manager.get('lastfm.api_key', '') and config_manager.get('lastfm.session_key', ''): connected.append('lastfm') @@ -27448,8 +27448,8 @@ def get_your_artists_sources(): and download_orchestrator.client("deezer_dl").is_authenticated()) if deezer_oauth or deezer_arl: connected.append('deezer') - except Exception: - pass + except Exception as e: + logger.debug("deezer auth check failed: %s", e) return jsonify({"success": True, "enabled": enabled, "connected": connected}) except Exception as e: @@ -27711,8 +27711,8 @@ def get_your_albums_sources(): try: if tidal_client and hasattr(tidal_client, '_ensure_valid_token') and tidal_client._ensure_valid_token(): connected.append('tidal') - except Exception: - pass + except Exception as e: + logger.debug("tidal auth check failed: %s", e) try: deezer_cl = _get_deezer_client() deezer_oauth = deezer_cl and hasattr(deezer_cl, 'is_user_authenticated') and deezer_cl.is_user_authenticated() @@ -27720,8 +27720,8 @@ def get_your_albums_sources(): and download_orchestrator.client("deezer_dl").is_authenticated()) if deezer_oauth or deezer_arl: connected.append('deezer') - except Exception: - pass + except Exception as e: + logger.debug("deezer auth check failed: %s", e) # Discogs: counts as "connected" when a personal access token is # configured. Username comes from /oauth/identity at fetch time; @@ -27729,8 +27729,8 @@ def get_your_albums_sources(): try: if config_manager.get('discogs.token', ''): connected.append('discogs') - except Exception: - pass + except Exception as e: + logger.debug("discogs token check failed: %s", e) return jsonify({"success": True, "enabled": enabled, "connected": connected}) except Exception as e: @@ -27913,8 +27913,8 @@ def get_your_artist_info(artist_id): 'lastfm_playcount': r.get('lastfm_playcount', 0), }) return jsonify(result) - except Exception: - pass + except Exception as e: + logger.debug("library artist lookup failed: %s", e) # 2. Try metadata cache try: @@ -27935,8 +27935,8 @@ def get_your_artist_info(artist_id): 'followers': cached.get('followers', {}).get('total', 0) if isinstance(cached.get('followers'), dict) else cached.get('followers', 0), }) return jsonify(result) - except Exception: - pass + except Exception as e: + logger.debug("metadata cache lookup failed: %s", e) # 3. Try Spotify API directly (genres, image, followers) try: @@ -31402,8 +31402,8 @@ def mirror_playlist_endpoint(): 'source': source, 'track_count': str(len(tracks)), }) - except Exception: - pass + except Exception as e: + logger.debug("mirrored_playlist_created emit failed: %s", e) return jsonify({"success": True, "playlist_id": playlist_id}) except Exception as e: @@ -31577,8 +31577,8 @@ def fix_discovery_pool_track(): cache_key[0], cache_key[1], _get_active_discovery_source(), 1.0, matched_data, row['track_name'], row['artist_name'] ) - except Exception: - pass + except Exception as e: + logger.debug("discovery cache match save failed: %s", e) return jsonify({"success": True}) except Exception as e: @@ -32002,8 +32002,8 @@ def playlist_explorer_album_tracks(album_id): if cache and tracks: try: cache.store_entity(source_name, 'album_tracks', cache_key, result) - except Exception: - pass + except Exception as e: + logger.debug("album_tracks cache store failed: %s", e) return jsonify(result) except Exception as e: @@ -33153,8 +33153,8 @@ try: state['log'].append({'type': 'success' if status == 'finished' else 'error', 'text': summary}) try: socketio.emit('repair:progress', {job_id: dict(state)}) - except Exception: - pass + except Exception as e: + logger.debug("repair progress emit failed: %s", e) repair_worker.register_progress_callbacks(_repair_job_start, _repair_job_progress, _repair_job_finish) # Store refs for WebSocket push loop @@ -33639,8 +33639,8 @@ def import_staging_hints(): if album: key = (album.strip(), (artist or '').strip()) tag_albums[key] = tag_albums.get(key, 0) + 1 - except Exception: - pass + except Exception as e: + logger.debug("tag read failed: %s", e) # Build search queries, prioritizing tag-based hints (more specific) queries = [] @@ -33796,8 +33796,8 @@ def import_album_process(): 'completed_tracks': str(processed), 'failed_tracks': str(len(errors)), }) - except Exception: - pass + except Exception as e: + logger.debug("album import automation emit failed: %s", e) # Rebuild suggestions cache since staging contents changed if processed > 0: @@ -33960,8 +33960,8 @@ def import_singles_process(): 'completed_tracks': str(processed), 'failed_tracks': str(len(errors)), }) - except Exception: - pass + except Exception as e: + logger.debug("singles import automation emit failed: %s", e) # Rebuild suggestions cache since staging contents changed if processed > 0: @@ -34147,8 +34147,8 @@ def _build_status_payload(): for t in download_tasks.values(): if t.get('status') in ('downloading', 'searching', 'post_processing', 'queued', 'pending'): active_dl_count += 1 - except Exception: - pass + except Exception as e: + logger.debug("active download count failed: %s", e) return { 'metadata_source': metadata_status['metadata_source'], @@ -34368,8 +34368,8 @@ def _has_active_downloads(): for batch_data in download_batches.values(): if batch_data.get('phase') not in ('complete', 'error', 'cancelled', None): return True - except Exception: - pass + except Exception as e: + logger.debug("active downloads check failed: %s", e) return False @@ -34403,8 +34403,8 @@ def _spotify_resume_pre_check(): try: if _spotify_rate_limited(): return (429, 'Cannot resume while Spotify is rate limited') - except Exception: - pass + except Exception as e: + logger.debug("spotify rate-limit pre-check failed: %s", e) return None @@ -34511,8 +34511,8 @@ def _emit_rate_monitor_loop(): } if svc_key == 'spotify' and enr.get('daily_budget'): entry['worker']['daily_budget'] = enr['daily_budget'] - except Exception: - pass + except Exception as e: + logger.debug("enrichment worker status build failed: %s", e) # Add Spotify rate limit state try: @@ -34522,8 +34522,8 @@ def _emit_rate_monitor_loop(): payload['spotify']['rate_limited'] = True payload['spotify']['rl_remaining'] = rl_info.get('remaining_seconds', 0) payload['spotify']['rl_endpoint'] = rl_info.get('endpoint', '') - except Exception: - pass + except Exception as e: + logger.debug("spotify rate-limit status read failed: %s", e) socketio.emit('rate-monitor:update', payload) except Exception as e: @@ -34763,8 +34763,8 @@ def _emit_sync_progress_loop(): socketio.emit('sync:progress', { 'playlist_id': pid, **state }, room=f'sync:{pid}') - except Exception: - pass + except Exception as e: + logger.debug("sync progress emit failed: %s", e) except Exception as e: logger.debug(f"Error in sync progress loop: {e}") @@ -34801,8 +34801,8 @@ def _emit_discovery_progress_loop(): 'complete': state.get('phase') == 'discovered', } socketio.emit('discovery:progress', payload, room=f'discovery:{pid}') - except Exception: - pass + except Exception as e: + logger.debug("discovery progress emit failed: %s", e) except Exception as e: logger.debug(f"Error in {platform_name} discovery loop: {e}") @@ -34945,8 +34945,8 @@ def start_runtime_services(): logger.info("Automation engine started") try: automation_engine.emit('app_started', {}) - except Exception: - pass + except Exception as e: + logger.debug("app_started emit failed: %s", e) except AttributeError as e: logger.error(f"Automation engine failed to start: {e}") logger.info(" If using Docker, check that your volume mount is /app/data (not /app/database)")