deezer-python-gql 0.6.0__tar.gz → 0.7.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.
- {deezer_python_gql-0.6.0/deezer_python_gql.egg-info → deezer_python_gql-0.7.0}/PKG-INFO +7 -1
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/README.md +6 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql/base_client.py +32 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0/deezer_python_gql.egg-info}/PKG-INFO +7 -1
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/pyproject.toml +1 -1
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/tests/test_client.py +63 -1
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/LICENSE +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/MANIFEST.in +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql/__init__.py +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql/py.typed +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/SOURCES.txt +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/dependency_links.txt +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/not-zip-safe +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/requires.txt +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/top_level.txt +0 -0
- {deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deezer-python-gql
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Async typed Python client for Deezer's Pipe GraphQL API.
|
|
5
5
|
Author-email: Julian Daberkow <jdaberkow@users.noreply.github.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -184,6 +184,12 @@ asyncio.run(main())
|
|
|
184
184
|
| `music_together_update_group_settings(...)` | Update group settings (name, family mode) |
|
|
185
185
|
| `music_together_generate_group_name()` | Generate a random group name |
|
|
186
186
|
|
|
187
|
+
### Utilities
|
|
188
|
+
|
|
189
|
+
| Method | Description |
|
|
190
|
+
| -------------------------------- | ---------------------------------------------------------------- |
|
|
191
|
+
| `check_audiobook_ids(album_ids)` | Batch-check which album IDs are audiobooks (single GraphQL call) |
|
|
192
|
+
|
|
187
193
|
All methods return fully-typed Pydantic models generated from the GraphQL schema.
|
|
188
194
|
|
|
189
195
|
## Development
|
|
@@ -156,6 +156,12 @@ asyncio.run(main())
|
|
|
156
156
|
| `music_together_update_group_settings(...)` | Update group settings (name, family mode) |
|
|
157
157
|
| `music_together_generate_group_name()` | Generate a random group name |
|
|
158
158
|
|
|
159
|
+
### Utilities
|
|
160
|
+
|
|
161
|
+
| Method | Description |
|
|
162
|
+
| -------------------------------- | ---------------------------------------------------------------- |
|
|
163
|
+
| `check_audiobook_ids(album_ids)` | Batch-check which album IDs are audiobooks (single GraphQL call) |
|
|
164
|
+
|
|
159
165
|
All methods return fully-typed Pydantic models generated from the GraphQL schema.
|
|
160
166
|
|
|
161
167
|
## Development
|
|
@@ -257,3 +257,35 @@ class DeezerBaseClient:
|
|
|
257
257
|
logger.debug("JWT acquired, expires at %s", self._jwt_expires_at)
|
|
258
258
|
|
|
259
259
|
return self._jwt
|
|
260
|
+
|
|
261
|
+
async def check_audiobook_ids(self, album_ids: list[str]) -> set[str]:
|
|
262
|
+
"""Check which album IDs are also valid audiobooks on Deezer.
|
|
263
|
+
|
|
264
|
+
Uses GraphQL aliases to batch-check many IDs in a single request.
|
|
265
|
+
Returns the subset of IDs that are audiobooks (i.e., the audiobook
|
|
266
|
+
query returns non-null for them).
|
|
267
|
+
|
|
268
|
+
:param album_ids: List of Deezer album/audiobook IDs to check.
|
|
269
|
+
"""
|
|
270
|
+
if not album_ids:
|
|
271
|
+
return set()
|
|
272
|
+
|
|
273
|
+
# Query displayTitle alongside id — querying only { id } echoes back the
|
|
274
|
+
# input without validating that the ID is actually an audiobook.
|
|
275
|
+
parts = [
|
|
276
|
+
f'a{i}: audiobook(audiobookId: "{aid}") {{ id displayTitle }}'
|
|
277
|
+
for i, aid in enumerate(album_ids)
|
|
278
|
+
]
|
|
279
|
+
query = "{ " + " ".join(parts) + " }"
|
|
280
|
+
|
|
281
|
+
resp = await self.execute(query)
|
|
282
|
+
data = self.get_data(resp)
|
|
283
|
+
|
|
284
|
+
audiobook_ids: set[str] = set()
|
|
285
|
+
for i, aid in enumerate(album_ids):
|
|
286
|
+
node = data.get(f"a{i}")
|
|
287
|
+
# The API echoes back {id} for any valid album, so we must check
|
|
288
|
+
# a real audiobook field like displayTitle to distinguish.
|
|
289
|
+
if node is not None and node.get("displayTitle") is not None:
|
|
290
|
+
audiobook_ids.add(aid)
|
|
291
|
+
return audiobook_ids
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deezer-python-gql
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Async typed Python client for Deezer's Pipe GraphQL API.
|
|
5
5
|
Author-email: Julian Daberkow <jdaberkow@users.noreply.github.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -184,6 +184,12 @@ asyncio.run(main())
|
|
|
184
184
|
| `music_together_update_group_settings(...)` | Update group settings (name, family mode) |
|
|
185
185
|
| `music_together_generate_group_name()` | Generate a random group name |
|
|
186
186
|
|
|
187
|
+
### Utilities
|
|
188
|
+
|
|
189
|
+
| Method | Description |
|
|
190
|
+
| -------------------------------- | ---------------------------------------------------------------- |
|
|
191
|
+
| `check_audiobook_ids(album_ids)` | Batch-check which album IDs are audiobooks (single GraphQL call) |
|
|
192
|
+
|
|
187
193
|
All methods return fully-typed Pydantic models generated from the GraphQL schema.
|
|
188
194
|
|
|
189
195
|
## Development
|
|
@@ -67,6 +67,9 @@ from deezer_python_gql.generated.get_podcast_episode import GetPodcastEpisode
|
|
|
67
67
|
from deezer_python_gql.generated.get_podcast_episode_bookmarks import (
|
|
68
68
|
GetPodcastEpisodeBookmarks,
|
|
69
69
|
)
|
|
70
|
+
from deezer_python_gql.generated.get_podcast_episodes_by_ids import (
|
|
71
|
+
GetPodcastEpisodesByIds,
|
|
72
|
+
)
|
|
70
73
|
from deezer_python_gql.generated.get_recently_played import GetRecentlyPlayed
|
|
71
74
|
from deezer_python_gql.generated.get_recommendations import GetRecommendations
|
|
72
75
|
from deezer_python_gql.generated.get_similar_tracks import GetSimilarTracks
|
|
@@ -209,6 +212,7 @@ def test_client_has_generated_methods() -> None:
|
|
|
209
212
|
"remove_tracks_from_playlist",
|
|
210
213
|
"get_podcast",
|
|
211
214
|
"get_podcast_episode",
|
|
215
|
+
"get_podcast_episodes_by_ids",
|
|
212
216
|
"get_favorite_podcasts",
|
|
213
217
|
"get_podcast_episode_bookmarks",
|
|
214
218
|
"add_podcast_to_favorite",
|
|
@@ -467,7 +471,51 @@ def test_get_data_returns_data_on_success() -> None:
|
|
|
467
471
|
|
|
468
472
|
|
|
469
473
|
# ---------------------------------------------------------------------------
|
|
470
|
-
#
|
|
474
|
+
# 4a. check_audiobook_ids tests
|
|
475
|
+
# ---------------------------------------------------------------------------
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@pytest.mark.asyncio
|
|
479
|
+
async def test_check_audiobook_ids_returns_matching() -> None:
|
|
480
|
+
"""Verify check_audiobook_ids returns IDs that are valid audiobooks."""
|
|
481
|
+
client = DeezerBaseClient(arl="test_arl")
|
|
482
|
+
client._jwt = _make_jwt(exp=time.time() + 600) # noqa: SLF001
|
|
483
|
+
client._jwt_expires_at = time.time() + 600 # noqa: SLF001
|
|
484
|
+
|
|
485
|
+
# a0 is an audiobook (has displayTitle), a1 is not (displayTitle is null)
|
|
486
|
+
gql_response = httpx.Response(
|
|
487
|
+
200,
|
|
488
|
+
json={
|
|
489
|
+
"data": {
|
|
490
|
+
"a0": {"id": "111", "displayTitle": "Test Book"},
|
|
491
|
+
"a1": {"id": "222", "displayTitle": None},
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
request=httpx.Request("POST", DeezerBaseClient.PIPE_URL),
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
with patch("deezer_python_gql.base_client.httpx.AsyncClient") as mock_client_cls:
|
|
498
|
+
mock_instance = AsyncMock()
|
|
499
|
+
mock_instance.post = AsyncMock(return_value=gql_response)
|
|
500
|
+
mock_instance.__aenter__ = AsyncMock(return_value=mock_instance)
|
|
501
|
+
mock_instance.__aexit__ = AsyncMock(return_value=False)
|
|
502
|
+
mock_client_cls.return_value = mock_instance
|
|
503
|
+
|
|
504
|
+
result = await client.check_audiobook_ids(["111", "222"])
|
|
505
|
+
|
|
506
|
+
assert result == {"111"}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
@pytest.mark.asyncio
|
|
510
|
+
async def test_check_audiobook_ids_empty_input() -> None:
|
|
511
|
+
"""Verify check_audiobook_ids returns empty set for empty input."""
|
|
512
|
+
client = DeezerBaseClient(arl="test_arl")
|
|
513
|
+
result = await client.check_audiobook_ids([])
|
|
514
|
+
assert result == set()
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# ---------------------------------------------------------------------------
|
|
518
|
+
# 5. Model smoke tests (one per query — fixture-based)
|
|
471
519
|
# ---------------------------------------------------------------------------
|
|
472
520
|
|
|
473
521
|
|
|
@@ -995,6 +1043,20 @@ def test_smoke_get_podcast_episode() -> None:
|
|
|
995
1043
|
assert ep.podcast.display_title == "Tech Weekly"
|
|
996
1044
|
|
|
997
1045
|
|
|
1046
|
+
def test_smoke_get_podcast_episodes_by_ids() -> None:
|
|
1047
|
+
"""Verify GetPodcastEpisodesByIds fixture parses with nullable entries."""
|
|
1048
|
+
data = _load_fixture("get_podcast_episodes_by_ids.json")
|
|
1049
|
+
episodes = GetPodcastEpisodesByIds.model_validate(data).podcast_episodes_by_ids
|
|
1050
|
+
assert len(episodes) == 3
|
|
1051
|
+
assert episodes[0] is not None
|
|
1052
|
+
assert episodes[0].id == "ep_100"
|
|
1053
|
+
assert episodes[0].title == "Episode 100: AI Revolution"
|
|
1054
|
+
assert episodes[0].duration == 2400
|
|
1055
|
+
assert episodes[1] is not None
|
|
1056
|
+
assert episodes[1].id == "ep_099"
|
|
1057
|
+
assert episodes[2] is None
|
|
1058
|
+
|
|
1059
|
+
|
|
998
1060
|
def test_smoke_get_favorite_podcasts() -> None:
|
|
999
1061
|
"""Verify GetFavoritePodcasts fixture parses with pagination."""
|
|
1000
1062
|
data = _load_fixture("get_favorite_podcasts.json")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deezer_python_gql-0.6.0 → deezer_python_gql-0.7.0}/deezer_python_gql.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|