diff --git a/core/spotify_client.py b/core/spotify_client.py index f04ca6d2..bb7af518 100644 --- a/core/spotify_client.py +++ b/core/spotify_client.py @@ -577,25 +577,17 @@ class SpotifyClient: except Exception: return False - def _has_spotify_credentials(self) -> bool: - """Whether Spotify client credentials are configured (a 'connected' - user, even if currently rate-limit-banned). Drives the auto-bridge.""" - try: - cfg = config_manager.get_spotify_config() or {} - return bool(cfg.get('client_id') and cfg.get('client_secret')) - except Exception: - return False - def _free_installed(self) -> bool: from core.spotify_free_metadata import spotify_free_installed return spotify_free_installed() def _free_wanted(self) -> bool: - """Does the user want Spotify metadata at all? Either they have - credentials (connected → eligible for the rate-limit auto-bridge) or - they explicitly chose 'Spotify Free'. This is what keeps the free source - from auto-scraping for someone who never opted into Spotify.""" - return self._has_spotify_credentials() or self._free_selected() + """Does the user actually want the free source? OPT-IN: only when they + picked 'Spotify Free'. This keeps free from auto-scraping for anyone who + didn't choose it — a plain-'Spotify' user just waits out a rate-limit ban + as before. A user on 'Spotify Free' who also connects an account uses the + official account normally and free only bridges their bans.""" + return self._free_selected() def _free_available(self) -> bool: """Free CAN serve: package installed AND the user wants Spotify.""" diff --git a/tests/test_spotify_free_metadata.py b/tests/test_spotify_free_metadata.py index ccfff97e..e09c367d 100644 --- a/tests/test_spotify_free_metadata.py +++ b/tests/test_spotify_free_metadata.py @@ -162,9 +162,23 @@ def test_connected_healthy_uses_official(): assert avail is True and free is False # official; free never opens -def test_connected_ratelimited_bridges_to_free(): +def test_plain_spotify_ratelimited_does_NOT_bridge(): + # OPT-IN: a user on plain 'Spotify' (didn't pick Spotify Free) waits out a + # rate-limit ban — free does NOT auto-bridge for them. No surprise scraping. avail, free = _gate(authed=False, has_creds=True, selected=False, installed=True, rate_limited=True) - assert avail is True and free is True # auto-bridge, no toggle needed + assert avail is False and free is False + + +def test_spotify_free_user_ratelimited_bridges(): + # A user who DID pick Spotify Free and also connected an account: official + # when healthy, free bridges during a ban. + avail, free = _gate(authed=False, has_creds=True, selected=True, installed=True, rate_limited=True) + assert avail is True and free is True + + +def test_spotify_free_user_healthy_uses_official(): + avail, free = _gate(authed=True, has_creds=True, selected=True, installed=True, rate_limited=False) + assert avail is True and free is False # picked Spotify Free but authed -> official def test_no_auth_picked_spotify_free_serves(): diff --git a/webui/index.html b/webui/index.html index 3092c057..99b951cc 100644 --- a/webui/index.html +++ b/webui/index.html @@ -3846,7 +3846,13 @@