diff --git a/Modelfile b/Modelfile new file mode 100644 index 00000000..f9a29148 --- /dev/null +++ b/Modelfile @@ -0,0 +1,2 @@ +FROM qwen2.5-coder:7b +PARAMETER num_ctx 16384 diff --git a/core/metadata/registry.py b/core/metadata/registry.py index a6c6ea39..946d84a3 100644 --- a/core/metadata/registry.py +++ b/core/metadata/registry.py @@ -230,14 +230,19 @@ def get_spotify_client_for_profile(profile_id: Optional[int] = None): return client try: - from core.spotify_client import SpotifyClient + from core.spotify_client import SpotifyClient, normalize_spotify_oauth_config from spotipy.oauth2 import SpotifyOAuth import spotipy + normalized_creds = normalize_spotify_oauth_config({ + "client_id": client_id, + "client_secret": client_secret, + "redirect_uri": redirect_uri, + }) auth_manager = SpotifyOAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, + client_id=normalized_creds.get("client_id", client_id), + client_secret=normalized_creds.get("client_secret", client_secret), + redirect_uri=normalized_creds.get("redirect_uri", redirect_uri), scope="user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email user-follow-read", cache_path=cache_path, state=f"profile_{profile_id}", diff --git a/core/spotify_client.py b/core/spotify_client.py index b5318037..cf3859ca 100644 --- a/core/spotify_client.py +++ b/core/spotify_client.py @@ -13,6 +13,30 @@ from core.metadata.cache import get_metadata_cache logger = get_logger("spotify_client") + +def normalize_spotify_oauth_config(config: Optional[Dict[str, Any]]) -> Dict[str, Any]: + """Normalize Spotify OAuth config before building an auth manager. + + Spotify rejects values that include surrounding whitespace, quotes, or + newline characters. The settings UI can paste values that carry such + formatting, so we trim them before sending them to Spotify. + """ + if not isinstance(config, dict): + return {} + + normalized = {} + for key in ("client_id", "client_secret", "redirect_uri"): + value = config.get(key, "") + if isinstance(value, str): + value = value.strip().strip('"').strip("'") + if key == "redirect_uri": + value = value.rstrip("/") + normalized[key] = value + else: + normalized[key] = value + return normalized + + def _upgrade_spotify_image_url(url: str) -> str: """Upgrade a Spotify CDN image URL to the highest available resolution. @@ -697,7 +721,7 @@ class SpotifyClient: self._setup_client() def _setup_client(self): - config = config_manager.get_spotify_config() + config = normalize_spotify_oauth_config(config_manager.get_spotify_config()) if not config.get('client_id') or not config.get('client_secret'): logger.warning("Spotify credentials not configured") diff --git a/web_server.py b/web_server.py index 15b5de83..47e846c6 100644 --- a/web_server.py +++ b/web_server.py @@ -4692,11 +4692,13 @@ def _profile_spotify_oauth(profile_id_int): chooser so a user can't silently inherit whatever Spotify session is active in their browser (e.g. the admin's). Returns None if no app creds exist.""" from spotipy.oauth2 import SpotifyOAuth + from core.spotify_client import normalize_spotify_oauth_config creds = (get_database().get_profile_spotify(profile_id_int) or {}) - cfg = config_manager.get_spotify_config() - client_id = creds.get('client_id') or cfg.get('client_id') - client_secret = creds.get('client_secret') or cfg.get('client_secret') - redirect_uri = creds.get('redirect_uri') or cfg.get('redirect_uri', 'http://127.0.0.1:8888/callback') + cfg = normalize_spotify_oauth_config(config_manager.get_spotify_config()) + profile_creds = normalize_spotify_oauth_config(creds) + client_id = profile_creds.get('client_id') or cfg.get('client_id') + client_secret = profile_creds.get('client_secret') or cfg.get('client_secret') + redirect_uri = profile_creds.get('redirect_uri') or cfg.get('redirect_uri', 'http://127.0.0.1:8888/callback') if not client_id or not client_secret: return None return SpotifyOAuth( @@ -5095,7 +5097,7 @@ def spotify_callback(): pass try: - from core.spotify_client import SpotifyClient + from core.spotify_client import SpotifyClient, normalize_spotify_oauth_config from spotipy.oauth2 import SpotifyOAuth from config.settings import config_manager @@ -5125,7 +5127,7 @@ def spotify_callback(): raise Exception("Failed to exchange authorization code for access token") # Global callback (admin) - config = config_manager.get_spotify_config() + config = normalize_spotify_oauth_config(config_manager.get_spotify_config()) configured_uri = config.get('redirect_uri', "http://127.0.0.1:8888/callback") logger.info(f"Using redirect_uri for token exchange: {configured_uri}") @@ -35864,9 +35866,10 @@ def start_oauth_callback_servers(): try: from spotipy.oauth2 import SpotifyOAuth from config.settings import config_manager + from core.spotify_client import normalize_spotify_oauth_config # Get Spotify config - config = config_manager.get_spotify_config() + config = normalize_spotify_oauth_config(config_manager.get_spotify_config()) configured_uri = config.get('redirect_uri', "http://127.0.0.1:8888/callback") _oauth_logger.info(f"Using redirect_uri for token exchange: {configured_uri}")