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.
- {yutipy-1.4.2 → yutipy-1.5.0}/PKG-INFO +39 -28
- {yutipy-1.4.2 → yutipy-1.5.0}/README.md +35 -3
- yutipy-1.5.0/docs/cli.rst +38 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/faq.rst +22 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/index.rst +2 -1
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/usage_examples.rst +13 -4
- {yutipy-1.4.2 → yutipy-1.5.0}/pyproject.toml +7 -3
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/__init__.py +2 -2
- yutipy-1.5.0/yutipy/cli/__init__.py +0 -0
- yutipy-1.5.0/yutipy/cli/config.py +86 -0
- yutipy-1.5.0/yutipy/cli/search.py +104 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/deezer.py +36 -5
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/itunes.py +18 -1
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/kkbox.py +16 -1
- yutipy-1.5.0/yutipy/logging.py +3 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/models.py +1 -1
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/musicyt.py +22 -1
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/spotify.py +17 -1
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/utils/__init__.py +1 -1
- yutipy-1.4.2/yutipy/utils/cheap_utils.py → yutipy-1.5.0/yutipy/utils/helpers.py +0 -4
- yutipy-1.5.0/yutipy/utils/logger.py +39 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/yutipy_music.py +39 -35
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/PKG-INFO +39 -28
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/SOURCES.txt +7 -1
- yutipy-1.5.0/yutipy.egg-info/entry_points.txt +3 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/requires.txt +1 -0
- yutipy-1.4.2/yutipy/utils/logger.py +0 -4
- {yutipy-1.4.2 → yutipy-1.5.0}/.gitattributes +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.github/FUNDING.yml +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.github/workflows/pytest-unit-testing.yml +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.github/workflows/release.yml +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.gitignore +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/.readthedocs.yaml +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/LICENSE +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/MANIFEST.in +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/Makefile +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/_static/yutipy_header.png +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/_static/yutipy_logo.png +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/api_reference.rst +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/available_platforms.rst +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/conf.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/installation.rst +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/make.bat +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/docs/requirements.txt +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/requirements-dev.txt +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/requirements.txt +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/setup.cfg +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/__init__.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_deezer.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_itunes.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_kkbox.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_models.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_musicyt.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_spotify.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_utils.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/tests/test_yutipy_music.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy/exceptions.py +0 -0
- {yutipy-1.4.2 → yutipy-1.5.0}/yutipy.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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
|
|
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
|
|
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.
|
|
@@ -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
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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 =
|
|
25
|
-
keywords = ["music", "API", "Deezer", "iTunes", "Spotify", "YouTube Music", "search", "retrieve", "information", "yutify"
|
|
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.
|
|
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,
|
|
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/{
|
|
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,
|
|
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/{
|
|
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: ")
|