yutipy 1.4.2__tar.gz → 1.5.0__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.

Potentially problematic release.


This version of yutipy might be problematic. Click here for more details.

Files changed (61) hide show
  1. {yutipy-1.4.2 → yutipy-1.5.0}/PKG-INFO +39 -28
  2. {yutipy-1.4.2 → yutipy-1.5.0}/README.md +35 -3
  3. yutipy-1.5.0/docs/cli.rst +38 -0
  4. {yutipy-1.4.2 → yutipy-1.5.0}/docs/faq.rst +22 -0
  5. {yutipy-1.4.2 → yutipy-1.5.0}/docs/index.rst +2 -1
  6. {yutipy-1.4.2 → yutipy-1.5.0}/docs/usage_examples.rst +13 -4
  7. {yutipy-1.4.2 → yutipy-1.5.0}/pyproject.toml +7 -3
  8. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/__init__.py +2 -2
  9. yutipy-1.5.0/yutipy/cli/__init__.py +0 -0
  10. yutipy-1.5.0/yutipy/cli/config.py +86 -0
  11. yutipy-1.5.0/yutipy/cli/search.py +104 -0
  12. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/deezer.py +36 -5
  13. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/itunes.py +18 -1
  14. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/kkbox.py +16 -1
  15. yutipy-1.5.0/yutipy/logging.py +3 -0
  16. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/models.py +1 -1
  17. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/musicyt.py +22 -1
  18. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/spotify.py +17 -1
  19. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/utils/__init__.py +1 -1
  20. yutipy-1.4.2/yutipy/utils/cheap_utils.py → yutipy-1.5.0/yutipy/utils/helpers.py +0 -4
  21. yutipy-1.5.0/yutipy/utils/logger.py +39 -0
  22. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/yutipy_music.py +39 -35
  23. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/PKG-INFO +39 -28
  24. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/SOURCES.txt +7 -1
  25. yutipy-1.5.0/yutipy.egg-info/entry_points.txt +3 -0
  26. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/requires.txt +1 -0
  27. yutipy-1.4.2/yutipy/utils/logger.py +0 -4
  28. {yutipy-1.4.2 → yutipy-1.5.0}/.gitattributes +0 -0
  29. {yutipy-1.4.2 → yutipy-1.5.0}/.github/FUNDING.yml +0 -0
  30. {yutipy-1.4.2 → yutipy-1.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  31. {yutipy-1.4.2 → yutipy-1.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  32. {yutipy-1.4.2 → yutipy-1.5.0}/.github/workflows/pytest-unit-testing.yml +0 -0
  33. {yutipy-1.4.2 → yutipy-1.5.0}/.github/workflows/release.yml +0 -0
  34. {yutipy-1.4.2 → yutipy-1.5.0}/.gitignore +0 -0
  35. {yutipy-1.4.2 → yutipy-1.5.0}/.readthedocs.yaml +0 -0
  36. {yutipy-1.4.2 → yutipy-1.5.0}/LICENSE +0 -0
  37. {yutipy-1.4.2 → yutipy-1.5.0}/MANIFEST.in +0 -0
  38. {yutipy-1.4.2 → yutipy-1.5.0}/docs/Makefile +0 -0
  39. {yutipy-1.4.2 → yutipy-1.5.0}/docs/_static/yutipy_header.png +0 -0
  40. {yutipy-1.4.2 → yutipy-1.5.0}/docs/_static/yutipy_logo.png +0 -0
  41. {yutipy-1.4.2 → yutipy-1.5.0}/docs/api_reference.rst +0 -0
  42. {yutipy-1.4.2 → yutipy-1.5.0}/docs/available_platforms.rst +0 -0
  43. {yutipy-1.4.2 → yutipy-1.5.0}/docs/conf.py +0 -0
  44. {yutipy-1.4.2 → yutipy-1.5.0}/docs/installation.rst +0 -0
  45. {yutipy-1.4.2 → yutipy-1.5.0}/docs/make.bat +0 -0
  46. {yutipy-1.4.2 → yutipy-1.5.0}/docs/requirements.txt +0 -0
  47. {yutipy-1.4.2 → yutipy-1.5.0}/requirements-dev.txt +0 -0
  48. {yutipy-1.4.2 → yutipy-1.5.0}/requirements.txt +0 -0
  49. {yutipy-1.4.2 → yutipy-1.5.0}/setup.cfg +0 -0
  50. {yutipy-1.4.2 → yutipy-1.5.0}/tests/__init__.py +0 -0
  51. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_deezer.py +0 -0
  52. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_itunes.py +0 -0
  53. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_kkbox.py +0 -0
  54. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_models.py +0 -0
  55. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_musicyt.py +0 -0
  56. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_spotify.py +0 -0
  57. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_utils.py +0 -0
  58. {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_yutipy_music.py +0 -0
  59. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/exceptions.py +0 -0
  60. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/dependency_links.txt +0 -0
  61. {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/top_level.txt +0 -0
@@ -1,39 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yutipy
3
- Version: 1.4.2
3
+ Version: 1.5.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>
7
7
  Maintainer-email: Cheap Nightbot <hi@cheapnightbot.slmail.me>
8
- License: MIT License
9
-
10
- Copyright (c) 2025 Cheap Nightbot
11
-
12
- Permission is hereby granted, free of charge, to any person obtaining a copy
13
- of this software and associated documentation files (the "Software"), to deal
14
- in the Software without restriction, including without limitation the rights
15
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
- copies of the Software, and to permit persons to whom the Software is
17
- furnished to do so, subject to the following conditions:
18
-
19
- The above copyright notice and this permission notice shall be included in all
20
- copies or substantial portions of the Software.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
- SOFTWARE.
29
-
8
+ License-Expression: MIT
30
9
  Project-URL: Homepage, https://github.com/CheapNightbot/yutipy
31
10
  Project-URL: Documentation, https://yutipy.readthedocs.io/
32
11
  Project-URL: Repository, https://github.com/CheapNightbot/yutipy.git
33
12
  Project-URL: Issues, https://github.com/CheapNightbot/yutipy/issues
34
13
  Project-URL: Changelog, https://github.com/CheapNightbot/yutipy/blob/master/CHANGELOG.md
35
14
  Project-URL: funding, https://ko-fi.com/cheapnightbot
36
- Keywords: music,API,Deezer,iTunes,Spotify,YouTube Music,search,retrieve,information,yutify,KKBox
15
+ Keywords: music,API,Deezer,iTunes,KKBox,Spotify,YouTube Music,search,retrieve,information,yutify
37
16
  Classifier: Development Status :: 4 - Beta
38
17
  Classifier: Intended Audience :: Developers
39
18
  Classifier: Topic :: Software Development :: Libraries
@@ -43,11 +22,11 @@ Classifier: Programming Language :: Python :: 3.9
43
22
  Classifier: Programming Language :: Python :: 3.10
44
23
  Classifier: Programming Language :: Python :: 3.11
45
24
  Classifier: Programming Language :: Python :: 3.12
46
- Classifier: License :: OSI Approved :: MIT License
47
25
  Classifier: Operating System :: OS Independent
48
26
  Requires-Python: >=3.8
49
27
  Description-Content-Type: text/markdown
50
28
  License-File: LICENSE
29
+ Requires-Dist: pykakasi==2.3.0
51
30
  Requires-Dist: python-dotenv==1.0.1
52
31
  Requires-Dist: rapidfuzz==3.12.1
53
32
  Requires-Dist: requests==2.32.3
@@ -89,6 +68,10 @@ A _**simple**_ Python package for searching and retrieving music information fro
89
68
  - [Available Music Platforms](#available-music-platforms)
90
69
  - [Installation](#installation)
91
70
  - [Usage Example](#usage-example)
71
+ - [Command-Line Interface (CLI)](#command-line-interface-cli)
72
+ - [Search for Music](#search-for-music)
73
+ - [Options](#options)
74
+ - [Configuration Wizard](#configuration-wizard)
92
75
  - [Contributing](#contributing)
93
76
  - [License](#license)
94
77
 
@@ -120,9 +103,7 @@ pip install -U yutipy
120
103
 
121
104
  ## Usage Example
122
105
 
123
- Here's a quick example of how to use the `yutipy` package to search for a song:
124
-
125
- ### Deezer
106
+ Here's a quick example of how to use the `yutipy` package to search for a song on **Deezer**:
126
107
 
127
108
  ```python
128
109
  from yutipy.deezer import Deezer
@@ -134,6 +115,36 @@ with Deezer() as deezer:
134
115
 
135
116
  For more usage examples, see the [Usage Examples](https://yutipy.readthedocs.io/en/latest/usage_examples.html) page in docs.
136
117
 
118
+ ## Command-Line Interface (CLI)
119
+
120
+ The `yutipy` package includes a CLI tool that allows you to search for music directly from the command line and configure API keys interactively.
121
+
122
+ ### Search for Music
123
+
124
+ You can use the CLI tool to search for music across multiple platforms:
125
+
126
+ ```bash
127
+ yutipy-cli "Rick Astley" "Never Gonna Give You Up" --limit 3 --normalize
128
+ ```
129
+
130
+ #### Options:
131
+ - `artist` (required): The name of the artist.
132
+ - `song` (required): The title of the song.
133
+ - `--limit`: The number of results to retrieve (default: 5).
134
+ - `--normalize`: Normalize non-English characters for comparison.
135
+ - `--verbose`: Enable logging in the terminal.
136
+ - `--service`: Specify a single service to search (e.g., `deezer`, `spotify`, `itunes`).
137
+
138
+ ### Configuration Wizard
139
+
140
+ To set up your API keys interactively, use the configuration wizard:
141
+
142
+ ```bash
143
+ yutipy-config
144
+ ```
145
+
146
+ The wizard will guide you through obtaining and setting up API keys for supported services like Spotify and KKBOX. If the required environment variables are already set, the wizard will skip those steps.
147
+
137
148
  ## Contributing
138
149
 
139
150
  Contributions are welcome! Please follow these steps:
@@ -31,6 +31,10 @@ A _**simple**_ Python package for searching and retrieving music information fro
31
31
  - [Available Music Platforms](#available-music-platforms)
32
32
  - [Installation](#installation)
33
33
  - [Usage Example](#usage-example)
34
+ - [Command-Line Interface (CLI)](#command-line-interface-cli)
35
+ - [Search for Music](#search-for-music)
36
+ - [Options](#options)
37
+ - [Configuration Wizard](#configuration-wizard)
34
38
  - [Contributing](#contributing)
35
39
  - [License](#license)
36
40
 
@@ -62,9 +66,7 @@ pip install -U yutipy
62
66
 
63
67
  ## Usage Example
64
68
 
65
- Here's a quick example of how to use the `yutipy` package to search for a song:
66
-
67
- ### Deezer
69
+ Here's a quick example of how to use the `yutipy` package to search for a song on **Deezer**:
68
70
 
69
71
  ```python
70
72
  from yutipy.deezer import Deezer
@@ -76,6 +78,36 @@ with Deezer() as deezer:
76
78
 
77
79
  For more usage examples, see the [Usage Examples](https://yutipy.readthedocs.io/en/latest/usage_examples.html) page in docs.
78
80
 
81
+ ## Command-Line Interface (CLI)
82
+
83
+ The `yutipy` package includes a CLI tool that allows you to search for music directly from the command line and configure API keys interactively.
84
+
85
+ ### Search for Music
86
+
87
+ You can use the CLI tool to search for music across multiple platforms:
88
+
89
+ ```bash
90
+ yutipy-cli "Rick Astley" "Never Gonna Give You Up" --limit 3 --normalize
91
+ ```
92
+
93
+ #### Options:
94
+ - `artist` (required): The name of the artist.
95
+ - `song` (required): The title of the song.
96
+ - `--limit`: The number of results to retrieve (default: 5).
97
+ - `--normalize`: Normalize non-English characters for comparison.
98
+ - `--verbose`: Enable logging in the terminal.
99
+ - `--service`: Specify a single service to search (e.g., `deezer`, `spotify`, `itunes`).
100
+
101
+ ### Configuration Wizard
102
+
103
+ To set up your API keys interactively, use the configuration wizard:
104
+
105
+ ```bash
106
+ yutipy-config
107
+ ```
108
+
109
+ The wizard will guide you through obtaining and setting up API keys for supported services like Spotify and KKBOX. If the required environment variables are already set, the wizard will skip those steps.
110
+
79
111
  ## Contributing
80
112
 
81
113
  Contributions are welcome! Please follow these steps:
@@ -0,0 +1,38 @@
1
+ ========================
2
+ Command-Line Interface
3
+ ========================
4
+
5
+ The **yutipy** package includes two CLI tools:
6
+
7
+ 1. **yutipy-cli**: Search for music across multiple platforms.
8
+ 2. **yutipy-config**: Configure API keys interactively.
9
+
10
+ yutipy-cli
11
+ ----------
12
+
13
+ Search for music directly from the command line:
14
+
15
+ .. code-block:: bash
16
+
17
+ yutipy-cli "Rick Astley" "Never Gonna Give You Up" --limit 3 --normalize
18
+
19
+ Options:
20
+ ^^^^^^^^
21
+
22
+ - **artist** (required): The name of the artist.
23
+ - **song** (required): The title of the song.
24
+ - **\-\-limit**: The number of results to retrieve (default: 5).
25
+ - **\-\-normalize**: Normalize non-English characters for comparison.
26
+ - **\-\-verbose**: Enable logging in the terminal.
27
+ - **\-\-service**: Specify a single service to search (e.g., ``deezer``, ``spotify``, ``itunes``).
28
+
29
+ yutipy-config
30
+ -------------
31
+
32
+ Set up API keys interactively:
33
+
34
+ .. code-block:: bash
35
+
36
+ yutipy-config
37
+
38
+ The wizard will guide you through obtaining and setting up API keys for supported services like Spotify and KKBOX.
@@ -70,6 +70,28 @@ Why am I receiving a ``KKBoxException`` when trying to use the ``KKBox`` class?
70
70
  Unfortunately, it's the same case as with Spotify. You will need a ``Client ID`` and ``Client Secret``
71
71
  obtained from the KKBOX for Developers website. Please visit https://developer.kkbox.com/ for more information.
72
72
 
73
+ How do I use the CLI tool to search for music?
74
+ ----------------------------------------------
75
+
76
+ You can use the CLI tool to search for music directly from the command line. For example:
77
+
78
+ .. code-block:: bash
79
+
80
+ yutipy-cli "Rick Astley" "Never Gonna Give You Up" --limit 3 --normalize
81
+
82
+ For more details, see the :doc:`usage_examples`.
83
+
84
+ How do I set up API keys for the library?
85
+ -----------------------------------------
86
+
87
+ You can use the configuration wizard to set up API keys interactively:
88
+
89
+ .. code-block:: bash
90
+
91
+ yutipy-config
92
+
93
+ The wizard will guide you through obtaining and setting up API keys for supported services like Spotify and KKBOX.
94
+
73
95
  ----
74
96
 
75
97
  .. [#] There may be additional features in the future.
@@ -45,8 +45,9 @@ Get Started
45
45
 
46
46
  installation
47
47
  available_platforms
48
- api_reference
49
48
  usage_examples
49
+ api_reference
50
+ cli
50
51
  faq
51
52
 
52
53
  .. toctree::
@@ -5,11 +5,20 @@ Usage Examples
5
5
  Here's a quick example of how to use the **yutipy** package to search for a song:
6
6
 
7
7
  .. important::
8
- All examples here—except for the `YouTube Music`_—use the ``with`` context manager to initialize an instance of the respective class,
8
+ All examples here use the ``with`` context manager to initialize an instance of the respective class,
9
9
  as those classes internally use ``requests.Session()`` for making requests to APIs.
10
10
  This approach ensures that the session is automatically closed once you exit the context. Although using ``with`` is not mandatory,
11
11
  if you instantiate an object without it, you are responsible for closing the session after use by calling the ``close_session()`` method on that object.
12
12
 
13
+ CLI Tool
14
+ --------
15
+
16
+ You can use the CLI tool to search for music directly from the command line:
17
+
18
+ .. code-block:: bash
19
+
20
+ yutipy-cli "Rick Astley" "Never Gonna Give You Up" --limit 3 --normalize
21
+
13
22
  Deezer
14
23
  ------
15
24
 
@@ -107,9 +116,9 @@ YouTube Music
107
116
 
108
117
  from yutipy.musicyt import MusicYT
109
118
 
110
- music_yt = MusicYT()
111
- result = music_yt.search("Artist Name", "Song Title")
112
- print(result)
119
+ with MusicYT() as music_yt:
120
+ result = music_yt.search("Artist Name", "Song Title")
121
+ print(result)
113
122
 
114
123
  Yutipy Music
115
124
  ------------
@@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "yutipy"
7
7
  dynamic = ["version"]
8
8
  dependencies = [
9
+ "pykakasi==2.3.0",
9
10
  "python-dotenv==1.0.1",
10
11
  "rapidfuzz==3.12.1",
11
12
  "requests==2.32.3",
@@ -21,8 +22,8 @@ maintainers = [
21
22
  ]
22
23
  description = "A simple package for retrieving music information from various music platforms APIs."
23
24
  readme = "README.md"
24
- license = {file = "LICENSE"}
25
- keywords = ["music", "API", "Deezer", "iTunes", "Spotify", "YouTube Music", "search", "retrieve", "information", "yutify", "KKBox"]
25
+ license = "MIT"
26
+ keywords = ["music", "API", "Deezer", "iTunes", "KKBox", "Spotify", "YouTube Music", "search", "retrieve", "information", "yutify"]
26
27
  classifiers = [
27
28
  "Development Status :: 4 - Beta",
28
29
  "Intended Audience :: Developers",
@@ -33,7 +34,6 @@ classifiers = [
33
34
  "Programming Language :: Python :: 3.10",
34
35
  "Programming Language :: Python :: 3.11",
35
36
  "Programming Language :: Python :: 3.12",
36
- "License :: OSI Approved :: MIT License",
37
37
  "Operating System :: OS Independent",
38
38
  ]
39
39
 
@@ -53,3 +53,7 @@ funding = "https://ko-fi.com/cheapnightbot"
53
53
  [tool.setuptools_scm]
54
54
  version_scheme = "guess-next-dev"
55
55
  local_scheme = "no-local-version"
56
+
57
+ [project.scripts]
58
+ yutipy-cli = "yutipy.cli.search:main"
59
+ yutipy-config = "yutipy.cli.config:run_config_wizard"
@@ -1,17 +1,17 @@
1
1
  from .deezer import Deezer
2
2
  from .itunes import Itunes
3
3
  from .kkbox import KKBox
4
- from .models import MusicInfo
5
4
  from .musicyt import MusicYT
6
5
  from .spotify import Spotify
7
6
  from .yutipy_music import YutipyMusic
7
+ from . import exceptions
8
8
 
9
9
  __all__ = [
10
10
  "Deezer",
11
11
  "Itunes",
12
12
  "KKBox",
13
- "MusicInfo",
14
13
  "MusicYT",
15
14
  "Spotify",
16
15
  "YutipyMusic",
16
+ "exceptions"
17
17
  ]
File without changes
@@ -0,0 +1,86 @@
1
+ import os
2
+ import webbrowser
3
+ from dotenv import load_dotenv, set_key
4
+
5
+
6
+ def run_config_wizard():
7
+ """Interactive configuration wizard for setting up API keys."""
8
+ print("Welcome to the yutipy Configuration Wizard!")
9
+ print("This wizard will help you set up your API keys for various services.\n")
10
+
11
+ # Load existing .env file if it exists
12
+ env_file = ".env"
13
+ load_dotenv(env_file)
14
+
15
+ # List of required environment variables and their instructions
16
+ required_vars = {
17
+ "SPOTIFY_CLIENT_ID": {
18
+ "description": "Spotify Client ID",
19
+ "url": "https://developer.spotify.com/dashboard",
20
+ "instructions": """
21
+ 1. Go to your Spotify Developer Dashboard: https://developer.spotify.com/dashboard
22
+ 2. Create a new app and fill in the required details.
23
+ 3. Copy the "Client ID" and "Client Secret" from the app's settings.
24
+ 4. Paste them here when prompted.
25
+ """,
26
+ },
27
+ "SPOTIFY_CLIENT_SECRET": {
28
+ "description": "Spotify Client Secret",
29
+ "url": "https://developer.spotify.com/dashboard",
30
+ "instructions": "See the steps above for Spotify Client ID.",
31
+ },
32
+ "KKBOX_CLIENT_ID": {
33
+ "description": "KKBox Client ID",
34
+ "url": "https://developer.kkbox.com/",
35
+ "instructions": """
36
+ 1. Go to the KKBOX Developer Portal: https://developer.kkbox.com/
37
+ 2. Log in and create a new application.
38
+ 3. Copy the "Client ID" and "Client Secret" from the app's settings.
39
+ 4. Paste them here when prompted.
40
+ """,
41
+ },
42
+ "KKBOX_CLIENT_SECRET": {
43
+ "description": "KKBox Client Secret",
44
+ "url": "https://developer.kkbox.com/",
45
+ "instructions": "See the steps above for KKBox Client ID.",
46
+ },
47
+ }
48
+
49
+ # Track whether the browser has already been opened for a service
50
+ browser_opened = set()
51
+
52
+ # Prompt the user for each variable
53
+ for var, details in required_vars.items():
54
+ current_value = os.getenv(var)
55
+ if current_value:
56
+ print(f"{details['description']} is already set.")
57
+ continue
58
+
59
+ print(f"\n{details['description']} is missing.")
60
+ print(details["instructions"])
61
+
62
+ # Check if the browser has already been opened for this service
63
+ if details["url"] not in browser_opened:
64
+ open_browser = (
65
+ input(
66
+ f"Do you want to open the website to get your {details['description']}? (y/N): "
67
+ )
68
+ .strip()
69
+ .lower()
70
+ )
71
+ if open_browser == "y":
72
+ webbrowser.open(details["url"])
73
+ print(f"The website has been opened in your browser: {details['url']}")
74
+ browser_opened.add(details["url"]) # Mark this URL as opened
75
+
76
+ # Prompt the user to enter the value
77
+ new_value = input(f"Enter your {details['description']}: ").strip()
78
+ if new_value:
79
+ set_key(env_file, var, new_value)
80
+ print(f"{details['description']} has been saved to the .env file.")
81
+
82
+ print("\nConfiguration complete! Your API keys have been saved to the .env file.")
83
+
84
+
85
+ if __name__ == "__main__":
86
+ run_config_wizard()
@@ -0,0 +1,104 @@
1
+ import argparse
2
+ from dataclasses import asdict
3
+ from pprint import pprint
4
+
5
+ from importlib.metadata import version, PackageNotFoundError
6
+
7
+ try:
8
+ __version__ = version("yutipy")
9
+ except PackageNotFoundError:
10
+ __version__ = "unknown"
11
+
12
+ from yutipy.deezer import Deezer
13
+ from yutipy.itunes import Itunes
14
+ from yutipy.kkbox import KKBox
15
+ from yutipy.musicyt import MusicYT
16
+ from yutipy.spotify import Spotify
17
+ from yutipy.utils.logger import disable_logging, enable_logging
18
+ from yutipy.yutipy_music import YutipyMusic
19
+
20
+
21
+ def main():
22
+ disable_logging()
23
+
24
+ # Create the argument parser
25
+ parser = argparse.ArgumentParser(
26
+ description="Search for music information across multiple platforms using yutipy."
27
+ )
28
+ parser.add_argument("artist", type=str, help="The name of the artist.")
29
+ parser.add_argument("song", type=str, help="The title of the song.")
30
+ parser.add_argument(
31
+ "--limit",
32
+ type=int,
33
+ default=5,
34
+ help="The number of results to retrieve (default: 5).",
35
+ )
36
+ parser.add_argument(
37
+ "--normalize",
38
+ action="store_true",
39
+ help="Normalize non-English characters.",
40
+ default=False,
41
+ )
42
+ parser.add_argument(
43
+ "--verbose",
44
+ action="store_true",
45
+ help="Enable logging in terminal",
46
+ default=False,
47
+ )
48
+ parser.add_argument(
49
+ "--version",
50
+ action="version",
51
+ version=f"yutipy v{__version__}",
52
+ help="Show the version of the yutipy and exit.",
53
+ )
54
+ parser.add_argument(
55
+ "--service",
56
+ type=str,
57
+ choices=["deezer", "itunes", "kkbox", "spotify", "ytmusic"],
58
+ help="Specify a single service to search (e.g., deezer, itunes, kkbox, spotify, ytmusic).",
59
+ )
60
+
61
+ # Parse the arguments
62
+ args = parser.parse_args()
63
+
64
+ if args.verbose:
65
+ enable_logging()
66
+
67
+ # Use the specified service or default to YutipyMusic
68
+ try:
69
+ if args.service:
70
+ service_map = {
71
+ "deezer": Deezer,
72
+ "itunes": Itunes,
73
+ "kkbox": KKBox,
74
+ "spotify": Spotify,
75
+ "ytmusic": MusicYT,
76
+ }
77
+ service_class = service_map[args.service]
78
+ with service_class() as service:
79
+ result = service.search(
80
+ artist=args.artist,
81
+ song=args.song,
82
+ limit=args.limit,
83
+ normalize_non_english=args.normalize,
84
+ )
85
+ else:
86
+ with YutipyMusic() as yutipy_music:
87
+ result = yutipy_music.search(
88
+ artist=args.artist,
89
+ song=args.song,
90
+ limit=args.limit,
91
+ normalize_non_english=args.normalize,
92
+ )
93
+
94
+ if result:
95
+ print("\nSEARCH RESULTS:\n")
96
+ pprint(asdict(result))
97
+ else:
98
+ print("No results found.")
99
+ except Exception as e:
100
+ print(f"An error occurred: {e}")
101
+
102
+
103
+ if __name__ == "__main__":
104
+ main()
@@ -10,7 +10,8 @@ from yutipy.exceptions import (
10
10
  NetworkException,
11
11
  )
12
12
  from yutipy.models import MusicInfo
13
- from yutipy.utils.cheap_utils import are_strings_similar, is_valid_string
13
+ from yutipy.utils.helpers import are_strings_similar, is_valid_string
14
+ from yutipy.utils.logger import logger
14
15
 
15
16
 
16
17
  class Deezer:
@@ -86,22 +87,34 @@ class Deezer:
86
87
  query_url = endpoint + query
87
88
 
88
89
  try:
90
+ logger.info(
91
+ f'Searching music info for `artist="{artist}"` and `song="{song}"`'
92
+ )
93
+ logger.debug(f"Query URL: {query_url}")
89
94
  response = self._session.get(query_url, timeout=30)
95
+ logger.debug(f"Response status code: {response.status_code}")
90
96
  response.raise_for_status()
91
97
  except requests.RequestException as e:
98
+ logger.error(f"Network error while fetching music info: {e}")
92
99
  raise NetworkException(f"Network error occurred: {e}")
93
100
  except Exception as e:
101
+ logger.exception(f"Unexpected error while searching Deezer: {e}")
94
102
  raise DeezerException(f"An error occurred while searching Deezer: {e}")
95
103
 
96
104
  try:
105
+ logger.debug(f"Parsing response JSON: {response.json()}")
97
106
  result = response.json()["data"]
98
107
  except (IndexError, KeyError, ValueError) as e:
108
+ logger.error(f"Invalid response structure from Deezer: {e}")
99
109
  raise InvalidResponseException(f"Invalid response received: {e}")
100
110
 
101
111
  music_info = self._parse_results(artist, song, result)
102
112
  if music_info:
103
113
  return music_info
104
114
 
115
+ logger.warning(
116
+ f"No matching results found for artist='{artist}' and song='{song}'"
117
+ )
105
118
  return None
106
119
 
107
120
  def _get_upc_isrc(self, music_id: int, music_type: str) -> Optional[Dict]:
@@ -127,7 +140,7 @@ class Deezer:
127
140
  else:
128
141
  raise DeezerException(f"Invalid music type: {music_type}")
129
142
 
130
- def _get_track_info(self, music_id: int) -> Optional[Dict]:
143
+ def _get_track_info(self, track_id: int) -> Optional[Dict]:
131
144
  """
132
145
  Retrieves track information for a given track ID.
133
146
 
@@ -141,18 +154,25 @@ class Deezer:
141
154
  Optional[Dict]
142
155
  A dictionary containing track information.
143
156
  """
144
- query_url = f"{self.api_url}/track/{music_id}"
157
+ query_url = f"{self.api_url}/track/{track_id}"
145
158
  try:
159
+ logger.info(f"Fetching track info for track_id: {track_id}")
160
+ logger.debug(f"Query URL: {query_url}")
146
161
  response = self._session.get(query_url, timeout=30)
162
+ logger.debug(f"Response status code: {response.status_code}")
147
163
  response.raise_for_status()
148
164
  except requests.RequestException as e:
165
+ logger.error(f"Error fetching track info: {e}")
149
166
  raise NetworkException(f"Network error occurred: {e}")
150
167
  except Exception as e:
168
+ logger.error(f"Error fetching track info: {e}")
151
169
  raise DeezerException(f"An error occurred while fetching track info: {e}")
152
170
 
153
171
  try:
172
+ logger.debug(f"Response JSON: {response.json()}")
154
173
  result = response.json()
155
174
  except ValueError as e:
175
+ logger.error(f"Invalid response received from Deezer: {e}")
156
176
  raise InvalidResponseException(f"Invalid response received: {e}")
157
177
 
158
178
  return {
@@ -161,7 +181,7 @@ class Deezer:
161
181
  "tempo": result.get("bpm"),
162
182
  }
163
183
 
164
- def _get_album_info(self, music_id: int) -> Optional[Dict]:
184
+ def _get_album_info(self, album_id: int) -> Optional[Dict]:
165
185
  """
166
186
  Retrieves album information for a given album ID.
167
187
 
@@ -175,18 +195,25 @@ class Deezer:
175
195
  Optional[Dict]
176
196
  A dictionary containing album information.
177
197
  """
178
- query_url = f"{self.api_url}/album/{music_id}"
198
+ query_url = f"{self.api_url}/album/{album_id}"
179
199
  try:
200
+ logger.info(f"Fetching album info for album_id: {album_id}")
201
+ logger.debug(f"Query URL: {query_url}")
180
202
  response = self._session.get(query_url, timeout=30)
203
+ logger.info(f"Response status code: {response.status_code}")
181
204
  response.raise_for_status()
182
205
  except requests.RequestException as e:
206
+ logger.error(f"Error fetching album info: {e}")
183
207
  raise NetworkException(f"Network error occurred: {e}")
184
208
  except Exception as e:
209
+ logger.error(f"Error fetching album info: {e}")
185
210
  raise DeezerException(f"An error occurred while fetching album info: {e}")
186
211
 
187
212
  try:
213
+ logger.debug(f"Response JSON: {response.json()}")
188
214
  result = response.json()
189
215
  except ValueError as e:
216
+ logger.error(f"Invalid response received from Deezer: {e}")
190
217
  raise InvalidResponseException(f"Invalid response received: {e}")
191
218
 
192
219
  return {
@@ -293,6 +320,10 @@ class Deezer:
293
320
 
294
321
 
295
322
  if __name__ == "__main__":
323
+ import logging
324
+ from yutipy.utils.logger import enable_logging
325
+
326
+ enable_logging(level=logging.DEBUG)
296
327
  deezer = Deezer()
297
328
  try:
298
329
  artist_name = input("Artist Name: ")