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
このコミットが含まれているのは:
コミット
1cd21aae01
|
@ -80,6 +80,19 @@ from genre.title import Title
|
|||
from genre.images import Images
|
||||
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',
|
||||
'Status', 'Price', 'Product', 'AutoRenewable', 'Settings', 'PermissionAlerts', 'Experiments', 'Cover',
|
||||
'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',
|
||||
'Suggestions', 'MixLink', 'BlockEntity', 'Block', 'PlayContextsData', 'TrackId', 'TrackShortOld',
|
||||
'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, \
|
||||
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.difference import Difference
|
||||
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)
|
||||
|
||||
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,
|
||||
timeout=None, *args, **kwargs):
|
||||
if user_id is None:
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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,
|
||||
uid,
|
||||
login,
|
||||
region,
|
||||
full_name,
|
||||
second_name,
|
||||
first_name,
|
||||
|
@ -16,7 +15,8 @@ class Account(YandexMusicObject):
|
|||
birthday,
|
||||
service_available,
|
||||
hosted_user,
|
||||
passport_phones,
|
||||
region=None,
|
||||
passport_phones=None,
|
||||
registered_at=None,
|
||||
has_info_for_app_metrica=False,
|
||||
client=None,
|
||||
|
|
|
@ -5,23 +5,29 @@ class Status(YandexMusicObject):
|
|||
def __init__(self,
|
||||
account,
|
||||
permissions,
|
||||
cache_limit,
|
||||
subscription,
|
||||
subeditor,
|
||||
subeditor_level,
|
||||
plus,
|
||||
cache_limit=None,
|
||||
subeditor=None,
|
||||
subeditor_level=None,
|
||||
plus=None,
|
||||
default_email=None,
|
||||
skips_per_hour=None,
|
||||
station_exists=None,
|
||||
premium_region=None,
|
||||
client=None,
|
||||
**kwargs):
|
||||
self.account = account
|
||||
self.permissions = permissions
|
||||
self.cache_limit = cache_limit
|
||||
self.subscription = subscription
|
||||
|
||||
self.cache_limit = cache_limit
|
||||
self.subeditor = subeditor
|
||||
self.subeditor_level = subeditor_level
|
||||
self.plus = plus
|
||||
|
||||
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._id_attrs = (self.account,)
|
||||
|
|
|
@ -3,14 +3,16 @@ from yandex_music import YandexMusicObject
|
|||
|
||||
class Subscription(YandexMusicObject):
|
||||
def __init__(self,
|
||||
auto_renewable,
|
||||
can_start_trial,
|
||||
mcdonalds,
|
||||
auto_renewable=None,
|
||||
can_start_trial=None,
|
||||
mcdonalds=None,
|
||||
end=None,
|
||||
client=None,
|
||||
**kwargs):
|
||||
self.auto_renewable = auto_renewable
|
||||
self.can_start_trial = can_start_trial
|
||||
self.mcdonalds = mcdonalds
|
||||
self.end = end
|
||||
|
||||
self.client = client
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class Track(YandexMusicObject):
|
|||
available_as_rbt=None,
|
||||
content_warning=None,
|
||||
explicit=None,
|
||||
preview_duration_ms=None,
|
||||
client=None,
|
||||
**kwargs):
|
||||
self.id = id
|
||||
|
@ -37,7 +38,7 @@ class Track(YandexMusicObject):
|
|||
self.real_id = real_id
|
||||
self.og_image = og_image
|
||||
self.type = type
|
||||
self.cover_uri = cover_uri
|
||||
self.cover_uri = 'https://' + cover_uri
|
||||
self.major = major
|
||||
self.duration_ms = duration_ms
|
||||
self.storage_dir = storage_dir
|
||||
|
@ -48,6 +49,7 @@ class Track(YandexMusicObject):
|
|||
self.available_as_rbt = available_as_rbt
|
||||
self.content_warning = content_warning
|
||||
self.explicit = explicit
|
||||
self.preview_duration_ms = preview_duration_ms
|
||||
|
||||
self.download_info = None
|
||||
|
||||
|
@ -59,8 +61,8 @@ class Track(YandexMusicObject):
|
|||
|
||||
return self.download_info
|
||||
|
||||
def download_cover(self, filename):
|
||||
self.client._request.download(self.cover_uri, filename)
|
||||
def download_cover(self, filename, size='200x200'):
|
||||
self.client._request.download(self.cover_uri.replace('%%', size), filename)
|
||||
|
||||
def download(self, filename, codec='mp3', bitrate_in_kbps=192):
|
||||
if self.download_info is None:
|
||||
|
|
読み込み中…
新しいイシューから参照