#!/usr/bin/env python3 """ Test Spotify metadata fetching with both token methods """ import json import logging import time from urllib.parse import urlencode import requests logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) def get_tokener_token(): """Get token from tokener API""" response = requests.get("https://spotify-tokener-api.vercel.app/api/getToken", timeout=10) if response.status_code == 200: data = response.json() return data.get('accessToken'), data.get('clientId') return None, None def get_totp_token(): """Get token from TOTP method""" import base64 import hashlib import hmac SPOTIFY_TOTP_SECRET = "GM3TMMJTGYZTQNZVGM4DINJZHA4TGOBYGMZTCMRTGEYDSMJRHE4TEOBUG4YTCMRUGQ4DQOJUGQYTAMRRGA2TCMJSHE3TCMBY" SPOTIFY_TOTP_VERSION = 61 # Generate TOTP secret_bytes = base64.b32decode(SPOTIFY_TOTP_SECRET) current_time = int(time.time() // 30) time_bytes = current_time.to_bytes(8, 'big') h = hmac.new(secret_bytes, time_bytes, hashlib.sha1) hmac_result = h.digest() offset = hmac_result[-1] & 0x0f code = ((hmac_result[offset] & 0x7f) << 24) | ((hmac_result[offset + 1] & 0xff) << 16) | ((hmac_result[offset + 2] & 0xff) << 8) | (hmac_result[offset + 3] & 0xff) totp_code = str(code % 1000000).zfill(6) # Get token params = { "reason": "init", "productType": "web-player", "totp": totp_code, "totpVer": SPOTIFY_TOTP_VERSION, "totpServer": totp_code, } url = f"https://open.spotify.com/api/token?{urlencode(params)}" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", } response = requests.get(url, headers=headers, timeout=30) if response.status_code == 200: data = response.json() return data.get('accessToken'), data.get('clientId') return None, None def test_track_lookup(token, client_id, method_name): """Test track lookup with given token""" logger.info(f"Testing track lookup with {method_name} token...") # Test with a well-known track payload = { "variables": { "uri": "spotify:track:4cOdK2wGLETOMrsVzAojDx" # "Shape of You" by Ed Sheeran }, "operationName": "getTrack", "extensions": { "persistedQuery": { "version": 1, "sha256Hash": "612585ae06ba435ad26369870deaae23b5c8800a256cd8a57e08eddc25a37294", } } } headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", } response = requests.post( "https://api-partner.spotify.com/pathfinder/v1/query", json=payload, headers=headers, timeout=30 ) if response.status_code == 200: data = response.json() if "data" in data and "trackUnion" in data["data"]: track = data["data"]["trackUnion"] logger.info(f"✅ {method_name} track lookup SUCCESS!") logger.info(f"Track: {track.get('name', 'Unknown')}") logger.info(f"Artists: {[artist.get('name', 'Unknown') for artist in track.get('artists', [])]}") logger.info(f"Album: {track.get('albumOfTrack', {}).get('name', 'Unknown')}") logger.info(f"Duration: {track.get('duration', {}).get('totalMilliseconds', 0)}ms") return True else: logger.error(f"❌ {method_name} track lookup: Invalid response structure") logger.error(f"Response: {json.dumps(data, indent=2)}") return False else: logger.error(f"❌ {method_name} track lookup FAILED: HTTP {response.status_code}") logger.error(f"Response: {response.text}") return False def test_album_lookup(token, client_id, method_name): """Test album lookup with given token""" logger.info(f"Testing album lookup with {method_name} token...") # Test with a well-known album payload = { "variables": { "uri": "spotify:album:1DFixLWUoKaZYxZkLUKQu9", # "Divide" by Ed Sheeran "locale": "", "offset": 0, "limit": 50, }, "operationName": "getAlbum", "extensions": { "persistedQuery": { "version": 1, "sha256Hash": "b9bfabef66ed756e5e13f68a942deb60bd4125ec1f1be8cc42769dc0259b4b10", } } } headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", } response = requests.post( "https://api-partner.spotify.com/pathfinder/v1/query", json=payload, headers=headers, timeout=30 ) if response.status_code == 200: data = response.json() if "data" in data and "albumUnion" in data["data"]: album = data["data"]["albumUnion"] logger.info(f"✅ {method_name} album lookup SUCCESS!") logger.info(f"Album: {album.get('name', 'Unknown')}") logger.info(f"Artists: {[artist.get('name', 'Unknown') for artist in album.get('artists', [])]}") logger.info(f"Total tracks: {album.get('tracksV2', {}).get('totalCount', 0)}") logger.info(f"Release year: {album.get('date', {}).get('year', 'Unknown')}") return True else: logger.error(f"❌ {method_name} album lookup: Invalid response structure") return False else: logger.error(f"❌ {method_name} album lookup FAILED: HTTP {response.status_code}") return False def main(): """Run metadata tests""" print("=" * 60) print("Spotify Metadata Fetching Test") print("=" * 60) # Test TOTP method print("\n1. Testing TOTP Method Metadata Fetch") print("-" * 40) totp_token, totp_client_id = get_totp_token() if totp_token: print("✅ TOTP token obtained") track_success = test_track_lookup(totp_token, totp_client_id, "TOTP") album_success = test_album_lookup(totp_token, totp_client_id, "TOTP") totp_success = track_success and album_success else: print("❌ Failed to get TOTP token") totp_success = False # Test tokener API method print("\n2. Testing Tokener API Metadata Fetch") print("-" * 40) tokener_token, tokener_client_id = get_tokener_token() if tokener_token: print("✅ Tokener API token obtained") track_success = test_track_lookup(tokener_token, tokener_client_id, "Tokener") album_success = test_album_lookup(tokener_token, tokener_client_id, "Tokener") tokener_success = track_success and album_success else: print("❌ Failed to get tokener API token") tokener_success = False # Summary print("\n" + "=" * 60) print("Metadata Test Results Summary") print("=" * 60) print(f"TOTP Method: {'✅ WORKING' if totp_success else '❌ FAILED'}") print(f"Tokener API: {'✅ WORKING' if tokener_success else '❌ FAILED'}") if totp_success and tokener_success: print("🎉 Both methods are fully working for metadata fetching!") elif totp_success or tokener_success: print("✅ At least one method is working for metadata fetching!") else: print("⚠️ Both methods failed for metadata fetching") print("=" * 60) if __name__ == "__main__": main()