yutipy 2.3.6__py3-none-any.whl → 2.4.1__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 CHANGED
@@ -71,6 +71,11 @@ class LastFm:
71
71
  """
72
72
  Fetches the user profile information for the provided username.
73
73
 
74
+ Parameters
75
+ ----------
76
+ username : str
77
+ The Last.fm username to fetch profile information for.
78
+
74
79
  Returns
75
80
  -------
76
81
  dict
@@ -109,7 +114,7 @@ class LastFm:
109
114
 
110
115
  def get_currently_playing(self, username: str) -> Optional[UserPlaying]:
111
116
  """
112
- Fetches information about the currently playing or most recent track for a user.
117
+ Fetches information about the currently playing track for a user.
113
118
 
114
119
  Parameters
115
120
  ----------
yutipy/listenbrainz.py ADDED
@@ -0,0 +1,141 @@
1
+ __all__ = ["ListenBrainz"]
2
+
3
+ from typing import Optional
4
+ from time import time
5
+
6
+ import requests
7
+
8
+ from yutipy.logger import logger
9
+ from yutipy.models import UserPlaying
10
+ from yutipy.utils.helpers import are_strings_similar
11
+
12
+
13
+ class ListenBrainz:
14
+ """A class to interact with the ListenBrainz API for fetching user music data."""
15
+
16
+ def __init__(self):
17
+ self._is_session_closed = False
18
+ self.__api_url = "https://api.listenbrainz.org"
19
+ self.__session = requests.Session()
20
+
21
+ def __enter__(self):
22
+ return self
23
+
24
+ def __exit__(self, exc_type, exc_value, exc_traceback):
25
+ self.close_session()
26
+
27
+ def close_session(self):
28
+ """Closes the current session(s)."""
29
+ if not self._is_session_closed:
30
+ self.__session.close()
31
+ self._is_session_closed = True
32
+
33
+ @property
34
+ def is_session_closed(self) -> bool:
35
+ """Checks if the session is closed."""
36
+ return self._is_session_closed
37
+
38
+ def find_user(self, username: str) -> Optional[str]:
39
+ """
40
+ Whether profile with the provided username exists on the ListenBrainz.
41
+
42
+ It searches ListenBrainz for the provided username and fuzzy matches
43
+ the provided username with the username(s) returned from ListenBrainz API.
44
+
45
+ Parameters
46
+ ----------
47
+ username : str
48
+ The username to search.
49
+
50
+ Returns
51
+ -------
52
+ Optional[str]
53
+ The username returned by ListenBrainz API if user found,
54
+ or ``None`` if the user with provided username does not exist.
55
+ """
56
+ endpoint = "/1/search/users/"
57
+ url = self.__api_url + endpoint
58
+
59
+ try:
60
+ response = self.__session.get(
61
+ url=url, params={"search_term": username}, timeout=30
62
+ )
63
+ response.raise_for_status()
64
+ except requests.RequestException as e:
65
+ logger.warning(f"Failed to search for the user: {e}")
66
+ return
67
+
68
+ response_json = response.json()
69
+ users = response_json.get("users")
70
+
71
+ if not users:
72
+ return
73
+
74
+ for user in users:
75
+ user_name = user.get("user_name")
76
+ if are_strings_similar(
77
+ username, user_name, threshold=100, use_translation=False
78
+ ):
79
+ return user_name
80
+
81
+ def get_currently_playing(self, username: str) -> Optional[UserPlaying]:
82
+ """
83
+ Fetches information about the currently playing track for a user.
84
+
85
+ Parameters
86
+ ----------
87
+ username : str
88
+ The ListenBrainz username to fetch data for.
89
+
90
+ Returns
91
+ -------
92
+ Optional[UserPlaying_]
93
+ An instance of the ``UserPlaying`` model containing details about the currently
94
+ playing track if available, or ``None`` if the request fails or no data is available.
95
+ """
96
+ endpoint = f"/1/user/{username}/playing-now"
97
+ url = self.__api_url + endpoint
98
+
99
+ try:
100
+ response = self.__session.get(url=url, timeout=30)
101
+ response.raise_for_status()
102
+ except requests.RequestException as e:
103
+ logger.warning(f"Failed to retrieve listening activity: {e}")
104
+ return
105
+
106
+ response_json = response.json()
107
+ if not response_json:
108
+ logger.info(
109
+ f"It seems no activity found for `{username}`. Might be user does not exist or not no data available."
110
+ )
111
+ return
112
+
113
+ result = response_json.get("payload", {})
114
+ is_playing = result.get("playing_now", False)
115
+ user_id = result.get("user_id", "")
116
+
117
+ if username.lower() != user_id.lower():
118
+ return
119
+
120
+ if result and is_playing:
121
+ track_metadata = result.get("listens", [{}])[0].get("track_metadata", {})
122
+ return UserPlaying(
123
+ artists=track_metadata.get("artist_name"),
124
+ id=None,
125
+ timestamp=time(),
126
+ title=track_metadata.get("track_name"),
127
+ url=track_metadata.get("additional_info", {}).get("origin_url"),
128
+ is_playing=is_playing,
129
+ )
130
+ return None
131
+
132
+
133
+ if __name__ == "__main__":
134
+ with ListenBrainz() as listenbrainz:
135
+ username = input("Enter ListenBrainz Username: ").strip()
136
+ result = listenbrainz.find_user(username)
137
+
138
+ if result:
139
+ print(f"User found with username: {result}")
140
+ else:
141
+ print(f"No user with username '{username}' found!")
yutipy/utils/helpers.py CHANGED
@@ -76,7 +76,7 @@ def are_strings_similar(
76
76
  Args:
77
77
  str1 (str): First string to compare.
78
78
  str2 (str): Second string to compare.
79
- threshold (int, optional): Similarity threshold. Defaults to 80.
79
+ threshold (int, optional): Similarity threshold. Defaults to 95.
80
80
  use_translation (bool, optional): Use translations to compare strings. Defaults to ``True``
81
81
  translation_session (requests.Session, optional): A `requests.Session` object to use for making the API request. If not provided, a new session will be created and closed within the function.
82
82
  Providing your own session can improve performance by reusing the same session for multiple requests. Don't forget to close the session afterwards.
@@ -207,3 +207,4 @@ def guess_album_type(total_tracks: int):
207
207
  return "ep"
208
208
  if total_tracks >= 7:
209
209
  return "album"
210
+ return "unknown"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yutipy
3
- Version: 2.3.6
3
+ Version: 2.4.1
4
4
  Summary: A simple Python package to interact with various music platforms APIs.
5
5
  Author: Cheap Nightbot
6
6
  Author-email: Cheap Nightbot <hi@cheapnightbot.slmail.me>
@@ -92,6 +92,7 @@ Feel free to request any music platform you would like me to add by opening an i
92
92
  - `iTunes`: https://music.apple.com
93
93
  - `KKBOX`: https://www.kkbox.com
94
94
  - `Lastfm`: https://last.fm
95
+ - `ListenBrainz`: https://listenbrainz.org
95
96
  - `Spotify`: https://spotify.com
96
97
  - `YouTube Music`: https://music.youtube.com
97
98
 
@@ -4,7 +4,8 @@ yutipy/deezer.py,sha256=WzqC4ylZo7VmbyAfVZarvaqmnlamdEge3ceY_OJ9h4Y,11021
4
4
  yutipy/exceptions.py,sha256=zz0XyyZr5xRcmRyw3hdTGaVRcwRn_RSYZdmwmuO0sEM,1379
5
5
  yutipy/itunes.py,sha256=_iWq1COXNAiLIrUtwfI1jNey9dXe3-gCzoRF0eGgTs8,8038
6
6
  yutipy/kkbox.py,sha256=nvclRgGqQHUzIS_zhbJp_PJHiWmkU3nzwLAUhdaPCLI,12103
7
- yutipy/lastfm.py,sha256=2CQJfHtJyrg0qQ_kVdG2v18n6K8nbnBEKgrLagLtIf8,5775
7
+ yutipy/lastfm.py,sha256=UylKs6yHOvnSguW7KJHx5yzPuOUXP3haqm_5TdOQKLA,5889
8
+ yutipy/listenbrainz.py,sha256=dhTy5sFHmMUGdp6MyDQtNH2xSKMobq7PzQqyWDi7YXw,4518
8
9
  yutipy/logger.py,sha256=GyLBlfQZ6pLNJ5MbyQSvcD_PkxmFdX41DPq5aeG1z68,1316
9
10
  yutipy/lrclib.py,sha256=7EFoCZ7kF2blbwA81tzyCrmIIvuuWsE2aO3SfNyyWEc,5220
10
11
  yutipy/models.py,sha256=45M-bNHusaAan_Ta_E9DyvsWujsT-ivbJqIfy2-i3R8,2343
@@ -15,10 +16,10 @@ yutipy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  yutipy/cli/config.py,sha256=e5RIq6RxVxxzx30nKVMa06gwyQ258s7U0WA1xvJuR_0,4543
16
17
  yutipy/cli/search.py,sha256=8SQw0bjRzRqAg-FuVz9aWjB2KBZqmCf38SyKAQ3rx5E,3025
17
18
  yutipy/utils/__init__.py,sha256=AZaqvs6AJwnqwJuodbGnHu702WSUqc8plVC16SppOcU,239
18
- yutipy/utils/helpers.py,sha256=UDhRIFC-WeAFwmGAkluqdcLMWTpFZPgp2EnNo7kiPe4,7413
19
- yutipy-2.3.6.dist-info/licenses/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
20
- yutipy-2.3.6.dist-info/METADATA,sha256=9_hS1xLfe0cKE4vmmRKzr0RodSKK-qO4WyzCnas41qw,6577
21
- yutipy-2.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- yutipy-2.3.6.dist-info/entry_points.txt,sha256=BrgmanaPjQqKQ3Ip76JLcsPgGANtrBSURf5CNIxl1HA,106
23
- yutipy-2.3.6.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
24
- yutipy-2.3.6.dist-info/RECORD,,
19
+ yutipy/utils/helpers.py,sha256=ed5zjBKs8SGNnkluC30XqiigelHcwiZsQLdT26slqqw,7434
20
+ yutipy-2.4.1.dist-info/licenses/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
21
+ yutipy-2.4.1.dist-info/METADATA,sha256=YbN9snMp306-xvm09UyNb4KBNVSD2rPtQyOQbc97zhY,6620
22
+ yutipy-2.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ yutipy-2.4.1.dist-info/entry_points.txt,sha256=BrgmanaPjQqKQ3Ip76JLcsPgGANtrBSURf5CNIxl1HA,106
24
+ yutipy-2.4.1.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
25
+ yutipy-2.4.1.dist-info/RECORD,,
File without changes