yutipy 1.4.22__tar.gz → 1.5.1__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.22 → yutipy-1.5.1}/PKG-INFO +37 -27
- {yutipy-1.4.22 → yutipy-1.5.1}/README.md +35 -3
- yutipy-1.5.1/docs/cli.rst +38 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/faq.rst +22 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/index.rst +2 -1
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/usage_examples.rst +13 -4
- {yutipy-1.4.22 → yutipy-1.5.1}/pyproject.toml +5 -2
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/__init__.py +0 -2
- yutipy-1.5.1/yutipy/cli/__init__.py +0 -0
- yutipy-1.5.1/yutipy/cli/config.py +86 -0
- yutipy-1.5.1/yutipy/cli/search.py +104 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/deezer.py +38 -5
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/exceptions.py +28 -14
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/itunes.py +20 -1
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/kkbox.py +18 -1
- yutipy-1.5.1/yutipy/logging.py +3 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/models.py +1 -1
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/musicyt.py +27 -4
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/spotify.py +20 -1
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/utils/__init__.py +1 -1
- yutipy-1.4.22/yutipy/utils/cheap_utils.py → yutipy-1.5.1/yutipy/utils/helpers.py +0 -4
- yutipy-1.5.1/yutipy/utils/logger.py +39 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy/yutipy_music.py +39 -35
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy.egg-info/PKG-INFO +37 -27
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy.egg-info/SOURCES.txt +7 -1
- yutipy-1.5.1/yutipy.egg-info/entry_points.txt +3 -0
- yutipy-1.4.22/yutipy/utils/logger.py +0 -4
- {yutipy-1.4.22 → yutipy-1.5.1}/.gitattributes +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.github/FUNDING.yml +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.github/workflows/pytest-unit-testing.yml +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.github/workflows/release.yml +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.gitignore +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/.readthedocs.yaml +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/LICENSE +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/MANIFEST.in +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/Makefile +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/_static/yutipy_header.png +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/_static/yutipy_logo.png +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/api_reference.rst +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/available_platforms.rst +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/conf.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/installation.rst +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/make.bat +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/docs/requirements.txt +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/requirements-dev.txt +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/requirements.txt +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/setup.cfg +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/__init__.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_deezer.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_itunes.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_kkbox.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_models.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_musicyt.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_spotify.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_utils.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/tests/test_yutipy_music.py +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy.egg-info/dependency_links.txt +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy.egg-info/requires.txt +0 -0
- {yutipy-1.4.22 → yutipy-1.5.1}/yutipy.egg-info/top_level.txt +0 -0
|
@@ -1,32 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yutipy
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.1
|
|
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
|
|
@@ -43,7 +22,6 @@ 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
|
|
@@ -90,6 +68,10 @@ A _**simple**_ Python package for searching and retrieving music information fro
|
|
|
90
68
|
- [Available Music Platforms](#available-music-platforms)
|
|
91
69
|
- [Installation](#installation)
|
|
92
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)
|
|
93
75
|
- [Contributing](#contributing)
|
|
94
76
|
- [License](#license)
|
|
95
77
|
|
|
@@ -121,9 +103,7 @@ pip install -U yutipy
|
|
|
121
103
|
|
|
122
104
|
## Usage Example
|
|
123
105
|
|
|
124
|
-
Here's a quick example of how to use the `yutipy` package to search for a song
|
|
125
|
-
|
|
126
|
-
### Deezer
|
|
106
|
+
Here's a quick example of how to use the `yutipy` package to search for a song on **Deezer**:
|
|
127
107
|
|
|
128
108
|
```python
|
|
129
109
|
from yutipy.deezer import Deezer
|
|
@@ -135,6 +115,36 @@ with Deezer() as deezer:
|
|
|
135
115
|
|
|
136
116
|
For more usage examples, see the [Usage Examples](https://yutipy.readthedocs.io/en/latest/usage_examples.html) page in docs.
|
|
137
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
|
+
|
|
138
148
|
## Contributing
|
|
139
149
|
|
|
140
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
|
------------
|
|
@@ -22,7 +22,7 @@ maintainers = [
|
|
|
22
22
|
]
|
|
23
23
|
description = "A simple package for retrieving music information from various music platforms APIs."
|
|
24
24
|
readme = "README.md"
|
|
25
|
-
license =
|
|
25
|
+
license = "MIT"
|
|
26
26
|
keywords = ["music", "API", "Deezer", "iTunes", "KKBox", "Spotify", "YouTube Music", "search", "retrieve", "information", "yutify"]
|
|
27
27
|
classifiers = [
|
|
28
28
|
"Development Status :: 4 - Beta",
|
|
@@ -34,7 +34,6 @@ classifiers = [
|
|
|
34
34
|
"Programming Language :: Python :: 3.10",
|
|
35
35
|
"Programming Language :: Python :: 3.11",
|
|
36
36
|
"Programming Language :: Python :: 3.12",
|
|
37
|
-
"License :: OSI Approved :: MIT License",
|
|
38
37
|
"Operating System :: OS Independent",
|
|
39
38
|
]
|
|
40
39
|
|
|
@@ -54,3 +53,7 @@ funding = "https://ko-fi.com/cheapnightbot"
|
|
|
54
53
|
[tool.setuptools_scm]
|
|
55
54
|
version_scheme = "guess-next-dev"
|
|
56
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,7 +1,6 @@
|
|
|
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
|
|
@@ -10,7 +9,6 @@ __all__ = [
|
|
|
10
9
|
"Deezer",
|
|
11
10
|
"Itunes",
|
|
12
11
|
"KKBox",
|
|
13
|
-
"MusicInfo",
|
|
14
12
|
"MusicYT",
|
|
15
13
|
"Spotify",
|
|
16
14
|
"YutipyMusic",
|
|
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()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
__all__ = ["Deezer", "DeezerException"]
|
|
2
|
+
|
|
1
3
|
from pprint import pprint
|
|
2
4
|
from typing import Dict, List, Optional
|
|
3
5
|
|
|
@@ -10,7 +12,8 @@ from yutipy.exceptions import (
|
|
|
10
12
|
NetworkException,
|
|
11
13
|
)
|
|
12
14
|
from yutipy.models import MusicInfo
|
|
13
|
-
from yutipy.utils.
|
|
15
|
+
from yutipy.utils.helpers import are_strings_similar, is_valid_string
|
|
16
|
+
from yutipy.utils.logger import logger
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class Deezer:
|
|
@@ -86,22 +89,34 @@ class Deezer:
|
|
|
86
89
|
query_url = endpoint + query
|
|
87
90
|
|
|
88
91
|
try:
|
|
92
|
+
logger.info(
|
|
93
|
+
f'Searching music info for `artist="{artist}"` and `song="{song}"`'
|
|
94
|
+
)
|
|
95
|
+
logger.debug(f"Query URL: {query_url}")
|
|
89
96
|
response = self._session.get(query_url, timeout=30)
|
|
97
|
+
logger.debug(f"Response status code: {response.status_code}")
|
|
90
98
|
response.raise_for_status()
|
|
91
99
|
except requests.RequestException as e:
|
|
100
|
+
logger.error(f"Network error while fetching music info: {e}")
|
|
92
101
|
raise NetworkException(f"Network error occurred: {e}")
|
|
93
102
|
except Exception as e:
|
|
103
|
+
logger.exception(f"Unexpected error while searching Deezer: {e}")
|
|
94
104
|
raise DeezerException(f"An error occurred while searching Deezer: {e}")
|
|
95
105
|
|
|
96
106
|
try:
|
|
107
|
+
logger.debug(f"Parsing response JSON: {response.json()}")
|
|
97
108
|
result = response.json()["data"]
|
|
98
109
|
except (IndexError, KeyError, ValueError) as e:
|
|
110
|
+
logger.error(f"Invalid response structure from Deezer: {e}")
|
|
99
111
|
raise InvalidResponseException(f"Invalid response received: {e}")
|
|
100
112
|
|
|
101
113
|
music_info = self._parse_results(artist, song, result)
|
|
102
114
|
if music_info:
|
|
103
115
|
return music_info
|
|
104
116
|
|
|
117
|
+
logger.warning(
|
|
118
|
+
f"No matching results found for artist='{artist}' and song='{song}'"
|
|
119
|
+
)
|
|
105
120
|
return None
|
|
106
121
|
|
|
107
122
|
def _get_upc_isrc(self, music_id: int, music_type: str) -> Optional[Dict]:
|
|
@@ -127,7 +142,7 @@ class Deezer:
|
|
|
127
142
|
else:
|
|
128
143
|
raise DeezerException(f"Invalid music type: {music_type}")
|
|
129
144
|
|
|
130
|
-
def _get_track_info(self,
|
|
145
|
+
def _get_track_info(self, track_id: int) -> Optional[Dict]:
|
|
131
146
|
"""
|
|
132
147
|
Retrieves track information for a given track ID.
|
|
133
148
|
|
|
@@ -141,18 +156,25 @@ class Deezer:
|
|
|
141
156
|
Optional[Dict]
|
|
142
157
|
A dictionary containing track information.
|
|
143
158
|
"""
|
|
144
|
-
query_url = f"{self.api_url}/track/{
|
|
159
|
+
query_url = f"{self.api_url}/track/{track_id}"
|
|
145
160
|
try:
|
|
161
|
+
logger.info(f"Fetching track info for track_id: {track_id}")
|
|
162
|
+
logger.debug(f"Query URL: {query_url}")
|
|
146
163
|
response = self._session.get(query_url, timeout=30)
|
|
164
|
+
logger.debug(f"Response status code: {response.status_code}")
|
|
147
165
|
response.raise_for_status()
|
|
148
166
|
except requests.RequestException as e:
|
|
167
|
+
logger.error(f"Error fetching track info: {e}")
|
|
149
168
|
raise NetworkException(f"Network error occurred: {e}")
|
|
150
169
|
except Exception as e:
|
|
170
|
+
logger.error(f"Error fetching track info: {e}")
|
|
151
171
|
raise DeezerException(f"An error occurred while fetching track info: {e}")
|
|
152
172
|
|
|
153
173
|
try:
|
|
174
|
+
logger.debug(f"Response JSON: {response.json()}")
|
|
154
175
|
result = response.json()
|
|
155
176
|
except ValueError as e:
|
|
177
|
+
logger.error(f"Invalid response received from Deezer: {e}")
|
|
156
178
|
raise InvalidResponseException(f"Invalid response received: {e}")
|
|
157
179
|
|
|
158
180
|
return {
|
|
@@ -161,7 +183,7 @@ class Deezer:
|
|
|
161
183
|
"tempo": result.get("bpm"),
|
|
162
184
|
}
|
|
163
185
|
|
|
164
|
-
def _get_album_info(self,
|
|
186
|
+
def _get_album_info(self, album_id: int) -> Optional[Dict]:
|
|
165
187
|
"""
|
|
166
188
|
Retrieves album information for a given album ID.
|
|
167
189
|
|
|
@@ -175,18 +197,25 @@ class Deezer:
|
|
|
175
197
|
Optional[Dict]
|
|
176
198
|
A dictionary containing album information.
|
|
177
199
|
"""
|
|
178
|
-
query_url = f"{self.api_url}/album/{
|
|
200
|
+
query_url = f"{self.api_url}/album/{album_id}"
|
|
179
201
|
try:
|
|
202
|
+
logger.info(f"Fetching album info for album_id: {album_id}")
|
|
203
|
+
logger.debug(f"Query URL: {query_url}")
|
|
180
204
|
response = self._session.get(query_url, timeout=30)
|
|
205
|
+
logger.info(f"Response status code: {response.status_code}")
|
|
181
206
|
response.raise_for_status()
|
|
182
207
|
except requests.RequestException as e:
|
|
208
|
+
logger.error(f"Error fetching album info: {e}")
|
|
183
209
|
raise NetworkException(f"Network error occurred: {e}")
|
|
184
210
|
except Exception as e:
|
|
211
|
+
logger.error(f"Error fetching album info: {e}")
|
|
185
212
|
raise DeezerException(f"An error occurred while fetching album info: {e}")
|
|
186
213
|
|
|
187
214
|
try:
|
|
215
|
+
logger.debug(f"Response JSON: {response.json()}")
|
|
188
216
|
result = response.json()
|
|
189
217
|
except ValueError as e:
|
|
218
|
+
logger.error(f"Invalid response received from Deezer: {e}")
|
|
190
219
|
raise InvalidResponseException(f"Invalid response received: {e}")
|
|
191
220
|
|
|
192
221
|
return {
|
|
@@ -293,6 +322,10 @@ class Deezer:
|
|
|
293
322
|
|
|
294
323
|
|
|
295
324
|
if __name__ == "__main__":
|
|
325
|
+
import logging
|
|
326
|
+
from yutipy.utils.logger import enable_logging
|
|
327
|
+
|
|
328
|
+
enable_logging(level=logging.DEBUG)
|
|
296
329
|
deezer = Deezer()
|
|
297
330
|
try:
|
|
298
331
|
artist_name = input("Artist Name: ")
|