spotapi 1.1.2__tar.gz → 1.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spotapi-1.1.2/spotapi.egg-info → spotapi-1.1.4}/PKG-INFO +2 -1
- {spotapi-1.1.2 → spotapi-1.1.4}/README.md +1 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/setup.py +1 -1
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/artist.py +36 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/client.py +7 -2
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/playlist.py +2 -1
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/user.py +10 -2
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/strings.py +5 -1
- {spotapi-1.1.2 → spotapi-1.1.4/spotapi.egg-info}/PKG-INFO +2 -1
- {spotapi-1.1.2 → spotapi-1.1.4}/LICENSE +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/setup.cfg +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/_tests/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/_tests/annotations_test.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/album.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/creator.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/exceptions/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/exceptions/errors.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/family.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/http/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/http/data.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/http/request.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/login.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/password.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/player.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/podcast.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/public.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/solvers/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/solvers/capmonster.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/solvers/capsolver.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/song.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/status.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/alias.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/annotations.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/data.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/interfaces.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/__init__.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/logger.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/saver.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/websocket.py +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/SOURCES.txt +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/dependency_links.txt +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/requires.txt +0 -0
- {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spotapi
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: A sleek API wrapper for Spotify's private API
|
|
5
5
|
Home-page: UNKNOWN
|
|
6
6
|
Author: Aran
|
|
@@ -48,6 +48,7 @@ Everything you can do with Spotify, **SpotAPI** can do with just a user’s
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
## Docs
|
|
51
|
+
- [**Public.py**](./docs/public.md)
|
|
51
52
|
- [**Artist.py**](./docs/artist.md)
|
|
52
53
|
- [**Album.py**](./docs/album.md)
|
|
53
54
|
- [**Creator.py**](./docs/creator.md)
|
|
@@ -83,6 +83,42 @@ class Artist:
|
|
|
83
83
|
|
|
84
84
|
return resp.response
|
|
85
85
|
|
|
86
|
+
def get_artist(
|
|
87
|
+
self, artist_id: str, /, *, locale_code: str = "en"
|
|
88
|
+
) -> Mapping[str, Any]:
|
|
89
|
+
"""Gets an artist by ID"""
|
|
90
|
+
if "artist:" in artist_id:
|
|
91
|
+
artist_id = artist_id.split("artist:")[-1]
|
|
92
|
+
|
|
93
|
+
url = "https://api-partner.spotify.com/pathfinder/v1/query"
|
|
94
|
+
params = {
|
|
95
|
+
"operationName": "queryArtistOverview",
|
|
96
|
+
"variables": json.dumps(
|
|
97
|
+
{
|
|
98
|
+
"uri": f"spotify:artist:{artist_id}",
|
|
99
|
+
"locale": locale_code,
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
"extensions": json.dumps(
|
|
103
|
+
{
|
|
104
|
+
"persistedQuery": {
|
|
105
|
+
"version": 1,
|
|
106
|
+
"sha256Hash": self.base.part_hash("queryArtistOverview"),
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
resp = self.base.client.get(url, params=params, authenticate=True)
|
|
113
|
+
|
|
114
|
+
if resp.fail:
|
|
115
|
+
raise ArtistError("Could not get artist by ID", error=resp.error.string)
|
|
116
|
+
|
|
117
|
+
if not isinstance(resp.response, Mapping):
|
|
118
|
+
raise ArtistError("Invalid JSON response")
|
|
119
|
+
|
|
120
|
+
return resp.response
|
|
121
|
+
|
|
86
122
|
def paginate_artists(
|
|
87
123
|
self, query: str, /
|
|
88
124
|
) -> Generator[Mapping[str, Any], None, None]:
|
|
@@ -75,8 +75,13 @@ class BaseClient:
|
|
|
75
75
|
if resp.fail:
|
|
76
76
|
raise BaseClientError("Could not get session", error=resp.error.string)
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
try:
|
|
79
|
+
pattern = r"https:\/\/open\.spotifycdn\.com\/cdn\/build\/web-player\/web-player.*?\.js"
|
|
80
|
+
self.js_pack = re.findall(pattern, resp.response)[1]
|
|
81
|
+
except IndexError:
|
|
82
|
+
pattern = r"https:\/\/open-exp.spotifycdn\.com\/cdn\/build\/web-player\/web-player.*?\.js"
|
|
83
|
+
self.js_pack = re.findall(pattern, resp.response)[1]
|
|
84
|
+
|
|
80
85
|
self.access_token = parse_json_string(resp.response, "accessToken")
|
|
81
86
|
self.client_id = parse_json_string(resp.response, "clientId")
|
|
82
87
|
self.device_id = parse_json_string(resp.response, "correlationId")
|
|
@@ -48,7 +48,7 @@ class PublicPlaylist:
|
|
|
48
48
|
self.playlist_link = f"https://open.spotify.com/playlist/{self.playlist_id}"
|
|
49
49
|
|
|
50
50
|
def get_playlist_info(
|
|
51
|
-
self, limit: int = 25, *, offset: int = 0
|
|
51
|
+
self, limit: int = 25, *, offset: int = 0, enable_watch_feed_entrypoint: bool = False
|
|
52
52
|
) -> Mapping[str, Any]:
|
|
53
53
|
"""Gets the public playlist information"""
|
|
54
54
|
url = "https://api-partner.spotify.com/pathfinder/v1/query"
|
|
@@ -59,6 +59,7 @@ class PublicPlaylist:
|
|
|
59
59
|
"uri": f"spotify:playlist:{self.playlist_id}",
|
|
60
60
|
"offset": offset,
|
|
61
61
|
"limit": limit,
|
|
62
|
+
"enableWatchFeedEntrypoint": enable_watch_feed_entrypoint,
|
|
62
63
|
}
|
|
63
64
|
),
|
|
64
65
|
"extensions": json.dumps(
|
|
@@ -4,12 +4,12 @@ from typing import Any
|
|
|
4
4
|
from collections.abc import Mapping
|
|
5
5
|
from spotapi.types.annotations import enforce
|
|
6
6
|
|
|
7
|
+
from spotapi import utils
|
|
7
8
|
from spotapi.exceptions import UserError
|
|
8
9
|
from spotapi.login import Login
|
|
9
10
|
|
|
10
11
|
__all__ = ["User", "UserError"]
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
@enforce
|
|
14
14
|
class User:
|
|
15
15
|
"""
|
|
@@ -110,9 +110,17 @@ class User:
|
|
|
110
110
|
"country": profile_dump["country"],
|
|
111
111
|
},
|
|
112
112
|
"recaptcha_token": captcha_response,
|
|
113
|
+
"client_nonce": utils.random_nonce(),
|
|
114
|
+
"callback_url": "https://www.spotify.com/account/profile/challenge",
|
|
115
|
+
"client_info": {
|
|
116
|
+
"locale": "en_US",
|
|
117
|
+
"capabilities": [
|
|
118
|
+
1
|
|
119
|
+
]
|
|
120
|
+
}
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
url = "https://www.spotify.com/api/account-settings/
|
|
123
|
+
url = "https://www.spotify.com/api/account-settings/v2/profile"
|
|
116
124
|
|
|
117
125
|
headers = {
|
|
118
126
|
"Content-Type": "application/json",
|
|
@@ -4,6 +4,7 @@ import random
|
|
|
4
4
|
import base64
|
|
5
5
|
import os
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
__all__ = [
|
|
8
9
|
"random_b64_string",
|
|
9
10
|
"random_hex_string",
|
|
@@ -12,9 +13,9 @@ __all__ = [
|
|
|
12
13
|
"random_domain",
|
|
13
14
|
"random_email",
|
|
14
15
|
"random_dob",
|
|
16
|
+
"random_nonce"
|
|
15
17
|
]
|
|
16
18
|
|
|
17
|
-
|
|
18
19
|
def random_b64_string(length: int) -> str:
|
|
19
20
|
"""Used by Spotify internally"""
|
|
20
21
|
|
|
@@ -84,3 +85,6 @@ def random_email() -> str:
|
|
|
84
85
|
|
|
85
86
|
def random_dob() -> str:
|
|
86
87
|
return f"{random.randint(1950, 2000)}-{random.randint(1, 12):02d}-{random.randint(1, 28):02d}"
|
|
88
|
+
|
|
89
|
+
def random_nonce() -> str:
|
|
90
|
+
return ''.join(str(random.getrandbits(32)) for _ in range(2))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spotapi
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: A sleek API wrapper for Spotify's private API
|
|
5
5
|
Home-page: UNKNOWN
|
|
6
6
|
Author: Aran
|
|
@@ -48,6 +48,7 @@ Everything you can do with Spotify, **SpotAPI** can do with just a user’s
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
## Docs
|
|
51
|
+
- [**Public.py**](./docs/public.md)
|
|
51
52
|
- [**Artist.py**](./docs/artist.md)
|
|
52
53
|
- [**Album.py**](./docs/album.md)
|
|
53
54
|
- [**Creator.py**](./docs/creator.md)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|