import json import os import time import webbrowser import requests import subprocess from urllib.parse import urlencode, urlparse, parse_qs from http.server import BaseHTTPRequestHandler, HTTPServer CONFIG_FILE = "config.json" TOKEN_URL = "https://accounts.spotify.com/api/token" AUTH_URL = "https://accounts.spotify.com/authorize" SCOPE = "user-library-read" def load_config(): with open(CONFIG_FILE, "r") as f: return json.load(f) def save_config(data): with open(CONFIG_FILE, "w") as f: json.dump(data, f, indent=2) def start_auth_flow(config): params = { "client_id": config["client_id"], "response_type": "code", "redirect_uri": config["redirect_uri"], "scope": SCOPE } auth_url = f"{AUTH_URL}?{urlencode(params)}" webbrowser.open(auth_url) print("Waiting for callback at /callback...") class SpotifyAuthHandler(BaseHTTPRequestHandler): def do_GET(self): query = urlparse(self.path).query params = parse_qs(query) code = params.get("code", [None])[0] self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(b"

You can close this window now.

") if code: self.server.auth_code = code httpd = HTTPServer(("localhost", 8888), SpotifyAuthHandler) httpd.handle_request() return getattr(httpd, "auth_code", None) def exchange_code_for_token(code, config): auth = (config["client_id"], config["client_secret"]) data = { "grant_type": "authorization_code", "code": code, "redirect_uri": config["redirect_uri"] } res = requests.post(TOKEN_URL, auth=auth, data=data) res.raise_for_status() token_data = res.json() config["access_token"] = token_data["access_token"] config["refresh_token"] = token_data["refresh_token"] config["expires_at"] = int(time.time()) + token_data["expires_in"] save_config(config) print("Token saved to config.json") return token_data["access_token"] def refresh_token_if_needed(config): now = int(time.time()) if "access_token" not in config or now >= config.get("expires_at", 0): print("Refreshing token...") auth = (config["client_id"], config["client_secret"]) data = { "grant_type": "refresh_token", "refresh_token": config["refresh_token"] } res = requests.post(TOKEN_URL, auth=auth, data=data) res.raise_for_status() token_data = res.json() config["access_token"] = token_data["access_token"] config["expires_at"] = now + token_data["expires_in"] save_config(config) print("Refreshed and saved token.") return config["access_token"] if __name__ == "__main__": config = load_config() if "refresh_token" not in config: code = start_auth_flow(config) if not code: print("Authorization failed.") exit(1) token = exchange_code_for_token(code, config) else: token = refresh_token_if_needed(config) print("Spotify access token ready.")