#798: make Spotify Free opt-in (not auto-bridge) + clearer help text

Per the cleaner model: the free source only runs for users who explicitly picked
'Spotify Free' — not for every connected user. _free_wanted() is now just
_free_selected() (dropped the has-credentials auto-trigger). So:
- Plain 'Spotify' user, rate-limited -> waits out the ban as before (no surprise
  background scraping, no ToS exposure for people who never chose free).
- 'Spotify Free' user, no auth -> free serves.
- 'Spotify Free' user who also connects an account -> official when healthy,
  free bridges only during a rate-limit, then switches back.

Rewrote the metadata-source help text as a plain per-source list with a clear
note on how Spotify Free + a connected account interact. Gate tests updated to
pin the opt-in behavior (plain-Spotify ratelimit = no bridge; Spotify-Free
ratelimit = bridge).
pull/803/head
BoulderBadgeDad 1 week ago
parent 0c03803a30
commit 4249856984

@ -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."""

@ -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():

@ -3846,7 +3846,13 @@
</select>
</div>
<div class="callback-info">
<div class="callback-help">Choose the primary source for artist, album, and track metadata. <strong>Spotify</strong> needs a connected account. <strong>Spotify Free</strong> pulls the same Spotify data without credentials (unofficial &amp; best-effort — can break if Spotify changes, can't search albums by name, no library access). Either way, a connected Spotify that hits a rate-limit is automatically bridged by the free source if it's installed. Discogs requires a personal token. MusicBrainz is always available but rate-limited to 1 req/sec.</div>
<div class="callback-help">Where artist, album, and track metadata comes from:<br>
<strong>Spotify</strong> — official Spotify; connect your account in the Spotify section below.<br>
<strong>Spotify Free</strong> — the same Spotify catalog with no account needed. It's unofficial and best-effort (may break if Spotify changes its site), can't search albums by name (album results come from iTunes/Deezer instead), and can't read your personal library or playlists.<br>
<strong>Deezer</strong> / <strong>iTunes</strong> — free, no account.<br>
<strong>MusicBrainz</strong> — free, but limited to 1 request/sec.<br>
<strong>Discogs</strong> — needs a personal access token.<br><br>
Tip: if you pick <strong>Spotify Free</strong> and later connect a real Spotify account, it uses the official account normally and only falls back to free while Spotify is rate-limited — then switches back automatically.</div>
</div>
</div>

Loading…
Cancel
Save