diff --git a/tests/test_tracks_similar.py b/tests/test_tracks_similar.py new file mode 100644 index 0000000..05ae9db --- /dev/null +++ b/tests/test_tracks_similar.py @@ -0,0 +1,42 @@ +import pytest + +from yandex_music import SimilarTracks + + +@pytest.fixture(scope='class') +def similar_tracks(track): + return SimilarTracks(track, [track]) + + +class TestSimilarTracks: + def test_expected_values(self, similar_tracks, track): + assert similar_tracks.track == track + assert similar_tracks.similar_tracks == [track] + + def test_de_json_none(self, client): + assert SimilarTracks.de_json({}, client) is None + + def test_de_json_required(self, client, track): + json_dict = {'track': track.to_dict(), 'similar_tracks': [track.to_dict()]} + similar_tracks = SimilarTracks.de_json(json_dict, client) + + assert similar_tracks.track == track + assert similar_tracks.similar_tracks == [track] + + def test_de_json_all(self, client, track): + json_dict = {'track': track.to_dict(), 'similar_tracks': [track.to_dict()]} + similar_tracks = SimilarTracks.de_json(json_dict, client) + + assert similar_tracks.track == track + assert similar_tracks.similar_tracks == [track] + + def test_equality(self, track): + a = SimilarTracks(track, [track]) + b = SimilarTracks(None, [track]) + c = SimilarTracks(track, [track]) + + assert a != b + assert hash(a) != hash(b) + assert a is not b + + assert a == c diff --git a/yandex_music/__init__.py b/yandex_music/__init__.py index f5d2c01..8e8b1c9 100644 --- a/yandex_music/__init__.py +++ b/yandex_music/__init__.py @@ -44,6 +44,7 @@ from .tracks_list import TracksList from .track.major import Major from .track.normalization import Normalization from .track.track import Track +from .track.tracks_similar import SimilarTracks from .likes.albums_likes import AlbumsLikes from .likes.artists_likes import ArtistsLikes @@ -119,4 +120,4 @@ __all__ = ['YandexMusicObject', 'Client', 'Account', 'PassportPhone', 'Invocatio 'Icon', 'Images', 'Id', 'Station', 'Dashboard', 'RotorSettings', 'AdParams', 'Restrictions', 'Value', 'Enum', 'DiscreteScale', 'StationResult', 'Sequence', 'StationTracksResult', 'BriefInfo', 'Description', 'PlaylistId', 'Vinyl', 'Supplement', 'Lyrics', 'VideoSupplement', 'ArtistTracks', 'Pager', 'ArtistAlbums', - 'PlaylistAbsence', 'Shot', 'ShotEvent', 'ShotType', 'ShotData'] + 'PlaylistAbsence', 'Shot', 'ShotEvent', 'ShotType', 'ShotData', 'SimilarTracks'] diff --git a/yandex_music/client.py b/yandex_music/client.py index 795c4d7..c05c0dc 100644 --- a/yandex_music/client.py +++ b/yandex_music/client.py @@ -6,7 +6,7 @@ from typing import Callable, Union, List, Optional from yandex_music import YandexMusicObject, Status, Settings, PermissionAlerts, Experiments, Artist, Album, Playlist, \ TracksList, Track, AlbumsLikes, ArtistsLikes, PlaylistsLikes, Feed, PromoCodeStatus, DownloadInfo, Search, \ Suggestions, Landing, Genre, Dashboard, StationResult, StationTracksResult, BriefInfo, Supplement, ArtistTracks, \ - ArtistAlbums, ShotEvent + ArtistAlbums, ShotEvent, SimilarTracks from yandex_music.utils.request import Request from yandex_music.utils.difference import Difference from yandex_music.exceptions import InvalidToken, Captcha @@ -457,6 +457,8 @@ class Client(YandexMusicObject): :obj:`yandex_music.Supplement`: Объект класса `yandex_music.Supplement` представляющий дополнительную информацию о треке. + Raises: + :class:`yandex_music.YandexMusicError` """ url = f'{self.base_url}/tracks/{track_id}/supplement' @@ -465,6 +467,31 @@ class Client(YandexMusicObject): return Supplement.de_json(result, self) + @log + def tracks_similar(self, track_id: Union[str, int], timeout: Union[int, float] = None, + *args, **kwargs) -> Optional: + """Получение похожих треков. + + Args: + track_id (:obj:`str`): Уникальный идентификатор трека. + timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания + ответа от сервера вместо указанного при создании пула. + **kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос). + + Returns: + :obj:`yandex_music.SimilarTracks`: Объект класса `yandex_music.SimilarTracks` представляющий список похожих + треков на другой трек. + + Raises: + :class:`yandex_music.YandexMusicError` + """ + + url = f'{self.base_url}/tracks/{track_id}/similar' + + result = self._request.get(url, timeout=timeout, *args, **kwargs) + + return SimilarTracks.de_json(result, self) + @log def play_audio(self, track_id: Union[str, int], diff --git a/yandex_music/track/tracks_similar.py b/yandex_music/track/tracks_similar.py new file mode 100644 index 0000000..0ac2ef4 --- /dev/null +++ b/yandex_music/track/tracks_similar.py @@ -0,0 +1,49 @@ +from typing import TYPE_CHECKING, Optional, List + +if TYPE_CHECKING: + from yandex_music import Client, Track + +from yandex_music import YandexMusicObject + + +class SimilarTracks(YandexMusicObject): + """Класс, представляющий список похожих треков на другой трек. + + Attributes: + track (:obj:`yandex_music.Track`): Объект класса :class:`yandex_music.Track` представляющий трек. + similar_tracks (:obj:`list` из :obj:`yandex_music.Track`): Список объектов класса + :class:`yandex_music.Track` представляющие похожие треки на `track`. + client (:obj:`yandex_music.Client`): Объект класса :class:`yandex_music.Client` представляющий клиент Yandex + Music. + + Args: + track (:obj:`yandex_music.Track`): Объект класса :class:`yandex_music.Track` представляющий трек. + similar_tracks (:obj:`list` из :obj:`yandex_music.Track`): Список объектов класса + :class:`yandex_music.Track` представляющие похожие треки на `track`. + client (:obj:`yandex_music.Client`, optional): Объект класса :class:`yandex_music.Client` представляющий клиент + Yandex Music. + **kwargs: Произвольные ключевые аргументы полученные от API. + """ + + def __init__(self, + track: Optional['Track'], + similar_tracks: List['Track'], + client: Optional['Client'] = None, + **kwargs) -> None: + self.track = track + self.similar_tracks = similar_tracks + + self.client = client + self._id_attrs = (self.track, self.similar_tracks) + + @classmethod + def de_json(cls, data: dict, client: 'Client') -> Optional['SimilarTracks']: + if not data: + return None + + data = super(SimilarTracks, cls).de_json(data, client) + from yandex_music import Track + data['track'] = Track.de_json(data.get('track'), client) + data['similar_tracks'] = Track.de_list(data.get('similar_tracks'), client) + + return cls(client=client, **data)