138 lines
4.3 KiB
Python
138 lines
4.3 KiB
Python
from typing import Tuple
|
|
import requests
|
|
import spotipy
|
|
from spotipy.oauth2 import SpotifyOAuth
|
|
import sys
|
|
from urllib.parse import urlencode
|
|
from urllib.parse import urlencode, urlparse, parse_qs
|
|
import webbrowser
|
|
import time
|
|
import base64
|
|
|
|
|
|
CLIENT_ID = 'a1b29f64bef643b5ade0944830637510'
|
|
CLIENT_SECRET = '1d74196e6fec41f9986917afd57df3da'
|
|
REDIRECT_URI = 'https://vinyly.couraud.xyz'
|
|
SCOPE = 'user-modify-playback-state user-read-playback-state'
|
|
TOKEN_FILE = 'spotify_token.json'
|
|
import json
|
|
|
|
def get_spotify_token():
|
|
# Try to load existing token
|
|
try:
|
|
with open(TOKEN_FILE) as f:
|
|
data = json.load(f)
|
|
if data['expires_at'] > time.time():
|
|
return data['access_token']
|
|
# refresh token
|
|
resp = requests.post(
|
|
'https://accounts.spotify.com/api/token',
|
|
data={'grant_type': 'refresh_token', 'refresh_token': data['refresh_token']},
|
|
headers={'Authorization': 'Basic ' + base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()}
|
|
).json()
|
|
data['access_token'] = resp['access_token']
|
|
data['expires_at'] = time.time() + resp['expires_in']
|
|
with open(TOKEN_FILE, 'w') as f2:
|
|
json.dump(data, f2)
|
|
return data['access_token']
|
|
except FileNotFoundError:
|
|
# Get authorization code
|
|
auth_url = 'https://accounts.spotify.com/authorize?' + urlencode({
|
|
'client_id': CLIENT_ID,
|
|
'response_type': 'code',
|
|
'redirect_uri': REDIRECT_URI,
|
|
'scope': SCOPE
|
|
})
|
|
print("Open this URL and authorize:", auth_url)
|
|
webbrowser.open(auth_url)
|
|
code = input("Paste the full redirected URL here: ")
|
|
code = parse_qs(urlparse(code).query)['code'][0]
|
|
|
|
# Exchange code for tokens
|
|
resp = requests.post(
|
|
'https://accounts.spotify.com/api/token',
|
|
data={
|
|
'grant_type': 'authorization_code',
|
|
'code': code,
|
|
'redirect_uri': REDIRECT_URI
|
|
},
|
|
headers={'Authorization': 'Basic ' + base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()}
|
|
).json()
|
|
|
|
data = {
|
|
'access_token': resp['access_token'],
|
|
'refresh_token': resp['refresh_token'],
|
|
'expires_at': time.time() + resp['expires_in']
|
|
}
|
|
with open(TOKEN_FILE, 'w') as f:
|
|
json.dump(data, f)
|
|
return data['access_token']
|
|
|
|
|
|
HEADERS_LUT = {
|
|
"X-Client-Id": "58bd3c95768941ea9eb4350aaa033eb3",
|
|
"Accept-Encoding": "gzip, deflate",
|
|
"Connection": "close",
|
|
"App-Platform": "iOS",
|
|
"Accept": "*/*",
|
|
"User-Agent": "Spotify/8.5.68 iOS/13.4 (iPhone9,3)",
|
|
"Accept-Language": "en",
|
|
"Spotify-App-Version": "8.5.68",
|
|
}
|
|
|
|
MEDIA_REF_LUT_URL = "https://spclient.wg.spotify.com:443/scannable-id/id"
|
|
|
|
auth_url = "https://accounts.spotify.com/authorize?" + urlencode({
|
|
"client_id": CLIENT_ID,
|
|
"response_type": "code",
|
|
"redirect_uri": REDIRECT_URI,
|
|
"scope": SCOPE
|
|
})
|
|
|
|
|
|
|
|
|
|
def get_uri(media_ref: int):
|
|
|
|
with open("/app/songs.json") as f:
|
|
code_to_url = json.load(f)
|
|
|
|
uri = code_to_url.get(str(media_ref))
|
|
if uri:
|
|
return uri
|
|
else:
|
|
return -1
|
|
|
|
|
|
def get_info(uri: str, token: str) -> Tuple[dict, dict]:
|
|
"""Query the Spotify API to get information about a URI."""
|
|
info_headers = {
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json",
|
|
"Authorization": f"Bearer {token}"
|
|
}
|
|
split = uri.split(":")
|
|
content_type = split[-2] + "s"
|
|
id = split[-1]
|
|
response = requests.get(f"https://api.spotify.com/v1/{content_type}/{id}", headers=info_headers)
|
|
response.raise_for_status()
|
|
resp = response.json()
|
|
result = {
|
|
"name": resp["name"],
|
|
"type": split[-2],
|
|
"url": resp["external_urls"]["spotify"],
|
|
}
|
|
if "artists" in resp:
|
|
result['artists'] = []
|
|
for a in resp['artists']:
|
|
result["artists"].append(a["name"])
|
|
if "album" in resp:
|
|
result['album'] = resp['album']['name']
|
|
|
|
if "description" in resp:
|
|
result["description"] = resp['description']
|
|
|
|
return result, resp
|
|
|
|
|