diff --git a/tests/conftest.py b/tests/conftest.py index a0e780d..11956ed 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,21 +3,22 @@ import pytest from yandex_music import Account, AdParams, Album, AlbumEvent, Artist, ArtistEvent, AutoRenewable, Best, Block, \ BlockEntity, CaseForms, Chart, ChartInfo, ChartInfoMenu, ChartInfoMenuItem, ChartItem, Client, Counts, Cover, Day, \ Description, DiscreteScale, Enum, Event, GeneratedPlaylist, Icon, Id, Images, InvocationInfo, Label, \ - LicenceTextPart, Link, Lyrics, MadeFor, Major, MetaData, MixLink, Normalization, Pager, PassportPhone, Permissions, \ - PersonalPlaylistsData, PlayContext, PlayContextsData, PlayCounter, Playlist, PlaylistAbsence, PlaylistId, Plus, \ - Price, Product, Promotion, Ratings, RenewableRemainder, Restrictions, RotorSettings, SearchResult, Sequence, \ - Settings, Shot, ShotData, ShotType, Station, StationResult, Status, Subscription, Tag, Title, Track, TrackId, \ - TrackPosition, TrackShort, TrackShortOld, TrackWithAds, User, Value, Video, VideoSupplement, Vinyl -from . import TestAccount, TestAdParams, TestAlbum, TestArtist, TestAutoRenewable, TestBest, TestBlock, TestBlockEntity, \ - TestCaseForms, TestChart, TestChartInfo, TestChartInfoMenuItem, TestCounts, TestCover, TestDay, TestDescription, \ - TestDiscreteScale, TestEnum, TestEvent, TestGeneratedPlaylist, TestIcon, TestId, TestImages, TestInvocationInfo, \ - TestLabel, TestLicenceTextPart, TestLink, TestLyrics, TestMajor, TestMetaData, TestMixLink, TestNormalization, \ - TestPager, TestPassportPhone, TestPermissions, TestPersonalPlaylistsData, TestPlayContext, TestPlayCounter, \ - TestPlaylist, TestPlaylistAbsence, TestPlaylistId, TestPlus, TestPrice, TestProduct, TestPromotion, TestRatings, \ - TestRenewableRemainder, TestRotorSettings, TestSearchResult, TestSequence, TestSettings, TestShot, TestShotData, \ - TestShotType, TestStation, TestStationResult, TestStatus, TestSubscription, TestTag, TestTitle, TestTrack, \ - TestTrackId, TestTrackPosition, TestTrackShort, TestTrackShortOld, TestTrackWithAds, TestUser, TestValue,\ - TestVideo, TestVideoSupplement, TestVinyl, TestArtistEvent + LicenceTextPart, Link, Lyrics, MadeFor, Major, MetaData, MixLink, Normalization, Pager, PassportPhone, \ + Permissions, PersonalPlaylistsData, PlayContext, PlayContextsData, PlayCounter, Playlist, PlaylistAbsence, \ + PlaylistId, Plus, Price, Product, Promotion, Ratings, RenewableRemainder, Restrictions, RotorSettings, \ + SearchResult, Sequence, Settings, Shot, ShotData, ShotType, Station, StationResult, Status, Subscription, Tag, \ + Title, Track, TrackId, TrackPosition, TrackShort, TrackShortOld, TrackWithAds, User, Value, Video, \ + VideoSupplement, Vinyl +from . import TestAccount, TestAdParams, TestAlbum, TestArtist, TestAutoRenewable, TestBest, TestBlock, \ + TestBlockEntity, TestCaseForms, TestChart, TestChartInfo, TestChartInfoMenuItem, TestCounts, TestCover, TestDay, \ + TestDescription, TestDiscreteScale, TestEnum, TestEvent, TestGeneratedPlaylist, TestIcon, TestId, TestImages, \ + TestInvocationInfo, TestLabel, TestLicenceTextPart, TestLink, TestLyrics, TestMajor, TestMetaData, TestMixLink, \ + TestNormalization, TestPager, TestPassportPhone, TestPermissions, TestPersonalPlaylistsData, TestPlayContext, \ + TestPlayCounter, TestPlaylist, TestPlaylistAbsence, TestPlaylistId, TestPlus, TestPrice, TestProduct,\ + TestPromotion, TestRatings, TestRenewableRemainder, TestRotorSettings, TestSearchResult, TestSequence, \ + TestSettings, TestShot, TestShotData, TestShotType, TestStation, TestStationResult, TestStatus, TestSubscription, \ + TestTag, TestTitle, TestTrack, TestTrackId, TestTrackPosition, TestTrackShort, TestTrackShortOld, \ + TestTrackWithAds, TestUser, TestValue, TestVideo, TestVideoSupplement, TestVinyl, TestArtistEvent @pytest.fixture(scope='session') @@ -118,7 +119,7 @@ def album_without_nested_albums(album_factory, artist_without_tracks, track_with @pytest.fixture(scope='session') -def playlist_factory(user, cover, made_for, track_short, play_counter, playlist_absence, artist): +def playlist_factory(user, cover, made_for, track_short, play_counter, playlist_absence, artist, track_id): class PlaylistFactory: def get(self, similar_playlists, last_owner_playlists): return Playlist(user, cover, made_for, play_counter, playlist_absence, TestPlaylist.uid, TestPlaylist.kind, @@ -128,11 +129,11 @@ def playlist_factory(user, cover, made_for, track_short, play_counter, playlist_ TestPlaylist.available, TestPlaylist.is_banner, TestPlaylist.is_premiere, TestPlaylist.duration_ms, TestPlaylist.og_image, TestPlaylist.og_title, TestPlaylist.og_description, TestPlaylist.image, cover, TestPlaylist.background_color, - TestPlaylist.text_color, TestPlaylist.id_for_from, [artist], [track_short], - TestPlaylist.prerolls, TestPlaylist.likes_count, similar_playlists, last_owner_playlists, - TestPlaylist.generated_playlist_type, TestPlaylist.animated_cover_uri, - TestPlaylist.ever_played, TestPlaylist.description, TestPlaylist.description_formatted, - TestPlaylist.is_for_from, TestPlaylist.regions) + TestPlaylist.text_color, TestPlaylist.id_for_from, TestPlaylist.coauthors, [artist], + [track_id], [track_short], TestPlaylist.prerolls, TestPlaylist.likes_count, + similar_playlists, last_owner_playlists, TestPlaylist.generated_playlist_type, + TestPlaylist.animated_cover_uri, TestPlaylist.ever_played, TestPlaylist.description, + TestPlaylist.description_formatted, TestPlaylist.is_for_from, TestPlaylist.regions) return PlaylistFactory() @@ -387,7 +388,7 @@ def renewable_remainder(): @pytest.fixture(scope='session') def user(): return User(TestUser.uid, TestUser.login, TestUser.name, TestUser.display_name, - TestUser.full_name, TestUser.sex, TestUser.verified) + TestUser.full_name, TestUser.sex, TestUser.verified, TestUser.regions) @pytest.fixture(scope='session') @@ -547,7 +548,7 @@ def restrictions(enum, discrete_scale): @pytest.fixture(scope='session') def results(track, artist, album, playlist, video, generated_playlist, promotion, chart_item, play_context, mix_link, - personal_playlists_data, play_contexts_data): + personal_playlists_data, play_contexts_data, user): return { 1: track, 2: artist, @@ -560,7 +561,10 @@ def results(track, artist, album, playlist, video, generated_playlist, promotion 9: play_context, 10: mix_link, 11: personal_playlists_data, - 12: play_contexts_data + 12: play_contexts_data, + 13: user, + 14: album, + 15: track } @@ -578,11 +582,14 @@ def types(): 9: 'play-context', 10: 'mix-link', 11: 'personal-playlists', - 12: 'play-contexts' + 12: 'play-contexts', + 13: 'user', + 14: 'podcast', + 15: 'podcast_episode' } -@pytest.fixture(scope='session', params=[1, 2, 3, 4, 5]) +@pytest.fixture(scope='session', params=[1, 2, 3, 4, 5, 13, 14, 15]) def result_with_type(request, results, types): return results[request.param], types[request.param] diff --git a/tests/test_like.py b/tests/test_like.py index b8421e4..b7ea2c0 100644 --- a/tests/test_like.py +++ b/tests/test_like.py @@ -5,13 +5,21 @@ from yandex_music import Like @pytest.fixture(scope='class', params=[2, 3, 4]) def like_with_param(request, results, types): - return Like(types[request.param], TestLike.id, TestLike.timestamp, + return Like(types[request.param], TestLike.id, TestLike.timestamp, short_description=TestLike.short_description, + description=TestLike.description, is_premiere=TestLike.is_premiere, is_banner=TestLike.is_banner, **{types[request.param]: results[request.param]}), request.param class TestLike: id = 5246018 timestamp = '2019-09-03T19:59:56+00:00' + short_description = 'Учим английский нескучно' + description = 'Английский по песням – это аудио- и видеоподкаст радио Unistar о том, как учить английский язык ' \ + 'по песням – хитам 90-х и 2000-х, которые звучат на Unistar. Никакого занудства, грамматических ' \ + 'правил и зубрежки. Только нескучный английский! Вы узнаете, о чем поют в любимых песнях, и как ' \ + 'это может помочь вам в общении во время путешествий.' + is_premiere = False + is_banner = True def test_expected_values(self, results, types, like_with_param): like, param = like_with_param @@ -19,6 +27,10 @@ class TestLike: assert like.type == types[param] assert like.id == self.id assert like.timestamp == self.timestamp + assert like.short_description == self.short_description + assert like.description == self.description + assert like.is_premiere == self.is_premiere + assert like.is_banner == self.is_banner assert getattr(like, like.type) == results[param] def test_de_json_none(self, client): @@ -31,12 +43,18 @@ class TestLike: def test_de_json_all(self, results, types, client, param): result, type_ = results[param], types[param] - json_dict = {'timestamp': self.timestamp, 'id_': self.id, type_: result.to_dict()} + json_dict = {'timestamp': self.timestamp, 'id_': self.id, type_: result.to_dict(), + 'short_description': self.short_description, 'description': self.description, + 'is_premiere': self.is_premiere, 'is_banner': self.is_banner} like = Like.de_json(json_dict, client, type_) assert like.type == type_ assert like.id == self.id assert like.timestamp == self.timestamp + assert like.short_description == self.short_description + assert like.description == self.description + assert like.is_premiere == self.is_premiere + assert like.is_banner == self.is_banner assert getattr(like, type_) == result @pytest.mark.parametrize('param', [2, 3, 4]) diff --git a/tests/test_playlist.py b/tests/test_playlist.py index 32bb380..627e1f0 100644 --- a/tests/test_playlist.py +++ b/tests/test_playlist.py @@ -25,6 +25,7 @@ class TestPlaylist: background_color = '' text_color = '' id_for_from = 'playlist_of_the_day' + coauthors = [1130000003905541] prerolls = [] likes_count = 1 generated_playlist_type = 'playlistOfTheDay' @@ -36,7 +37,7 @@ class TestPlaylist: regions = None def test_expected_values(self, playlist, user, cover, made_for, track_short, play_counter, playlist_absence, - playlist_without_nested_playlists, artist): + playlist_without_nested_playlists, artist, track_id): assert playlist.owner == user assert playlist.uid == self.uid assert playlist.kind == self.kind @@ -66,7 +67,9 @@ class TestPlaylist: assert playlist.background_color == self.background_color assert playlist.text_color == self.text_color assert playlist.id_for_from == self.id_for_from + assert playlist.coauthors == self.coauthors assert playlist.top_artist == [artist] + assert playlist.recent_tracks == [track_id] assert playlist.tracks == [track_short] assert playlist.prerolls == self.prerolls assert playlist.likes_count == self.likes_count @@ -98,7 +101,7 @@ class TestPlaylist: assert playlist.playlist_absence == playlist_absence def test_de_json_all(self, client, user, cover, made_for, track_short, play_counter, playlist_absence, - playlist_without_nested_playlists, artist): + playlist_without_nested_playlists, artist, track_id): json_dict = {'owner': user.to_dict(), 'uid': self.uid, 'kind': self.kind, 'title': self.title, 'track_count': self.track_count, 'cover': cover.to_dict(), 'made_for': made_for.to_dict(), 'play_counter': play_counter.to_dict(), 'playlist_absence': playlist_absence.to_dict(), @@ -112,10 +115,11 @@ class TestPlaylist: 'description': self.description, 'description_formatted': self.description_formatted, 'is_for_from': self.is_for_from, 'regions': self.regions, 'og_title': self.og_title, 'image': self.image, 'id_for_from': self.id_for_from, 'background_color': self.background_color, - 'text_color': self.text_color, 'cover_without_text': cover.to_dict(), + 'text_color': self.text_color, 'cover_without_text': cover.to_dict(), 'coauthors': self.coauthors, 'similar_playlists': [playlist_without_nested_playlists.to_dict()], 'last_owner_playlists': [playlist_without_nested_playlists.to_dict()], - 'og_description': self.og_description, 'top_artist': [artist.to_dict()]} + 'og_description': self.og_description, 'top_artist': [artist.to_dict()], + 'recent_tracks': [track_id.to_dict()]} playlist = Playlist.de_json(json_dict, client) assert playlist.owner == user @@ -147,7 +151,9 @@ class TestPlaylist: assert playlist.background_color == self.background_color assert playlist.text_color == self.text_color assert playlist.id_for_from == self.id_for_from + assert playlist.coauthors == self.coauthors assert playlist.top_artist == [artist] + assert playlist.recent_tracks == [track_id] assert playlist.tracks == [track_short] assert playlist.prerolls == self.prerolls assert playlist.likes_count == self.likes_count diff --git a/tests/test_search.py b/tests/test_search.py index 8c38d3b..5f9c04e 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -16,14 +16,18 @@ def search_result(results, types): @pytest.fixture(scope='class') def search(best, search_result): return Search(TestSearch.search_request_id, TestSearch.text, best, - search_result(3), search_result(2), search_result(4), - search_result(1), search_result(5), TestSearch.misspell_corrected, TestSearch.nocorrect) + search_result(3), search_result(2), search_result(4), search_result(1), search_result(5), + search_result(13), search_result(14), search_result(15), TestSearch.type_, TestSearch.page, + TestSearch.per_page, TestSearch.misspell_corrected, TestSearch.nocorrect) class TestSearch: search_request_id = 'myt1-0261-c2e-msk-myt-music-st-e72-18274.gencfg-c.yandex.net-1573323135801461' \ '-3742331365077765411-1573323135819 ' text = 'NCS' + type_ = 'artist' + page = 0 + per_page = 10 misspell_corrected = False nocorrect = False @@ -36,6 +40,12 @@ class TestSearch: assert search.playlists == search_result(4) assert search.tracks == search_result(1) assert search.videos == search_result(5) + assert search.users == search_result(13) + assert search.podcasts == search_result(14) + assert search.podcast_episodes == search_result(15) + assert search.type_ == self.type_ + assert search.page == self.page + assert search.per_page == self.per_page assert search.misspell_corrected == self.misspell_corrected assert search.nocorrect == self.nocorrect @@ -46,7 +56,8 @@ class TestSearch: json_dict = {'search_request_id': self.search_request_id, 'text': self.text, 'best': best.to_dict(), 'albums': search_result(3).to_dict(), 'artists': search_result(2).to_dict(), 'playlists': search_result(4).to_dict(), 'tracks': search_result(1).to_dict(), - 'videos': search_result(5).to_dict()} + 'videos': search_result(5).to_dict(), 'users': search_result(13).to_dict(), + 'podcasts': search_result(14).to_dict(), 'podcast_episodes': search_result(15).to_dict()} search = Search.de_json(json_dict, client) assert search.search_request_id == self.search_request_id @@ -57,13 +68,18 @@ class TestSearch: assert search.playlists == search_result(4) assert search.tracks == search_result(1) assert search.videos == search_result(5) + assert search.users == search_result(13) + assert search.podcasts == search_result(14) + assert search.podcast_episodes == search_result(15) def test_de_json_all(self, client, best, search_result): json_dict = {'search_request_id': self.search_request_id, 'text': self.text, 'best': best.to_dict(), 'albums': search_result(3).to_dict(), 'artists': search_result(2).to_dict(), 'playlists': search_result(4).to_dict(), 'tracks': search_result(1).to_dict(), - 'videos': search_result(5).to_dict(), 'misspell_corrected': self.misspell_corrected, - 'nocorrect': self.nocorrect} + 'videos': search_result(5).to_dict(), 'users': search_result(13).to_dict(), + 'podcasts': search_result(14).to_dict(), 'podcast_episodes': search_result(15).to_dict(), + 'misspell_corrected': self.misspell_corrected, 'nocorrect': self.nocorrect, + 'type_': self.type_, 'page': self.page, 'per_page': self.per_page} search = Search.de_json(json_dict, client) assert search.search_request_id == self.search_request_id @@ -74,16 +90,22 @@ class TestSearch: assert search.playlists == search_result(4) assert search.tracks == search_result(1) assert search.videos == search_result(5) + assert search.users == search_result(13) + assert search.podcasts == search_result(14) + assert search.podcast_episodes == search_result(15) + assert search.type_ == self.type_ + assert search.page == self.page + assert search.per_page == self.per_page assert search.misspell_corrected == self.misspell_corrected assert search.nocorrect == self.nocorrect def test_equality(self, best, search_result): a = Search(self.search_request_id, self.text, best, search_result(3), search_result(2), search_result(4), - search_result(1), search_result(5)) + search_result(1), search_result(5), search_result(13), search_result(14), search_result(15)) b = Search(self.search_request_id, '', best, search_result(3), None, search_result(4), search_result(1), - search_result(5)) + search_result(5), search_result(13), None, search_result(15)) c = Search(self.search_request_id, self.text, best, search_result(3), search_result(2), search_result(4), - search_result(1), search_result(5)) + search_result(1), search_result(5), search_result(13), search_result(14), search_result(15)) assert a != b assert hash(a) != hash(b) diff --git a/tests/test_track_id.py b/tests/test_track_id.py index a3e3ee5..2755658 100644 --- a/tests/test_track_id.py +++ b/tests/test_track_id.py @@ -12,6 +12,9 @@ class TestTrackId: def test_de_json_none(self, client): assert TrackId.de_json({}, client) is None + def test_de_list_none(self, client): + assert TrackId.de_list({}, client) == [] + def test_de_json_required(self, client): json_dict = {'id_': self.id} track_id = TrackId.de_json(json_dict, client) diff --git a/tests/test_user.py b/tests/test_user.py index 804f29a..c1f8f18 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -9,6 +9,7 @@ class TestUser: full_name = 'Илья' sex = 'unknown' verified = False + regions = ['RUSSIA_PREMIUM', 'RUSSIA'] def test_expected_values(self, user): assert user.uid == self.uid @@ -18,10 +19,14 @@ class TestUser: assert user.full_name == self.full_name assert user.sex == self.sex assert user.verified == self.verified + assert user.regions == self.regions def test_de_json_none(self, client): assert User.de_json({}, client) is None + def test_de_list_none(self, client): + assert User.de_list({}, client) == [] + def test_de_json_required(self, client): json_dict = {'uid': self.uid, 'login': self.login} user = User.de_json(json_dict, client) @@ -31,7 +36,8 @@ class TestUser: def test_de_json_all(self, client): json_dict = {'uid': self.uid, 'login': self.login, 'name': self.name, 'sex': self.sex, - 'verified': self.verified, 'display_name': self.display_name, 'full_name': self.full_name} + 'verified': self.verified, 'display_name': self.display_name, 'full_name': self.full_name, + 'regions': self.regions} user = User.de_json(json_dict, client) assert user.uid == self.uid @@ -41,6 +47,7 @@ class TestUser: assert user.full_name == self.full_name assert user.sex == self.sex assert user.verified == self.verified + assert user.regions == self.regions def test_equality(self): a = User(self.uid, self.login) diff --git a/yandex_music/client.py b/yandex_music/client.py index 7b898f4..5d1e2c8 100644 --- a/yandex_music/client.py +++ b/yandex_music/client.py @@ -763,11 +763,17 @@ class Client(YandexMusicObject): *args, **kwargs) -> Optional[Search]: """Осуществление поиска по запросу и типу, получение результатов. + Note: + Известные значения для поля `type_`: `all`, `artist`, `user`, `album`, `playlist`, `track`, `podcast`, + `podcast_episode`. + + При поиске `type=all` не возвращаются подкасты и эпизоды. Указывайте конкретный тип для поиска. + Args: text (:obj:`str`): Текст запроса. nocorrect (:obj:`bool`): Если :obj:`False`, то ошибочный запрос будет исправлен. Например, запрос "Гражданская абарона" будет исправлен на "Гражданская оборона". - type_ (:obj:`str`): Среди какого типа искать (трек, плейлист, альбом, исполнитель). + type_ (:obj:`str`): Среди какого типа искать (трек, плейлист, альбом, исполнитель, пользователь, подкаст). page (:obj:`int`): Номер страницы. playlist_in_best (:obj:`bool`): Выдавать ли плейлисты лучшим вариантом поиска. timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания diff --git a/yandex_music/landing/track_id.py b/yandex_music/landing/track_id.py index 9af1132..a842f81 100644 --- a/yandex_music/landing/track_id.py +++ b/yandex_music/landing/track_id.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Optional, List from yandex_music import YandexMusicObject @@ -51,3 +51,19 @@ class TrackId(YandexMusicObject): data = super(TrackId, cls).de_json(data, client) return cls(client=client, **data) + + @classmethod + def de_list(cls, data: dict, client: 'Client') -> List['TrackId']: + """Десериализация списка объектов. + + Args: + data (:obj:`list`): Список словарей с полями и значениями десериализуемого объекта. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + + Returns: + :obj:`list` из :obj:`yandex_music.TrackId`: Уникальные идентификаторы треков. + """ + if not data: + return [] + + return [cls.de_json(track_id, client) for track_id in data] diff --git a/yandex_music/like.py b/yandex_music/like.py index 8441204..efcb165 100644 --- a/yandex_music/like.py +++ b/yandex_music/like.py @@ -25,6 +25,10 @@ class Like(YandexMusicObject): album (:obj:`yandex_music.Album`): Понравившейся альбом. artist (:obj:`yandex_music.Artist`): Понравившейся артист. playlist (:obj:`yandex_music.Playlist`): Понравившейся плейлист. + short_description (:obj:`str`): Короткое описание. + description (:obj:`str`): Описание. + is_premiere (:obj:`bool`): Премьера ли. + is_banner (:obj:`bool`): Является ли баннером. client (:obj:`yandex_music.Client`): Клиент Yandex Music. Args: @@ -34,6 +38,10 @@ class Like(YandexMusicObject): album (:obj:`yandex_music.Album`, optional): Понравившейся альбом. artist (:obj:`yandex_music.Artist`, optional): Понравившейся артист. playlist (:obj:`yandex_music.Playlist`, optional): Понравившейся плейлист. + short_description (:obj:`str`, optional): Короткое описание. + description (:obj:`str`, optional): Описание. + is_premiere (:obj:`bool`, optional): Премьера ли. + is_banner (:obj:`bool`, optional): Является ли баннером. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. """ @@ -45,6 +53,10 @@ class Like(YandexMusicObject): album: Optional['Album'] = None, artist: Optional['Artist'] = None, playlist: Optional['Playlist'] = None, + short_description: Optional[str] = None, + description: Optional[str] = None, + is_premiere: Optional[bool] = None, + is_banner: Optional[bool] = None, client: Optional['Client'] = None, **kwargs) -> None: self.id = id_ @@ -54,6 +66,10 @@ class Like(YandexMusicObject): self.artist = artist self.playlist = playlist self.timestamp = timestamp + self.short_description = short_description + self.description = description + self.is_premiere = is_premiere + self.is_banner = is_banner self.client = client self._id_attrs = (self.id, self.type, self.timestamp, self.album, self.artist, self.playlist) diff --git a/yandex_music/playlist/playlist.py b/yandex_music/playlist/playlist.py index afeb367..af5052c 100644 --- a/yandex_music/playlist/playlist.py +++ b/yandex_music/playlist/playlist.py @@ -4,7 +4,7 @@ from yandex_music import YandexMusicObject if TYPE_CHECKING: from yandex_music import Client, User, Cover, MadeFor, TrackShort, PlaylistAbsence, PlayCounter,\ - PlaylistRecommendations, Artist + PlaylistRecommendations, Artist, TrackId class Playlist(YandexMusicObject): @@ -47,7 +47,9 @@ class Playlist(YandexMusicObject): background_color (:obj:`str`): Цвет заднего фона TODO. text_color (:obj:`str`): Цвет текста TODO. id_for_from (:obj:`str`): Откуда пришло событие (уникальный идентификатор объекта) TODO. + coauthors (:obj:`list` из :obj:`int`): Перечень ID аккаунтов соавторов плейлиста. top_artist (:obj:`list` из :obj:`yandex_music.Artist`): Топ артистов TODO. + recent_tracks (:obj:`list` из :obj:`yandex_music.TrackId`): Список ID недавних треков. tracks (:obj:`list` из :obj:`yandex_music.TrackShort`): Список треков. prerolls (:obj:`list`): Прерол, проигрываемый перед плейлистом. Присутствует только у персональных плейлистов. likes_count (:obj:`int`): Количество лайков. @@ -93,7 +95,9 @@ class Playlist(YandexMusicObject): background_color (:obj:`str`, optional): Цвет заднего фона TODO. text_color (:obj:`str`, optional): Цвет текста TODO. id_for_from (:obj:`str`, optional): Откуда пришло событие (уникальный идентификатор объекта) TODO. + coauthors (:obj:`list` из :obj:`int`, optional): Перечень ID аккаунтов соавторов плейлиста. top_artist (:obj:`list` из :obj:`yandex_music.Artist`, optional): Топ артистов TODO. + recent_tracks (:obj:`list` из :obj:`yandex_music.TrackId`, optional): Список ID недавних треков. tracks (:obj:`list` из :obj:`yandex_music.TrackShort`, optional): Список треков. prerolls (:obj:`list`, optional): Прерол, проигрываемый перед плейлистом. Присутствует только у персональных плейлистов. @@ -141,7 +145,9 @@ class Playlist(YandexMusicObject): background_color: Optional[str] = None, text_color: Optional[str] = None, id_for_from: Optional[str] = None, + coauthors: List[int] = None, top_artist: List['Artist'] = None, + recent_tracks: List['TrackId'] = None, tracks: List['TrackShort'] = None, prerolls: Optional[list] = None, likes_count: Optional[int] = None, @@ -185,7 +191,9 @@ class Playlist(YandexMusicObject): self.background_color = background_color self.text_color = text_color self.id_for_from = id_for_from + self.coauthors = coauthors self.top_artist = top_artist + self.recent_tracks = recent_tracks self.tracks = tracks self.prerolls = prerolls self.likes_count = likes_count @@ -282,12 +290,13 @@ class Playlist(YandexMusicObject): return None data = super(Playlist, cls).de_json(data, client) - from yandex_music import User, MadeFor, Cover, PlayCounter, TrackShort, PlaylistAbsence, Artist + from yandex_music import User, MadeFor, Cover, PlayCounter, TrackShort, PlaylistAbsence, Artist, TrackId data['owner'] = User.de_json(data.get('owner'), client) data['cover'] = Cover.de_json(data.get('cover'), client) data['cover_without_text'] = Cover.de_json(data.get('cover_without_text'), client) data['made_for'] = MadeFor.de_json(data.get('made_for'), client) data['tracks'] = TrackShort.de_list(data.get('tracks'), client) + data['recent_tracks'] = TrackId.de_list(data.get('recent_tracks'), client) data['play_counter'] = PlayCounter.de_json(data.get('play_counter'), client) data['top_artist'] = Artist.de_list(data.get('top_artist'), client) diff --git a/yandex_music/playlist/user.py b/yandex_music/playlist/user.py index 814dba5..7838114 100644 --- a/yandex_music/playlist/user.py +++ b/yandex_music/playlist/user.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Optional, List from yandex_music import YandexMusicObject @@ -16,6 +16,8 @@ class User(YandexMusicObject): При наличии экземпляра класса в `user_info` у `Track` (у самозагруженных треков) доступны только `uid`, '`login`, 'display_name` и `full_name`. + Поле `regions` есть только при возвращении пользователей в результатах поисках. + Attributes: uid (:obj:`int`): Идентификатор пользователя. login (:obj:`str`): Логин пользователя. @@ -24,6 +26,7 @@ class User(YandexMusicObject): full_name (:obj:`str`, optional): Полное имя пользователя. sex (:obj:`str`): Пол пользователя. verified (:obj:`bool`): Участвует ли пользователь в генерации плейлистов дня и т.д., и т.п. + regions (:obj:`list` из :obj:`int`): Список регионов TODO. client (:obj:`yandex_music.Client`): Клиент Yandex Music. Args: @@ -34,6 +37,7 @@ class User(YandexMusicObject): full_name (:obj:`str`, optional): Полное имя пользователя. sex (:obj:`str`, optional): Пол пользователя. verified (:obj:`bool`, optional): Участвует ли пользователь в генерации плейлистов дня и т.д., и т.п. + regions (:obj:`list` из :obj:`int`, optional): Список регионов TODO. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. """ @@ -46,6 +50,7 @@ class User(YandexMusicObject): full_name: Optional[str] = None, sex: Optional[str] = None, verified: Optional[bool] = None, + regions: List[int] = None, client: Optional['Client'] = None, **kwargs) -> None: self.uid = uid @@ -56,6 +61,7 @@ class User(YandexMusicObject): self.full_name = full_name self.sex = sex self.verified = verified + self.regions = regions self.client = client self._id_attrs = (self.uid, self.login) @@ -89,6 +95,22 @@ class User(YandexMusicObject): return cls(client=client, **data) + @classmethod + def de_list(cls, data: dict, client: 'Client') -> List['User']: + """Десериализация списка объектов. + + Args: + data (:obj:`list`): Список словарей с полями и значениями десериализуемого объекта. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + + Returns: + :obj:`list` из :obj:`yandex_music.User`: Пользователи. + """ + if not data: + return [] + + return [cls.de_json(user, client) for user in data] + # camelCase псевдонимы #: Псевдоним для :attr:`download_avatar` diff --git a/yandex_music/search/search.py b/yandex_music/search/search.py index a5f4877..154ae97 100644 --- a/yandex_music/search/search.py +++ b/yandex_music/search/search.py @@ -18,6 +18,12 @@ class Search(YandexMusicObject): playlists (:obj:`yandex_music.SearchResult`): Найденные плейлисты. tracks (:obj:`yandex_music.SearchResult`): Найденные треки. videos (:obj:`yandex_music.SearchResult`): Найденные видео. + users (:obj:`yandex_music.SearchResult`): Найденные пользователи. + podcasts (:obj:`yandex_music.SearchResult`): Найденные подскасты. + podcast_episodes (:obj:`yandex_music.SearchResult`): Найденные выпуски подкастов. + type_ (:obj:`str`): Тип результата по которому искали (аргумент в Client.search). + page (:obj:`int`): Текущая страница. + per_page (:obj:`int`): Результатов на странице. misspell_corrected (:obj:`bool`): Был ли исправлен запрос. nocorrect (:obj:`bool`): Было ли отключено исправление результата. client (:obj:`yandex_music.Client`): Клиент Yandex Music. @@ -31,6 +37,12 @@ class Search(YandexMusicObject): playlists (:obj:`yandex_music.SearchResult`): Найденные плейлисты. tracks (:obj:`yandex_music.SearchResult`): Найденные треки. videos (:obj:`yandex_music.SearchResult`): Найденные видео. + users (:obj:`yandex_music.SearchResult`): Найденные пользователи. + podcasts (:obj:`yandex_music.SearchResult`): Найденные подскасты. + podcast_episodes (:obj:`yandex_music.SearchResult`): Найденные выпуски подкастов. + type_ (:obj:`str`), optional: Тип результата по которому искали (аргумент в Client.search). + page (:obj:`int`, optional): Текущая страница. + per_page (:obj:`int`, optional): Результатов на странице. misspell_corrected (:obj:`bool`, optional): Был ли исправлен запрос. nocorrect (:obj:`bool`, optional): Было ли отключено исправление результата. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. @@ -46,6 +58,12 @@ class Search(YandexMusicObject): playlists: Optional['SearchResult'], tracks: Optional['SearchResult'], videos: Optional['SearchResult'], + users: Optional['SearchResult'], + podcasts: Optional['SearchResult'], + podcast_episodes: Optional['SearchResult'], + type_: Optional[str] = None, + page: Optional[int] = None, + per_page: Optional[int] = None, misspell_corrected: Optional[bool] = None, nocorrect: Optional[bool] = None, client: Optional['Client'] = None, @@ -58,13 +76,19 @@ class Search(YandexMusicObject): self.playlists = playlists self.tracks = tracks self.videos = videos + self.users = users + self.podcasts = podcasts + self.podcast_episodes = podcast_episodes + self.type_ = type_ + self.page = page + self.per_page = per_page self.misspell_corrected = misspell_corrected self.nocorrect = nocorrect self.client = client - self._id_attrs = (self.search_request_id, self.text, self.best, self.albums, - self.artists, self.playlists, self.tracks, self.videos) + self._id_attrs = (self.search_request_id, self.text, self.best, self.albums, self.artists, self.playlists, + self.tracks, self.videos, self.users, self.podcasts, self.podcast_episodes) super().handle_unknown_kwargs(self, **kwargs) @@ -90,5 +114,8 @@ class Search(YandexMusicObject): data['playlists'] = SearchResult.de_json(data.get('playlists'), client, 'playlist') data['tracks'] = SearchResult.de_json(data.get('tracks'), client, 'track') data['videos'] = SearchResult.de_json(data.get('videos'), client, 'video') + data['users'] = SearchResult.de_json(data.get('users'), client, 'user') + data['podcasts'] = SearchResult.de_json(data.get('podcasts'), client, 'podcast') + data['podcast_episodes'] = SearchResult.de_json(data.get('podcast_episodes'), client, 'podcast_episode') return cls(client=client, **data) diff --git a/yandex_music/search/search_result.py b/yandex_music/search/search_result.py index 5fca539..fa31159 100644 --- a/yandex_music/search/search_result.py +++ b/yandex_music/search/search_result.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Optional, List, Union -from yandex_music import YandexMusicObject, Artist, Album, Track, Playlist, Video +from yandex_music import YandexMusicObject, Artist, Album, Track, Playlist, Video, User if TYPE_CHECKING: from yandex_music import Client @@ -12,6 +12,9 @@ de_json_result = { 'album': Album.de_list, 'playlist': Playlist.de_list, 'video': Video.de_list, + 'user': User.de_list, + 'podcast': Album.de_list, + 'podcast_episode': Track.de_list, }