yandex-music-api/yandex_music/track/track.py

572 行
26 KiB
Python
Raw Blame 履歴

このファイルには曖昧(ambiguous)なUnicode文字が含まれています

このファイルには、他の文字と見間違える可能性があるUnicode文字が含まれています。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 それらの文字を表示するにはエスケープボタンを使用します。

from typing import TYPE_CHECKING, Optional, List, Union
from yandex_music import YandexMusicObject
from yandex_music.utils import model
from yandex_music.exceptions import InvalidBitrateError
if TYPE_CHECKING:
from yandex_music import (
Client,
Normalization,
Major,
Album,
Artist,
Supplement,
DownloadInfo,
User,
MetaData,
PoetryLoverMatch,
TrackLyrics,
LyricsInfo,
R128,
)
@model
class Track(YandexMusicObject):
"""Класс, представляющий трек.
Note:
Известные значения поля `content_warning`: `explicit`.
Известные значения поля `type`: `music`.
Известные значения поля `track_sharing_flag`: `VIDEO_ALLOWED`, `COVER_ONLY`.
Известные значения поля `track_source`: `OWN`, `OWN_REPLACED_TO_UGC`.
Известные значения поля `available_for_options`: `bookmate`.
Поля `can_publish`, `state`, `desired_visibility`, `filename`, `user_info` доступны только у треков что были
загружены пользователем.
Обычно у подкастов поле `remember_position == True`, а у треков `remember_position == False`.
Полное описание можно получить используя :func:`Track.get_supplement`.
Attributes:
id (:obj:`int` | :obj:`str`): Уникальный идентификатор.
title (:obj:`str`, optional): Название.
available (:obj:`bool`, optional): Доступен ли для прослушивания.
artists (:obj:`list` из :obj:`yandex_music.Artist`, optional): Исполнители.
albums (:obj:`list` из :obj:`yandex_music.Album`, optional): Альбомы.
available_for_premium_users (:obj:`bool`, optional): Доступен ли для пользователей с подпиской.
lyrics_available (:obj:`bool`, optional): Доступен ли текст песни.
poetry_lover_matches (:obj:`list` из :obj:`yandex_music.PoetryLoverMatch`, optional): Список слов TODO.
best (:obj:`bool`, optional): Лучшей ли трек TODO.
real_id (:obj:`int` | :obj:`str`, optional): TODO.
og_image (:obj:`str`, optional): Ссылка на превью Open Graph.
type (:obj:`str`, optional): Тип.
cover_uri (:obj:`str`, optional): Ссылка на изображение с обложкой.
major (:obj:`yandex_music.Major`, optional): Мейджор-лейбл.
duration_ms (:obj:`int`, optional): Длительность трека в миллисекундах.
storage_dir (:obj:`str`, optional): В какой папке на сервере хранится файл TODO.
file_size (:obj:`int`, optional): Размер файла. TODO добавить единицу измерения.
substituted (:obj:`yandex_music.Track`, optional): Замещённый трек.
matched_track (:obj:`yandex_music.Track`, optional): Соответствующий трек TODO.
normalization (:obj:`list` из :obj:`yandex_music.Normalization`, optional): Значения для нормализации трека.
error (:obj:`str`, optional): Сообщение об ошибке.
can_publish (:obj:`bool`, optional): Может ли быть опубликован.
state (:obj:`str`, optional): Состояние, например, playable.
desired_visibility (:obj:`str`, optional): Видимость трека.
filename (:obj:`str`, optional): Название файла.
user_info (:obj:`yandex_music.User`, optional): Информация о пользователе, который загрузил трек.
meta_data (:obj:`yandex_music.MetaData`, optional): Информация о метаданных трека.
regions (:obj:`list` из :obj:`str`, optional): Регион TODO.
available_as_rbt (:obj:`bool`, optional): TODO.
content_warning (:obj:`str`, optional): Тип откровенного контента.
explicit (:obj:`bool`, optional): Содержит ли откровенный контент.
preview_duration_ms (:obj:`int`, optional): TODO.
available_full_without_permission (:obj:`bool`, optional): Доступен ли без подписки.
version (:obj:`str`, optional): Версия.
remember_position (:obj:`bool`, optional): Если :obj:`True`, то запоминается последняя позиция прослушивания,
иначе позиция не запоминается.
background_video_uri (:obj:`str`, optional): Ссылка на видеошот.
short_description (:obj:`str`, optional): Краткое описание эпизода подкаста.
is_suitable_for_children (:obj:`bool`, optional): Подходит ли для детей TODO.
track_source (:obj:`str`, optional): Источник трека.
available_for_options (:obj:`list` из :obj:`str`, optional): Возможные опции для трека.
r128 (:obj:`yandex_music.R128`, optional): Параметры нормализации громкости трека
в соответствии с рекомендацией EBU R 128.
lyrics_info (:obj:`yandex_music.LyricsInfo`, optional): Данные о наличии текстов трека.
track_sharing_flag (:obj:`str`, optional): TODO.
client (:obj:`yandex_music.Client`): Клиент Yandex Music.
"""
id: Union[str, int]
title: Optional[str] = None
available: Optional[bool] = None
artists: List['Artist'] = None
albums: List['Album'] = None
available_for_premium_users: Optional[bool] = None
lyrics_available: Optional[bool] = None
poetry_lover_matches: List['PoetryLoverMatch'] = None
best: Optional[bool] = None
real_id: Optional[Union[str, int]] = None
og_image: Optional[str] = None
type: Optional[str] = None
cover_uri: Optional[str] = None
major: Optional['Major'] = None
duration_ms: Optional[int] = None
storage_dir: Optional[str] = None
file_size: Optional[int] = None
substituted: Optional['Track'] = None
matched_track: Optional['Track'] = None
normalization: Optional['Normalization'] = None
error: Optional[str] = None
can_publish: Optional[bool] = None
state: Optional[str] = None
desired_visibility: Optional[str] = None
filename: Optional[str] = None
user_info: Optional['User'] = None
meta_data: Optional['MetaData'] = None
regions: Optional[List[str]] = None
available_as_rbt: Optional[bool] = None
content_warning: Optional[str] = None
explicit: Optional[bool] = None
preview_duration_ms: Optional[int] = None
available_full_without_permission: Optional[bool] = None
version: Optional[str] = None
remember_position: Optional[bool] = None
background_video_uri: Optional[str] = None
short_description: Optional[str] = None
is_suitable_for_children: Optional[bool] = None
track_source: Optional[str] = None
available_for_options: Optional[List[str]] = None
r128: Optional['R128'] = None
lyrics_info: Optional['LyricsInfo'] = None
track_sharing_flag: Optional[str] = None
client: Optional['Client'] = None
def __post_init__(self):
self.download_info = None
self._id_attrs = (self.id,)
def get_download_info(self, get_direct_links=False) -> List['DownloadInfo']:
"""Сокращение для::
client.tracks_download_info(self.track_id, get_direct_links)
"""
self.download_info = self.client.tracks_download_info(self.track_id, get_direct_links)
return self.download_info
async def get_download_info_async(self, get_direct_links=False) -> List['DownloadInfo']:
"""Сокращение для::
await client.tracks_download_info(self.track_id, get_direct_links)
"""
self.download_info = await self.client.tracks_download_info(self.track_id, get_direct_links)
return self.download_info
def get_supplement(self, *args, **kwargs) -> Optional['Supplement']:
"""Сокращение для::
client.track_supplement(track.id, *args, **kwargs)
"""
return self.client.track_supplement(self.id, *args, **kwargs)
async def get_supplement_async(self, *args, **kwargs) -> Optional['Supplement']:
"""Сокращение для::
await client.track_supplement(track.id, *args, **kwargs)
"""
return await self.client.track_supplement(self.id, *args, **kwargs)
def get_lyrics(self, *args, **kwargs) -> Optional['TrackLyrics']:
"""Сокращение для::
client.tracks_lyrics(track.id, *args, **kwargs)
"""
return self.client.tracks_lyrics(self.id, *args, **kwargs)
async def get_lyrics_async(self, *args, **kwargs) -> Optional['TrackLyrics']:
"""Сокращение для::
client.tracks_lyrics(track.id, *args, **kwargs)
"""
return await self.client.tracks_lyrics(self.id, *args, **kwargs)
def get_cover_url(self, size: str = '200x200') -> str:
"""Возвращает URL обложки.
Args:
size (:obj:`str`, optional): Размер обложки.
Returns:
:obj:`str`: URL обложки.
"""
return f'https://{self.cover_uri.replace("%%", size)}'
def get_og_image_url(self, size: str = '200x200') -> str:
"""Возвращает URL OG обложки.
Args:
size (:obj:`str`, optional): Размер обложки.
Returns:
:obj:`str`: URL обложки.
"""
return f'https://{self.og_image.replace("%%", size)}'
def download_cover(self, filename: str, size: str = '200x200') -> None:
"""Загрузка обложки.
Args:
filename (:obj:`str`): Путь для сохранения файла с названием и расширением.
size (:obj:`str`, optional): Размер обложки.
"""
self.client.request.download(self.get_cover_url(size), filename)
async def download_cover_async(self, filename: str, size: str = '200x200') -> None:
"""Загрузка обложки.
Args:
filename (:obj:`str`): Путь для сохранения файла с названием и расширением.
size (:obj:`str`, optional): Размер обложки.
"""
await self.client.request.download(self.get_cover_url(size), filename)
def download_og_image(self, filename: str, size: str = '200x200') -> None:
"""Загрузка обложки.
Предпочтительнее использовать `self.download_cover()`.
Args:
filename (:obj:`str`): Путь для сохранения файла с названием и расширением.
size (:obj:`str`, optional): Размер обложки.
"""
self.client.request.download(self.get_og_image_url(size), filename)
async def download_og_image_async(self, filename: str, size: str = '200x200') -> None:
"""Загрузка обложки.
Предпочтительнее использовать `self.download_cover_async()`.
Args:
filename (:obj:`str`): Путь для сохранения файла с названием и расширением.
size (:obj:`str`, optional): Размер обложки.
"""
await self.client.request.download(self.get_og_image_url(size), filename)
def download_cover_bytes(self, size: str = '200x200') -> bytes:
"""Загрузка обложки и возврат в виде байтов.
Args:
size (:obj:`str`, optional): Размер обложки.
Returns:
:obj:`bytes`: Обложка в виде байтов.
"""
return self.client.request.retrieve(self.get_cover_url(size))
async def download_cover_bytes_async(self, size: str = '200x200') -> bytes:
"""Загрузка обложки и возврат в виде байтов.
Args:
size (:obj:`str`, optional): Размер обложки.
Returns:
:obj:`bytes`: Обложка в виде байтов.
"""
return await self.client.request.retrieve(self.get_cover_url(size))
def download_og_image_bytes(self, size: str = '200x200') -> bytes:
"""Загрузка обложки и возврат в виде байтов.
Предпочтительнее использовать `self.download_cover()`.
Args:
size (:obj:`str`, optional): Размер обложки.
Returns:
:obj:`bytes`: Обложка в виде байтов.
"""
return self.client.request.retrieve(self.get_og_image_url(size))
async def download_og_image_bytes_async(self, size: str = '200x200') -> bytes:
"""Загрузка обложки и возврат в виде байтов.
Предпочтительнее использовать `self.download_cover_async()`.
Args:
size (:obj:`str`, optional): Размер обложки.
Returns:
:obj:`bytes`: Обложка в виде байтов.
"""
return await self.client.request.retrieve(self.get_og_image_url(size))
def get_specific_download_info(self, codec: str, bitrate_in_kbps: int) -> Optional['DownloadInfo']:
"""Возвращает вариант загрузки по критериям.
Args:
codec (:obj:`str`, optional): Кодек из доступных в `self.download_info`.
bitrate_in_kbps (:obj:`int`, optional): Битрейт из доступных в `self.download_info` для данного кодека.
Returns:
:obj:`yandex_music.DownloadInfo` | :obj:`None`: Вариант загрузки трека или :obj:`None`.
"""
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:
return info
return None
async def get_specific_download_info_async(self, codec: str, bitrate_in_kbps: int) -> Optional['DownloadInfo']:
"""Возвращает вариант загрузки по критериям.
Args:
codec (:obj:`str`, optional): Кодек из доступных в `self.download_info`.
bitrate_in_kbps (:obj:`int`, optional): Битрейт из доступных в `self.download_info` для данного кодека.
Returns:
:obj:`yandex_music.DownloadInfo` | :obj:`None`: Вариант загрузки трека или :obj:`None`.
"""
if self.download_info is None:
await self.get_download_info_async()
for info in self.download_info:
if info.codec == codec and info.bitrate_in_kbps == bitrate_in_kbps:
return info
return None
def download(self, filename: str, codec: str = 'mp3', bitrate_in_kbps: int = 192) -> None:
"""Загрузка трека.
Note:
Известные значения `codec`: `mp3`, `aac`.
Известные значения `bitrate_in_kbps`: `64`, `128`, `192`, `320`.
Args:
filename (:obj:`str`): Путь для сохранения файла с названием и расширением.
codec (:obj:`str`, optional): Кодек из доступных в `self.download_info`.
bitrate_in_kbps (:obj:`int`, optional): Битрейт из доступных в `self.download_info` для данного кодека.
Raises:
:class:`yandex_music.exceptions.InvalidBitrateError`: Если в `self.download_info` не найден подходящий трек.
"""
info = self.get_specific_download_info(codec, bitrate_in_kbps)
if info:
info.download(filename)
else:
raise InvalidBitrateError('Unavailable bitrate')
async def download_async(self, filename: str, codec: str = 'mp3', bitrate_in_kbps: int = 192) -> None:
"""Загрузка трека.
Note:
Известные значения `codec`: `mp3`, `aac`.
Известные значения `bitrate_in_kbps`: `64`, `128`, `192`, `320`.
Args:
filename (:obj:`str`): Путь для сохранения файла с названием и расширением.
codec (:obj:`str`, optional): Кодек из доступных в `self.download_info`.
bitrate_in_kbps (:obj:`int`, optional): Битрейт из доступных в `self.download_info` для данного кодека.
Raises:
:class:`yandex_music.exceptions.InvalidBitrateError`: Если в `self.download_info` не найден подходящий трек.
"""
info = await self.get_specific_download_info_async(codec, bitrate_in_kbps)
if info:
await info.download_async(filename)
else:
raise InvalidBitrateError('Unavailable bitrate')
def download_bytes(self, codec: str = 'mp3', bitrate_in_kbps: int = 192) -> bytes:
"""Загрузка трека и возврат в виде байтов.
Note:
Известные значения `codec`: `mp3`, `aac`.
Известные значения `bitrate_in_kbps`: `64`, `128`, `192`, `320`.
Args:
codec (:obj:`str`, optional): Кодек из доступных в `self.download_info`.
bitrate_in_kbps (:obj:`int`, optional): Битрейт из доступных в `self.download_info` для данного кодека.
Raises:
:class:`yandex_music.exceptions.InvalidBitrateError`: Если в `self.download_info` не найден подходящий трек.
Returns:
:obj:`bytes`: Трек в виде байтов.
"""
info = self.get_specific_download_info(codec, bitrate_in_kbps)
if info:
return info.download_bytes()
else:
raise InvalidBitrateError('Unavailable bitrate')
async def download_bytes_async(self, codec: str = 'mp3', bitrate_in_kbps: int = 192) -> bytes:
"""Загрузка трека и возврат в виде байтов.
Note:
Известные значения `codec`: `mp3`, `aac`.
Известные значения `bitrate_in_kbps`: `64`, `128`, `192`, `320`.
Args:
codec (:obj:`str`, optional): Кодек из доступных в `self.download_info`.
bitrate_in_kbps (:obj:`int`, optional): Битрейт из доступных в `self.download_info` для данного кодека.
Raises:
:class:`yandex_music.exceptions.InvalidBitrateError`: Если в `self.download_info` не найден подходящий трек.
Returns:
:obj:`bytes`: Трек в виде байтов.
"""
info = await self.get_specific_download_info_async(codec, bitrate_in_kbps)
if info:
return await info.download_bytes_async()
else:
raise InvalidBitrateError('Unavailable bitrate')
def like(self, *args, **kwargs) -> bool:
"""Сокращение для::
client.users_likes_tracks_add(track.id, user.id, *args, **kwargs)
"""
return self.client.users_likes_tracks_add(self.track_id, self.client.me.account.uid, *args, **kwargs)
async def like_async(self, *args, **kwargs) -> bool:
"""Сокращение для::
await client.users_likes_tracks_add(track.id, user.id, *args, **kwargs)
"""
return await self.client.users_likes_tracks_add(self.track_id, self.client.me.account.uid, *args, **kwargs)
def dislike(self, *args, **kwargs) -> bool:
"""Сокращение для::
client.users_likes_tracks_remove(track.id, user.id *args, **kwargs)
"""
return self.client.users_likes_tracks_remove(self.track_id, self.client.me.account.uid, *args, **kwargs)
async def dislike_async(self, *args, **kwargs) -> bool:
"""Сокращение для::
await client.users_likes_tracks_remove(track.id, user.id *args, **kwargs)
"""
return await self.client.users_likes_tracks_remove(self.track_id, self.client.me.account.uid, *args, **kwargs)
def artists_name(self) -> List[str]:
"""Получает имена всех исполнителей.
Returns:
:obj:`list` из :obj:`str`: Имена исполнителей.
"""
return [i.name for i in self.artists]
@property
def track_id(self) -> str:
""":obj:`str`: Уникальный идентификатор трека состоящий из его номера и номера альбома или просто из номера."""
if self.albums:
return f'{self.id}:{self.albums[0].id}'
return f'{self.id}'
@classmethod
def de_json(cls, data: dict, client: 'Client') -> Optional['Track']:
"""Десериализация объекта.
Args:
data (:obj:`dict`): Поля и значения десериализуемого объекта.
client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music.
Returns:
:obj:`yandex_music.Track`: Трек.
"""
if not data:
return None
data = super(Track, cls).de_json(data, client)
from yandex_music import Normalization, Major, Album, Artist, User, MetaData, PoetryLoverMatch, R128, LyricsInfo
data['albums'] = Album.de_list(data.get('albums'), client)
data['artists'] = Artist.de_list(data.get('artists'), client)
data['normalization'] = Normalization.de_json(data.get('normalization'), client)
data['major'] = Major.de_json(data.get('major'), client)
data['substituted'] = Track.de_json(data.get('substituted'), client)
data['matched_track'] = Track.de_json(data.get('matched_track'), client)
data['user_info'] = User.de_json(data.get('user_info'), client)
data['meta_data'] = MetaData.de_json(data.get('meta_data'), client)
data['poetry_lover_matches'] = PoetryLoverMatch.de_list(data.get('poetry_lover_matches'), client)
data['r128'] = R128.de_json(data.get('r128'), client)
data['lyrics_info'] = LyricsInfo.de_json(data.get('lyrics_info'), client)
return cls(client=client, **data)
@classmethod
def de_list(cls, data: dict, client: 'Client') -> List['Track']:
"""Десериализация списка объектов.
Args:
data (:obj:`list`): Список словарей с полями и значениями десериализуемого объекта.
client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music.
Returns:
:obj:`list` из :obj:`yandex_music.Track`: Треки.
"""
if not data:
return []
return [cls.de_json(track, client) for track in data]
# camelCase псевдонимы
#: Псевдоним для :attr:`get_download_info`
getDownloadInfo = get_download_info
#: Псевдоним для :attr:`get_download_info_async`
getDownloadInfoAsync = get_download_info_async
#: Псевдоним для :attr:`get_supplement`
getSupplement = get_supplement
#: Псевдоним для :attr:`get_supplement_async`
getSupplementAsync = get_supplement_async
#: Псевдоним для :attr:`get_lyrics`
getLyrics = get_lyrics
#: Псевдоним для :attr:`get_lyrics_async`
getLyricsAsync = get_lyrics_async
#: Псевдоним для :attr:`get_cover_url`
getCoverUrl = get_cover_url
#: Псевдоним для :attr:`get_og_image_url`
getOgImageUrl = get_og_image_url
#: Псевдоним для :attr:`download_cover`
downloadCover = download_cover
#: Псевдоним для :attr:`download_cover_async`
downloadCoverAsync = download_cover_async
#: Псевдоним для :attr:`download_og_image`
downloadOgImage = download_og_image
#: Псевдоним для :attr:`download_og_image_async`
downloadOgImageAsync = download_og_image_async
#: Псевдоним для :attr:`download_cover_bytes`
downloadCoverBytes = download_cover_bytes
#: Псевдоним для :attr:`download_cover_bytes_async`
downloadCoverBytesAsync = download_cover_bytes_async
#: Псевдоним для :attr:`download_og_image_bytes`
downloadOgImageBytes = download_og_image_bytes
#: Псевдоним для :attr:`download_og_image_bytes_async`
downloadOgImageBytesAsync = download_og_image_bytes_async
#: Псевдоним для :attr:`get_specific_download_info`
getSpecificDownloadInfo = get_specific_download_info
#: Псевдоним для :attr:`get_specific_download_info_async`
getSpecificDownloadInfoAsync = get_specific_download_info_async
#: Псевдоним для :attr:`download_async`
downloadAsync = download_async
#: Псевдоним для :attr:`download_bytes`
downloadBytes = download_bytes
#: Псевдоним для :attr:`download_bytes_async`
downloadBytesAsync = download_bytes_async
#: Псевдоним для :attr:`like_async`
likeAsync = like_async
#: Псевдоним для :attr:`dislike_async`
dislikeAsync = dislike_async
#: Псевдоним для :attr:`artists_name`
artistsName = artists_name
#: Псевдоним для :attr:`track_id`
trackId = track_id