yutipy 2.2.4__py3-none-any.whl → 2.2.5__py3-none-any.whl
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.
Potentially problematic release.
This version of yutipy might be problematic. Click here for more details.
- yutipy/lastfm.py +61 -17
- yutipy/spotify.py +44 -20
- {yutipy-2.2.4.dist-info → yutipy-2.2.5.dist-info}/METADATA +1 -1
- {yutipy-2.2.4.dist-info → yutipy-2.2.5.dist-info}/RECORD +8 -8
- {yutipy-2.2.4.dist-info → yutipy-2.2.5.dist-info}/WHEEL +0 -0
- {yutipy-2.2.4.dist-info → yutipy-2.2.5.dist-info}/entry_points.txt +0 -0
- {yutipy-2.2.4.dist-info → yutipy-2.2.5.dist-info}/licenses/LICENSE +0 -0
- {yutipy-2.2.4.dist-info → yutipy-2.2.5.dist-info}/top_level.txt +0 -0
yutipy/lastfm.py
CHANGED
|
@@ -73,6 +73,46 @@ class LastFm:
|
|
|
73
73
|
"""Checks if the session is closed."""
|
|
74
74
|
return self._is_session_closed
|
|
75
75
|
|
|
76
|
+
def get_user_profile(self, username: str):
|
|
77
|
+
"""
|
|
78
|
+
Fetches the user profile information for the provided username.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
dict
|
|
83
|
+
A dictionary containing the user's profile information or error is username does not exist.
|
|
84
|
+
"""
|
|
85
|
+
query = (
|
|
86
|
+
f"?method=user.getinfo&user={username}&api_key={self.api_key}&format=json"
|
|
87
|
+
)
|
|
88
|
+
query_url = self.__api_url + query
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
response = self.__session.get(query_url, timeout=30)
|
|
92
|
+
except requests.RequestException as e:
|
|
93
|
+
logger.warning(f"Failed to fetch user profile: {e}")
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
response_json = response.json()
|
|
97
|
+
result = response_json.get("user")
|
|
98
|
+
error = response_json.get("message")
|
|
99
|
+
if result:
|
|
100
|
+
images = [
|
|
101
|
+
{"size": image.get("size"), "url": image.get("#text")}
|
|
102
|
+
for image in result.get("image", [])
|
|
103
|
+
]
|
|
104
|
+
return {
|
|
105
|
+
"name": result.get("realname"),
|
|
106
|
+
"username": result.get("name"),
|
|
107
|
+
"type": result.get("type"),
|
|
108
|
+
"url": result.get("url"),
|
|
109
|
+
"images": images,
|
|
110
|
+
}
|
|
111
|
+
elif error:
|
|
112
|
+
return {"error": error}
|
|
113
|
+
else:
|
|
114
|
+
return None
|
|
115
|
+
|
|
76
116
|
def get_currently_playing(self, username: str) -> Optional[UserPlaying]:
|
|
77
117
|
"""
|
|
78
118
|
Fetches information about the currently playing or most recent track for a user.
|
|
@@ -100,29 +140,33 @@ class LastFm:
|
|
|
100
140
|
|
|
101
141
|
response_json = response.json()
|
|
102
142
|
result = response_json.get("recenttracks", {}).get("track", [])[0]
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
143
|
+
if result:
|
|
144
|
+
album_art = [
|
|
145
|
+
img.get("#text")
|
|
146
|
+
for img in result.get("image", [])
|
|
147
|
+
if img.get("size") == "extralarge"
|
|
148
|
+
]
|
|
149
|
+
return UserPlaying(
|
|
150
|
+
album_art="".join(album_art),
|
|
151
|
+
album_title=result.get("album", {}).get("#text"),
|
|
152
|
+
artists=", ".join(
|
|
153
|
+
separate_artists(result.get("artist", {}).get("#text"))
|
|
154
|
+
),
|
|
155
|
+
id=result.get("mbid"),
|
|
156
|
+
timestamp=result.get("date", {}).get("uts"),
|
|
157
|
+
title=result.get("name"),
|
|
158
|
+
url=result.get("url"),
|
|
159
|
+
is_playing=result.get("@attr", {}).get("nowplaying", False),
|
|
160
|
+
)
|
|
161
|
+
return None
|
|
118
162
|
|
|
119
163
|
|
|
120
164
|
if __name__ == "__main__":
|
|
121
165
|
with LastFm() as lastfm:
|
|
122
166
|
username = input("Enter Lasfm Username: ").strip()
|
|
123
|
-
result = lastfm.
|
|
167
|
+
result = lastfm.get_user_profile(username=username)
|
|
124
168
|
|
|
125
169
|
if result:
|
|
126
|
-
pprint(
|
|
170
|
+
pprint(result)
|
|
127
171
|
else:
|
|
128
172
|
print("No result was found. Make sure the username is correct!")
|
yutipy/spotify.py
CHANGED
|
@@ -196,8 +196,9 @@ class Spotify:
|
|
|
196
196
|
logger.debug(f"Authentication response status code: {response.status_code}")
|
|
197
197
|
response.raise_for_status()
|
|
198
198
|
except requests.RequestException as e:
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
raise requests.RequestException(
|
|
200
|
+
f"Network error during Spotify authentication: {e}"
|
|
201
|
+
)
|
|
201
202
|
|
|
202
203
|
if response.status_code == 200:
|
|
203
204
|
response_json = response.json()
|
|
@@ -210,6 +211,9 @@ class Spotify:
|
|
|
210
211
|
|
|
211
212
|
def __refresh_access_token(self):
|
|
212
213
|
"""Refreshes the token if it has expired."""
|
|
214
|
+
if not self.__access_token:
|
|
215
|
+
raise SpotifyAuthException("No access token was found.")
|
|
216
|
+
|
|
213
217
|
try:
|
|
214
218
|
if time() - self.__token_requested_at >= self.__token_expires_in:
|
|
215
219
|
token_info = self.__get_access_token()
|
|
@@ -224,12 +228,16 @@ class Spotify:
|
|
|
224
228
|
self.__token_requested_at = token_info.get("requested_at")
|
|
225
229
|
|
|
226
230
|
logger.info("The access token is still valid, no need to refresh.")
|
|
231
|
+
except (AuthenticationException, requests.RequestException) as e:
|
|
232
|
+
logger.warning(
|
|
233
|
+
f"Failed to refresh the access toke due to following error: {e}"
|
|
234
|
+
)
|
|
227
235
|
except TypeError:
|
|
228
236
|
logger.debug(
|
|
229
237
|
f"token requested at: {self.__token_requested_at} | token expires in: {self.__token_expires_in}"
|
|
230
238
|
)
|
|
231
239
|
logger.info(
|
|
232
|
-
"Something went wrong while trying to refresh the token. Set logging level to `DEBUG` to see the issue."
|
|
240
|
+
"Something went wrong while trying to refresh the access token. Set logging level to `DEBUG` to see the issue."
|
|
233
241
|
)
|
|
234
242
|
|
|
235
243
|
def save_access_token(self, token_info: dict) -> None:
|
|
@@ -341,6 +349,7 @@ class Spotify:
|
|
|
341
349
|
)
|
|
342
350
|
response.raise_for_status()
|
|
343
351
|
except requests.RequestException as e:
|
|
352
|
+
logger.warning(f"Network error during Spotify search: {e}")
|
|
344
353
|
return None
|
|
345
354
|
|
|
346
355
|
if response.status_code != 200:
|
|
@@ -408,6 +417,7 @@ class Spotify:
|
|
|
408
417
|
)
|
|
409
418
|
response.raise_for_status()
|
|
410
419
|
except requests.RequestException as e:
|
|
420
|
+
logger.warning(f"Network error during Spotify search (advanced): {e}")
|
|
411
421
|
return None
|
|
412
422
|
|
|
413
423
|
if response.status_code != 200:
|
|
@@ -441,6 +451,7 @@ class Spotify:
|
|
|
441
451
|
)
|
|
442
452
|
response.raise_for_status()
|
|
443
453
|
except requests.RequestException as e:
|
|
454
|
+
logger.warning(f"Network error during Spotify get artist ids: {e}")
|
|
444
455
|
return None
|
|
445
456
|
|
|
446
457
|
if response.status_code != 200:
|
|
@@ -820,8 +831,9 @@ class SpotifyAuth:
|
|
|
820
831
|
logger.debug(f"Authentication response status code: {response.status_code}")
|
|
821
832
|
response.raise_for_status()
|
|
822
833
|
except requests.RequestException as e:
|
|
823
|
-
|
|
824
|
-
|
|
834
|
+
raise requests.RequestException(
|
|
835
|
+
f"Network error during Spotify authentication: {e}"
|
|
836
|
+
)
|
|
825
837
|
|
|
826
838
|
if response.status_code == 200:
|
|
827
839
|
response_json = response.json()
|
|
@@ -837,20 +849,30 @@ class SpotifyAuth:
|
|
|
837
849
|
if not self.__access_token:
|
|
838
850
|
raise SpotifyAuthException("No access token was found.")
|
|
839
851
|
|
|
840
|
-
|
|
841
|
-
|
|
852
|
+
try:
|
|
853
|
+
if time() - self.__token_requested_at >= self.__token_expires_in:
|
|
854
|
+
token_info = self.__get_access_token(refresh_token=self.__refresh_token)
|
|
842
855
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
856
|
+
try:
|
|
857
|
+
self.save_access_token(token_info)
|
|
858
|
+
except NotImplementedError as e:
|
|
859
|
+
logger.warning(e)
|
|
847
860
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
861
|
+
self.__access_token = token_info.get("access_token")
|
|
862
|
+
self.__refresh_token = token_info.get("refresh_token")
|
|
863
|
+
self.__token_expires_in = token_info.get("expires_in")
|
|
864
|
+
self.__token_requested_at = token_info.get("requested_at")
|
|
852
865
|
|
|
853
|
-
|
|
866
|
+
logger.info("The access token is still valid, no need to refresh.")
|
|
867
|
+
except (AuthenticationException, requests.RequestException) as e:
|
|
868
|
+
logger.warning(f"Failed to refresh the access toke due to following error: {e}")
|
|
869
|
+
except TypeError:
|
|
870
|
+
logger.debug(
|
|
871
|
+
f"token requested at: {self.__token_requested_at} | token expires in: {self.__token_expires_in}"
|
|
872
|
+
)
|
|
873
|
+
logger.warning(
|
|
874
|
+
"Something went wrong while trying to refresh the access token. Set logging level to `DEBUG` to see the issue."
|
|
875
|
+
)
|
|
854
876
|
|
|
855
877
|
@staticmethod
|
|
856
878
|
def generate_state() -> str:
|
|
@@ -1028,7 +1050,7 @@ class SpotifyAuth:
|
|
|
1028
1050
|
except NotImplementedError as e:
|
|
1029
1051
|
logger.warning(e)
|
|
1030
1052
|
|
|
1031
|
-
def get_user_profile(self):
|
|
1053
|
+
def get_user_profile(self) -> Optional[dict]:
|
|
1032
1054
|
"""
|
|
1033
1055
|
Fetches the user's display name and profile images.
|
|
1034
1056
|
|
|
@@ -1063,10 +1085,11 @@ class SpotifyAuth:
|
|
|
1063
1085
|
logger.warning(f"Unexpected response: {response.json()}")
|
|
1064
1086
|
return None
|
|
1065
1087
|
|
|
1066
|
-
|
|
1088
|
+
result = response.json()
|
|
1067
1089
|
return {
|
|
1068
|
-
"display_name":
|
|
1069
|
-
"images":
|
|
1090
|
+
"display_name": result.get("display_name"),
|
|
1091
|
+
"images": result.get("images", []),
|
|
1092
|
+
"url": result.get("external_urls", {}).get("spotify")
|
|
1070
1093
|
}
|
|
1071
1094
|
|
|
1072
1095
|
def get_currently_playing(self) -> Optional[UserPlaying]:
|
|
@@ -1105,6 +1128,7 @@ class SpotifyAuth:
|
|
|
1105
1128
|
response = self.__session.get(query_url, headers=header, timeout=30)
|
|
1106
1129
|
response.raise_for_status()
|
|
1107
1130
|
except requests.RequestException as e:
|
|
1131
|
+
logger.warning(f"Error while getting Spotify user activity: {e}")
|
|
1108
1132
|
return None
|
|
1109
1133
|
|
|
1110
1134
|
if response.status_code == 204:
|
|
@@ -3,20 +3,20 @@ yutipy/deezer.py,sha256=xI7ZDoPHES64_uRN4aYA-kj56V4tFU8xYkZYPXidl_I,11266
|
|
|
3
3
|
yutipy/exceptions.py,sha256=zz0XyyZr5xRcmRyw3hdTGaVRcwRn_RSYZdmwmuO0sEM,1379
|
|
4
4
|
yutipy/itunes.py,sha256=3YBLoWPhIx6ODE2G-msRppCHo0DNDGZADj-XvL_f5H0,7867
|
|
5
5
|
yutipy/kkbox.py,sha256=ZOIW8rQuiYEWmsXW3Dw9Q5mw71Jh_tMRAULvEgAuDew,19310
|
|
6
|
-
yutipy/lastfm.py,sha256=
|
|
6
|
+
yutipy/lastfm.py,sha256=qv834EdJH5We90JME06dvUQBc0JLDphJOnbbzMfEQAQ,5797
|
|
7
7
|
yutipy/logger.py,sha256=GyLBlfQZ6pLNJ5MbyQSvcD_PkxmFdX41DPq5aeG1z68,1316
|
|
8
8
|
yutipy/models.py,sha256=45M-bNHusaAan_Ta_E9DyvsWujsT-ivbJqIfy2-i3R8,2343
|
|
9
9
|
yutipy/musicyt.py,sha256=WKR4J4ru1uqNa7ORSZKlet1FxXQlHzlXTg9CIUqnsoc,9462
|
|
10
|
-
yutipy/spotify.py,sha256=
|
|
10
|
+
yutipy/spotify.py,sha256=aKOntF5uBh_X8A779sDlRUAqihJid5NCSmEGOxTIQF0,45708
|
|
11
11
|
yutipy/yutipy_music.py,sha256=MNNh2WT-7GTAykAabLF6p4-0uXiIIbuogswmb-_QqtQ,7272
|
|
12
12
|
yutipy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
yutipy/cli/config.py,sha256=e5RIq6RxVxxzx30nKVMa06gwyQ258s7U0WA1xvJuR_0,4543
|
|
14
14
|
yutipy/cli/search.py,sha256=8SQw0bjRzRqAg-FuVz9aWjB2KBZqmCf38SyKAQ3rx5E,3025
|
|
15
15
|
yutipy/utils/__init__.py,sha256=AZaqvs6AJwnqwJuodbGnHu702WSUqc8plVC16SppOcU,239
|
|
16
16
|
yutipy/utils/helpers.py,sha256=W3g9iqoSygcFFCKCp2sk0NQrZOEG26wI2XuNi9pgAXE,5207
|
|
17
|
-
yutipy-2.2.
|
|
18
|
-
yutipy-2.2.
|
|
19
|
-
yutipy-2.2.
|
|
20
|
-
yutipy-2.2.
|
|
21
|
-
yutipy-2.2.
|
|
22
|
-
yutipy-2.2.
|
|
17
|
+
yutipy-2.2.5.dist-info/licenses/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
|
|
18
|
+
yutipy-2.2.5.dist-info/METADATA,sha256=fTeNY_lf4jWyeVv1uhY74Ak1G58R3SOr-uFJGh0hGeI,6522
|
|
19
|
+
yutipy-2.2.5.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
|
|
20
|
+
yutipy-2.2.5.dist-info/entry_points.txt,sha256=BrgmanaPjQqKQ3Ip76JLcsPgGANtrBSURf5CNIxl1HA,106
|
|
21
|
+
yutipy-2.2.5.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
|
|
22
|
+
yutipy-2.2.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|