diff --git a/votify/downloader.py b/votify/downloader.py index b77827a..e012feb 100644 --- a/votify/downloader.py +++ b/votify/downloader.py @@ -365,14 +365,49 @@ def update_playlist_file( playlist_file_lines[playlist_track - 1] = final_path_relative.as_posix() + "\n" with playlist_file_path.open("w", encoding="utf8") as playlist_file: playlist_file.writelines(playlist_file_lines) - + def get_gid_metadata( self, media_id: str, media_type: str, ) -> dict: gid = self.spotify_api.media_id_to_gid(media_id) - return self.spotify_api.get_gid_metadata(gid, media_type) + metadata = self.spotify_api.get_gid_metadata(gid, media_type) + + target_key = "file" if media_type == "track" else "audio" + + existing_entries = metadata.get(target_key, []) + if any(e.get("format") in ("MP4_256", "MP4_128") for e in existing_entries): + return metadata + + playback_info = self.spotify_api.get_track_playback_info(media_id, media_type) + if not playback_info: + return metadata + + media_key = f"spotify:{media_type}:{media_id}" + file_ids_mp4 = ( + playback_info.get("media", {}) + .get(media_key, {}) + .get("item", {}) + .get("manifest", {}) + .get("file_ids_mp4", []) + ) + if not file_ids_mp4: + return metadata + + metadata.setdefault(target_key, []) + + for item in file_ids_mp4: + file_id = item.get("file_id") + bitrate = item.get("bitrate") + + fmt = {256000: "MP4_256", 128000: "MP4_128"}.get(bitrate) + if file_id and fmt: + entry = {"file_id": file_id, "format": fmt} + if entry not in metadata[target_key]: + metadata[target_key].append(entry) + + return metadata def get_playplay_decryption_key(self, file_id: str) -> bytes: raise NotImplementedError() diff --git a/votify/spotify_api.py b/votify/spotify_api.py index cb637eb..63eb65a 100644 --- a/votify/spotify_api.py +++ b/votify/spotify_api.py @@ -28,6 +28,7 @@ class SpotifyApi: GID_METADATA_API_URL = "https://spclient.wg.spotify.com/metadata/4/{media_type}/{gid}?market=from_token" PATHFINDER_API_URL = "https://api-partner.spotify.com/pathfinder/v1/query" VIDEO_MANIFEST_API_URL = "https://gue1-spclient.spotify.com/manifests/v7/json/sources/{gid}/options/supports_drm" + TRACK_PLAYBACK_API_URL = "https://gue1-spclient.spotify.com/track-playback/v1/media/spotify:{type}:{id}" PLAYPLAY_LICENSE_API_URL = ( "https://gew4-spclient.spotify.com/playplay/v1/key/{file_id}" ) @@ -43,6 +44,7 @@ class SpotifyApi: EXTEND_TRACK_COLLECTION_WAIT_TIME = 0.5 SERVER_TIME_URL = "https://open.spotify.com/api/server-time" SESSION_TOKEN_URL = "https://open.spotify.com/api/token" + CLIENT_TOKEN_URL = "https://clienttoken.spotify.com/v1/clienttoken" def __init__( self, @@ -183,6 +185,23 @@ def get_gid_metadata( check_response(response) return response.json() + def get_track_playback_info( + self, + media_id: str, + media_type: str + ) -> dict | None: + self._refresh_session_auth() + + params = {"manifestFileFormat": ["file_ids_mp4"]} + + response = self.session.get( + self.TRACK_PLAYBACK_API_URL.format(type=media_type, id=media_id), + params=params + ) + + check_response(response) + return response.json() + def get_lyrics(self, track_id: str) -> dict | None: self._refresh_session_auth() response = self.session.get(self.LYRICS_API_URL.format(track_id=track_id)) diff --git a/votify/totp.py b/votify/totp.py index 9e6ea32..f6f142a 100644 --- a/votify/totp.py +++ b/votify/totp.py @@ -8,7 +8,7 @@ # thanks to https://github.com/glomatico/votify/pull/42#issuecomment-2727036757 class TOTP: - SPOTIFY_SECRETS_JSON = "https://raw.githubusercontent.com/Thereallo1026/spotify-secrets/refs/heads/main/secrets/secretDict.json" + SPOTIFY_SECRETS_JSON = "https://code.thetadev.de/ThetaDev/spotify-secrets/raw/branch/main/secrets/secretDict.json" PERIOD = 30 DIGITS = 6