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.
Files changed (45) hide show
  1. {spotapi-1.1.2/spotapi.egg-info → spotapi-1.1.4}/PKG-INFO +2 -1
  2. {spotapi-1.1.2 → spotapi-1.1.4}/README.md +1 -0
  3. {spotapi-1.1.2 → spotapi-1.1.4}/setup.py +1 -1
  4. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/artist.py +36 -0
  5. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/client.py +7 -2
  6. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/playlist.py +2 -1
  7. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/user.py +10 -2
  8. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/strings.py +5 -1
  9. {spotapi-1.1.2 → spotapi-1.1.4/spotapi.egg-info}/PKG-INFO +2 -1
  10. {spotapi-1.1.2 → spotapi-1.1.4}/LICENSE +0 -0
  11. {spotapi-1.1.2 → spotapi-1.1.4}/setup.cfg +0 -0
  12. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/__init__.py +0 -0
  13. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/_tests/__init__.py +0 -0
  14. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/_tests/annotations_test.py +0 -0
  15. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/album.py +0 -0
  16. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/creator.py +0 -0
  17. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/exceptions/__init__.py +0 -0
  18. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/exceptions/errors.py +0 -0
  19. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/family.py +0 -0
  20. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/http/__init__.py +0 -0
  21. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/http/data.py +0 -0
  22. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/http/request.py +0 -0
  23. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/login.py +0 -0
  24. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/password.py +0 -0
  25. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/player.py +0 -0
  26. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/podcast.py +0 -0
  27. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/public.py +0 -0
  28. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/solvers/__init__.py +0 -0
  29. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/solvers/capmonster.py +0 -0
  30. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/solvers/capsolver.py +0 -0
  31. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/song.py +0 -0
  32. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/status.py +0 -0
  33. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/__init__.py +0 -0
  34. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/alias.py +0 -0
  35. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/annotations.py +0 -0
  36. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/data.py +0 -0
  37. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/types/interfaces.py +0 -0
  38. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/__init__.py +0 -0
  39. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/logger.py +0 -0
  40. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/utils/saver.py +0 -0
  41. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi/websocket.py +0 -0
  42. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/SOURCES.txt +0 -0
  43. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/dependency_links.txt +0 -0
  44. {spotapi-1.1.2 → spotapi-1.1.4}/spotapi.egg-info/requires.txt +0 -0
  45. {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.2
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)
@@ -33,6 +33,7 @@ Everything you can do with Spotify, **SpotAPI** can do with just a user’s logi
33
33
 
34
34
 
35
35
  ## Docs
36
+ - [**Public.py**](./docs/public.md)
36
37
  - [**Artist.py**](./docs/artist.md)
37
38
  - [**Album.py**](./docs/album.md)
38
39
  - [**Creator.py**](./docs/creator.md)
@@ -55,5 +55,5 @@ setup(
55
55
  ],
56
56
  long_description=long_description,
57
57
  long_description_content_type="text/markdown",
58
- version="1.1.2",
58
+ version="1.1.4",
59
59
  )
@@ -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
- pattern = r"https:\/\/open\.spotifycdn\.com\/cdn\/build\/web-player\/web-player.*?\.js"
79
- self.js_pack = re.findall(pattern, resp.response)[1]
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/v1/profile"
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.2
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