Surface silent exceptions in web_server.py — 81 sites

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("<context>: %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
pull/516/head
Broque Thomas 3 weeks ago
parent 763d160691
commit b0c58a0f91

@ -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"<h1>No Access Token</h1><p>Deezer response: {resp.text[:200]}</p>", 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)")

Loading…
Cancel
Save