first commit
This commit is contained in:
commit
e2b781c3a2
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
auth.json
|
|
@ -0,0 +1,106 @@
|
||||||
|
#!/bin/python3
|
||||||
|
|
||||||
|
CLIENT_ID = "zU4XHVVkc2tDPo4t"
|
||||||
|
CLIENT_SECRET = "VJKhDFqJPqvsPVNBV6ukXTJmwlvbttP7wlMlrc72se4"
|
||||||
|
URL_BASE = "https://auth.tidal.com/v1/oauth2"
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Login:
|
||||||
|
deviceCode: str | None
|
||||||
|
userCode: str | None
|
||||||
|
verificationUrl: str | None
|
||||||
|
timeout: int | None
|
||||||
|
interval: int | None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Auth:
|
||||||
|
userId: str | None
|
||||||
|
countryCode: str | None
|
||||||
|
accessToken: str | None
|
||||||
|
refreshToken: str | None
|
||||||
|
expiresIn: str | None
|
||||||
|
|
||||||
|
|
||||||
|
def post(path, data, auth=None) -> dict:
|
||||||
|
return requests.post(URL_BASE + path, data=data, auth=auth).json()
|
||||||
|
|
||||||
|
|
||||||
|
def getLogin() -> Login:
|
||||||
|
data = {"client_id": CLIENT_ID, "scope": "r_usr+w_usr+w_sub"}
|
||||||
|
|
||||||
|
result = post("/device_authorization", data)
|
||||||
|
|
||||||
|
if "status" in result and result["status"] != 200:
|
||||||
|
raise Exception("Client ID not accepted by Tidal")
|
||||||
|
|
||||||
|
return Login(
|
||||||
|
deviceCode=result["deviceCode"],
|
||||||
|
userCode=result["userCode"],
|
||||||
|
verificationUrl=result["verificationUri"],
|
||||||
|
timeout=result["expiresIn"],
|
||||||
|
interval=result["interval"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def getAuth(login: Login) -> Auth | None:
|
||||||
|
data = {
|
||||||
|
"client_id": CLIENT_ID,
|
||||||
|
"device_code": login.deviceCode,
|
||||||
|
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
"scope": "r_usr+w_usr+w_sub",
|
||||||
|
}
|
||||||
|
|
||||||
|
result = post("/token", data, (CLIENT_ID, CLIENT_SECRET))
|
||||||
|
|
||||||
|
if "status" in result and result["status"] != 200:
|
||||||
|
if result["status"] == 400 and result["sub_status"] == 1002:
|
||||||
|
return None # Not logged in yet
|
||||||
|
else:
|
||||||
|
raise Exception("Failed to check authorization status")
|
||||||
|
|
||||||
|
return Auth(
|
||||||
|
result["user"]["userId"],
|
||||||
|
result["user"]["countryCode"],
|
||||||
|
result["access_token"],
|
||||||
|
result["refresh_token"],
|
||||||
|
result["expires_in"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def loginByWeb() -> Auth:
|
||||||
|
login = getLogin()
|
||||||
|
url = f"http://{login.verificationUrl}/{login.userCode}"
|
||||||
|
print(f"Log in at {url}")
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
elapsed = 0
|
||||||
|
timeout = login.timeout if login.timeout else 300
|
||||||
|
interval = login.interval if login.interval else 2
|
||||||
|
|
||||||
|
while elapsed < timeout:
|
||||||
|
elapsed = time.time() - start
|
||||||
|
auth = getAuth(login)
|
||||||
|
if not auth:
|
||||||
|
time.sleep(interval)
|
||||||
|
else:
|
||||||
|
return auth
|
||||||
|
|
||||||
|
raise Exception("Failed to log in")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
with open("auth.json", "rb") as f:
|
||||||
|
a = json.load(f)
|
||||||
|
auth = Auth(a['userId'], a['countryCode'], a['accessToken'], a['refreshToken'], a['expiresIn'])
|
||||||
|
except (OSError, IndexError):
|
||||||
|
auth = loginByWeb()
|
||||||
|
with open("auth.json", "w") as f:
|
||||||
|
json.dump(auth.__dict__, f)
|
Loading…
Reference in New Issue