mirror of
https://github.com/glomatico/votify.git
synced 2026-10-01 13:32:06 +02:00
Rework/fix authorization
Co-Authored-By: Arsenii es3n1n <40367813+es3n1n@users.noreply.github.com>
This commit is contained in:
co-authored by
Arsenii es3n1n
parent
96477e276a
commit
4de9fc97c2
+5
-8
@@ -401,7 +401,7 @@ def main(
|
||||
return
|
||||
logger.info("Starting Votify")
|
||||
spotify_api = SpotifyApi.from_cookies_file(cookies_path)
|
||||
if spotify_api.config_info["isAnonymous"]:
|
||||
if spotify_api.session_info["isAnonymous"]:
|
||||
logger.critical(
|
||||
"Failed to get a valid session. Try logging in and exporting your cookies again"
|
||||
)
|
||||
@@ -459,8 +459,8 @@ def main(
|
||||
downloader_music_video = DownloaderMusicVideo(
|
||||
downloader_video,
|
||||
)
|
||||
spotify_api.config_info["isPremium"] = (
|
||||
True if force_premium else spotify_api.config_info["isPremium"]
|
||||
is_premium = (
|
||||
True if force_premium else spotify_api.user_profile["product"] == "premium"
|
||||
)
|
||||
if not lrc_only:
|
||||
if audio_quality in AAC_AUDIO_QUALITIES:
|
||||
@@ -497,10 +497,7 @@ def main(
|
||||
if download_mode == DownloadMode.ARIA2C and not downloader.aria2c_path_full:
|
||||
logger.critical(X_NOT_FOUND_STRING.format("aria2c", aria2c_path))
|
||||
return
|
||||
if (
|
||||
not spotify_api.config_info["isPremium"]
|
||||
and audio_quality in PREMIUM_AUDIO_QUALITIES
|
||||
):
|
||||
if not is_premium and audio_quality in PREMIUM_AUDIO_QUALITIES:
|
||||
logger.critical("Cannot download at chosen quality with a free account")
|
||||
return
|
||||
can_download_music_videos = True
|
||||
@@ -532,7 +529,7 @@ def main(
|
||||
)
|
||||
else:
|
||||
downloader.set_cdm()
|
||||
if not spotify_api.config_info["isPremium"]:
|
||||
if is_premium:
|
||||
music_video_warning_message.append(
|
||||
"Cannot download music videos with a non-premium account"
|
||||
)
|
||||
|
||||
+25
-22
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import typing
|
||||
from http.cookiejar import CookieJar, MozillaCookieJar
|
||||
@@ -11,6 +10,7 @@ from pathlib import Path
|
||||
import base62
|
||||
import requests
|
||||
|
||||
from .totp import TOTP
|
||||
from .utils import check_response
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ class SpotifyApi:
|
||||
return cls(cookies)
|
||||
|
||||
def _set_session(self):
|
||||
self.totp = TOTP()
|
||||
self.session = requests.Session()
|
||||
if self.cookies is not None:
|
||||
self.session.cookies.update(self.cookies)
|
||||
@@ -72,25 +73,29 @@ class SpotifyApi:
|
||||
"app-platform": "WebPlayer",
|
||||
}
|
||||
)
|
||||
self._set_session_auth()
|
||||
self._set_session_info()
|
||||
self._set_user_profile()
|
||||
|
||||
def _set_session_auth(self):
|
||||
home_page = self.get_home_page()
|
||||
self.session_info = json.loads(
|
||||
re.search(
|
||||
r'<script id="session" data-testid="session" type="application/json">(.+?)</script>',
|
||||
home_page,
|
||||
).group(1)
|
||||
)
|
||||
self.config_info = json.loads(
|
||||
re.search(
|
||||
r'<script id="config" data-testid="config" type="application/json">(.+?)</script>',
|
||||
home_page,
|
||||
).group(1)
|
||||
def _set_session_info(self):
|
||||
server_time_response = self.session.get("https://open.spotify.com/server-time")
|
||||
check_response(server_time_response)
|
||||
server_time = 1e3 * server_time_response.json()["serverTime"]
|
||||
totp = self.totp.generate(timestamp=server_time)
|
||||
session_info_response = self.session.get(
|
||||
"https://open.spotify.com/get_access_token",
|
||||
params={
|
||||
"reason": "init",
|
||||
"productType": "web-player",
|
||||
"totp": totp,
|
||||
"totpVer": str(self.totp.version),
|
||||
"ts": str(server_time),
|
||||
},
|
||||
)
|
||||
check_response(session_info_response)
|
||||
self.session_info = session_info_response.json()
|
||||
self.session.headers.update(
|
||||
{
|
||||
"Authorization": f"Bearer {self.session_info['accessToken']}",
|
||||
"authorization": f"Bearer {self.session_info['accessToken']}",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -101,14 +106,12 @@ class SpotifyApi:
|
||||
timestamp_now = time.time() * 1000
|
||||
if timestamp_now < timestamp_session_expire:
|
||||
return
|
||||
self._set_session_auth()
|
||||
self._set_session_info()
|
||||
|
||||
def get_home_page(self) -> str:
|
||||
response = self.session.get(
|
||||
SpotifyApi.SPOTIFY_HOME_PAGE_URL,
|
||||
)
|
||||
def _set_user_profile(self):
|
||||
response = self.session.get(self.METADATA_API_URL.format(type="me", item_id=""))
|
||||
check_response(response)
|
||||
return response.text
|
||||
self.user_profile = response.json()
|
||||
|
||||
@staticmethod
|
||||
def media_id_to_gid(media_id: str) -> str:
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import hashlib
|
||||
import hmac
|
||||
import math
|
||||
|
||||
# thanks to https://github.com/glomatico/votify/pull/42#issuecomment-2727036757
|
||||
class TOTP:
|
||||
def __init__(self) -> None:
|
||||
# dumped directly from the object, after all decryptions
|
||||
self.secret = b"5507145853487499592248630329347"
|
||||
self.version = 5
|
||||
self.period = 30
|
||||
self.digits = 6
|
||||
|
||||
def generate(self, timestamp: int) -> str:
|
||||
counter = math.floor(timestamp / 1000 / self.period)
|
||||
counter_bytes = counter.to_bytes(8, byteorder="big")
|
||||
|
||||
h = hmac.new(self.secret, counter_bytes, hashlib.sha1)
|
||||
hmac_result = h.digest()
|
||||
|
||||
offset = hmac_result[-1] & 0x0F
|
||||
binary = (
|
||||
(hmac_result[offset] & 0x7F) << 24
|
||||
| (hmac_result[offset + 1] & 0xFF) << 16
|
||||
| (hmac_result[offset + 2] & 0xFF) << 8
|
||||
| (hmac_result[offset + 3] & 0xFF)
|
||||
)
|
||||
|
||||
return str(binary % (10**self.digits)).zfill(self.digits)
|
||||
Reference in New Issue
Block a user