yutipy 1.2.0__py3-none-any.whl → 1.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 +2 -0
- yutipy/deezer.py +3 -3
- yutipy/itunes.py +2 -2
- yutipy/kkbox.py +1 -1
- yutipy/models.py +33 -20
- yutipy/spotify.py +1 -1
- yutipy/utils/cheap_utils.py +44 -2
- yutipy/yutipy_music.py +122 -0
- {yutipy-1.2.0.dist-info → yutipy-1.3.0.dist-info}/METADATA +3 -2
- yutipy-1.3.0.dist-info/RECORD +16 -0
- {yutipy-1.2.0.dist-info → yutipy-1.3.0.dist-info}/WHEEL +1 -1
- yutipy-1.2.0.dist-info/RECORD +0 -15
- {yutipy-1.2.0.dist-info → yutipy-1.3.0.dist-info/licenses}/LICENSE +0 -0
- {yutipy-1.2.0.dist-info → yutipy-1.3.0.dist-info}/top_level.txt +0 -0
yutipy/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@ from .itunes import Itunes
|
|
|
3
3
|
from .models import MusicInfo
|
|
4
4
|
from .musicyt import MusicYT
|
|
5
5
|
from .spotify import Spotify
|
|
6
|
+
from .yutipy_music import YutipyMusic
|
|
6
7
|
|
|
7
8
|
__all__ = [
|
|
8
9
|
"Deezer",
|
|
@@ -10,4 +11,5 @@ __all__ = [
|
|
|
10
11
|
"MusicInfo",
|
|
11
12
|
"MusicYT",
|
|
12
13
|
"Spotify",
|
|
14
|
+
"YutipyMusic",
|
|
13
15
|
]
|
yutipy/deezer.py
CHANGED
|
@@ -28,7 +28,7 @@ class Deezer:
|
|
|
28
28
|
|
|
29
29
|
def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
|
|
30
30
|
"""Exits the runtime context related to this object."""
|
|
31
|
-
self.
|
|
31
|
+
self.close_session()
|
|
32
32
|
|
|
33
33
|
def close_session(self) -> None:
|
|
34
34
|
"""Closes the current session."""
|
|
@@ -238,7 +238,7 @@ class Deezer:
|
|
|
238
238
|
album_title=(
|
|
239
239
|
result["album"]["title"] if music_type == "track" else result["title"]
|
|
240
240
|
),
|
|
241
|
-
album_type=result.get("record_type", music_type),
|
|
241
|
+
album_type=result.get("record_type", music_type.replace("track", "single")),
|
|
242
242
|
artists=result["artist"]["name"],
|
|
243
243
|
genre=None,
|
|
244
244
|
id=result["id"],
|
|
@@ -273,4 +273,4 @@ if __name__ == "__main__":
|
|
|
273
273
|
song_name = input("Song Name: ")
|
|
274
274
|
pprint(deezer.search(artist_name, song_name))
|
|
275
275
|
finally:
|
|
276
|
-
deezer.
|
|
276
|
+
deezer.close_session()
|
yutipy/itunes.py
CHANGED
|
@@ -29,7 +29,7 @@ class Itunes:
|
|
|
29
29
|
|
|
30
30
|
def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
|
|
31
31
|
"""Exits the runtime context related to this object."""
|
|
32
|
-
self.
|
|
32
|
+
self.close_session()
|
|
33
33
|
|
|
34
34
|
def close_session(self) -> None:
|
|
35
35
|
"""Closes the current session."""
|
|
@@ -186,4 +186,4 @@ if __name__ == "__main__":
|
|
|
186
186
|
song_name = input("Song Name: ")
|
|
187
187
|
pprint(itunes.search(artist_name, song_name))
|
|
188
188
|
finally:
|
|
189
|
-
itunes.
|
|
189
|
+
itunes.close_session()
|
yutipy/kkbox.py
CHANGED
yutipy/models.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Optional
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional, Dict, Union
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@dataclass
|
|
@@ -19,8 +19,8 @@ class MusicInfo:
|
|
|
19
19
|
Name(s) of the artist(s).
|
|
20
20
|
genre : Optional[str]
|
|
21
21
|
Genre of the music.
|
|
22
|
-
id : str
|
|
23
|
-
Unique identifier for the music.
|
|
22
|
+
id : Union[int, str, Dict[str, str]]
|
|
23
|
+
Unique identifier(s) for the music from different platforms.
|
|
24
24
|
isrc : Optional[str]
|
|
25
25
|
International Standard Recording Code.
|
|
26
26
|
lyrics : Optional[str]
|
|
@@ -35,21 +35,34 @@ class MusicInfo:
|
|
|
35
35
|
Type of the music (e.g., track, album).
|
|
36
36
|
upc : Optional[str]
|
|
37
37
|
Universal Product Code.
|
|
38
|
-
url : str
|
|
39
|
-
URL to the music on
|
|
38
|
+
url : Union[str, Dict[str, str]]
|
|
39
|
+
URL(s) to the music on different platforms.
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
|
-
album_art: Optional[str]
|
|
43
|
-
album_title: Optional[str]
|
|
44
|
-
album_type: Optional[str]
|
|
45
|
-
artists: str
|
|
46
|
-
genre: Optional[str]
|
|
47
|
-
id: str
|
|
48
|
-
isrc: Optional[str]
|
|
49
|
-
lyrics: Optional[str]
|
|
50
|
-
release_date: Optional[str]
|
|
51
|
-
tempo: Optional[float]
|
|
52
|
-
title: str
|
|
53
|
-
type: Optional[str]
|
|
54
|
-
upc: Optional[str]
|
|
55
|
-
url: str
|
|
42
|
+
album_art: Optional[str] = None
|
|
43
|
+
album_title: Optional[str] = None
|
|
44
|
+
album_type: Optional[str] = None
|
|
45
|
+
artists: str = ""
|
|
46
|
+
genre: Optional[str] = None
|
|
47
|
+
id: Union[int, str, Dict[str, int]] = field(default_factory=dict)
|
|
48
|
+
isrc: Optional[str] = None
|
|
49
|
+
lyrics: Optional[str] = None
|
|
50
|
+
release_date: Optional[str] = None
|
|
51
|
+
tempo: Optional[float] = None
|
|
52
|
+
title: str = ""
|
|
53
|
+
type: Optional[str] = None
|
|
54
|
+
upc: Optional[str] = None
|
|
55
|
+
url: Union[str, Dict[str, str]] = field(default_factory=dict)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class MusicInfos(MusicInfo):
|
|
60
|
+
"""A data class to store music information from different services.
|
|
61
|
+
|
|
62
|
+
Attributes
|
|
63
|
+
----------
|
|
64
|
+
album_art_source : Optional[str]
|
|
65
|
+
The source of the album art.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
album_art_source: Optional[str] = None
|
yutipy/spotify.py
CHANGED
|
@@ -69,7 +69,7 @@ class Spotify:
|
|
|
69
69
|
|
|
70
70
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
71
71
|
"""Exits the runtime context related to this object."""
|
|
72
|
-
self.
|
|
72
|
+
self.close_session()
|
|
73
73
|
|
|
74
74
|
def close_session(self) -> None:
|
|
75
75
|
"""Closes the current session."""
|
yutipy/utils/cheap_utils.py
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from rapidfuzz import fuzz
|
|
3
|
+
from rapidfuzz.utils import default_process
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def translate_text(
|
|
7
|
+
text: str,
|
|
8
|
+
sl: str = None,
|
|
9
|
+
dl: str = "en",
|
|
10
|
+
) -> dict:
|
|
11
|
+
"""
|
|
12
|
+
Translate text from one language to another.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
text (str): The text to be translated.
|
|
16
|
+
sl (str, optional): The source language code (e.g., 'en' for English, 'es' for Spanish). If not provided, the API will attempt to detect the source language.
|
|
17
|
+
dl (str, optional): The destination language code (default is 'en' for English).
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
dict: A dictionary containing the following keys:
|
|
22
|
+
- 'source-text': The original text.
|
|
23
|
+
- 'source-language': The detected or provided source language code.
|
|
24
|
+
- 'destination-text': The translated text.
|
|
25
|
+
- 'destination-language': The destination language code.
|
|
26
|
+
"""
|
|
27
|
+
if sl:
|
|
28
|
+
url = f"https://ftapi.pythonanywhere.com/translate?sl={sl}&dl={dl}&text={text}"
|
|
29
|
+
else:
|
|
30
|
+
url = f"https://ftapi.pythonanywhere.com/translate?dl={dl}&text={text}"
|
|
31
|
+
|
|
32
|
+
response = requests.get(url)
|
|
33
|
+
response_json = response.json()
|
|
34
|
+
result = {
|
|
35
|
+
"source-text": response_json["source-text"],
|
|
36
|
+
"source-language": response_json["source-language"],
|
|
37
|
+
"destination-text": response_json["destination-text"],
|
|
38
|
+
"destination-language": response_json["destination-language"],
|
|
39
|
+
}
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
1
43
|
def are_strings_similar(str1: str, str2: str, threshold: int = 80) -> bool:
|
|
2
44
|
"""
|
|
3
45
|
Determine if two strings are similar based on a given threshold.
|
|
@@ -10,8 +52,8 @@ def are_strings_similar(str1: str, str2: str, threshold: int = 80) -> bool:
|
|
|
10
52
|
Returns:
|
|
11
53
|
bool: True if the strings are similar, otherwise False.
|
|
12
54
|
"""
|
|
13
|
-
|
|
14
|
-
|
|
55
|
+
str1 = translate_text(str1)["destination-text"]
|
|
56
|
+
str2 = translate_text(str2)["destination-text"]
|
|
15
57
|
|
|
16
58
|
similarity_score = fuzz.WRatio(str1, str2, processor=default_process)
|
|
17
59
|
return similarity_score > threshold
|
yutipy/yutipy_music.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
2
|
+
from pprint import pprint
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from yutipy.deezer import Deezer
|
|
6
|
+
from yutipy.exceptions import InvalidValueException
|
|
7
|
+
from yutipy.itunes import Itunes
|
|
8
|
+
from yutipy.kkbox import KKBox
|
|
9
|
+
from yutipy.models import MusicInfo, MusicInfos
|
|
10
|
+
from yutipy.musicyt import MusicYT
|
|
11
|
+
from yutipy.spotify import Spotify
|
|
12
|
+
from yutipy.utils.cheap_utils import is_valid_string
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class YutipyMusic:
|
|
16
|
+
"""A class that can be used to retrieve music information from all music platforms available in ``yutipy``.
|
|
17
|
+
|
|
18
|
+
This is useful when you want to get music information (especially streaming link) from all available platforms.
|
|
19
|
+
Instead of calling each service separately, you can use this class to get the information from all services at once.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
"""Initializes the YutipyMusic class."""
|
|
24
|
+
self.music_info = MusicInfos()
|
|
25
|
+
self.album_art_priority = ["deezer", "kkbox", "spotify", "musicyt", "itunes"]
|
|
26
|
+
|
|
27
|
+
def search(self, artist: str, song: str) -> Optional[MusicInfos]:
|
|
28
|
+
"""
|
|
29
|
+
Searches for a song by artist and title.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
artist : str
|
|
34
|
+
The name of the artist.
|
|
35
|
+
song : str
|
|
36
|
+
The title of the song.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
Optional[MusicInfos_]
|
|
41
|
+
The music information if found, otherwise None.
|
|
42
|
+
"""
|
|
43
|
+
if not is_valid_string(artist) or not is_valid_string(song):
|
|
44
|
+
raise InvalidValueException(
|
|
45
|
+
"Artist and song names must be valid strings and can't be empty."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
services = [
|
|
49
|
+
(Deezer, "deezer"),
|
|
50
|
+
(Itunes, "itunes"),
|
|
51
|
+
(KKBox, "kkbox"),
|
|
52
|
+
(MusicYT, "musicyt"),
|
|
53
|
+
(Spotify, "spotify"),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
with ThreadPoolExecutor() as executor:
|
|
57
|
+
futures = {
|
|
58
|
+
executor.submit(service().search, artist, song): name
|
|
59
|
+
for service, name in services
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for future in as_completed(futures):
|
|
63
|
+
service_name = futures[future]
|
|
64
|
+
result = future.result()
|
|
65
|
+
self._combine_results(result, service_name)
|
|
66
|
+
|
|
67
|
+
return self.music_info if self.music_info else None
|
|
68
|
+
|
|
69
|
+
def _combine_results(self, result: Optional[MusicInfo], service_name: str) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Combines the results from different services.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
result : Optional[MusicInfo]
|
|
76
|
+
The music information from a service.
|
|
77
|
+
service_name : str
|
|
78
|
+
The name of the streaming service.
|
|
79
|
+
"""
|
|
80
|
+
if not result:
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
attributes = [
|
|
84
|
+
"album_title",
|
|
85
|
+
"album_type",
|
|
86
|
+
"artists",
|
|
87
|
+
"genre",
|
|
88
|
+
"isrc",
|
|
89
|
+
"lyrics",
|
|
90
|
+
"release_date",
|
|
91
|
+
"tempo",
|
|
92
|
+
"title",
|
|
93
|
+
"type",
|
|
94
|
+
"upc",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
for attr in attributes:
|
|
98
|
+
if getattr(result, attr) and (
|
|
99
|
+
not getattr(self.music_info, attr) or service_name == "spotify"
|
|
100
|
+
):
|
|
101
|
+
setattr(self.music_info, attr, getattr(result, attr))
|
|
102
|
+
|
|
103
|
+
if result.album_art:
|
|
104
|
+
current_priority = self.album_art_priority.index(service_name)
|
|
105
|
+
existing_priority = (
|
|
106
|
+
self.album_art_priority.index(self.music_info.album_art_source)
|
|
107
|
+
if self.music_info.album_art_source
|
|
108
|
+
else len(self.album_art_priority)
|
|
109
|
+
)
|
|
110
|
+
if current_priority < existing_priority:
|
|
111
|
+
self.music_info.album_art = result.album_art
|
|
112
|
+
self.music_info.album_art_source = service_name
|
|
113
|
+
|
|
114
|
+
self.music_info.id[service_name] = result.id
|
|
115
|
+
self.music_info.url[service_name] = result.url
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
yutipy_music = YutipyMusic()
|
|
120
|
+
artist_name = input("Artist Name: ")
|
|
121
|
+
song_name = input("Song Name: ")
|
|
122
|
+
pprint(yutipy_music.search(artist_name, song_name))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: yutipy
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A simple package for retrieving music information from various music platforms APIs.
|
|
5
5
|
Author: Cheap Nightbot
|
|
6
6
|
Author-email: Cheap Nightbot <hi@cheapnightbot.slmail.me>
|
|
@@ -54,6 +54,7 @@ Requires-Dist: requests==2.32.3
|
|
|
54
54
|
Requires-Dist: ytmusicapi==1.10.1
|
|
55
55
|
Provides-Extra: dev
|
|
56
56
|
Requires-Dist: pytest; extra == "dev"
|
|
57
|
+
Dynamic: license-file
|
|
57
58
|
|
|
58
59
|
<p align="center">
|
|
59
60
|
<img src="https://raw.githubusercontent.com/CheapNightbot/yutipy/main/docs/_static/yutipy_header.png" alt="yutipy" />
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
yutipy/__init__.py,sha256=UIymi9iT8s1nGuqcQcFudZYNsJDR4RzXe6LUaN9V8l0,289
|
|
2
|
+
yutipy/deezer.py,sha256=eLaDNBnpdFpvdxEQrgTZiLl-FBjSs1R0CKEgpgRi0Us,8666
|
|
3
|
+
yutipy/exceptions.py,sha256=4L0Oe1PwFP34LoFTy-Fruipk7uB-JkaackRmkjlaZJU,1138
|
|
4
|
+
yutipy/itunes.py,sha256=SmW6bCGKB9ADG2FxggGDmEOvqAIvuRi7aOkBSyHm2HQ,5841
|
|
5
|
+
yutipy/kkbox.py,sha256=9sz8eb-pu-47DTP_4kgackX1ZLijnCnz7q_1M8BqLWo,12185
|
|
6
|
+
yutipy/models.py,sha256=si_qgaApAYDfSyE8cl_Yg4IfWOtxk1I5JCT8bZsmV4U,1931
|
|
7
|
+
yutipy/musicyt.py,sha256=kvRYuhlNVuvy-WahJq895T3JAzmGTTiKB89r_uTMTAI,7155
|
|
8
|
+
yutipy/spotify.py,sha256=Oaktvdz7gLe2_PaktOmjlc8QpfhZWkmQnIZDmwoXlRE,13608
|
|
9
|
+
yutipy/yutipy_music.py,sha256=YZ_5iIjHr_QA3YAm2usR7hUqJMevu2ZVSQ59Q82Axj0,4053
|
|
10
|
+
yutipy/utils/__init__.py,sha256=7UFcFZ7fBtNXOTngjnRD3MeobT3x5UT2Gag94TXVgLk,169
|
|
11
|
+
yutipy/utils/cheap_utils.py,sha256=Yl0ssVbyvrdVeSPF7PqpGJpwTYLVa4CMgLtr6uk67v0,3104
|
|
12
|
+
yutipy-1.3.0.dist-info/licenses/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
|
|
13
|
+
yutipy-1.3.0.dist-info/METADATA,sha256=p76rSDWegRztp7pl8eZ8LQh5FQsYi3aIH2eun9raX-A,6489
|
|
14
|
+
yutipy-1.3.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
15
|
+
yutipy-1.3.0.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
|
|
16
|
+
yutipy-1.3.0.dist-info/RECORD,,
|
yutipy-1.2.0.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
yutipy/__init__.py,sha256=t0w-pRLjIGD3UtDyd-JpeL-RrMona7YTV_0lZCpGLEc,232
|
|
2
|
-
yutipy/deezer.py,sha256=TbPk6f1ytrtk9GHXOoRZ948fcgOi0619-rQC9xfTYu0,8641
|
|
3
|
-
yutipy/exceptions.py,sha256=4L0Oe1PwFP34LoFTy-Fruipk7uB-JkaackRmkjlaZJU,1138
|
|
4
|
-
yutipy/itunes.py,sha256=NhrllA3U0PUY3dNoMyiG1oZuyvdMQKYRBHzYSkt8ikM,5843
|
|
5
|
-
yutipy/kkbox.py,sha256=yuyEanxxOXsiL2ZTtJMraETQxDetWfUu9K8IULSKQrQ,12186
|
|
6
|
-
yutipy/models.py,sha256=RnfWjoNO1UoNsRvNRYd-4TpkU-4jHKu5t394vIqOOgw,1360
|
|
7
|
-
yutipy/musicyt.py,sha256=kvRYuhlNVuvy-WahJq895T3JAzmGTTiKB89r_uTMTAI,7155
|
|
8
|
-
yutipy/spotify.py,sha256=CZA9XF0EV5wSrLrN9cURtiFKSEHsIvqVlXLKkvb4JEM,13609
|
|
9
|
-
yutipy/utils/__init__.py,sha256=7UFcFZ7fBtNXOTngjnRD3MeobT3x5UT2Gag94TXVgLk,169
|
|
10
|
-
yutipy/utils/cheap_utils.py,sha256=LIuEHib_97NuLahXxdHJUD9v-ccXNUc3NrLYk8EQ52A,1652
|
|
11
|
-
yutipy-1.2.0.dist-info/LICENSE,sha256=_89JsS2QnBG8tAb5-VWbJDj_uJ002zPJAYBJJdh3DPY,1071
|
|
12
|
-
yutipy-1.2.0.dist-info/METADATA,sha256=GZOfBMev_zfoZ2JWhFM60t85GkOU2rerxowo1CFqR4c,6467
|
|
13
|
-
yutipy-1.2.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
14
|
-
yutipy-1.2.0.dist-info/top_level.txt,sha256=t2A5V2_mUcfnHkbCy6tAQlb3909jDYU5GQgXtA4756I,7
|
|
15
|
-
yutipy-1.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|