Reduce Spotify status polling

- cache Spotify auth and rate-limit status separately from the generic metadata source snapshot
- refresh Spotify status only on explicit auth/disconnect/test paths or after the TTL expires
- keep the legacy OAuth callback paths aligned with the same invalidation helper
This commit is contained in:
Antti Kettunen 2026-05-02 11:29:43 +03:00
parent e2bd0e1871
commit 3c7187fb32
No known key found for this signature in database
GPG key ID: C6B2A3D250359BD7

View file

@ -821,6 +821,24 @@ _status_cache_timestamps: dict[str, float] = {
'soulseek': 0,
}
STATUS_CACHE_TTL = 120
SPOTIFY_STATUS_TTL_ACTIVE = 15
SPOTIFY_STATUS_TTL_IDLE = 300
_spotify_status_cache = {
'connected': False,
'authenticated': False,
'rate_limited': False,
'rate_limit': None,
'post_ban_cooldown': None,
}
_spotify_status_timestamp = 0.0
def _invalidate_metadata_status_caches():
"""Mark the metadata-source and Spotify status snapshots stale."""
global _spotify_status_timestamp
_status_cache_timestamps['metadata_source'] = 0
_spotify_status_timestamp = 0
dev_mode_enabled = False
_hydrabase_ws = None
@ -4155,7 +4173,7 @@ def handle_settings():
if tidal_enrichment_worker:
tidal_enrichment_worker.client = tidal_client
# Invalidate status cache so next poll reflects new settings (e.g. fallback source change)
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
logger.info("Service clients re-initialized with new settings.")
return jsonify({"success": True, "message": "Settings saved successfully."})
except Exception as e:
@ -4772,8 +4790,7 @@ def test_connection_endpoint():
if success:
current_time = time.time()
if service == 'spotify':
spotify_session_active = spotify_client.is_spotify_authenticated() if spotify_client else False
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
logger.info("Updated Spotify status cache after successful test")
elif service in ['plex', 'jellyfin', 'navidrome', 'soulsync']:
_status_cache['media_server']['connected'] = True
@ -4937,8 +4954,7 @@ def test_dashboard_connection_endpoint():
if success:
current_time = time.time()
if service == 'spotify':
spotify_session_active = spotify_client.is_spotify_authenticated() if spotify_client else False
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
logger.info("Updated Spotify status cache after successful dashboard test")
elif service in ['plex', 'jellyfin', 'navidrome', 'soulsync']:
_status_cache['media_server']['connected'] = True
@ -5817,7 +5833,7 @@ def spotify_callback():
return _spotify_auth_result_page("Your personal Spotify account is now connected. You can close this window.", authenticated=True)
if profile_client:
profile_client._invalidate_auth_cache()
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
add_activity_item("", "Spotify Auth Warning", f"Profile {profile_id_from_state} completed OAuth but Spotify did not confirm an authenticated session", "Now")
return _spotify_auth_result_page(
"Spotify authorization completed, but SoulSync could not confirm an authenticated Spotify session for this profile. You can close this window and try Authenticate again.",
@ -5853,7 +5869,7 @@ def spotify_callback():
_clear_rate_limit()
spotify_client._invalidate_auth_cache()
# Invalidate status cache so next poll picks up the new connection
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
# Refresh enrichment worker's client so it picks up new auth
if spotify_enrichment_worker and hasattr(spotify_enrichment_worker, 'client'):
spotify_enrichment_worker.client.reload_config()
@ -5863,7 +5879,7 @@ def spotify_callback():
else:
logger.warning("Spotify OAuth token exchange succeeded but authentication validation failed")
spotify_client._invalidate_auth_cache()
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
add_activity_item("", "Spotify Auth Warning", "OAuth completed, but Spotify did not confirm an authenticated session", "Now")
return _spotify_auth_result_page(
"Spotify authorization completed, but SoulSync could not confirm an authenticated Spotify session. You can close this window and try Authenticate again.",
@ -5892,7 +5908,7 @@ def spotify_disconnect():
source_label = get_metadata_source_label(active_source)
if configured_source == 'spotify':
config_manager.set('metadata.fallback_source', active_source)
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
add_activity_item("", "Spotify Disconnected", f"Using {source_label} for metadata", "Now")
return jsonify({
'success': True,
@ -32027,7 +32043,7 @@ def start_oauth_callback_servers():
_clear_rate_limit()
spotify_client._invalidate_auth_cache()
# Invalidate status cache so next poll picks up the new connection
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
# Refresh enrichment worker's client so it picks up new auth
if spotify_enrichment_worker and hasattr(spotify_enrichment_worker, 'client'):
spotify_enrichment_worker.client.reload_config()
@ -32040,7 +32056,7 @@ def start_oauth_callback_servers():
else:
_oauth_logger.warning("Spotify token exchange succeeded but authentication validation failed")
spotify_client._invalidate_auth_cache()
_status_cache_timestamps['metadata_source'] = 0
_invalidate_metadata_status_caches()
add_activity_item("", "Spotify Auth Warning", "OAuth completed, but Spotify did not confirm an authenticated session", "Now")
self.send_response(200)
self.send_header('Content-type', 'text/html')
@ -34029,7 +34045,15 @@ def _build_status_payload():
def _build_spotify_status_payload():
"""Build a Spotify-specific status snapshot for auth and rate-limit state."""
status_data = {}
import time
global _spotify_status_timestamp
current_time = time.time()
configured_source = config_manager.get('metadata.fallback_source', 'deezer') or 'deezer'
ttl = SPOTIFY_STATUS_TTL_ACTIVE if configured_source == 'spotify' else SPOTIFY_STATUS_TTL_IDLE
if _spotify_status_timestamp and current_time - _spotify_status_timestamp <= ttl:
return dict(_spotify_status_cache)
try:
# Always include fresh rate limit info because it can change independently of cache TTL.
@ -34038,17 +34062,18 @@ def _build_spotify_status_payload():
cooldown_remaining = spotify_client.get_post_ban_cooldown_remaining() if spotify_client else 0
authenticated = spotify_client.is_spotify_authenticated() if spotify_client else False
status_data.update({
_spotify_status_cache.update({
'connected': authenticated,
'authenticated': authenticated,
'rate_limited': is_rate_limited,
'rate_limit': rate_limit_info,
'post_ban_cooldown': cooldown_remaining if cooldown_remaining > 0 else None,
})
_spotify_status_timestamp = current_time
except Exception:
pass
return status_data
return dict(_spotify_status_cache)
def _build_watchlist_count_payload(profile_id=1):
"""Build the same payload used by GET /api/watchlist/count."""