yutipy 1.5.2__tar.gz → 1.6.12__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.5.2 → yutipy-1.6.12}/PKG-INFO +2 -1
  2. {yutipy-1.5.2 → yutipy-1.6.12}/README.md +1 -0
  3. {yutipy-1.5.2 → yutipy-1.6.12}/docs/api_reference.rst +30 -9
  4. {yutipy-1.5.2 → yutipy-1.6.12}/docs/conf.py +1 -1
  5. {yutipy-1.5.2 → yutipy-1.6.12}/docs/index.rst +1 -0
  6. {yutipy-1.5.2 → yutipy-1.6.12}/docs/usage_examples.rst +89 -2
  7. yutipy-1.6.12/tests/test_spotify.py +130 -0
  8. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/cli/search.py +1 -1
  9. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/deezer.py +2 -2
  10. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/exceptions.py +18 -34
  11. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/itunes.py +2 -2
  12. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/kkbox.py +2 -2
  13. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/musicyt.py +2 -2
  14. yutipy-1.6.12/yutipy/spotify.py +981 -0
  15. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/utils/__init__.py +1 -1
  16. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/yutipy_music.py +2 -2
  17. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy.egg-info/PKG-INFO +2 -1
  18. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy.egg-info/SOURCES.txt +2 -2
  19. yutipy-1.5.2/tests/test_spotify.py +0 -53
  20. yutipy-1.5.2/yutipy/spotify.py +0 -506
  21. {yutipy-1.5.2 → yutipy-1.6.12}/.gitattributes +0 -0
  22. {yutipy-1.5.2 → yutipy-1.6.12}/.github/FUNDING.yml +0 -0
  23. {yutipy-1.5.2 → yutipy-1.6.12}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  24. {yutipy-1.5.2 → yutipy-1.6.12}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  25. {yutipy-1.5.2 → yutipy-1.6.12}/.github/workflows/pytest-unit-testing.yml +0 -0
  26. {yutipy-1.5.2 → yutipy-1.6.12}/.github/workflows/release.yml +0 -0
  27. {yutipy-1.5.2 → yutipy-1.6.12}/.gitignore +0 -0
  28. {yutipy-1.5.2 → yutipy-1.6.12}/.readthedocs.yaml +0 -0
  29. {yutipy-1.5.2 → yutipy-1.6.12}/LICENSE +0 -0
  30. {yutipy-1.5.2 → yutipy-1.6.12}/MANIFEST.in +0 -0
  31. {yutipy-1.5.2 → yutipy-1.6.12}/docs/Makefile +0 -0
  32. {yutipy-1.5.2 → yutipy-1.6.12}/docs/_static/yutipy_header.png +0 -0
  33. {yutipy-1.5.2 → yutipy-1.6.12}/docs/_static/yutipy_logo.png +0 -0
  34. {yutipy-1.5.2 → yutipy-1.6.12}/docs/available_platforms.rst +0 -0
  35. {yutipy-1.5.2 → yutipy-1.6.12}/docs/cli.rst +0 -0
  36. {yutipy-1.5.2 → yutipy-1.6.12}/docs/faq.rst +0 -0
  37. {yutipy-1.5.2 → yutipy-1.6.12}/docs/installation.rst +0 -0
  38. {yutipy-1.5.2 → yutipy-1.6.12}/docs/make.bat +0 -0
  39. {yutipy-1.5.2 → yutipy-1.6.12}/docs/requirements.txt +0 -0
  40. {yutipy-1.5.2 → yutipy-1.6.12}/pyproject.toml +0 -0
  41. {yutipy-1.5.2 → yutipy-1.6.12}/requirements-dev.txt +0 -0
  42. {yutipy-1.5.2 → yutipy-1.6.12}/requirements.txt +0 -0
  43. {yutipy-1.5.2 → yutipy-1.6.12}/setup.cfg +0 -0
  44. {yutipy-1.5.2 → yutipy-1.6.12}/tests/__init__.py +0 -0
  45. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_deezer.py +0 -0
  46. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_itunes.py +0 -0
  47. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_kkbox.py +0 -0
  48. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_models.py +0 -0
  49. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_musicyt.py +0 -0
  50. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_utils.py +0 -0
  51. {yutipy-1.5.2 → yutipy-1.6.12}/tests/test_yutipy_music.py +0 -0
  52. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/__init__.py +0 -0
  53. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/cli/__init__.py +0 -0
  54. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/cli/config.py +0 -0
  55. {yutipy-1.5.2/yutipy/utils → yutipy-1.6.12/yutipy}/logger.py +0 -0
  56. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/models.py +0 -0
  57. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy/utils/helpers.py +0 -0
  58. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy.egg-info/dependency_links.txt +0 -0
  59. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy.egg-info/entry_points.txt +0 -0
  60. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy.egg-info/requires.txt +0 -0
  61. {yutipy-1.5.2 → yutipy-1.6.12}/yutipy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yutipy
3
- Version: 1.5.2
3
+ Version: 1.6.12
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>
@@ -81,6 +81,7 @@ A _**simple**_ Python package for searching and retrieving music information fro
81
81
  - Search for music by artist and song title across multiple platforms.
82
82
  - It uses `RapidFuzz` to compare & return the best match so that you can be sure you got what you asked for without having to worry and doing all that work by yourself.
83
83
  - Retrieve detailed music information, including album art, release dates, lyrics, ISRC, and UPC codes.
84
+ - Authorize and access user resources easily.
84
85
 
85
86
  ### Available Music Platforms
86
87
 
@@ -44,6 +44,7 @@ A _**simple**_ Python package for searching and retrieving music information fro
44
44
  - Search for music by artist and song title across multiple platforms.
45
45
  - It uses `RapidFuzz` to compare & return the best match so that you can be sure you got what you asked for without having to worry and doing all that work by yourself.
46
46
  - Retrieve detailed music information, including album art, release dates, lyrics, ISRC, and UPC codes.
47
+ - Authorize and access user resources easily.
47
48
 
48
49
  ### Available Music Platforms
49
50
 
@@ -41,6 +41,12 @@ Spotify
41
41
  :noindex:
42
42
  :exclude-members: is_session_closed
43
43
 
44
+ .. autoclass:: yutipy.spotify.SpotifyAuth
45
+ :members:
46
+ :inherited-members:
47
+ :noindex:
48
+ :exclude-members: is_session_closed
49
+
44
50
  YouTube Music
45
51
  -------------
46
52
 
@@ -79,61 +85,76 @@ MusicInfos
79
85
  Exceptions
80
86
  =============
81
87
 
88
+ Base Exception
89
+ --------------
90
+
82
91
  .. autoclass:: yutipy.exceptions.YutipyException
83
92
  :members:
84
93
  :inherited-members:
85
94
  :noindex:
86
95
  :exclude-members: add_note, args, with_traceback
87
96
 
88
- .. autoclass:: yutipy.exceptions.InvalidValueException
97
+ Generic Exceptions
98
+ ------------------
99
+
100
+ .. autoclass:: yutipy.exceptions.AuthenticationException
89
101
  :members:
90
102
  :inherited-members:
91
103
  :noindex:
92
104
  :exclude-members: add_note, args, with_traceback
93
105
 
94
- .. autoclass:: yutipy.exceptions.DeezerException
106
+ .. autoclass:: yutipy.exceptions.InvalidResponseException
95
107
  :members:
96
108
  :inherited-members:
97
109
  :noindex:
98
110
  :exclude-members: add_note, args, with_traceback
99
111
 
100
- .. autoclass:: yutipy.exceptions.ItunesException
112
+ .. autoclass:: yutipy.exceptions.InvalidValueException
101
113
  :members:
102
114
  :inherited-members:
103
115
  :noindex:
104
116
  :exclude-members: add_note, args, with_traceback
105
117
 
106
- .. autoclass:: yutipy.exceptions.SpotifyException
118
+ .. autoclass:: yutipy.exceptions.NetworkException
107
119
  :members:
108
120
  :inherited-members:
109
121
  :noindex:
110
122
  :exclude-members: add_note, args, with_traceback
111
123
 
112
- .. autoclass:: yutipy.exceptions.MusicYTException
124
+ Service Exceptions
125
+ ------------------
126
+
127
+ .. autoclass:: yutipy.exceptions.DeezerException
113
128
  :members:
114
129
  :inherited-members:
115
130
  :noindex:
116
131
  :exclude-members: add_note, args, with_traceback
117
132
 
118
- .. autoclass:: yutipy.exceptions.AuthenticationException
133
+ .. autoclass:: yutipy.exceptions.ItunesException
119
134
  :members:
120
135
  :inherited-members:
121
136
  :noindex:
122
137
  :exclude-members: add_note, args, with_traceback
123
138
 
124
- .. autoclass:: yutipy.exceptions.NetworkException
139
+ .. autoclass:: yutipy.exceptions.KKBoxException
125
140
  :members:
126
141
  :inherited-members:
127
142
  :noindex:
128
143
  :exclude-members: add_note, args, with_traceback
129
144
 
130
- .. autoclass:: yutipy.exceptions.InvalidResponseException
145
+ .. autoclass:: yutipy.exceptions.MusicYTException
131
146
  :members:
132
147
  :inherited-members:
133
148
  :noindex:
134
149
  :exclude-members: add_note, args, with_traceback
135
150
 
136
- .. autoclass:: yutipy.exceptions.KKBoxException
151
+ .. autoclass:: yutipy.exceptions.SpotifyException
152
+ :members:
153
+ :inherited-members:
154
+ :noindex:
155
+ :exclude-members: add_note, args, with_traceback
156
+
157
+ .. autoclass:: yutipy.exceptions.SpotifyAuthException
137
158
  :members:
138
159
  :inherited-members:
139
160
  :noindex:
@@ -14,7 +14,7 @@ sys.path.insert(0, os.path.abspath("../yutipy"))
14
14
  project = "yutipy"
15
15
  copyright = "%Y, Cheap Nightbot"
16
16
  author = "Cheap Nightbot"
17
- release = "0.1.0"
17
+ release = ""
18
18
 
19
19
  # -- General configuration ---------------------------------------------------
20
20
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -36,6 +36,7 @@ Features
36
36
  - Search for music by artist and song title across multiple platforms.
37
37
  - It uses ``RapidFuzz`` to compare & return the best match so that you can be sure you got what you asked for without having to worry and doing all that work by yourself.
38
38
  - Retrieve detailed music information, including album art, release dates, lyrics, ISRC, and UPC codes.
39
+ - Authorize and access user resources easily.
39
40
 
40
41
  Get Started
41
42
  ===========
@@ -73,7 +73,11 @@ Alternatively, you can manually provide these values when creating an object of
73
73
  Spotify
74
74
  -------
75
75
 
76
- To use the Spotify API, you need to set the ``SPOTIFY_CLIENT_ID`` and ``SPOTIFY_CLIENT_SECRET`` for Spotify. You can do this by creating a ``.env`` file in the root directory of your project with the following content:
76
+ Client Credentials Grant Type
77
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78
+
79
+ To use the Spotify API with Client Credentials flow, you need to set the ``SPOTIFY_CLIENT_ID`` and ``SPOTIFY_CLIENT_SECRET`` for Spotify.
80
+ You can do this by creating a ``.env`` file in the root directory of your project with the following content:
77
81
 
78
82
  .. admonition:: .env
79
83
 
@@ -82,7 +86,7 @@ To use the Spotify API, you need to set the ``SPOTIFY_CLIENT_ID`` and ``SPOTIFY_
82
86
  SPOTIFY_CLIENT_ID=<your_spotify_client_id>
83
87
  SPOTIFY_CLIENT_SECRET=<your_spotify_client_secret>
84
88
 
85
- Alternatively, you can manually provide these values when creating an object of the `Spotify` class:
89
+ Alternatively, you can manually provide these values when creating an object of the ``Spotify`` class:
86
90
 
87
91
  .. code-block:: python
88
92
 
@@ -109,6 +113,89 @@ OR, if you have the ":abbr:`ISRC (International Standard Recording Code)`" or ":
109
113
  result = spotify.search_advanced("Artist Name", "Song Title", isrc="USAT29900609", upc="00602517078194")
110
114
  print(result)
111
115
 
116
+ Authorization Code Flow
117
+ ~~~~~~~~~~~~~~~~~~~~~~~
118
+
119
+ To use the Spotify API with the Authorization Code Flow, you need to set the ``SPOTIFY_CLIENT_ID``, ``SPOTIFY_CLIENT_SECRET``, and ``SPOTIFY_REDIRECT_URI``.
120
+ You can do this by creating a ``.env`` file in the root directory of your project with the following content:
121
+
122
+ .. admonition:: .env
123
+
124
+ .. code-block:: bash
125
+
126
+ SPOTIFY_CLIENT_ID=<your_spotify_client_id>
127
+ SPOTIFY_CLIENT_SECRET=<your_spotify_client_secret>
128
+ SPOTIFY_REDIRECT_URI=<your_redirect_uri>
129
+
130
+ Alternatively, you can manually provide these values when creating an object of the ``SpotifyAuth`` class.
131
+
132
+ Here’s an example of how to use the ``SpotifyAuth`` class in a web application:
133
+
134
+ .. code-block:: python
135
+
136
+ from flask import Flask, request, session, redirect, url_for
137
+ from yutipy.spotify import SpotifyAuth, SpotifyAuthException
138
+
139
+ app = Flask(__name__)
140
+ app.secret_key = "your_secret_key" # Replace with a secure secret key
141
+
142
+ # Initialize SpotifyAuth with your credentials
143
+ SPOTIFY_CLIENT_ID = "your_spotify_client_id"
144
+ SPOTIFY_CLIENT_SECRET = "your_spotify_client_secret"
145
+ SPOTIFY_REDIRECT_URI = "http://localhost:5000/callback"
146
+
147
+ spotify_auth = SpotifyAuth(
148
+ client_id=SPOTIFY_CLIENT_ID,
149
+ client_secret=SPOTIFY_CLIENT_SECRET,
150
+ redirect_uri=SPOTIFY_REDIRECT_URI,
151
+ scopes=["user-read-email", "user-read-private"],
152
+ )
153
+
154
+ @app.route("/")
155
+ def home():
156
+ """Home route to start the authorization process."""
157
+ state = spotify_auth.generate_state()
158
+ session["state"] = state # Save the state in the session for validation
159
+ auth_url = spotify_auth.get_authorization_url(state=state)
160
+ return redirect(auth_url)
161
+
162
+ @app.route("/callback")
163
+ def callback():
164
+ """Callback route to handle Spotify's response after user authorization."""
165
+ code = request.args.get("code")
166
+ state = request.args.get("state")
167
+ expected_state = session.get("state")
168
+
169
+ if not code or not state:
170
+ return "Authorization failed: Missing code or state.", 400
171
+
172
+ try:
173
+ spotify_auth.callback_handler(code, state, expected_state)
174
+ user_profile = spotify_auth.get_user_profile()
175
+ if user_profile:
176
+ return f"Successfully authenticated! Welcome, {user_profile['display_name']}."
177
+ else:
178
+ return "Authentication successful, but failed to fetch user profile."
179
+ except SpotifyAuthException as e:
180
+ return f"Authorization failed: {e}", 400
181
+
182
+ @app.route("/logout")
183
+ def logout():
184
+ """Logout route to close the session."""
185
+ spotify_auth.close_session()
186
+ session.clear()
187
+ return "Logged out successfully."
188
+
189
+ if __name__ == "__main__":
190
+ app.run(debug=True)
191
+
192
+ .. note::
193
+
194
+ - Avoid hard-coding your credentials in the code. Instead, define them in a ``.env`` file, which will be automatically read by the library.
195
+ - Ensure that the redirect URI matches the one configured in your Spotify Developer Dashboard.
196
+ - This example uses Flask, but it can be adapted to other web frameworks as needed.
197
+
198
+
112
199
  YouTube Music
113
200
  -------------
114
201
 
@@ -0,0 +1,130 @@
1
+ import pytest
2
+
3
+ from yutipy.exceptions import SpotifyException
4
+ from yutipy.models import MusicInfo
5
+ from yutipy.spotify import Spotify, SpotifyAuth
6
+
7
+
8
+ @pytest.fixture(scope="module")
9
+ def spotify():
10
+ try:
11
+ return Spotify()
12
+ except SpotifyException:
13
+ pytest.skip("Spotify credentials not found")
14
+
15
+
16
+ @pytest.fixture(scope="module")
17
+ def spotify_auth():
18
+ return SpotifyAuth(
19
+ client_id="test_client_id",
20
+ client_secret="test_client_secret",
21
+ redirect_uri="http://localhost/callback",
22
+ scopes=["user-read-email", "user-read-private"],
23
+ )
24
+
25
+
26
+ def test_search(spotify):
27
+ artist = "Adele"
28
+ song = "Hello"
29
+ result = spotify.search(artist, song)
30
+ assert result is not None
31
+ assert isinstance(result, MusicInfo)
32
+ assert result.title == song
33
+ assert artist in result.artists
34
+
35
+
36
+ def test_search_advanced_with_isrc(spotify):
37
+ artist = "Adele"
38
+ song = "Hello"
39
+ isrc = "GBBKS1500214"
40
+ result = spotify.search_advanced(artist, song, isrc=isrc)
41
+ assert result is not None
42
+ assert result.isrc == isrc
43
+
44
+
45
+ def test_search_advanced_with_upc(spotify):
46
+ artist = "Miles Davis"
47
+ album = "Kind Of Blue (Legacy Edition)"
48
+ upc = "888880696069"
49
+ result = spotify.search_advanced(artist, album, upc=upc)
50
+ print(result)
51
+ assert result is not None
52
+
53
+
54
+ def test_get_artists_ids(spotify):
55
+ artist = "Adele"
56
+ artist_ids = spotify._get_artists_ids(artist)
57
+ assert isinstance(artist_ids, list)
58
+ assert len(artist_ids) > 0
59
+
60
+
61
+ def test_close_session(spotify):
62
+ spotify.close_session()
63
+ assert spotify.is_session_closed
64
+
65
+
66
+ def test_get_authorization_url(spotify_auth):
67
+ state = spotify_auth.generate_state()
68
+ auth_url = spotify_auth.get_authorization_url(state=state)
69
+ assert "https://accounts.spotify.com/authorize" in auth_url
70
+ assert "response_type=code" in auth_url
71
+ assert f"client_id={spotify_auth.client_id}" in auth_url
72
+
73
+
74
+ def test_get_user_profile(spotify_auth, monkeypatch):
75
+ def mock_authorization_header():
76
+ return {"Authorization": "Bearer test_token"}
77
+
78
+ def mock_get(*args, **kwargs):
79
+ class MockResponse:
80
+ status_code = 200
81
+
82
+ @staticmethod
83
+ def raise_for_status():
84
+ pass # Simulates a successful response with no exceptions raised
85
+
86
+ @staticmethod
87
+ def json():
88
+ return {
89
+ "display_name": "Test User",
90
+ "images": [
91
+ {
92
+ "url": "https://example.com/image.jpg",
93
+ "height": 300,
94
+ "width": 300,
95
+ }
96
+ ],
97
+ }
98
+
99
+ return MockResponse()
100
+
101
+ monkeypatch.setattr(
102
+ spotify_auth, "_SpotifyAuth__authorization_header", mock_authorization_header
103
+ )
104
+ monkeypatch.setattr(spotify_auth._SpotifyAuth__session, "get", mock_get)
105
+
106
+ user_profile = spotify_auth.get_user_profile()
107
+ assert user_profile is not None
108
+ assert user_profile["display_name"] == "Test User"
109
+ assert len(user_profile["images"]) == 1
110
+ assert user_profile["images"][0]["url"] == "https://example.com/image.jpg"
111
+
112
+
113
+ def test_callback_handler(spotify_auth, monkeypatch):
114
+ def mock_get_access_token(*args, **kwargs):
115
+ return {
116
+ "access_token": "test_access_token",
117
+ "refresh_token": "test_refresh_token",
118
+ "expires_in": 3600,
119
+ "requested_at": 1234567890,
120
+ }
121
+
122
+ monkeypatch.setattr(
123
+ spotify_auth, "_SpotifyAuth__get_access_token", mock_get_access_token
124
+ )
125
+
126
+ spotify_auth.callback_handler("test_code", "test_state", "test_state")
127
+ assert spotify_auth._SpotifyAuth__access_token == "test_access_token"
128
+ assert spotify_auth._SpotifyAuth__refresh_token == "test_refresh_token"
129
+ assert spotify_auth._SpotifyAuth__token_expires_in == 3600
130
+ assert spotify_auth._SpotifyAuth__token_requested_at == 1234567890
@@ -14,7 +14,7 @@ from yutipy.itunes import Itunes
14
14
  from yutipy.kkbox import KKBox
15
15
  from yutipy.musicyt import MusicYT
16
16
  from yutipy.spotify import Spotify
17
- from yutipy.utils.logger import disable_logging, enable_logging
17
+ from yutipy.logger import disable_logging, enable_logging
18
18
  from yutipy.yutipy_music import YutipyMusic
19
19
 
20
20
 
@@ -13,7 +13,7 @@ from yutipy.exceptions import (
13
13
  )
14
14
  from yutipy.models import MusicInfo
15
15
  from yutipy.utils.helpers import are_strings_similar, is_valid_string
16
- from yutipy.utils.logger import logger
16
+ from yutipy.logger import logger
17
17
 
18
18
 
19
19
  class Deezer:
@@ -323,7 +323,7 @@ class Deezer:
323
323
 
324
324
  if __name__ == "__main__":
325
325
  import logging
326
- from yutipy.utils.logger import enable_logging
326
+ from yutipy.logger import enable_logging
327
327
 
328
328
  enable_logging(level=logging.DEBUG)
329
329
  deezer = Deezer()
@@ -11,60 +11,44 @@ __all__ = [
11
11
  class YutipyException(Exception):
12
12
  """Base class for exceptions in the Yutipy package."""
13
13
 
14
- pass
14
+
15
+ # Generic Exceptions
16
+ class AuthenticationException(YutipyException):
17
+ """Exception raised for authentication errors."""
18
+
19
+
20
+ class InvalidResponseException(YutipyException):
21
+ """Exception raised for invalid responses from APIs."""
22
+
23
+
24
+ class InvalidValueException(YutipyException):
25
+ """Exception raised for invalid values."""
26
+
27
+
28
+ class NetworkException(YutipyException):
29
+ """Exception raised for network-related errors."""
15
30
 
16
31
 
17
32
  # Service Exceptions
18
33
  class DeezerException(YutipyException):
19
34
  """Exception raised for errors related to the Deezer API."""
20
35
 
21
- pass
22
-
23
36
 
24
37
  class ItunesException(YutipyException):
25
38
  """Exception raised for errors related to the iTunes API."""
26
39
 
27
- pass
28
-
29
40
 
30
41
  class KKBoxException(YutipyException):
31
42
  """Exception raised for erros related to the KKBOX Open API."""
32
43
 
33
- pass
34
-
35
44
 
36
45
  class MusicYTException(YutipyException):
37
46
  """Exception raised for errors related to the YouTube Music API."""
38
47
 
39
- pass
40
-
41
48
 
42
49
  class SpotifyException(YutipyException):
43
50
  """Exception raised for errors related to the Spotify API."""
44
51
 
45
- pass
46
-
47
-
48
- # Generic Exceptions
49
- class AuthenticationException(YutipyException):
50
- """Exception raised for authentication errors."""
51
-
52
- pass
53
-
54
-
55
- class InvalidResponseException(YutipyException):
56
- """Exception raised for invalid responses from APIs."""
57
-
58
- pass
59
-
60
-
61
- class InvalidValueException(YutipyException):
62
- """Exception raised for invalid values."""
63
-
64
- pass
65
-
66
-
67
- class NetworkException(YutipyException):
68
- """Exception raised for network-related errors."""
69
52
 
70
- pass
53
+ class SpotifyAuthException(AuthenticationException):
54
+ """Exception raised for Spotify authorization code grant type / flow"""
@@ -18,7 +18,7 @@ from yutipy.utils.helpers import (
18
18
  guess_album_type,
19
19
  is_valid_string,
20
20
  )
21
- from yutipy.utils.logger import logger
21
+ from yutipy.logger import logger
22
22
 
23
23
 
24
24
  class Itunes:
@@ -228,7 +228,7 @@ class Itunes:
228
228
 
229
229
  if __name__ == "__main__":
230
230
  import logging
231
- from yutipy.utils.logger import enable_logging
231
+ from yutipy.logger import enable_logging
232
232
 
233
233
  enable_logging(level=logging.DEBUG)
234
234
  itunes = Itunes()
@@ -18,7 +18,7 @@ from yutipy.exceptions import (
18
18
  )
19
19
  from yutipy.models import MusicInfo
20
20
  from yutipy.utils.helpers import are_strings_similar, is_valid_string
21
- from yutipy.utils.logger import logger
21
+ from yutipy.logger import logger
22
22
 
23
23
  load_dotenv()
24
24
 
@@ -417,7 +417,7 @@ class KKBox:
417
417
 
418
418
  if __name__ == "__main__":
419
419
  import logging
420
- from yutipy.utils.logger import enable_logging
420
+ from yutipy.logger import enable_logging
421
421
 
422
422
  enable_logging(level=logging.DEBUG)
423
423
  kkbox = KKBox(KKBOX_CLIENT_ID, KKBOX_CLIENT_SECRET)
@@ -13,7 +13,7 @@ from yutipy.exceptions import (
13
13
  )
14
14
  from yutipy.models import MusicInfo
15
15
  from yutipy.utils.helpers import are_strings_similar, is_valid_string
16
- from yutipy.utils.logger import logger
16
+ from yutipy.logger import logger
17
17
 
18
18
 
19
19
  class MusicYT:
@@ -296,7 +296,7 @@ class MusicYT:
296
296
  if __name__ == "__main__":
297
297
  import logging
298
298
 
299
- from yutipy.utils.logger import enable_logging
299
+ from yutipy.logger import enable_logging
300
300
 
301
301
  enable_logging(level=logging.DEBUG)
302
302
  music_yt = MusicYT()