#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).
This commit is contained in:
parent
0c03803a30
commit
4249856984
3 changed files with 29 additions and 17 deletions
|
|
@ -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 & 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…
Reference in a new issue