From 2603f12fdf50ad78d58698c866b8c18e8ed31dcd Mon Sep 17 00:00:00 2001 From: Alexandr Kuryanov Date: Fri, 3 Apr 2026 18:05:27 +0300 Subject: [PATCH 1/3] fix models for get_album_with_tracks method --- tests/albums/test_get_album_with_tracks.py | 21 +++++++++++++++++++++ ymdantic/models/tracks/__init__.py | 2 +- ymdantic/models/tracks/album.py | 3 ++- ymdantic/models/tracks/podcast.py | 15 ++++++++++++++- ymdantic/models/tracks/track.py | 6 ++++-- 5 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 tests/albums/test_get_album_with_tracks.py diff --git a/tests/albums/test_get_album_with_tracks.py b/tests/albums/test_get_album_with_tracks.py new file mode 100644 index 0000000..14e5f2b --- /dev/null +++ b/tests/albums/test_get_album_with_tracks.py @@ -0,0 +1,21 @@ +import pytest +from ymdantic import YMClient +from ymdantic.models import Album + + +@pytest.mark.anyio +async def test_get_available_track_download_info(client: YMClient) -> None: + album_id = 1814060 + + result = await client.get_album_with_tracks(album_id=album_id) + + assert result is not None + assert isinstance(result, Album) + assert result.id == album_id + assert result.title is not None + assert result.artists is not None + assert len(result.artists) > 0 + assert result.track_count > 0 + assert result.volumes is not None + assert len(result.volumes) > 0 + assert len(result.volumes[0]) > 0 diff --git a/ymdantic/models/tracks/__init__.py b/ymdantic/models/tracks/__init__.py index da44d86..fc3deb9 100644 --- a/ymdantic/models/tracks/__init__.py +++ b/ymdantic/models/tracks/__init__.py @@ -10,7 +10,7 @@ from ymdantic.models.tracks.r128 import R128 from ymdantic.models.tracks.track import Track, UnavailableTrack -TrackType = Union[UnavailablePodcast, Podcast, Track, UnavailableTrack] +TrackType = Union[Track, UnavailableTrack, Podcast, UnavailablePodcast] __all__ = ( "R128", diff --git a/ymdantic/models/tracks/album.py b/ymdantic/models/tracks/album.py index 0a11325..2cdb803 100644 --- a/ymdantic/models/tracks/album.py +++ b/ymdantic/models/tracks/album.py @@ -12,4 +12,5 @@ class TrackAlbum(BaseAlbum): # Дата начала альбома. track_position: Optional[TrackPosition] = None # Позиция трека в альбоме (если есть). - listening_finished: bool + listening_finished: Optional[bool] = None + # Флаг, указывающий, завершено ли прослушивание альбома. diff --git a/ymdantic/models/tracks/podcast.py b/ymdantic/models/tracks/podcast.py index 917f052..c6b725b 100644 --- a/ymdantic/models/tracks/podcast.py +++ b/ymdantic/models/tracks/podcast.py @@ -1,7 +1,9 @@ """Модели подкастов.""" from datetime import date -from typing import Literal +from typing import Any, Literal + +from pydantic import model_validator from ymdantic.mixins import DeprecatedMixin from ymdantic.models.tracks.album import TrackAlbum @@ -25,6 +27,17 @@ class UnavailablePodcast(UnavailableTrack, DeprecatedMixin): short_description: str | None = None # Краткое описание подкаста (если есть). + @model_validator(mode="before") + @classmethod + def validate_not_available(cls, data: dict[str, Any]) -> dict[str, Any]: + """Убеждаемся, что это действительно недоступный трек.""" + # Если трек доступен и это не подкаст-эпизод — не используем эту модель + if data.get("available") is True and data.get("type") != "podcast-episode": + raise ValueError( + f"Available track {data.get('id')} should not be UnavailablePodcast", + ) + return data + class Podcast(Track): """Модель информации о доступном подкасте.""" diff --git a/ymdantic/models/tracks/track.py b/ymdantic/models/tracks/track.py index 4917e4b..ec6f59c 100644 --- a/ymdantic/models/tracks/track.py +++ b/ymdantic/models/tracks/track.py @@ -81,6 +81,8 @@ class BaseTrack(YMBaseModel, DeprecatedMixin): best: bool | None = None # Является ли трек лучшим (поле доступно при получении альбома с треками # `get_album_with_tracks`). + listening_finished: bool | None = None + # Флаг, указывающий, завершено ли прослушивание альбома. @property def artists_names(self) -> str | None: @@ -131,7 +133,7 @@ class UnavailableTrack(BaseTrack): type: Literal["music", "asmr", "audiobook", "noise", "fairy-tale"] # Тип трека. - available: Literal[False] + available: Literal[False] = False # Доступность трека. В данном случае трек недоступен. error: Literal["no-rights"] | None = None # Ошибка, связанная с треком. В данном случае может быть ошибка @@ -165,7 +167,7 @@ class Track(BaseTrack): type: Literal["music", "asmr", "audiobook", "noise", "fairy-tale"] # Тип трека. - available: Literal[True] + available: bool = True # Доступность трека. В данном случае трек доступен. title: str # Название трека. From 512808b90a5408f2b17ce728f02da68ab991cd4a Mon Sep 17 00:00:00 2001 From: Alexandr Kuryanov Date: Fri, 3 Apr 2026 20:18:06 +0300 Subject: [PATCH 2/3] fix models --- tests/albums/test_get_album_with_tracks.py | 27 +++++++++++++++++++++- ymdantic/models/tracks/__init__.py | 2 +- ymdantic/models/tracks/podcast.py | 15 +----------- ymdantic/models/tracks/track.py | 4 ++-- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/tests/albums/test_get_album_with_tracks.py b/tests/albums/test_get_album_with_tracks.py index 14e5f2b..4494a59 100644 --- a/tests/albums/test_get_album_with_tracks.py +++ b/tests/albums/test_get_album_with_tracks.py @@ -1,16 +1,18 @@ import pytest from ymdantic import YMClient +from ymdantic.exceptions import YMError from ymdantic.models import Album @pytest.mark.anyio -async def test_get_available_track_download_info(client: YMClient) -> None: +async def test_get_available_album_with_tracks(client: YMClient) -> None: album_id = 1814060 result = await client.get_album_with_tracks(album_id=album_id) assert result is not None assert isinstance(result, Album) + assert result.meta_type == "music" assert result.id == album_id assert result.title is not None assert result.artists is not None @@ -19,3 +21,26 @@ async def test_get_available_track_download_info(client: YMClient) -> None: assert result.volumes is not None assert len(result.volumes) > 0 assert len(result.volumes[0]) > 0 + + +@pytest.mark.anyio +async def test_get_not_found_album_with_tracks(client: YMClient) -> None: + with pytest.raises(YMError): + await client.get_album_with_tracks(album_id=181406011111) + + +async def test_get_available_album_with_podcast(client: YMClient) -> None: + album_id = 10141723 + + result = await client.get_album_with_tracks(album_id=album_id) + + assert result is not None + assert isinstance(result, Album) + assert result.meta_type == "podcast" + assert result.id == album_id + assert result.title is not None + assert result.artists is not None + assert result.track_count > 0 + assert result.volumes is not None + assert len(result.volumes) > 0 + assert len(result.volumes[0]) > 0 diff --git a/ymdantic/models/tracks/__init__.py b/ymdantic/models/tracks/__init__.py index fc3deb9..da44d86 100644 --- a/ymdantic/models/tracks/__init__.py +++ b/ymdantic/models/tracks/__init__.py @@ -10,7 +10,7 @@ from ymdantic.models.tracks.r128 import R128 from ymdantic.models.tracks.track import Track, UnavailableTrack -TrackType = Union[Track, UnavailableTrack, Podcast, UnavailablePodcast] +TrackType = Union[UnavailablePodcast, Podcast, Track, UnavailableTrack] __all__ = ( "R128", diff --git a/ymdantic/models/tracks/podcast.py b/ymdantic/models/tracks/podcast.py index c6b725b..917f052 100644 --- a/ymdantic/models/tracks/podcast.py +++ b/ymdantic/models/tracks/podcast.py @@ -1,9 +1,7 @@ """Модели подкастов.""" from datetime import date -from typing import Any, Literal - -from pydantic import model_validator +from typing import Literal from ymdantic.mixins import DeprecatedMixin from ymdantic.models.tracks.album import TrackAlbum @@ -27,17 +25,6 @@ class UnavailablePodcast(UnavailableTrack, DeprecatedMixin): short_description: str | None = None # Краткое описание подкаста (если есть). - @model_validator(mode="before") - @classmethod - def validate_not_available(cls, data: dict[str, Any]) -> dict[str, Any]: - """Убеждаемся, что это действительно недоступный трек.""" - # Если трек доступен и это не подкаст-эпизод — не используем эту модель - if data.get("available") is True and data.get("type") != "podcast-episode": - raise ValueError( - f"Available track {data.get('id')} should not be UnavailablePodcast", - ) - return data - class Podcast(Track): """Модель информации о доступном подкасте.""" diff --git a/ymdantic/models/tracks/track.py b/ymdantic/models/tracks/track.py index ec6f59c..69fab91 100644 --- a/ymdantic/models/tracks/track.py +++ b/ymdantic/models/tracks/track.py @@ -133,7 +133,7 @@ class UnavailableTrack(BaseTrack): type: Literal["music", "asmr", "audiobook", "noise", "fairy-tale"] # Тип трека. - available: Literal[False] = False + available: Literal[False] # Доступность трека. В данном случае трек недоступен. error: Literal["no-rights"] | None = None # Ошибка, связанная с треком. В данном случае может быть ошибка @@ -167,7 +167,7 @@ class Track(BaseTrack): type: Literal["music", "asmr", "audiobook", "noise", "fairy-tale"] # Тип трека. - available: bool = True + available: Literal[True] # Доступность трека. В данном случае трек доступен. title: str # Название трека. From 15a97509331c2469196a47662f4aa69a66b0b078 Mon Sep 17 00:00:00 2001 From: Alexandr Kuryanov Date: Fri, 3 Apr 2026 20:57:26 +0300 Subject: [PATCH 3/3] add test_get_random_album --- tests/albums/test_get_album_with_tracks.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/albums/test_get_album_with_tracks.py b/tests/albums/test_get_album_with_tracks.py index 4494a59..952216e 100644 --- a/tests/albums/test_get_album_with_tracks.py +++ b/tests/albums/test_get_album_with_tracks.py @@ -1,3 +1,6 @@ +import asyncio +import secrets + import pytest from ymdantic import YMClient from ymdantic.exceptions import YMError @@ -44,3 +47,18 @@ async def test_get_available_album_with_podcast(client: YMClient) -> None: assert result.volumes is not None assert len(result.volumes) > 0 assert len(result.volumes[0]) > 0 + + +@pytest.mark.anyio +async def test_get_random_album(client: YMClient) -> None: + album_ids = [secrets.randbelow(140_000_000) for _ in range(50)] + results = await asyncio.gather( + *[client.get_album_with_tracks(album_id=album_id) for album_id in album_ids], + return_exceptions=True, + ) + error_results = [ + result + for result in results + if isinstance(result, BaseException) and not isinstance(result, YMError) + ] + assert len(error_results) == 0