From f96732d674317ef9de4e970a603bb9cea1f99021 Mon Sep 17 00:00:00 2001 From: Il`ya Semyonov Date: Sat, 16 May 2020 01:00:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BA=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D1=81:=20LandingList.=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B:=20new=5Freleases,=20new?= =?UTF-8?q?=5Fplaylists,=20podcasts.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yandex_music.landing.landing_list.rst | 7 ++ docs/source/yandex_music.landing.rst | 1 + tests/test_landing_list.py | 64 +++++++++++++++ yandex_music/__init__.py | 3 +- yandex_music/client.py | 72 ++++++++++++++++- yandex_music/landing/landing_list.py | 81 +++++++++++++++++++ 6 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 docs/source/yandex_music.landing.landing_list.rst create mode 100644 tests/test_landing_list.py create mode 100644 yandex_music/landing/landing_list.py diff --git a/docs/source/yandex_music.landing.landing_list.rst b/docs/source/yandex_music.landing.landing_list.rst new file mode 100644 index 0000000..7fab5da --- /dev/null +++ b/docs/source/yandex_music.landing.landing_list.rst @@ -0,0 +1,7 @@ +yandex_music.LandingList +======================== + +.. autoclass:: yandex_music.LandingList + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/yandex_music.landing.rst b/docs/source/yandex_music.landing.rst index 8cfb6ff..4c6d562 100644 --- a/docs/source/yandex_music.landing.rst +++ b/docs/source/yandex_music.landing.rst @@ -18,3 +18,4 @@ yandex_music.landing.chart_info yandex_music.landing.chart_info_menu yandex_music.landing.chart_info_menu_item + yandex_music.landing.landing_list diff --git a/tests/test_landing_list.py b/tests/test_landing_list.py new file mode 100644 index 0000000..1ac5548 --- /dev/null +++ b/tests/test_landing_list.py @@ -0,0 +1,64 @@ +import pytest + +from yandex_music import LandingList + + +@pytest.fixture(scope='class') +def landing_list(playlist_id): + return LandingList(TestLandingList.type, TestLandingList.type_for_from, TestLandingList.title, TestLandingList.id, + TestLandingList.new_releases, [playlist_id], TestLandingList.podcasts) + + +class TestLandingList: + id = 'fNdCYuAs' + title = 'Новые треки, альбомы и сборники' + type = 'new-releases' + type_for_from = 'new-releases' + new_releases = [10704986, 10527291, 9479589] + podcasts = [10532030, 8693523, 10509632] + + def test_expected_values(self, landing_list, playlist_id): + assert landing_list.id == self.id + assert landing_list.title == self.title + assert landing_list.type == self.type + assert landing_list.type_for_from == self.type_for_from + assert landing_list.new_releases == self.new_releases + assert landing_list.podcasts == self.podcasts + assert landing_list.new_playlists == [playlist_id] + + def test_de_json_none(self, client): + assert LandingList.de_json({}, client) is None + + def test_de_json_required(self, client): + json_dict = {'title': self.title, 'type_': self.type, 'type_for_from': self.type_for_from} + landing_list = LandingList.de_json(json_dict, client) + + assert landing_list.title == self.title + assert landing_list.type == self.type + assert landing_list.type_for_from == self.type_for_from + + def test_de_json_all(self, client, playlist_id): + json_dict = {'title': self.title, 'type_': self.type, 'type_for_from': self.type_for_from, 'id_': self.id, + 'new_releases': self.new_releases, 'podcasts': self.podcasts, + 'new_playlists': [playlist_id.to_dict()]} + landing_list = LandingList.de_json(json_dict, client) + + assert landing_list.id == self.id + assert landing_list.title == self.title + assert landing_list.type == self.type + assert landing_list.type_for_from == self.type_for_from + assert landing_list.new_releases == self.new_releases + assert landing_list.podcasts == self.podcasts + assert landing_list.new_playlists == [playlist_id] + + def test_equality(self, playlist_id): + a = LandingList(self.type, self.type_for_from, self.title, self.id, self.new_releases, [playlist_id], []) + b = LandingList(self.type, self.type_for_from, self.title, self.id, self.new_releases, [], []) + c = LandingList(self.type, self.type_for_from, self.title, '', self.new_releases, [playlist_id], []) + d = LandingList(self.type, self.type_for_from, self.title, self.id, self.new_releases, [playlist_id], []) + + assert a != b != c + assert hash(a) != hash(b) != hash(c) + assert a is not b is not c + + assert a == d diff --git a/yandex_music/__init__.py b/yandex_music/__init__.py index 427d319..39a6bb7 100644 --- a/yandex_music/__init__.py +++ b/yandex_music/__init__.py @@ -76,6 +76,7 @@ from .landing.promotion import Promotion from .landing.block_entity import BlockEntity from .landing.landing import Landing from .landing.block import Block +from .landing.landing_list import LandingList from .landing.chart_info_menu_item import ChartInfoMenuItem from .landing.chart_info_menu import ChartInfoMenu from .landing.chart_info import ChartInfo @@ -126,4 +127,4 @@ __all__ = ['YandexMusicObject', 'Client', 'Account', 'PassportPhone', 'Invocatio 'Sequence', 'StationTracksResult', 'BriefInfo', 'Description', 'PlaylistId', 'Vinyl', 'Supplement', 'Lyrics', 'VideoSupplement', 'ArtistTracks', 'Pager', 'ArtistAlbums', 'PlaylistAbsence', 'Shot', 'ShotEvent', 'ShotType', 'ShotData', 'SimilarTracks', 'UserSettings', 'RenewableRemainder', 'ChartInfo', 'ChartInfoMenu', - 'ChartInfoMenuItem', 'Tag', 'TagResult', 'PlaylistRecommendations'] + 'ChartInfoMenuItem', 'Tag', 'TagResult', 'PlaylistRecommendations', 'LandingList'] diff --git a/yandex_music/client.py b/yandex_music/client.py index 916166a..dbe8716 100644 --- a/yandex_music/client.py +++ b/yandex_music/client.py @@ -6,7 +6,7 @@ from typing import Callable, Dict, List, Optional, Union from yandex_music import Album, Artist, ArtistAlbums, ArtistTracks, BriefInfo, Dashboard, DownloadInfo, Experiments, \ Feed, Genre, Landing, Like, PermissionAlerts, Playlist, PromoCodeStatus, Search, Settings, ShotEvent, Supplement, \ StationResult, StationTracksResult, Status, Suggestions, SimilarTracks, Track, TracksList, UserSettings, \ - YandexMusicObject, ChartInfo, TagResult, PlaylistRecommendations + YandexMusicObject, ChartInfo, TagResult, PlaylistRecommendations, LandingList from yandex_music.exceptions import Captcha, InvalidToken from yandex_music.utils.difference import Difference from yandex_music.utils.request import Request @@ -473,6 +473,72 @@ class Client(YandexMusicObject): return ChartInfo.de_json(result, self) + @log + def new_releases(self, timeout: Union[int, float] = None, *args, **kwargs) -> Optional[LandingList]: + """Получение полного списка всех новых релизов (альбомов). + + Args: + timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания + ответа от сервера вместо указанного при создании пула. + **kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос). + + Returns: + :obj:`yandex_music.LandingList`: Список новых альбомов. + + Raises: + :class:`yandex_music.exceptions.YandexMusicError`: Базовое исключение библиотеки. + """ + + url = f'{self.base_url}/landing3/new-releases' + + result = self._request.get(url, timeout=timeout, *args, **kwargs) + + return LandingList.de_json(result, self) + + @log + def new_playlists(self, timeout: Union[int, float] = None, *args, **kwargs) -> Optional[LandingList]: + """Получение полного списка всех новых плейлистов. + + Args: + timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания + ответа от сервера вместо указанного при создании пула. + **kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос). + + Returns: + :obj:`yandex_music.LandingList`: Список новых плейлистов. + + Raises: + :class:`yandex_music.exceptions.YandexMusicError`: Базовое исключение библиотеки. + """ + + url = f'{self.base_url}/landing3/new-playlists' + + result = self._request.get(url, timeout=timeout, *args, **kwargs) + + return LandingList.de_json(result, self) + + @log + def podcasts(self, timeout: Union[int, float] = None, *args, **kwargs) -> Optional[LandingList]: + """Получение подкастов с лендинга. + + Args: + timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания + ответа от сервера вместо указанного при создании пула. + **kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос). + + Returns: + :obj:`yandex_music.LandingList`: Список подскастов. + + Raises: + :class:`yandex_music.exceptions.YandexMusicError`: Базовое исключение библиотеки. + """ + + url = f'{self.base_url}/landing3/podcasts' + + result = self._request.get(url, timeout=timeout, *args, **kwargs) + + return LandingList.de_json(result, self) + @log def genres(self, timeout: Union[int, float] = None, *args, **kwargs) -> List[Genre]: """Получение жанров музыки. @@ -2212,6 +2278,10 @@ class Client(YandexMusicObject): consumePromoCode = consume_promo_code #: Псевдоним для :attr:`feed_wizard_is_passed` feedWizardIsPassed = feed_wizard_is_passed + #: Псевдоним для :attr:`new_releases` + newReleases = new_releases + #: Псевдоним для :attr:`new_playlists` + newPlaylists = new_playlists #: Псевдоним для :attr:`tracks_download_info` tracksDownloadInfo = tracks_download_info #: Псевдоним для :attr:`track_supplement` diff --git a/yandex_music/landing/landing_list.py b/yandex_music/landing/landing_list.py new file mode 100644 index 0000000..b3e063e --- /dev/null +++ b/yandex_music/landing/landing_list.py @@ -0,0 +1,81 @@ +from typing import TYPE_CHECKING, Optional, List + +from yandex_music import YandexMusicObject + +if TYPE_CHECKING: + from yandex_music import Client + + +class LandingList(YandexMusicObject): + """Класс, представляющий список объектов лендинга. + + Note: + Известные значения поля `type`: `new-playlists`, `new-releases`, `podcasts`. + + В зависимости от типа будет заполнено то, или иное поле. + + Attributes: + type (:obj:`str`): Тип результата. + type_for_from (:obj:`str`): Откуда пришло событие. + title (:obj:`str`): Заголовок страницы. + id (:obj:`str`): Уникальный идентификатор списка. + new_releases (:obj:`list` из :obj:`int`): Новые альбомы. + new_playlists (:obj:`list` из :obj:`int`): Новые плейлисты. + podcasts (:obj:`list` из :obj:`int`): Подкасты. + client (:obj:`yandex_music.Client`): Клиент Yandex Music. + + Args: + type (:obj:`str`): Тип результата. + type_for_from (:obj:`str`): Откуда пришло событие. + title (:obj:`str`): Заголовок страницы. + id (:obj:`str`, optional): Уникальный идентификатор списка. + new_releases (:obj:`list` из :obj:`int`, optional): Новые альбомы. + new_playlists (:obj:`list` из :obj:`int`, optional): Новые плейлисты. + podcasts (:obj:`list` из :obj:`int`, optional): Подкасты. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + **kwargs: Произвольные ключевые аргументы полученные от API. + """ + + def __init__(self, + type_: str, + type_for_from: str, + title: str, + id_: Optional[str] = None, + new_releases: List[int] = None, + new_playlists: List[int] = None, + podcasts: List[int] = None, + client: Optional['Client'] = None, + **kwargs) -> None: + self.type = type_ + self.type_for_from = type_for_from + self.title = title + + self.id = id_ + self.new_releases = new_releases + self.new_playlists = new_playlists + self.podcasts = podcasts + + self.client = client + self._id_attrs = (self.id, self.new_releases, self.new_playlists, self.podcasts) + + super().handle_unknown_kwargs(self, **kwargs) + + @classmethod + def de_json(cls, data: dict, client: 'Client') -> Optional['Chart']: + """Десериализация объекта. + + Args: + data (:obj:`dict`): Поля и значения десериализуемого объекта. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + + Returns: + :obj:`yandex_music.LandingList`: Список объектов лендинга. + """ + if not data: + return None + + data = super(LandingList, cls).de_json(data, client) + from yandex_music import PlaylistId + data['new_playlists'] = PlaylistId.de_list(data.get('new_playlists'), client) + + return cls(client=client, **data)