diff --git a/yandex_music/__init__.py b/yandex_music/__init__.py index b225700..d4ee2fa 100644 --- a/yandex_music/__init__.py +++ b/yandex_music/__init__.py @@ -57,6 +57,19 @@ from search.playlist_search_result import PlaylistSearchResult from search.track_search_result import TrackSearchResult from search.video_search_result import VideoSearchResult +from landing.chart_item import ChartItem +from landing.play_context import PlayContext +from landing.track_short_old import TrackShortOld +from landing.mix_link import MixLink +from landing.promotion import Promotion +from landing.block_entity import BlockEntity +from landing.landing import Landing +from landing.block import Block +from landing.track_id import TrackId +from landing.chart import Chart +from landing.play_contexts_data import PlayContextsData +from landing.personal_playlists_data import PersonalPlaylistsData + __all__ = ['YandexMusicObject', 'Account', 'PassportPhone', 'InvocationInfo', 'Permissions', 'Plus', 'Subscription', 'Status', 'Price', 'Product', 'AutoRenewable', 'Settings', 'PermissionAlerts', 'Experiments', 'Cover', 'Ratings', 'Counts', 'Link', 'Artist', 'User', 'CaseForms', 'MadeFor', 'Label', 'Album', 'PlayCounter', @@ -64,4 +77,5 @@ __all__ = ['YandexMusicObject', 'Account', 'PassportPhone', 'InvocationInfo', 'P 'ArtistsLikes', 'PlaylistsLikes', 'GeneratedPlaylist', 'TrackWithAds', 'Day', 'ArtistEvent', 'AlbumEvent', 'Feed', 'Event', 'PromoCodeStatus', 'DownloadInfo', 'Video', 'SearchResult', 'AlbumSearchResult', 'Best', 'ArtistSearchResult', 'PlaylistSearchResult', 'TrackSearchResult', 'VideoSearchResult', 'Search', - 'Suggestions'] + 'Suggestions', 'MixLink', 'BlockEntity', 'Block', 'PlayContextsData', 'TrackId', 'TrackShortOld', + 'PersonalPlaylistsData', 'Promotion', 'Landing', 'Chart', 'ChartItem', 'PlayContext'] diff --git a/yandex_music/client.py b/yandex_music/client.py index 581339f..526471e 100644 --- a/yandex_music/client.py +++ b/yandex_music/client.py @@ -3,7 +3,7 @@ from datetime import datetime from yandex_music import YandexMusicObject, Status, Settings, PermissionAlerts, Experiments, Artist, Album, Playlist, \ TracksLikes, Track, AlbumsLikes, ArtistsLikes, PlaylistsLikes, Feed, PromoCodeStatus, DownloadInfo, Search, \ - Suggestions + Suggestions, Landing from yandex_music.utils.request import Request from yandex_music.exceptions import InvalidToken @@ -109,6 +109,13 @@ class Client(YandexMusicObject): return Feed.de_json(result, self) + def landing(self, blocks: str or list, timeout=None, *args, **kwargs): + url = f'{self.base_url}/landing3' + + result = self._request.get(url, {'blocks': blocks}, timeout=timeout, *args, **kwargs) + + return Landing.de_json(result, self) + def tracks_download_info(self, track_id: str or int, get_direct_links=False, timeout=None, *args, **kwargs): url = f'{self.base_url}/tracks/{track_id}/download-info' diff --git a/yandex_music/cover.py b/yandex_music/cover.py index d7a34ff..aedb5ca 100644 --- a/yandex_music/cover.py +++ b/yandex_music/cover.py @@ -23,6 +23,9 @@ class Cover(YandexMusicObject): self.client = client + def download(self, filename): + self.client._request.download(self.uri, filename) + @classmethod def de_json(cls, data, client): if not data: diff --git a/yandex_music/landing/__init__.py b/yandex_music/landing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yandex_music/landing/block.py b/yandex_music/landing/block.py new file mode 100644 index 0000000..031a745 --- /dev/null +++ b/yandex_music/landing/block.py @@ -0,0 +1,54 @@ +from yandex_music import YandexMusicObject + + +class Block(YandexMusicObject): + def __init__(self, + id, + type, + type_for_from, + title, + entities, + description=None, + data=None, + client=None, + **kwargs): + + self.id = id + self.type = type + self.type_for_from = type_for_from + self.title = title + self.entities = entities + + self.description = description + self.data = data + + self.client = client + self._id_attrs = (self.id,) + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(Block, cls).de_json(data, client) + from yandex_music import BlockEntity, PlayContextsData, PersonalPlaylistsData + data['entities'] = BlockEntity.de_list(data.get('entities'), client) + + block_type = data.get('type') + if block_type == 'personal-playlists': + data['data'] = PersonalPlaylistsData.de_json(data.get('data'), client) + elif block_type == 'play-contexts': + data['data'] = PlayContextsData.de_json(data.get('data'), client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data, client): + if not data: + return [] + + blocks = list() + for block in data: + blocks.append(cls.de_json(block, client)) + + return blocks diff --git a/yandex_music/landing/block_entity.py b/yandex_music/landing/block_entity.py new file mode 100644 index 0000000..f4bd85e --- /dev/null +++ b/yandex_music/landing/block_entity.py @@ -0,0 +1,49 @@ +from yandex_music import YandexMusicObject, Promotion, Album, Playlist, MixLink, PlayContext, ChartItem, GeneratedPlaylist + + +de_json = { + 'personal-playlist': GeneratedPlaylist.de_json, + 'promotion': Promotion.de_json, + 'album': Album.de_json, + 'playlist': Playlist.de_json, + 'chart-item': ChartItem.de_json, + 'play-context': PlayContext.de_json, + 'mix-link': MixLink.de_json +} + + +class BlockEntity(YandexMusicObject): + def __init__(self, + id, + type, + data, + client=None, + **kwargs): + + self.id = id + self.type = type + self.data = data + + self.client = client + self._id_attrs = (self.id,) + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(BlockEntity, cls).de_json(data, client) + data['data'] = de_json.get(data.get('type'))(data.get('data'), client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data, client): + if not data: + return [] + + entities = list() + for entity in data: + entities.append(cls.de_json(entity, client)) + + return entities diff --git a/yandex_music/landing/chart.py b/yandex_music/landing/chart.py new file mode 100644 index 0000000..d462ff5 --- /dev/null +++ b/yandex_music/landing/chart.py @@ -0,0 +1,26 @@ +from yandex_music import YandexMusicObject + + +class Chart(YandexMusicObject): + def __init__(self, + position, + progress, + listeners, + shift, + client=None, + **kwargs): + self.position = position + self.progress = progress + self.listeners = listeners + self.shift = shift + + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(Chart, cls).de_json(data, client) + + return cls(client=client, **data) diff --git a/yandex_music/landing/chart_item.py b/yandex_music/landing/chart_item.py new file mode 100644 index 0000000..cf828c7 --- /dev/null +++ b/yandex_music/landing/chart_item.py @@ -0,0 +1,36 @@ +from yandex_music import YandexMusicObject + + +class ChartItem(YandexMusicObject): + def __init__(self, + track, + chart, + client=None, + **kwargs): + self.track = track + self.chart = chart + + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(ChartItem, cls).de_json(data, client) + from yandex_music import Chart, Track + data['track'] = Track.de_json(data.get('track'), client) + data['chart'] = Chart.de_json(data.get('chart'), client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data, client): + if not data: + return [] + + tracks = list() + for track in data: + tracks.append(cls.de_json(track, client)) + + return tracks diff --git a/yandex_music/landing/landing.py b/yandex_music/landing/landing.py new file mode 100644 index 0000000..8f62c23 --- /dev/null +++ b/yandex_music/landing/landing.py @@ -0,0 +1,28 @@ +from yandex_music import YandexMusicObject + + +class Landing(YandexMusicObject): + def __init__(self, + pumpkin, + content_id, + blocks, + client=None, + **kwargs): + + self.pumpkin = pumpkin + self.content_id = content_id + self.blocks = blocks + + self.client = client + self._id_attrs = (self.content_id,) + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(Landing, cls).de_json(data, client) + from yandex_music import Block + data['blocks'] = Block.de_list(data.get('blocks'), client) + + return cls(client=client, **data) diff --git a/yandex_music/landing/mix_link.py b/yandex_music/landing/mix_link.py new file mode 100644 index 0000000..52674f1 --- /dev/null +++ b/yandex_music/landing/mix_link.py @@ -0,0 +1,43 @@ +from yandex_music import YandexMusicObject + + +class MixLink(YandexMusicObject): + def __init__(self, + title, + url, + url_scheme, + text_color, + background_color, + background_image_uri, + cover_white, + client=None, + **kwargs): + self.title = title + self.url = url + self.url_scheme = url_scheme + self.text_color = text_color + self.background_color = background_color + self.background_image_uri = background_image_uri + self.cover_white = cover_white + + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(MixLink, cls).de_json(data, client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data, client): + if not data: + return [] + + mix_links = list() + for mix_link in data: + mix_links.append(cls.de_json(mix_link, client)) + + return mix_links diff --git a/yandex_music/landing/personal_playlists_data.py b/yandex_music/landing/personal_playlists_data.py new file mode 100644 index 0000000..dd98929 --- /dev/null +++ b/yandex_music/landing/personal_playlists_data.py @@ -0,0 +1,20 @@ +from yandex_music import YandexMusicObject + + +class PersonalPlaylistsData(YandexMusicObject): + def __init__(self, + is_wizard_passed, + client=None, + **kwargs): + self.is_wizard_passed = is_wizard_passed + + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(PersonalPlaylistsData, cls).de_json(data, client) + + return cls(client=client, **data) diff --git a/yandex_music/landing/play_context.py b/yandex_music/landing/play_context.py new file mode 100644 index 0000000..ab3ffcf --- /dev/null +++ b/yandex_music/landing/play_context.py @@ -0,0 +1,28 @@ +from yandex_music import YandexMusicObject + + +class PlayContext(YandexMusicObject): + def __init__(self, + client_, + context, + context_item, + tracks, + client=None, + **kwargs): + + self.client_ = client_ + self.context = context + self.context_item = context_item + self.tracks = tracks + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(PlayContext, cls).de_json(data, client) + from yandex_music import TrackShortOld + data['tracks'] = TrackShortOld.de_list(data.get('tracks'), client) + + return cls(client=client, **data) diff --git a/yandex_music/landing/play_contexts_data.py b/yandex_music/landing/play_contexts_data.py new file mode 100644 index 0000000..fbab3ee --- /dev/null +++ b/yandex_music/landing/play_contexts_data.py @@ -0,0 +1,22 @@ +from yandex_music import YandexMusicObject + + +class PlayContextsData(YandexMusicObject): + def __init__(self, + other_tracks, + client=None, + **kwargs): + self.other_tracks = other_tracks + + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(PlayContextsData, cls).de_json(data, client) + from yandex_music import TrackShortOld + data['other_tracks'] = TrackShortOld.de_list(data.get('other_tracks'), client) + + return cls(client=client, **data) diff --git a/yandex_music/landing/promotion.py b/yandex_music/landing/promotion.py new file mode 100644 index 0000000..331c17c --- /dev/null +++ b/yandex_music/landing/promotion.py @@ -0,0 +1,47 @@ +from yandex_music import YandexMusicObject + + +class Promotion(YandexMusicObject): + def __init__(self, + promo_id, + title, + subtitle, + heading, + url, + url_scheme, + text_color, + gradient, + image, + client=None, + **kwargs): + self.promo_id = promo_id + self.title = title + self.subtitle = subtitle + self.heading = heading + self.url = url + self.url_scheme = url_scheme + self.text_color = text_color + self.gradient = gradient + self.image = image + + self.client = client + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(Promotion, cls).de_json(data, client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data, client): + if not data: + return [] + + promotions = list() + for promotion in data: + promotions.append(cls.de_json(promotion, client)) + + return promotions diff --git a/yandex_music/landing/track_id.py b/yandex_music/landing/track_id.py new file mode 100644 index 0000000..9121755 --- /dev/null +++ b/yandex_music/landing/track_id.py @@ -0,0 +1,23 @@ +from yandex_music import YandexMusicObject + + +class TrackId(YandexMusicObject): + def __init__(self, + id, + album_id, + client=None, + **kwargs): + self.id = id + self.album_id = album_id + + self.client = client + self._id_attrs = (self.id,) + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(TrackId, cls).de_json(data, client) + + return cls(client=client, **data) diff --git a/yandex_music/landing/track_short_old.py b/yandex_music/landing/track_short_old.py new file mode 100644 index 0000000..15e17fd --- /dev/null +++ b/yandex_music/landing/track_short_old.py @@ -0,0 +1,36 @@ +from yandex_music import YandexMusicObject + + +class TrackShortOld(YandexMusicObject): + def __init__(self, + track_id, + timestamp, + client=None, + **kwargs): + self.track_id = track_id + self.timestamp = timestamp + + self.client = client + self._id_attrs = (self.track_id,) + + @classmethod + def de_json(cls, data, client): + if not data: + return None + + data = super(TrackShortOld, cls).de_json(data, client) + from yandex_music import TrackId + data['track_id'] = TrackId.de_json(data.get('track_id'), client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data, client): + if not data: + return [] + + tracks = list() + for track in data: + tracks.append(cls.de_json(track, client)) + + return tracks diff --git a/yandex_music/playlist.py b/yandex_music/playlist.py index 1c68a86..4abf58e 100644 --- a/yandex_music/playlist.py +++ b/yandex_music/playlist.py @@ -11,9 +11,9 @@ class Playlist(YandexMusicObject): title, track_count, cover, - tags, made_for, play_counter, + tags=None, revision=None, snapshot=None, visibility=None, @@ -42,7 +42,6 @@ class Playlist(YandexMusicObject): self.title = title self.track_count = track_count self.cover = cover - self.tags = tags self.made_for = made_for self.play_counter = play_counter @@ -66,6 +65,7 @@ class Playlist(YandexMusicObject): self.generated_playlist_type = generated_playlist_type self.is_for_from = is_for_from self.regions = regions + self.tags = tags self.client = client self._id_attrs = (self.uid,) diff --git a/yandex_music/track.py b/yandex_music/track.py index 51f9c3c..c7c3151 100644 --- a/yandex_music/track.py +++ b/yandex_music/track.py @@ -59,6 +59,9 @@ class Track(YandexMusicObject): return self.download_info + def download_cover(self, filename): + self.client._request.download(self.cover_uri, filename) + def download(self, filename, codec='mp3', bitrate_in_kbps=192): if self.download_info is None: self.get_download_info() diff --git a/yandex_music/utils/request.py b/yandex_music/utils/request.py index a9cbcd1..f783f9a 100644 --- a/yandex_music/utils/request.py +++ b/yandex_music/utils/request.py @@ -45,6 +45,7 @@ class Request(object): cleaned_object = {} for key, value in obj.items(): key = Request._convert_camel_to_snake(key.replace('-', '_')) + key = key.replace('client', 'client_') cleaned_object.update({key: value}) return cleaned_object