удалено получение токена по логину и паролю

このコミットが含まれているのは:
Il'ya (Marshal) 2022-02-19 14:07:47 +01:00
コミット 4a11b73e57
7個のファイルの変更22行の追加331行の削除

ファイルの表示

@ -89,17 +89,13 @@ Yandex Music API
классов-обёрток объектов высокого уровня дабы сделать разработку клиентов
и скриптов простой и понятной.
--------------------------------
Доступ к вашим данным на Яндексе
--------------------------------
-----------------------------------
Доступ к вашим данным Яндекс.Музыка
-----------------------------------
Значения констант
`CLIENT_ID и CLIENT_SECRET <https://github.com/MarshalX/yandex-music-api/blob/main/yandex_music/client.py#L52>`_
позаимствовано у официального приложения-клиента сервиса Яндекс.Музыка из магазина
Microsoft Store. Так как API является закрытым и используется только внутри
компании Яндекс сейчас невозможно зарегистрировать своё собственное приложение на
`oauth.yandex.ru <https://oauth.yandex.ru/>`_, а следовательно, использовать свои
значения констант.
Начиная с версии 2.0.0 библиотека больше не предоставляет интерфейсы для работы
с OAuth Яндекс и Яндекс.Паспорт. Задача по получению токена для доступа к данным
на плечах разработчиков использующих данную библиотеку.
=========
Установка
@ -133,26 +129,20 @@ Microsoft Store. Так как API является закрытым и испо
client = Client()
Работа без авторизации ограничена. Так, например, для загрузки будут доступны
только первые 30 секунд аудиофайла. Для понимания всех ограничений зайдите на
сайт Яндекс.Музыка под инкогнито и воспользуйтесь сервисом.
Для доступа к своим личным данным следует авторизоваться.
Это можно осуществить через OAuth токен или логин с паролем.
Это осуществляется через токен аккаунта Яндекс.Музыка.
Авторизация по логину и паролю:
.. code:: python
from yandex_music import Client
client = Client.from_credentials('example@yandex.com', 'password')
Авторизация по токену:
Авторизация:
.. code:: python
from yandex_music import Client
client = Client('token')
# или
client = Client.from_token('token')
После успешного создания клиента Вы вольны в выборе необходимого метода
из API. Все они доступны у объекта класса ``Client``. Подробнее в методах клиента
@ -164,7 +154,7 @@ Microsoft Store. Так как API является закрытым и испо
from yandex_music import Client
client = Client.from_credentials('example@yandex.com', 'password')
client = Client('token')
client.users_likes_tracks()[0].fetch_track().download('example.mp3')
В примере выше клиент получает список треков которые были отмечены как
@ -208,36 +198,6 @@ music.yandex.ru/album/**1193829**/track/**10994777**
Больше примеров тут: `proxies - advanced usage - requests <https://2.python-requests.org/en/master/user/advanced/#proxies>`_
Пример инициализации клиента с обработкой капчи при помощи callback-функции:
.. code:: python
def proc_captcha(captcha_image_url):
print(captcha_image_url)
return input('Код с картинки: ')
client = Client.from_credentials('login', 'pass', captcha_callback=proc_captcha)
Пример инициализации клиента с обработкой капчи:
.. code:: python
def init_client():
client = track_id = captcha_image_url = captcha_answer = None
while not client:
try:
client = Client.from_credentials('login', 'pass', track_id, captcha_answer)
except Captcha as e:
track_id = e.track_id
if e.captcha_image_url:
captcha_image_url = e.captcha_image_url
else:
print('Вы отправили ответ не посмотрев на картинку..')
captcha_answer = input(f'{captcha_image_url}\nВведите код с картинки: ')
return client
--------------------
Изучение по примерам
--------------------

ファイルの表示

@ -6,15 +6,10 @@ from yandex_music.client import Client
if len(sys.argv) == 1 or len(sys.argv) > 3:
print('Usage: DailyPlaylistUpdater.py token')
print('token - Authentication token')
print('\nUsage: DailyPlaylistUpdater.py username password')
print('username - username in format \'example@yandex.ru\'')
print('password - your password')
quit()
# Authorization
elif len(sys.argv) == 2:
client = Client.fromToken(sys.argv[1])
elif len(sys.argv) == 3:
client = Client.fromCredentials(sys.argv[1], sys.argv[2])
client = Client(sys.argv[1])
# Current daily playlist
PersonalPlaylistBlocks = client.landing(blocks=['personalplaylists']).blocks[0]

ファイルの表示

@ -53,7 +53,7 @@ else:
print('Config file not found. Use --token to create it')
sys.exit(2)
client = Client.from_token(args.token, report_new_fields=False)
client = Client(args.token, report_unknown_fields=False)
print('Hello,', client.me.account.first_name)
if client.me.account.now and client.me.account.now.split('T')[0] == client.me.account.birthday:
@ -65,7 +65,7 @@ if args.playlist == 'user':
print('specify --playlist-name', list(p.title for p in user_playlists))
sys.exit(1)
playlist = next((p for p in user_playlists if p.title == args.playlist_name), None)
if playlist == None:
if playlist is None:
print(f'playlist "{args.playlist_name}" not found')
sys.exit(1)
total_tracks = playlist.track_count

ファイルの表示

@ -7,7 +7,7 @@ from yandex_music import Client
from radio import Radio
# API instance
client = Client(token="YOUR_TOKEN_HERE")
client = Client(token='YOUR_TOKEN_HERE')
# Get random station
_stations = client.rotor_stations_list()
@ -21,10 +21,10 @@ radio = Radio(client)
# start radio and get first track
first_track = radio.start_radio(_station_id, _station_from)
print("[Radio] First track is:", first_track)
print('[Radio] First track is:', first_track)
# get new track every 5 sec
while True:
sleep(5)
next_track = radio.play_next()
print("[Radio] Next track is:", next_track)
print('[Radio] Next track is:', next_track)

ファイルの表示

@ -5,7 +5,7 @@ from yandex_music import Client
from radio import Radio
# API instance
client = Client(token="YOUR_API_KEY_HERE")
client = Client(token='YOUR_API_KEY_HERE')
# get some track
track = client.tracks(['2816574:303266'])[0]
@ -26,10 +26,10 @@ radio = Radio(client)
# start radio and get first track
first_track = radio.start_radio(_station_id, _station_from)
print("[Radio] First track is:", first_track)
print('[Radio] First track is:', first_track)
# get new track every 5 sec
while True:
sleep(5)
next_track = radio.play_next()
print("[Radio] Next track is:", next_track)
print('[Radio] Next track is:', next_track)

ファイルの表示

@ -42,16 +42,9 @@ from yandex_music import (
__license__,
__version__,
)
from yandex_music.exceptions import BadRequest, Captcha, CaptchaNotShown, CaptchaRequired, Unauthorized
from yandex_music.utils.difference import Difference
from yandex_music.utils.request import Request
CLIENT_ID = '23cabbbdc6cd418abb4b39c32c41195d'
CLIENT_SECRET = '53bc75238f0c4d08a118e51fe9203300'
X_TOKEN_CLIENT_ID = 'c0ebe342af7d48fbbbfcf2d2eedb8f9e'
X_TOKEN_CLIENT_SECRET = 'ad0a908f0aa341a182a37ecd75bc319e'
de_list = {
'artist': Artist.de_list,
'album': Album.de_list,
@ -96,7 +89,6 @@ class Client(YandexMusicObject):
logger (:obj:`logging.Logger`): Объект логгера.
token (:obj:`str`): Уникальный ключ для аутентификации.
base_url (:obj:`str`): Ссылка на API Yandex Music.
passport_url (:obj:`str`): Ссылка на Mobileproxy Passport Yandex Music.
me (:obj:`yandex_music.Status`): Информация об аккаунте.
device (:obj:`str`): Строка, содержащая сведения об устройстве, с которого выполняются запросы.
report_unknown_fields (:obj:`bool`): Включены ли предупреждения о неизвестных полях от API,
@ -106,7 +98,6 @@ class Client(YandexMusicObject):
token (:obj:`str`, optional): Уникальный ключ для аутентификации.
fetch_account_status (:obj:`bool`, optional): Получить ли информацию об аккаунте при инициализации объекта.
base_url (:obj:`str`, optional): Ссылка на API Yandex Music.
passport_url (:obj:`str`, optional): Ссылка на Mobileproxy Passport Yandex Music.
request (:obj:`yandex_music.utils.request.Request`, optional): Пре-инициализация
:class:`yandex_music.utils.request.Request`.
language (:obj:`str`, optional): Язык, на котором будут приходить ответы от API.
@ -121,7 +112,6 @@ class Client(YandexMusicObject):
token: str = None,
fetch_account_status: bool = True,
base_url: str = None,
passport_url: str = None,
request: Request = None,
language: str = 'ru',
report_unknown_fields=True,
@ -136,11 +126,8 @@ class Client(YandexMusicObject):
if base_url is None:
base_url = 'https://api.music.yandex.net'
if passport_url is None:
passport_url = 'https://mobileproxy.passport.yandex.net/'
self.base_url = base_url
self.passport_url = passport_url
self.report_new_fields = report_unknown_fields
@ -157,222 +144,11 @@ class Client(YandexMusicObject):
'os=Python; os_version=; manufacturer=Marshal; '
'model=Yandex Music API; clid=; device_id=random; uuid=random'
)
self._auth_sdk_params = 'app_id=ru.yandex.mobile.music&app_version_name=5.18&app_platform=iPad'
self.me = None
if fetch_account_status:
self.me = self.account_status()
@classmethod
def from_credentials(
cls,
login: str,
password: str,
track_id: str = None,
captcha_answer: str = None,
captcha_callback: Callable[[str], str] = None,
timeout: Union[int, float] = None,
*args,
**kwargs,
) -> 'Client':
"""Инициализция клиента по логину и паролю.
Note:
Данный метод получает токен каждый раз при вызове. Рекомендуется сгенерировать его самостоятельно, сохранить
и использовать при следующих инициализациях клиента. Не храните логины и пароли!
Args:
login (:obj:`str`): Логин клиента (идентификатор).
password (:obj:`str`): Пароль клиента (аутентификатор).
track_id (:obj:`str`): Идентификатора сессии аутентификации (для ввода капчи на старой сессии).
captcha_answer (:obj:`str`, optional): Ответ на капчу (цифры с картинки).
captcha_callback (:obj:`function`, optional): Функция обратного вызова для обработки капчи, должна
принимать строку с ссылкой на капчу и возвращать проверочный код.
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
ответа от сервера вместо указанного при создании пула.
*args: Произвольные аргументы для `requests.request` и `Client`.
**kwargs: Произвольные ключевые аргументы для `requests.request` и `Client`.
Returns:
:obj:`yandex_music.Client`.
Raises:
:class:`yandex_music.exceptions.CaptchaRequired`: При необходимости пройти капчу и отсутствию коллбека.
:class:`yandex_music.exceptions.CaptchaNotShown`: При попытке отправить ответ на капчу не посмотрев её.
:class:`yandex_music.exceptions.YandexMusicError`: Базовое исключение библиотеки.
"""
client = cls(*args, **kwargs)
if not track_id:
track_id = client._start_authentication(login, timeout, *args, **kwargs)
x_token = None
while not x_token:
try:
x_token = client._send_authentication_password(
track_id, password, captcha_answer, timeout, *args, **kwargs
)
except Captcha as e:
if not captcha_callback or not e.captcha_image_url:
raise e
captcha_answer = captcha_callback(e.captcha_image_url)
token = client._generate_yandex_music_token_by_x_token(x_token, timeout, *args, **kwargs)
return cls(token, *args, **kwargs)
@classmethod
def from_token(cls, token: str, *args, **kwargs) -> 'Client':
"""Инициализация клиента по токену.
Note:
Ничем не отличается от `Client(token)`. Так исторически сложилось.
Args:
token (:obj:`str`, optional): Уникальный ключ для аутентификации.
*args: Произвольные аргументы для `Client`.
**kwargs: Произвольные ключевые аргументы для `Client`.
Returns:
:obj:`yandex_music.Client`.
"""
return cls(token, *args, **kwargs)
@log
def _start_authentication(self, login: Union[str, int], timeout: Union[int, float] = None, *args, **kwargs) -> str:
"""Отправка логина и проверка на его существование. Получение идентификатора аутентификации.
Args:
login (:obj:`str`): Логин клиента (идентификатор).
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
ответа от сервера вместо указанного при создании пула.
*args: Произвольные аргументы для `requests.request`.
**kwargs: Произвольные ключевые аргументы для `requests.request`.
Returns:
:obj:`str`: `track_id` необходимый для отправки пароля к логину.
Raises:
:class:`yandex_music.exceptions.BadRequest`: При неправильном запросе.
"""
url = f'{self.passport_url}/2/bundle/mobile/start'
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'display_language': self.language,
'login': login,
'x_token_client_id': X_TOKEN_CLIENT_ID,
'x_token_client_secret': X_TOKEN_CLIENT_SECRET,
}
result = self._request.post(url, data, timeout=timeout, *args, **kwargs)
if result.get('status', 'error') == 'error':
raise BadRequest(result)
return result['track_id']
@log
def _send_authentication_password(
self,
track_id: str,
password: str,
captcha_answer: str = None,
timeout: Union[int, float] = None,
*args,
**kwargs,
) -> str:
"""Отправка пароля к существующему логину.
Notes:
Капча появляется после 5 неудачных попыток ввода пароля.
При получении капчи обязательно выполнить запрос на `captcha_image_url` (посмотреть капчу) перед
отправкой ответа.
Args:
track_id (:obj:`str`): Идентификатор аутентификации (получен на шаге валидации логина).
password (:obj:`str`): Пароль клиента (аутентификатор).
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
ответа от сервера вместо указанного при создании пула.
*args: Произвольные аргументы для `requests.request`.
**kwargs: Произвольные ключевые аргументы для `requests.request`.
Returns:
:obj:`str`: `X-Token` необходимый для получения токена ЯМ.
Raises:
:class:`yandex_music.exceptions.TimedOut`: При превышении времени ожидания.
:class:`yandex_music.exceptions.BadRequest`: При неправильном или неизвестном библиотеке запросе.
:class:`yandex_music.exceptions.Unauthorized`: При неправильном пароле.
:class:`yandex_music.exceptions.CaptchaRequired`: При необходимости пройти капчу.
:class:`yandex_music.exceptions.CaptchaNotShown`: При попытке отправить ответ на капчу не посмотрев её.
"""
url = f'{self.passport_url}/1/bundle/mobile/auth/password'
data = {
'track_id': track_id,
'password': password,
}
if captcha_answer:
data['captcha_answer'] = captcha_answer
result = self._request.post(url, data, timeout=timeout, *args, **kwargs)
status = result.get('status', 'error')
if status == 'ok':
return result['x_token']
if 'password.not_matched' in result['errors']:
raise Unauthorized(result)
elif 'captcha.required' in result['errors']:
raise CaptchaRequired('captcha.required', track_id, result['captcha_image_url'])
elif 'captcha.not_shown' in result['errors']:
raise CaptchaNotShown('captcha.not_shown', track_id)
else:
raise BadRequest(result)
@log
def _generate_yandex_music_token_by_x_token(
self, x_token: str, timeout: Union[int, float] = None, *args, **kwargs
) -> str:
"""Получения токена для Яндекс.Музыка по X-Token.
Args:
x_token (:obj:`str`): X-Token полученный в ответ на отправленный пароль.
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
ответа от сервера вместо указанного при создании пула.
*args: Произвольные аргументы для `requests.request`.
**kwargs: Произвольные ключевые аргументы для `requests.request`.
Returns:
:obj:`str`: `access_token` токен Яндекс.Музыка пригодный для использования в клиенте.
Raises:
:class:`yandex_music.exceptions.BadRequest`: При неправильном запросе.
"""
url = f'{self.passport_url}/1/token/?{self._auth_sdk_params}'
data = {
'access_token': x_token,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'grant_type': 'x-token',
}
result = self._request.post(url, data, timeout=timeout, *args, **kwargs)
if 'access_token' in result:
return result['access_token']
raise BadRequest(result)
@property
def request(self) -> Request:
""":obj:`yandex_music.utils.request.Request`: Объект вспомогательного класса для отправки запросов."""
@ -2816,10 +2592,6 @@ class Client(YandexMusicObject):
# camelCase псевдонимы
#: Псевдоним для :attr:`from_credentials`
fromCredentials = from_credentials
#: Псевдоним для :attr:`from_token`
fromToken = from_token
#: Псевдоним для :attr:`account_status`
accountStatus = account_status
#: Псевдоним для :attr:`account_settings`

ファイルの表示

@ -1,6 +1,3 @@
from typing import TYPE_CHECKING
class YandexMusicError(Exception):
"""Базовый класс, представляющий исключения общего характера."""
@ -17,39 +14,6 @@ class InvalidBitrate(YandexMusicError):
"""
class Captcha(YandexMusicError):
"""Базовый класс, представляющий исключение связанное с капчей.
Attributes:
track_id (:obj:`str`): Идентификатора сессии аутентификации.
captcha_image_url (:obj:`str` | :obj:`None`): Ссылка на изображение с капчей.
Args:
msg (:obj:`str`): Сообщение с ошибкой.
track_id (:obj:`str`, optional): Идентификатора сессии аутентификации.
captcha_image_url (:obj:`str`, optional): Ссылка на изображение с капчей.
"""
def __init__(self, msg: str, track_id: str = None, captcha_image_url: str = None, *args):
super().__init__(msg, *args)
self.track_id = track_id
self.captcha_image_url = captcha_image_url
class CaptchaRequired(Captcha):
"""Класс исключения, вызываемый в случае необходимости ввода проверочного кода."""
class CaptchaNotShown(Captcha):
"""Класс исключения, вызываемый в случае когда капча была получена, но изображение не было загружено.
Notes:
Будет вызвано если не сделать запрос на `captcha_image_url` полученный в `CaptchaRequired`. Получив данное
исключение можно выполнить запрос еще раз для получения новой капчи.
"""
class NetworkError(YandexMusicError):
"""Базовый класс исключений, вызываемых для ошибок, связанных с
запросами к серверу.