Added ability to download tracks
このコミットが含まれているのは:
コミット
922424feb5
|
@ -109,12 +109,12 @@ class Client(YandexMusicObject):
|
|||
|
||||
return Feed.de_json(result, self)
|
||||
|
||||
def tracks_download_info(self, track_id: str or int, timeout=None, *args, **kwargs):
|
||||
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'
|
||||
|
||||
result = self._request.get(url, timeout=timeout, *args, **kwargs)
|
||||
|
||||
return DownloadInfo.de_list(result, self)
|
||||
return DownloadInfo.de_list(result, self, get_direct_links)
|
||||
|
||||
def play_audio(self,
|
||||
track_id: str or int,
|
||||
|
@ -287,8 +287,10 @@ class Client(YandexMusicObject):
|
|||
|
||||
return de_list.get(object_type)(result, self)
|
||||
|
||||
def users_likes_tracks(self, user_id: int or str = None, if_modified_since_revision=0, timeout=None, *args, **kwargs):
|
||||
return self._get_likes('track', user_id, {'if-modified-since-revision': if_modified_since_revision}, timeout, *args, **kwargs)
|
||||
def users_likes_tracks(self, user_id: int or str = None, if_modified_since_revision=0, timeout=None,
|
||||
*args, **kwargs):
|
||||
return self._get_likes('track', user_id, {'if-modified-since-revision': if_modified_since_revision}, timeout,
|
||||
*args, **kwargs)
|
||||
|
||||
def users_likes_albums(self, user_id: int or str = None, rich=True, timeout=None, *args, **kwargs):
|
||||
return self._get_likes('album', user_id, {'rich': rich}, timeout, *args, **kwargs)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import xml.dom.minidom as minidom
|
||||
|
||||
from yandex_music import YandexMusicObject
|
||||
|
||||
|
||||
|
@ -16,8 +18,37 @@ class DownloadInfo(YandexMusicObject):
|
|||
self.preview = preview
|
||||
self.download_info_url = download_info_url
|
||||
|
||||
self.direct_link = None
|
||||
|
||||
self.client = client
|
||||
|
||||
@staticmethod
|
||||
def _get_text_node_data(elements):
|
||||
for element in elements:
|
||||
nodes = element.childNodes
|
||||
for node in nodes:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
return node.data
|
||||
|
||||
def get_direct_link(self):
|
||||
# Available within one minute after receiving download_info, otherwise 410!
|
||||
result = self.client._request.retrieve(self.download_info_url)
|
||||
|
||||
doc = minidom.parseString(result.text)
|
||||
host = self._get_text_node_data(doc.getElementsByTagName('host'))
|
||||
path = self._get_text_node_data(doc.getElementsByTagName('path'))
|
||||
ts = self._get_text_node_data(doc.getElementsByTagName('ts'))
|
||||
|
||||
self.direct_link = f'https://{host}/get-{self.codec}/randomTrash/{ts}{path}'
|
||||
|
||||
return self.direct_link
|
||||
|
||||
def download(self, filename):
|
||||
if self.direct_link is None:
|
||||
self.get_direct_link()
|
||||
|
||||
self.client._request.download(self.direct_link, filename)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, client):
|
||||
if not data:
|
||||
|
@ -28,7 +59,7 @@ class DownloadInfo(YandexMusicObject):
|
|||
return cls(client=client, **data)
|
||||
|
||||
@classmethod
|
||||
def de_list(cls, data, client):
|
||||
def de_list(cls, data, client, get_direct_links=False):
|
||||
if not data:
|
||||
return []
|
||||
|
||||
|
@ -36,4 +67,8 @@ class DownloadInfo(YandexMusicObject):
|
|||
for download_info in data:
|
||||
downloads_info.append(cls.de_json(download_info, client))
|
||||
|
||||
if get_direct_links:
|
||||
for info in downloads_info:
|
||||
info.get_direct_link()
|
||||
|
||||
return downloads_info
|
||||
|
|
|
@ -14,6 +14,12 @@ class TracksLikes(YandexMusicObject):
|
|||
|
||||
self.client = client
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.tracks[item]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.tracks)
|
||||
|
||||
@property
|
||||
def tracks_ids(self):
|
||||
return [track.track_id for track in self.tracks]
|
||||
|
|
|
@ -49,9 +49,24 @@ class Track(YandexMusicObject):
|
|||
self.content_warning = content_warning
|
||||
self.explicit = explicit
|
||||
|
||||
self.download_info = None
|
||||
|
||||
self.client = client
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
def get_download_info(self, get_direct_links=False):
|
||||
self.download_info = self.client.tracks_download_info(self.track_id, get_direct_links)
|
||||
|
||||
return self.download_info
|
||||
|
||||
def download(self, filename, codec='mp3', bitrate_in_kbps=192):
|
||||
if self.download_info is None:
|
||||
self.get_download_info()
|
||||
|
||||
for info in self.download_info:
|
||||
if info.codec == codec and info.bitrate_in_kbps == bitrate_in_kbps:
|
||||
info.download(filename)
|
||||
|
||||
@property
|
||||
def track_id(self):
|
||||
return f'{self.id}'
|
||||
|
|
|
@ -14,8 +14,19 @@ class TrackShort(YandexMusicObject):
|
|||
self.album_id = album_id
|
||||
self.timestamp = datetime.fromisoformat(timestamp)
|
||||
|
||||
self._track = None
|
||||
|
||||
self.client = client
|
||||
|
||||
@property
|
||||
def track(self):
|
||||
if self._track:
|
||||
return self._track
|
||||
|
||||
self._track = self.client.tracks(self.track_id)[0]
|
||||
|
||||
return self._track
|
||||
|
||||
@property
|
||||
def track_id(self):
|
||||
return f'{self.id}:{self.album_id}'
|
||||
|
|
|
@ -76,14 +76,9 @@ class Request(object):
|
|||
raise NetworkError(e)
|
||||
|
||||
if 200 <= resp.status_code <= 299:
|
||||
return self._parse(resp.content).result
|
||||
return resp
|
||||
|
||||
try:
|
||||
message = self._parse(resp.content).error
|
||||
if message is None:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
message = 'Unknown HTTPError'
|
||||
message = self._parse(resp.content).error or 'Unknown HTTPError'
|
||||
|
||||
if resp.status_code in (401, 403):
|
||||
raise Unauthorized(message)
|
||||
|
@ -98,8 +93,22 @@ class Request(object):
|
|||
raise NetworkError(f'{message} ({resp.status_code})')
|
||||
|
||||
def get(self, url, params=None, timeout=5, *args, **kwargs):
|
||||
return self._request_wrapper('GET', url, params=params, headers=self.headers, timeout=timeout, *args, **kwargs)
|
||||
result = self._request_wrapper('GET', url, params=params, headers=self.headers, timeout=timeout,
|
||||
*args, **kwargs)
|
||||
|
||||
return self._parse(result.content).result
|
||||
|
||||
def post(self, url, data=None, timeout=5, *args, **kwargs):
|
||||
return self._request_wrapper('POST', url, headers=self.headers, data=data, timeout=timeout, *args, **kwargs)
|
||||
result = self._request_wrapper('POST', url, headers=self.headers, data=data, timeout=timeout,
|
||||
*args, **kwargs)
|
||||
|
||||
return self._parse(result.content).result
|
||||
|
||||
def retrieve(self, url, params=None, timeout=5, *args, **kwargs):
|
||||
return self._request_wrapper('GET', url, params=params, headers=self.headers, timeout=timeout,
|
||||
*args, **kwargs)
|
||||
|
||||
def download(self, url, filename, timeout=5, *args, **kwargs):
|
||||
result = self.retrieve(url, timeout=timeout, *args, *kwargs)
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(result.content)
|
||||
|
|
|
@ -7,15 +7,21 @@ class Response(YandexMusicObject):
|
|||
invocation_info=None,
|
||||
result=None,
|
||||
error=None,
|
||||
error_description=None,
|
||||
client=None,
|
||||
**kwargs):
|
||||
self.data = data
|
||||
self.invocation_info = invocation_info
|
||||
self._result = result
|
||||
self.error = error
|
||||
self._error = error
|
||||
self.error_description = error_description
|
||||
|
||||
self.client = client
|
||||
|
||||
@property
|
||||
def error(self):
|
||||
return f'{self._error} {self.error_description if self.error_description else ""}'
|
||||
|
||||
@property
|
||||
def result(self):
|
||||
return self._result or self.data
|
||||
|
|
読み込み中…
新しいイシューから参照