New supported objects: Station, StationResult, StationTrackResult, Value, Sequence, RotorSettings, Restrictions, Id, Enum, DiscreteScale, DashBoard, AdParams

The following methods are wrapped:
- /rotor/account/status
- /rotor/stations/dashboard
- /rotor/stations/list
- /rotor/station/genre:{genre}/feedback
- /rotor/station/genre:{genre}/info
- /rotor/station/genre:{genre}/tracks
The following fields are now optional: Account[region, passport_phones], Status[cache_limit, subeditor, subeditor_level, plus], Subscription[auto_renewable, can_start_trial, mcdonalds]
Added new fields: Subscription.end, Status[skips_per_hour, station_exists, premium_region], Track..preview_duration_ms
Fixed downloading the cover of the track
このコミットが含まれているのは:
Marshal 2019-06-03 16:16:24 +03:00
コミット 1cd21aae01
19個のファイルの変更501行の追加16行の削除

ファイルの表示

@ -80,6 +80,19 @@ from genre.title import Title
from genre.images import Images from genre.images import Images
from genre.genre import Genre from genre.genre import Genre
from rotor.id import Id
from rotor.value import Value
from rotor.enum import Enum
from rotor.sequence import Sequence
from rotor.discrete_scale import DiscreteScale
from rotor.ad_params import AdParams
from rotor.restrictions import Restrictions
from rotor.rotor_settings import RotorSettings
from rotor.station import Station
from rotor.station_tracks_result import StationTracksResult
from rotor.station_result import StationResult
from rotor.dashboard import Dashboard
__all__ = ['YandexMusicObject', 'Account', 'PassportPhone', 'InvocationInfo', 'Permissions', 'Plus', 'Subscription', __all__ = ['YandexMusicObject', 'Account', 'PassportPhone', 'InvocationInfo', 'Permissions', 'Plus', 'Subscription',
'Status', 'Price', 'Product', 'AutoRenewable', 'Settings', 'PermissionAlerts', 'Experiments', 'Cover', 'Status', 'Price', 'Product', 'AutoRenewable', 'Settings', 'PermissionAlerts', 'Experiments', 'Cover',
'Ratings', 'Counts', 'Link', 'Artist', 'User', 'CaseForms', 'MadeFor', 'Label', 'Album', 'PlayCounter', 'Ratings', 'Counts', 'Link', 'Artist', 'User', 'CaseForms', 'MadeFor', 'Label', 'Album', 'PlayCounter',
@ -89,4 +102,5 @@ __all__ = ['YandexMusicObject', 'Account', 'PassportPhone', 'InvocationInfo', 'P
'ArtistSearchResult', 'PlaylistSearchResult', 'TrackSearchResult', 'VideoSearchResult', 'Search', 'ArtistSearchResult', 'PlaylistSearchResult', 'TrackSearchResult', 'VideoSearchResult', 'Search',
'Suggestions', 'MixLink', 'BlockEntity', 'Block', 'PlayContextsData', 'TrackId', 'TrackShortOld', 'Suggestions', 'MixLink', 'BlockEntity', 'Block', 'PlayContextsData', 'TrackId', 'TrackShortOld',
'PersonalPlaylistsData', 'Promotion', 'Landing', 'Chart', 'ChartItem', 'PlayContext', 'Title', 'Genre', 'PersonalPlaylistsData', 'Promotion', 'Landing', 'Chart', 'ChartItem', 'PlayContext', 'Title', 'Genre',
'Icon', 'Images'] 'Icon', 'Images', 'Id', 'Station', 'Dashboard', 'RotorSettings', 'AdParams', 'Restrictions', 'Value', 'Enum',
'DiscreteScale', 'StationResult', 'Sequence', 'StationTracksResult']

ファイルの表示

@ -3,7 +3,7 @@ from datetime import datetime
from yandex_music import YandexMusicObject, Status, Settings, PermissionAlerts, Experiments, Artist, Album, Playlist, \ from yandex_music import YandexMusicObject, Status, Settings, PermissionAlerts, Experiments, Artist, Album, Playlist, \
TracksLikes, Track, AlbumsLikes, ArtistsLikes, PlaylistsLikes, Feed, PromoCodeStatus, DownloadInfo, Search, \ TracksLikes, Track, AlbumsLikes, ArtistsLikes, PlaylistsLikes, Feed, PromoCodeStatus, DownloadInfo, Search, \
Suggestions, Landing, Genre Suggestions, Landing, Genre, Dashboard, StationResult, StationTracksResult
from yandex_music.utils.request import Request from yandex_music.utils.request import Request
from yandex_music.utils.difference import Difference from yandex_music.utils.difference import Difference
from yandex_music.exceptions import InvalidToken from yandex_music.exceptions import InvalidToken
@ -289,6 +289,74 @@ class Client(YandexMusicObject):
return self.users_playlists_change(kind, diff.to_json(), revision, user_id, timeout, *args, **kwargs) return self.users_playlists_change(kind, diff.to_json(), revision, user_id, timeout, *args, **kwargs)
def rotor_account_status(self, timeout=None, *args, **kwargs):
url = f'{self.base_url}/rotor/account/status'
result = self._request.get(url, timeout=timeout, *args, **kwargs)
return Status.de_json(result, self)
def rotor_stations_dashboard(self, timeout=None, *args, **kwargs):
url = f'{self.base_url}/rotor/stations/dashboard'
result = self._request.get(url, timeout=timeout, *args, **kwargs)
return Dashboard.de_json(result, self)
def rotor_stations_list(self, language: str = 'en', timeout=None, *args, **kwargs):
url = f'{self.base_url}/rotor/stations/list'
result = self._request.get(url, {'language': language}, timeout=timeout, *args, **kwargs)
return StationResult.de_list(result, self)
def rotor_station_genre_feedback(self, genre: str, type_: str, timestamp=None, from_: str = None,
batch_id: str or int = None, track_id: str = None, timeout=None, *args, **kwargs):
if timestamp is None:
timestamp = datetime.now().timestamp()
url = f'{self.base_url}/rotor/station/genre:{genre}/feedback'
params = {}
data = {
'type': type_,
'timestamp': timestamp
}
if batch_id and track_id:
data.update({'trackId': track_id})
params = {'batch-id': batch_id}
if from_:
data.update({'from': from_})
result = self._request.post(url, params=params, json=data, timeout=timeout, *args, **kwargs)
return result == 'ok'
def rotor_station_genre_feedback_radio_started(self, genre: str, from_: str, timestamp=None,
timeout=None, *args, **kwargs):
return self.rotor_station_genre_feedback(genre, 'radioStarted', timestamp, from_, timeout, *args, **kwargs)
def rotor_station_genre_feedback_track_started(self, genre: str, track_id: str, batch_id: str or int,
timestamp=None, timeout=None, *args, **kwargs):
return self.rotor_station_genre_feedback(genre, 'trackStarted', timestamp, track_id=track_id, batch_id=batch_id,
timeout=timeout, *args, **kwargs)
def rotor_station_genre_info(self, genre: str, timeout=None, *args, **kwargs):
url = f'{self.base_url}/rotor/station/genre:{genre}/info'
result = self._request.get(url, timeout=timeout, *args, **kwargs)
return StationResult.de_list(result, self)
def rotor_station_genre_tracks(self, genre: str, timeout=None, *args, **kwargs):
url = f'{self.base_url}/rotor/station/genre:{genre}/tracks'
result = self._request.get(url, timeout=timeout, *args, **kwargs)
return StationTracksResult.de_json(result, self)
def _add_like(self, object_type: str, ids: str or int or list, remove: bool = False, user_id: str or int = None, def _add_like(self, object_type: str, ids: str or int or list, remove: bool = False, user_id: str or int = None,
timeout=None, *args, **kwargs): timeout=None, *args, **kwargs):
if user_id is None: if user_id is None:

35
yandex_music/rotor/ad_params.py ノーマルファイル
ファイルの表示

@ -0,0 +1,35 @@
from yandex_music import YandexMusicObject
class AdParams(YandexMusicObject):
def __init__(self,
partner_id,
category_id,
page_ref,
target_ref,
other_params,
ad_volume,
genre_id=None,
genre_name=None,
client=None,
**kwargs):
self.partner_id = partner_id
self.category_id = category_id
self.page_ref = page_ref
self.target_ref = target_ref
self.other_params = other_params
self.ad_volume = ad_volume
self.genre_id = genre_id
self.genre_name = genre_name
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(AdParams, cls).de_json(data, client)
return cls(client=client, **data)

27
yandex_music/rotor/dashboard.py ノーマルファイル
ファイルの表示

@ -0,0 +1,27 @@
from yandex_music import YandexMusicObject
class Dashboard(YandexMusicObject):
def __init__(self,
dashboard_id,
stations,
pumpkin,
client=None,
**kwargs):
self.dashboard_id = dashboard_id
self.stations = stations
self.pumpkin = pumpkin
self.client = client
self._id_attrs = (self.dashboard_id, )
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Dashboard, cls).de_json(data, client)
from yandex_music import StationResult
data['stations'] = StationResult.de_list(data.get('stations'), client)
return cls(client=client, **data)

29
yandex_music/rotor/discrete_scale.py ノーマルファイル
ファイルの表示

@ -0,0 +1,29 @@
from yandex_music import YandexMusicObject
class DiscreteScale(YandexMusicObject):
def __init__(self,
type,
name,
min,
max,
client=None,
**kwargs):
self.type = type
self.name = name
self.min = min
self.max = max
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(DiscreteScale, cls).de_json(data, client)
from yandex_music import Value
data['min'] = Value.de_json(data.get('min'), client)
data['max'] = Value.de_json(data.get('max'), client)
return cls(client=client, **data)

26
yandex_music/rotor/enum.py ノーマルファイル
ファイルの表示

@ -0,0 +1,26 @@
from yandex_music import YandexMusicObject
class Enum(YandexMusicObject):
def __init__(self,
type,
name,
possible_values,
client=None,
**kwargs):
self.type = type
self.name = name
self.possible_values = possible_values
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Enum, cls).de_json(data, client)
from yandex_music import Value
data['possible_values'] = Value.de_list(data.get('possible_values'), client)
return cls(client=client, **data)

22
yandex_music/rotor/id.py ノーマルファイル
ファイルの表示

@ -0,0 +1,22 @@
from yandex_music import YandexMusicObject
class Id(YandexMusicObject):
def __init__(self,
type,
tag,
client=None,
**kwargs):
self.type = type
self.tag = tag
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Id, cls).de_json(data, client)
return cls(client=client, **data)

34
yandex_music/rotor/restrictions.py ノーマルファイル
ファイルの表示

@ -0,0 +1,34 @@
from yandex_music import YandexMusicObject
class Restrictions(YandexMusicObject):
def __init__(self,
language,
diversity,
mood=None,
energy=None,
mood_energy=None,
client=None,
**kwargs):
self.language = language
self.diversity = diversity
self.mood = mood
self.energy = energy
self.mood_energy = mood_energy
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Restrictions, cls).de_json(data, client)
from yandex_music import Enum, DiscreteScale
for key, value in data.items():
restriction_type = data[key].get('type')
data[key] = Enum.de_json(data[key], client) if restriction_type == 'enum'\
else DiscreteScale.de_json(data[key], client)
return cls(client=client, **data)

29
yandex_music/rotor/rotor_settings.py ノーマルファイル
ファイルの表示

@ -0,0 +1,29 @@
from yandex_music import YandexMusicObject
class RotorSettings(YandexMusicObject):
def __init__(self,
language,
diversity,
mood=None,
energy=None,
mood_energy=None,
client=None,
**kwargs):
self.language = language
self.diversity = diversity
self.mood = mood
self.energy = energy
self.mood_energy = mood_energy
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(RotorSettings, cls).de_json(data, client)
return cls(client=client, **data)

37
yandex_music/rotor/sequence.py ノーマルファイル
ファイルの表示

@ -0,0 +1,37 @@
from yandex_music import YandexMusicObject
class Sequence(YandexMusicObject):
def __init__(self,
type,
track,
liked,
client=None,
**kwargs):
self.type = type
self.track = track
self.liked = liked
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Sequence, cls).de_json(data, client)
from yandex_music import Track
data['track'] = Track.de_json(data.get('track'), client)
return cls(client=client, **data)
@classmethod
def de_list(cls, data, client):
if not data:
return []
sequences = list()
for sequence in data:
sequences.append(cls.de_json(sequence, client))
return sequences

45
yandex_music/rotor/station.py ノーマルファイル
ファイルの表示

@ -0,0 +1,45 @@
from yandex_music import YandexMusicObject
class Station(YandexMusicObject):
def __init__(self,
id,
name,
icon,
mts_icon,
geocell_icon,
id_for_from,
restrictions,
restrictions2,
parent_id=None,
client=None,
**kwargs):
self.id = id
self.name = name
self.icon = icon
self.mts_icon = mts_icon
self.geocell_icon = geocell_icon
self.id_for_from = id_for_from
self.restrictions = restrictions
self.restrictions2 = restrictions2
self.parent_id = parent_id
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Station, cls).de_json(data, client)
from yandex_music import Id, Icon, Restrictions
data['id'] = Id.de_json(data.get('id'), client)
data['parent_id'] = Id.de_json(data.get('parent_id'), client)
data['icon'] = Icon.de_json(data.get('icon'), client)
data['mts_icon'] = Icon.de_json(data.get('mts_icon'), client)
data['geocell_icon'] = Icon.de_json(data.get('geocell_icon'), client)
data['restrictions'] = Restrictions.de_json(data.get('restrictions'), client)
data['restrictions2'] = Restrictions.de_json(data.get('restrictions2'), client)
return cls(client=client, **data)

46
yandex_music/rotor/station_result.py ノーマルファイル
ファイルの表示

@ -0,0 +1,46 @@
from yandex_music import YandexMusicObject
class StationResult(YandexMusicObject):
def __init__(self,
station,
settings,
settings2,
ad_params,
explanation=None,
prerolls=None,
client=None,
**kwargs):
self.station = station
self.settings = settings
self.settings2 = settings2
self.ad_params = ad_params
self.explanation = explanation
self.prerolls = prerolls
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(StationResult, cls).de_json(data, client)
from yandex_music import Station, RotorSettings, AdParams
data['station'] = Station.de_json(data.get('station'), client)
data['settings'] = RotorSettings.de_json(data.get('settings'), client)
data['settings2'] = RotorSettings.de_json(data.get('settings2'), client)
data['ad_params'] = AdParams.de_json(data.get('ad_params'), client)
return cls(client=client, **data)
@classmethod
def de_list(cls, data, client):
if not data:
return []
station_results = list()
for station_result in data:
station_results.append(cls.de_json(station_result, client))
return station_results

29
yandex_music/rotor/station_tracks_result.py ノーマルファイル
ファイルの表示

@ -0,0 +1,29 @@
from yandex_music import YandexMusicObject
class StationTracksResult(YandexMusicObject):
def __init__(self,
id,
sequence,
batch_id,
pumpkin,
client=None,
**kwargs):
self.id = id
self.sequence = sequence
self.batch_id = batch_id
self.pumpkin = pumpkin
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(StationTracksResult, cls).de_json(data, client)
from yandex_music import Id, Sequence
data['id'] = Id.de_json(data.get('id'), client)
data['sequence'] = Sequence.de_list(data.get('sequence'), client)
return cls(client=client, **data)

34
yandex_music/rotor/value.py ノーマルファイル
ファイルの表示

@ -0,0 +1,34 @@
from yandex_music import YandexMusicObject
class Value(YandexMusicObject):
def __init__(self,
value,
name,
client=None,
**kwargs):
self.value = value
self.name = name
self.client = client
@classmethod
def de_json(cls, data, client):
if not data:
return None
data = super(Value, cls).de_json(data, client)
return cls(client=client, **data)
@classmethod
def de_list(cls, data, client):
if not data:
return []
values = list()
for value in data:
values.append(cls.de_json(value, client))
return values

ファイルの表示

@ -8,7 +8,6 @@ class Account(YandexMusicObject):
now, now,
uid, uid,
login, login,
region,
full_name, full_name,
second_name, second_name,
first_name, first_name,
@ -16,7 +15,8 @@ class Account(YandexMusicObject):
birthday, birthday,
service_available, service_available,
hosted_user, hosted_user,
passport_phones, region=None,
passport_phones=None,
registered_at=None, registered_at=None,
has_info_for_app_metrica=False, has_info_for_app_metrica=False,
client=None, client=None,

ファイルの表示

@ -5,23 +5,29 @@ class Status(YandexMusicObject):
def __init__(self, def __init__(self,
account, account,
permissions, permissions,
cache_limit,
subscription, subscription,
subeditor, cache_limit=None,
subeditor_level, subeditor=None,
plus, subeditor_level=None,
plus=None,
default_email=None, default_email=None,
skips_per_hour=None,
station_exists=None,
premium_region=None,
client=None, client=None,
**kwargs): **kwargs):
self.account = account self.account = account
self.permissions = permissions self.permissions = permissions
self.cache_limit = cache_limit
self.subscription = subscription self.subscription = subscription
self.cache_limit = cache_limit
self.subeditor = subeditor self.subeditor = subeditor
self.subeditor_level = subeditor_level self.subeditor_level = subeditor_level
self.plus = plus self.plus = plus
self.default_email = default_email self.default_email = default_email
self.skips_per_hour = skips_per_hour
self.station_exists = station_exists
self.premium_region = premium_region
self.client = client self.client = client
self._id_attrs = (self.account,) self._id_attrs = (self.account,)

ファイルの表示

@ -3,14 +3,16 @@ from yandex_music import YandexMusicObject
class Subscription(YandexMusicObject): class Subscription(YandexMusicObject):
def __init__(self, def __init__(self,
auto_renewable, auto_renewable=None,
can_start_trial, can_start_trial=None,
mcdonalds, mcdonalds=None,
end=None,
client=None, client=None,
**kwargs): **kwargs):
self.auto_renewable = auto_renewable self.auto_renewable = auto_renewable
self.can_start_trial = can_start_trial self.can_start_trial = can_start_trial
self.mcdonalds = mcdonalds self.mcdonalds = mcdonalds
self.end = end
self.client = client self.client = client

ファイルの表示

@ -24,6 +24,7 @@ class Track(YandexMusicObject):
available_as_rbt=None, available_as_rbt=None,
content_warning=None, content_warning=None,
explicit=None, explicit=None,
preview_duration_ms=None,
client=None, client=None,
**kwargs): **kwargs):
self.id = id self.id = id
@ -37,7 +38,7 @@ class Track(YandexMusicObject):
self.real_id = real_id self.real_id = real_id
self.og_image = og_image self.og_image = og_image
self.type = type self.type = type
self.cover_uri = cover_uri self.cover_uri = 'https://' + cover_uri
self.major = major self.major = major
self.duration_ms = duration_ms self.duration_ms = duration_ms
self.storage_dir = storage_dir self.storage_dir = storage_dir
@ -48,6 +49,7 @@ class Track(YandexMusicObject):
self.available_as_rbt = available_as_rbt self.available_as_rbt = available_as_rbt
self.content_warning = content_warning self.content_warning = content_warning
self.explicit = explicit self.explicit = explicit
self.preview_duration_ms = preview_duration_ms
self.download_info = None self.download_info = None
@ -59,8 +61,8 @@ class Track(YandexMusicObject):
return self.download_info return self.download_info
def download_cover(self, filename): def download_cover(self, filename, size='200x200'):
self.client._request.download(self.cover_uri, filename) self.client._request.download(self.cover_uri.replace('%%', size), filename)
def download(self, filename, codec='mp3', bitrate_in_kbps=192): def download(self, filename, codec='mp3', bitrate_in_kbps=192):
if self.download_info is None: if self.download_info is None: