yutipy 2.2.15__py3-none-any.whl → 2.3.0__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/__init__.py CHANGED
@@ -5,6 +5,7 @@ from yutipy import (
5
5
  kkbox,
6
6
  lastfm,
7
7
  logger,
8
+ lrclib,
8
9
  musicyt,
9
10
  spotify,
10
11
  yutipy_music,
@@ -17,6 +18,7 @@ __all__ = [
17
18
  "kkbox",
18
19
  "lastfm",
19
20
  "logger",
21
+ "lrclib",
20
22
  "musicyt",
21
23
  "spotify",
22
24
  "yutipy_music",
yutipy/base_clients.py CHANGED
@@ -384,6 +384,11 @@ class BaseAuthClient:
384
384
  "refresh_token": self._refresh_token,
385
385
  }
386
386
 
387
+ if not data:
388
+ raise AuthenticationException(
389
+ "Either `authorization_code` or `refresh_token` must be provided to get access token."
390
+ )
391
+
387
392
  try:
388
393
  logger.info(
389
394
  f"Authenticating with {self.SERVICE_NAME} API using Authorization Code grant type."
@@ -404,6 +409,17 @@ class BaseAuthClient:
404
409
 
405
410
  def _refresh_access_token(self):
406
411
  """Refreshes the token if it has expired."""
412
+ try:
413
+ self.load_token_after_init()
414
+ except NotImplementedError as e:
415
+ logger.warning(e)
416
+
417
+ if not self._access_token or not self._refresh_token:
418
+ logger.warning(
419
+ "No access token or refresh token found. You must authenticate to obtain a new token."
420
+ )
421
+ return
422
+
407
423
  try:
408
424
  if time() - self._token_requested_at >= self._token_expires_in:
409
425
  token_info = self._get_access_token(refresh_token=self._refresh_token)
yutipy/deezer.py CHANGED
@@ -9,6 +9,7 @@ from yutipy.exceptions import DeezerException, InvalidValueException
9
9
  from yutipy.logger import logger
10
10
  from yutipy.models import MusicInfo
11
11
  from yutipy.utils.helpers import are_strings_similar, is_valid_string
12
+ from yutipy.lrclib import LrcLib
12
13
 
13
14
 
14
15
  class Deezer:
@@ -95,7 +96,7 @@ class Deezer:
95
96
  return None
96
97
 
97
98
  try:
98
- logger.debug(f"Parsing response JSON.")
99
+ logger.debug("Parsing response JSON.")
99
100
  result = response.json()["data"]
100
101
  except (IndexError, KeyError, ValueError) as e:
101
102
  logger.warning(f"Invalid response structure from Deezer: {e}")
@@ -159,7 +160,7 @@ class Deezer:
159
160
  return None
160
161
 
161
162
  try:
162
- logger.debug(f"Parsing Response JSON.")
163
+ logger.debug("Parsing Response JSON.")
163
164
  result = response.json()
164
165
  except ValueError as e:
165
166
  logger.warning(f"Invalid response received from Deezer: {e}")
@@ -303,6 +304,13 @@ class Deezer:
303
304
  music_info.release_date = album_info.get("release_date")
304
305
  music_info.genre = album_info.get("genre")
305
306
 
307
+ with LrcLib() as lrc_lib:
308
+ lyrics = lrc_lib.get_lyrics(
309
+ artist=music_info.artists, song=music_info.title
310
+ )
311
+ if lyrics:
312
+ music_info.lyrics = lyrics.get("plainLyrics")
313
+
306
314
  return music_info
307
315
 
308
316
 
yutipy/itunes.py CHANGED
@@ -10,6 +10,7 @@ from yutipy.exceptions import InvalidValueException, ItunesException
10
10
  from yutipy.logger import logger
11
11
  from yutipy.models import MusicInfo
12
12
  from yutipy.utils.helpers import are_strings_similar, guess_album_type, is_valid_string, separate_artists
13
+ from yutipy.lrclib import LrcLib
13
14
 
14
15
 
15
16
  class Itunes:
@@ -95,7 +96,7 @@ class Itunes:
95
96
  return None
96
97
 
97
98
  try:
98
- logger.debug(f"Parsing response JSON.")
99
+ logger.debug("Parsing response JSON.")
99
100
  result = response.json()["results"]
100
101
  except (IndexError, KeyError, ValueError) as e:
101
102
  logger.warning(f"Invalid response structure from iTunes: {e}")
@@ -151,7 +152,7 @@ class Itunes:
151
152
  release_date = self._format_release_date(result["releaseDate"])
152
153
  artists = separate_artists(result["artistName"])
153
154
 
154
- return MusicInfo(
155
+ music_info = MusicInfo(
155
156
  album_art=result["artworkUrl100"],
156
157
  album_title=album_title,
157
158
  album_type=album_type.lower(),
@@ -168,6 +169,14 @@ class Itunes:
168
169
  url=result.get("trackViewUrl", result["collectionViewUrl"]),
169
170
  )
170
171
 
172
+ with LrcLib() as lrc_lib:
173
+ lyrics = lrc_lib.get_lyrics(
174
+ artist=music_info.artists, song=music_info.title
175
+ )
176
+ if lyrics:
177
+ music_info.lyrics = lyrics.get("plainLyrics")
178
+
179
+ return music_info
171
180
  return None
172
181
 
173
182
  def _extract_album_info(self, result: dict) -> tuple:
yutipy/kkbox.py CHANGED
@@ -13,6 +13,7 @@ from yutipy.exceptions import InvalidValueException, KKBoxException
13
13
  from yutipy.logger import logger
14
14
  from yutipy.models import MusicInfo
15
15
  from yutipy.utils.helpers import are_strings_similar, is_valid_string
16
+ from yutipy.lrclib import LrcLib
16
17
 
17
18
  load_dotenv()
18
19
 
@@ -256,7 +257,7 @@ class KKBox(BaseClient):
256
257
  )
257
258
 
258
259
  if matching_artists:
259
- return MusicInfo(
260
+ music_info = MusicInfo(
260
261
  album_art=track.get("album", {}).get("images", [])[2]["url"],
261
262
  album_title=track.get("album", {}).get("name"),
262
263
  album_type=None,
@@ -273,6 +274,13 @@ class KKBox(BaseClient):
273
274
  url=track.get("url"),
274
275
  )
275
276
 
277
+ with LrcLib() as lrc_lib:
278
+ lyrics = lrc_lib.get_lyrics(
279
+ artist=music_info.artists, song=music_info.title
280
+ )
281
+ if lyrics:
282
+ music_info.lyrics = lyrics.get("plainLyrics")
283
+ return music_info
276
284
  return None
277
285
 
278
286
  def _find_album(self, song: str, artist: str, album: dict) -> Optional[MusicInfo]:
yutipy/lrclib.py ADDED
@@ -0,0 +1,168 @@
1
+ __all__ = ["LrcLib"]
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+ from typing import Optional
5
+
6
+ import requests
7
+
8
+ from yutipy.exceptions import InvalidValueException
9
+ from yutipy.logger import logger
10
+ from yutipy.utils import are_strings_similar, is_valid_string
11
+
12
+
13
+ class LrcLib:
14
+ """
15
+ A class to interact with the `LRCLIB <lrclib.net>`_ API for fetching lyrics.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ app_name: str = "yutipy",
21
+ app_version: str = None,
22
+ app_url: str = "https://github.com/CheapNightbot/yutipy",
23
+ ) -> None:
24
+ """Initializes the LrcLib with the API URL and application details.
25
+
26
+ Parameters
27
+ ----------
28
+ app_name : str
29
+ The name of the application.
30
+ app_version : str, optional
31
+ The version of the application.
32
+ app_url : str, optional
33
+ The URL of the application.
34
+
35
+ Notes
36
+ -----
37
+ These are used to set the User-Agent header for requests made to the API as suggested by the API documentation of `LRCLIB <lrclib.net>`_.
38
+ """
39
+ self.api_url = "https://lrclib.net/api"
40
+ self.app_name = app_name
41
+ self.app_url = app_url
42
+ if not app_version:
43
+ try:
44
+ self.app_version = f"v{version('yutipy')}"
45
+ except PackageNotFoundError:
46
+ self.app_version = "N/A"
47
+ else:
48
+ self.app_version = app_version
49
+
50
+ self._is_session_closed = False
51
+ self.__session = requests.Session()
52
+ self.__session.headers.update(
53
+ {"User-Agent": f"{self.app_name} {self.app_version} ({self.app_url})"}
54
+ )
55
+ self._translation_session = requests.Session()
56
+
57
+ def __enter__(self):
58
+ return self
59
+
60
+ def __exit__(self, exc_type, exc_value, traceback):
61
+ self.close_session()
62
+
63
+ def close_session(self):
64
+ """
65
+ Closes the session if it is not already closed.
66
+ """
67
+ if not self._is_session_closed:
68
+ self.__session.close()
69
+ self._translation_session.close()
70
+ self._is_session_closed = True
71
+
72
+ @property
73
+ def is_session_closed(self) -> bool:
74
+ return self._is_session_closed
75
+
76
+ def get_lyrics(
77
+ self,
78
+ artist: str,
79
+ song: str,
80
+ album: str = None,
81
+ normalize_non_english: bool = True,
82
+ ) -> Optional[dict]:
83
+ """
84
+ Fetches lyrics for a given artist and song.
85
+
86
+ Parameters
87
+ ----------
88
+ artist : str
89
+ The name of the artist.
90
+ song : str
91
+ The title of the song.
92
+ album : str, optional
93
+ The title of the album.
94
+ normalize_non_english : bool, optional
95
+ Whether to normalize non-English characters for comparison (default is True).
96
+
97
+ Returns
98
+ -------
99
+ Optional[dict]
100
+ The lyrics information if found, otherwise None.
101
+ """
102
+
103
+ if not is_valid_string(artist) or not is_valid_string(song):
104
+ raise InvalidValueException(
105
+ "Artist and song names must be valid strings and can't be empty."
106
+ )
107
+
108
+ endpoint = f"{self.api_url}/search"
109
+ query = f"?artist_name={artist}&track_name={song}"
110
+ query += f"&album_name={album}" if album else ""
111
+ query_url = endpoint + query
112
+
113
+ try:
114
+ logger.info(
115
+ f"Fetching lyrics for artist: {artist}, song: {song}, album: {album}"
116
+ )
117
+ response = self.__session.get(query_url, timeout=30)
118
+ logger.debug(f"Response status code: {response.status_code}")
119
+ response.raise_for_status()
120
+ except requests.RequestException as e:
121
+ logger.warning(f"Unexpected error while fetching lyrics: {e}")
122
+ return
123
+
124
+ results = response.json()
125
+
126
+ for result in results:
127
+ if are_strings_similar(
128
+ result.get("trackName"),
129
+ song,
130
+ use_translation=normalize_non_english,
131
+ translation_session=self._translation_session,
132
+ ) and are_strings_similar(
133
+ result.get("artistName"),
134
+ artist,
135
+ use_translation=normalize_non_english,
136
+ translation_session=self._translation_session,
137
+ ):
138
+ if album:
139
+ if not are_strings_similar(
140
+ result.get("albumName"),
141
+ album,
142
+ use_translation=normalize_non_english,
143
+ translation_session=self._translation_session,
144
+ ):
145
+ continue
146
+ return result
147
+ return None
148
+
149
+
150
+ if __name__ == "__main__":
151
+ import logging
152
+
153
+ from yutipy.logger import enable_logging
154
+
155
+ enable_logging(level=logging.DEBUG)
156
+
157
+ with LrcLib() as lyric_lib:
158
+ artist_name = input("Artist Name: ").strip()
159
+ song_title = input("Song Title: ").strip()
160
+ lyrics = lyric_lib.get_lyrics(artist_name, song_title)
161
+ print(f"\nLyrics for '{song_title}' by {artist_name}:\n{'-' * 40}\n")
162
+
163
+ if lyrics:
164
+ print(lyrics.get("plainLyrics"))
165
+ else:
166
+ print(
167
+ "It seems that the lyrics were not found! You might have to guess them..."
168
+ )
yutipy/musicyt.py CHANGED
@@ -14,6 +14,7 @@ from yutipy.exceptions import (
14
14
  from yutipy.logger import logger
15
15
  from yutipy.models import MusicInfo
16
16
  from yutipy.utils.helpers import are_strings_similar, is_valid_string
17
+ from yutipy.lrclib import LrcLib
17
18
 
18
19
 
19
20
  class MusicYT:
@@ -224,12 +225,13 @@ class MusicYT:
224
225
 
225
226
  try:
226
227
  lyrics = self.ytmusic.get_lyrics(lyrics_id.get("lyrics"))
228
+ lyrics = lyrics.get("lyrics")
227
229
  except exceptions.YTMusicUserError:
228
- lyrics = {}
230
+ lyrics = None
229
231
 
230
232
  album_art = result.get("thumbnails", [{}])[-1].get("url", None)
231
233
 
232
- return MusicInfo(
234
+ music_info = MusicInfo(
233
235
  album_art=album_art,
234
236
  album_title=None,
235
237
  album_type="single",
@@ -237,7 +239,7 @@ class MusicYT:
237
239
  genre=None,
238
240
  id=video_id,
239
241
  isrc=None,
240
- lyrics=lyrics.get("lyrics"),
242
+ lyrics=lyrics,
241
243
  release_date=release_date,
242
244
  tempo=None,
243
245
  title=title,
@@ -246,6 +248,16 @@ class MusicYT:
246
248
  url=song_url,
247
249
  )
248
250
 
251
+ if not music_info.lyrics:
252
+ with LrcLib() as lrc_lib:
253
+ lyrics = lrc_lib.get_lyrics(
254
+ artist=music_info.artists, song=music_info.title
255
+ )
256
+ if lyrics:
257
+ music_info.lyrics = lyrics.get("plainLyrics")
258
+
259
+ return music_info
260
+
249
261
  def _get_album(self, result: dict) -> MusicInfo:
250
262
  """
251
263
  Return album info as a `MusicInfo` object.
yutipy/spotify.py CHANGED
@@ -22,6 +22,7 @@ from yutipy.utils.helpers import (
22
22
  is_valid_string,
23
23
  separate_artists,
24
24
  )
25
+ from yutipy.lrclib import LrcLib
25
26
 
26
27
  load_dotenv()
27
28
 
@@ -135,7 +136,7 @@ class Spotify(BaseClient):
135
136
  )
136
137
  response.raise_for_status()
137
138
  except requests.RequestException as e:
138
- logger.warning(f"Failed to search for music: {response.json()}")
139
+ logger.warning(f"Failed to search for music: {e}")
139
140
  return None
140
141
 
141
142
  artist_ids = artist_ids if artist_ids else self._get_artists_ids(artist)
@@ -199,9 +200,7 @@ class Spotify(BaseClient):
199
200
  )
200
201
  response.raise_for_status()
201
202
  except requests.RequestException as e:
202
- raise logger.warning(
203
- f"Failed to search music with ISRC/UPC: {response.json()}"
204
- )
203
+ raise logger.warning(f"Failed to search music with ISRC/UPC: {e}")
205
204
  return None
206
205
 
207
206
  artist_ids = self._get_artists_ids(artist)
@@ -328,7 +327,7 @@ class Spotify(BaseClient):
328
327
  ]
329
328
 
330
329
  if matching_artists:
331
- return MusicInfo(
330
+ music_info = MusicInfo(
332
331
  album_art=track["album"]["images"][0]["url"],
333
332
  album_title=track["album"]["name"],
334
333
  album_type=track["album"]["album_type"],
@@ -345,6 +344,13 @@ class Spotify(BaseClient):
345
344
  url=track["external_urls"]["spotify"],
346
345
  )
347
346
 
347
+ with LrcLib() as lrc_lib:
348
+ lyrics = lrc_lib.get_lyrics(
349
+ artist=music_info.artists, song=music_info.title
350
+ )
351
+ if lyrics:
352
+ music_info.lyrics = lyrics.get("plainLyrics")
353
+ return music_info
348
354
  return None
349
355
 
350
356
  def _find_album(
@@ -571,7 +577,7 @@ class SpotifyAuth(BaseAuthClient):
571
577
  )
572
578
  # Spotify returns timestamp in milliseconds, so convert milliseconds to seconds:
573
579
  timestamp = response_json.get("timestamp") / 1000.0
574
- return UserPlaying(
580
+ user_playing = UserPlaying(
575
581
  album_art=result.get("album", {}).get("images", [])[0].get("url"),
576
582
  album_title=result.get("album", {}).get("name"),
577
583
  album_type=(
@@ -594,6 +600,13 @@ class SpotifyAuth(BaseAuthClient):
594
600
  url=result.get("external_urls", {}).get("spotify"),
595
601
  )
596
602
 
603
+ with LrcLib() as lrc_lib:
604
+ lyrics = lrc_lib.get_lyrics(
605
+ artist=user_playing.artists, song=user_playing.title
606
+ )
607
+ if lyrics:
608
+ user_playing.lyrics = lyrics.get("plainLyrics")
609
+ return user_playing
597
610
  return None
598
611
 
599
612
 
yutipy/yutipy_music.py CHANGED
@@ -23,10 +23,10 @@ class YutipyMusic:
23
23
  """
24
24
 
25
25
  def __init__(
26
- self,
27
- custom_kkbox_class = KKBox,
28
- custom_spotify_class = Spotify,
29
- ) -> None:
26
+ self,
27
+ custom_kkbox_class=KKBox,
28
+ custom_spotify_class=Spotify,
29
+ ) -> None:
30
30
  """
31
31
  Initializes the YutipyMusic class.
32
32
 
@@ -201,11 +201,13 @@ if __name__ == "__main__":
201
201
  import logging
202
202
  from yutipy.logger import enable_logging
203
203
 
204
+ from dataclasses import asdict
205
+
204
206
  enable_logging(level=logging.DEBUG)
205
207
  yutipy_music = YutipyMusic()
206
208
 
207
209
  artist_name = input("Artist Name: ")
208
210
  song_name = input("Song Name: ")
209
211
 
210
- pprint(yutipy_music.search(artist_name, song_name))
212
+ pprint(asdict(yutipy_music.search(artist_name, song_name)))
211
213
  yutipy_music.close_sessions()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yutipy
3
- Version: 2.2.15
3
+ Version: 2.3.0
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>
@@ -57,6 +57,9 @@ Dynamic: license-file
57
57
  </a>
58
58
  </p>
59
59
 
60
+ > **Looking for an easy-to-use API or GUI to search for music, instead of using the CLI or building your own integration?**
61
+ > Check out [yutify](https://yutify.cheapnightbot.me) — it’s powered by yutipy!
62
+
60
63
  A _**simple**_ Python package to interact with various music platforms APIs.
61
64
 
62
65
  ## Table of Contents
@@ -0,0 +1,24 @@
1
+ yutipy/__init__.py,sha256=DjHnnzikF6QIvCVqpelYUGQalYjovs6hSf6vbfPnEqM,320
2
+ yutipy/base_clients.py,sha256=CLRCB69ZMdviluCfTU10bcwG8N3N1adpgWdYcSQonY4,22222
3
+ yutipy/deezer.py,sha256=SYzC_XDOwub-pCtF_j_cgTveeHynAg71GtDwnRjQw6I,10726
4
+ yutipy/exceptions.py,sha256=zz0XyyZr5xRcmRyw3hdTGaVRcwRn_RSYZdmwmuO0sEM,1379
5
+ yutipy/itunes.py,sha256=vVPK-KeOVXLMI69lRxUN12iUpfKjDINLkXlH21UsvIs,7718
6
+ yutipy/kkbox.py,sha256=IBEEkKh5nkmPSXB3sUnV1BWz53BBJWJUMvyu-o2knBw,11827
7
+ yutipy/lastfm.py,sha256=hOFQOZdf51Gp2m02b4NjKRmQ9yQZ9Yas6MaM78c-oCg,5970
8
+ yutipy/logger.py,sha256=GyLBlfQZ6pLNJ5MbyQSvcD_PkxmFdX41DPq5aeG1z68,1316
9
+ yutipy/lrclib.py,sha256=RxB7g4L-4cikg8Wpjlj2UyC4L_Qhwb_vO_Et0XpqoOk,5329
10
+ yutipy/models.py,sha256=45M-bNHusaAan_Ta_E9DyvsWujsT-ivbJqIfy2-i3R8,2343
11
+ yutipy/musicyt.py,sha256=ApTddFWBMX6gnSY-WbEGHrEMk3AsCfNa7AtbVUuj1z0,9858
12
+ yutipy/spotify.py,sha256=o4vSSrMzTK1Oe800vhFh1RLoylXOB-0DGV5QnhH5feM,23183
13
+ yutipy/yutipy_music.py,sha256=uIN1zeu0aJGEOcQcfFQ88I2kkhFgK8gzrGmWeFPTWNY,7328
14
+ yutipy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ yutipy/cli/config.py,sha256=e5RIq6RxVxxzx30nKVMa06gwyQ258s7U0WA1xvJuR_0,4543
16
+ yutipy/cli/search.py,sha256=8SQw0bjRzRqAg-FuVz9aWjB2KBZqmCf38SyKAQ3rx5E,3025
17
+ yutipy/utils/__init__.py,sha256=AZaqvs6AJwnqwJuodbGnHu702WSUqc8plVC16SppOcU,239
18
+ yutipy/utils/helpers.py,sha256=-iH0bx_sxW3Y3jjl6eTbY6QOBoG5t4obRcp7GGyw3ro,7476
19
+ yutipy-2.3.0.dist-info/licenses/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
20
+ yutipy-2.3.0.dist-info/METADATA,sha256=VLzLHljOSZ-XF61eVmUFyQml_DHNdy20WNJEVYV1BL8,6577
21
+ yutipy-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ yutipy-2.3.0.dist-info/entry_points.txt,sha256=BrgmanaPjQqKQ3Ip76JLcsPgGANtrBSURf5CNIxl1HA,106
23
+ yutipy-2.3.0.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
24
+ yutipy-2.3.0.dist-info/RECORD,,
@@ -1,23 +0,0 @@
1
- yutipy/__init__.py,sha256=Zrw3cr_6khXp1IgQdZxGcUM9A64GYgPs-6rlqSukW5Q,294
2
- yutipy/base_clients.py,sha256=FHCyCUQ-qE2Jo5JH-DZCxupoZTlb5ADs8XKDbHDVHwA,21687
3
- yutipy/deezer.py,sha256=ZI1C5gam8NiNznyyagn5r0Potpg25MXja8UXg-9i9ug,10463
4
- yutipy/exceptions.py,sha256=zz0XyyZr5xRcmRyw3hdTGaVRcwRn_RSYZdmwmuO0sEM,1379
5
- yutipy/itunes.py,sha256=dOkz7RqUCIaGggrNWOyxJebrv0f-mF1s9VOG2PVuFbY,7394
6
- yutipy/kkbox.py,sha256=Pfx-ZgAI9F1cbxjr7MCsMi-QulNt67t60L7y9lNmo5g,11503
7
- yutipy/lastfm.py,sha256=hOFQOZdf51Gp2m02b4NjKRmQ9yQZ9Yas6MaM78c-oCg,5970
8
- yutipy/logger.py,sha256=GyLBlfQZ6pLNJ5MbyQSvcD_PkxmFdX41DPq5aeG1z68,1316
9
- yutipy/models.py,sha256=45M-bNHusaAan_Ta_E9DyvsWujsT-ivbJqIfy2-i3R8,2343
10
- yutipy/musicyt.py,sha256=n3yaH9qyGlsW2HKPAmqYQNGzhuDH4s5Gh0R5v4JPoeg,9472
11
- yutipy/spotify.py,sha256=RQvzP62-bIXCLhMocRNFgst0xwDliFLYxU8nzdeWxRo,22616
12
- yutipy/yutipy_music.py,sha256=MNNh2WT-7GTAykAabLF6p4-0uXiIIbuogswmb-_QqtQ,7272
13
- yutipy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- yutipy/cli/config.py,sha256=e5RIq6RxVxxzx30nKVMa06gwyQ258s7U0WA1xvJuR_0,4543
15
- yutipy/cli/search.py,sha256=8SQw0bjRzRqAg-FuVz9aWjB2KBZqmCf38SyKAQ3rx5E,3025
16
- yutipy/utils/__init__.py,sha256=AZaqvs6AJwnqwJuodbGnHu702WSUqc8plVC16SppOcU,239
17
- yutipy/utils/helpers.py,sha256=-iH0bx_sxW3Y3jjl6eTbY6QOBoG5t4obRcp7GGyw3ro,7476
18
- yutipy-2.2.15.dist-info/licenses/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
19
- yutipy-2.2.15.dist-info/METADATA,sha256=jUEpFLxQnWbxKn4FVOFNF4hs2avjkoVRQhemNHNYl_4,6369
20
- yutipy-2.2.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- yutipy-2.2.15.dist-info/entry_points.txt,sha256=BrgmanaPjQqKQ3Ip76JLcsPgGANtrBSURf5CNIxl1HA,106
22
- yutipy-2.2.15.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
23
- yutipy-2.2.15.dist-info/RECORD,,