mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
cbf646e25b
## Major Changes - Fixed all TypeScript errors in web client for successful compilation - Resolved 82+ Python lint errors across backend services - Updated Flutter SDK compatibility for mobile app - Fixed security workflow configuration ## Web Client Fixes - Fixed import path in DragonflyDashboard.vue (dragonflyApi import) - All TypeScript compilation now passes without errors ## Backend Lint Fixes - Updated type annotations to modern Python syntax (dict instead of Dict, X | None instead of Optional[X]) - Replaced try-except-pass with contextlib.suppress(Exception) - Removed unused imports (Dict, Optional, Any, Iterator, etc.) - Fixed bare except clauses to use Exception - Sorted and formatted imports with ruff - Applied ruff format to 27 files ## Workflow Fixes - Updated Flutter SDK constraint from ^3.10.4 to ^3.5.0 (compatible with Flutter 3.24.0) - Changed pip-audit format from github to json in security.yml - Added comprehensive CI workflows (readiness-gate.yml, security.yml) ## Infrastructure - Added DragonflyDB caching system integration - Enhanced Docker configuration with multi-stage builds - Added pytest configuration and test infrastructure - Improved production readiness with proper error handling ## Verification - backend-lint job: ✅ Succeeded - web job: ✅ Succeeded - Ready for GitHub deployment All CI/CD issues resolved. Codebase now passes all quality checks.
216 lines
7.7 KiB
Python
216 lines
7.7 KiB
Python
#!/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()
|