Мердж капчи в дев
このコミットが含まれているのは:
コミット
633057b8ba
17
README.rst
17
README.rst
|
@ -195,6 +195,23 @@ music.yandex.ru/album/**1193829**/track/**10994777**
|
|||
|
||||
Больше примеров тут: `proxies - advanced usage - requests <https://2.python-requests.org/en/master/user/advanced/#proxies>`_
|
||||
|
||||
Пример инициализации клиента с обработкой капчи:
|
||||
|
||||
.. code:: python
|
||||
|
||||
def init_client():
|
||||
client = captcha_key = captcha_answer = None
|
||||
while not client:
|
||||
try:
|
||||
client = Client.from_credentials('login', 'pass', captcha_answer, captcha_key)
|
||||
except Captcha as e:
|
||||
e.captcha.download('captcha.png')
|
||||
|
||||
captcha_key = e.captcha.x_captcha_key
|
||||
captcha_answer = input('Число с картинки: ')
|
||||
|
||||
return client
|
||||
|
||||
--------------------
|
||||
Изучение по примерам
|
||||
--------------------
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
yandex_music.utils.captcha_response.CaptchaResponse
|
||||
===================================================
|
||||
|
||||
.. autoclass:: yandex_music.utils.captcha_response.CaptchaResponse
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
|
@ -5,4 +5,5 @@
|
|||
|
||||
yandex_music.utils.request
|
||||
yandex_music.utils.response
|
||||
yandex_music.utils.captcha_response
|
||||
yandex_music.utils.difference
|
|
@ -86,7 +86,7 @@ class Client(YandexMusicObject):
|
|||
self.account = self.account_status().account
|
||||
|
||||
@classmethod
|
||||
def from_credentials(cls, username, password, *args, **kwargs):
|
||||
def from_credentials(cls, username, password, x_captcha_answer=None, x_captcha_key=None, *args, **kwargs):
|
||||
"""Инициализция клиента по логину и паролю.
|
||||
|
||||
Данный метод получает токен каждый раз при вызове. Рекомендуется сгенерировать его самостоятельно, сохранить и
|
||||
|
@ -95,13 +95,16 @@ class Client(YandexMusicObject):
|
|||
Args:
|
||||
username (:obj:`str`): Логин клиента (идентификатор).
|
||||
password (:obj:`str`): Пароль клиента (аутентификатор).
|
||||
x_captcha_answer (:obj:`str`, optional): Ответ на капчу (цифры с картинки).
|
||||
x_captcha_key (:obj:`str`, optional): Уникальный ключ капчи.
|
||||
**kwargs (:obj:`dict`, optional): Аргументы для конструктора клиента.
|
||||
|
||||
Returns:
|
||||
:obj:`yandex_music.Client`.
|
||||
"""
|
||||
|
||||
return cls(cls().generate_token_by_username_and_password(username, password), *args, **kwargs)
|
||||
return cls(cls().generate_token_by_username_and_password(username, password, x_captcha_answer=x_captcha_answer,
|
||||
x_captcha_key=x_captcha_key), *args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_token(cls, token, *args, **kwargs):
|
||||
|
@ -120,14 +123,16 @@ class Client(YandexMusicObject):
|
|||
return cls(token=token, *args, **kwargs)
|
||||
|
||||
@log
|
||||
def generate_token_by_username_and_password(self, username, password, grant_type='password',
|
||||
timeout=None, *args, **kwargs):
|
||||
def generate_token_by_username_and_password(self, username, password, grant_type='password', x_captcha_answer=None,
|
||||
x_captcha_key=None, timeout=None, *args, **kwargs):
|
||||
"""Метод получения OAuth токена по логину и паролю.
|
||||
|
||||
Args:
|
||||
username (:obj:`str`): Логин клиента (идентификатор).
|
||||
password (:obj:`str`): Пароль клиента (аутентификатор).
|
||||
grant_type (:obj:`str`, optional): Тип разрешения OAuth.
|
||||
x_captcha_answer (:obj:`str`, optional): Ответ на капчу (цифры с картинки).
|
||||
x_captcha_key (:obj:`str`, optional): Уникальный ключ капчи.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
|
||||
ответа от сервера вместо указанного при создании пула.
|
||||
**kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос).
|
||||
|
@ -149,6 +154,9 @@ class Client(YandexMusicObject):
|
|||
'password': password
|
||||
}
|
||||
|
||||
if x_captcha_answer and x_captcha_key:
|
||||
data.update({'x_captcha_answer': x_captcha_answer, 'x_captcha_key': x_captcha_key})
|
||||
|
||||
result = self._request.post(url, data, timeout=timeout, *args, **kwargs)
|
||||
|
||||
return result.get('access_token')
|
||||
|
|
|
@ -20,6 +20,38 @@ class Unauthorized(YandexMusicError):
|
|||
pass
|
||||
|
||||
|
||||
class Captcha(YandexMusicError):
|
||||
"""Базовый класс, представляющий исключение связанное с капчей.
|
||||
|
||||
Attributes:
|
||||
captcha (:obj:`yandex_music.utils.captcha_response.CaptchaResponse`): Объект класса
|
||||
:class:`yandex_music.utils.captcha_response.CaptchaResponse` представляющий капчу.
|
||||
|
||||
Args:
|
||||
msg (:obj:`str`): Сообщение с ошибкой.
|
||||
captcha (:obj:`yandex_music.utils.captcha_response.CaptchaResponse`): Объект класса
|
||||
:class:`yandex_music.utils.captcha_response.CaptchaResponse` представляющий капчу.
|
||||
"""
|
||||
|
||||
def __init__(self, msg, captcha, *args, **kwargs):
|
||||
self.captcha = captcha
|
||||
super().__init__(msg, *args, **kwargs)
|
||||
|
||||
|
||||
class CaptchaRequired(Captcha):
|
||||
"""Класс исключения, вызываемый в случае необходимости ввода проверочного кода.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CaptchaWrong(Captcha):
|
||||
"""Класс исключения, вызываемый в случае неправильного ввода капчи.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class NetworkError(YandexMusicError):
|
||||
"""Базовый класс исключений, вызываемых для ошибок, связанных с
|
||||
запросами к серверу.
|
||||
|
@ -37,4 +69,4 @@ class TimedOut(NetworkError):
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(TimedOut, self).__init__('Timed out')
|
||||
super().__init__('Timed out')
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
from yandex_music import YandexMusicObject
|
||||
|
||||
|
||||
class CaptchaResponse(YandexMusicObject):
|
||||
"""Класс представляющий ответ сервера с запросом на ввод капчи.
|
||||
|
||||
Attributes:
|
||||
x_captcha_url (:obj:`str`): Ссылка на изображение с капчей.
|
||||
x_captcha_key (:obj:`str`): Уникальный ключ капчи.
|
||||
error_description (:obj:`str`): Описание ошибки.
|
||||
error (:obj:`str`): Код ошибки.
|
||||
client (:obj:`yandex_music.Client`): Объект класса :class:`yandex_music.Client` представляющий клиент Yandex
|
||||
Music.
|
||||
|
||||
Args:
|
||||
x_captcha_url (:obj:`str`): Ссылка на изображение с капчей.
|
||||
x_captcha_key (:obj:`str`): Уникальный ключ капчи.
|
||||
error_description (:obj:`str`): Описание ошибки.
|
||||
error (:obj:`str`): Код ошибки.
|
||||
client (:obj:`yandex_music.Client`, optional): Объект класса :class:`yandex_music.Client` представляющий клиент
|
||||
Yandex Music.
|
||||
**kwargs: Произвольные ключевые аргументы полученные от API.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
x_captcha_url,
|
||||
x_captcha_key,
|
||||
error_description,
|
||||
error,
|
||||
client=None,
|
||||
**kwargs):
|
||||
self.x_captcha_url = x_captcha_url
|
||||
self.x_captcha_key = x_captcha_key
|
||||
self.error_description = error_description
|
||||
self.error = error
|
||||
|
||||
self.client = client
|
||||
self._id_attrs = (self.x_captcha_key, self.x_captcha_url)
|
||||
|
||||
def download(self, filename=None):
|
||||
"""Загрузка изображения с капчей.
|
||||
|
||||
Args:
|
||||
filename (:obj:`str`, optional): Путь и(или) название файла вместе с расширением. По умолчанию ключ
|
||||
капчи и расширение `.gif`.
|
||||
"""
|
||||
|
||||
if not filename:
|
||||
filename = f'{self.x_captcha_key}.gif'
|
||||
|
||||
self.client.request.download(self.x_captcha_url, filename)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, client):
|
||||
"""Десериализация объекта.
|
||||
|
||||
Args:
|
||||
data (:obj:`dict`): Поля и значения десериализуемого объекта.
|
||||
client (:obj:`yandex_music.Client`): Объект класса :class:`yandex_music.Client` представляющий клиент Yandex
|
||||
Music.
|
||||
|
||||
Returns:
|
||||
:obj:`yandex_music.utils.captcha_response.CaptchaResponse`: Объект класса
|
||||
:class:`yandex_music.utils.captcha_response.CaptchaResponse`.
|
||||
"""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(CaptchaResponse, cls).de_json(data, client)
|
||||
|
||||
return cls(client=client, **data)
|
|
@ -3,9 +3,10 @@ import json
|
|||
import logging
|
||||
import requests
|
||||
|
||||
from yandex_music.utils.captcha_response import CaptchaResponse
|
||||
from yandex_music.utils.response import Response
|
||||
from yandex_music.exceptions import Unauthorized, BadRequest, NetworkError, YandexMusicError
|
||||
|
||||
from yandex_music.exceptions import Unauthorized, BadRequest, NetworkError, YandexMusicError, CaptchaRequired, \
|
||||
CaptchaWrong
|
||||
|
||||
USER_AGENT = 'Yandex-Music-API'
|
||||
HEADERS = {
|
||||
|
@ -78,6 +79,9 @@ class Request:
|
|||
except (AttributeError, ValueError):
|
||||
raise YandexMusicError('Invalid server response')
|
||||
|
||||
if not data.get('result'):
|
||||
data = {'result': data, 'error': data.get('error'), 'error_description': data.get('error_description')}
|
||||
|
||||
return Response.de_json(data, self.client)
|
||||
|
||||
def _request_wrapper(self, *args, **kwargs):
|
||||
|
@ -96,9 +100,13 @@ class Request:
|
|||
if 200 <= resp.status_code <= 299:
|
||||
return resp
|
||||
|
||||
message = self._parse(resp.content).error or 'Unknown HTTPError'
|
||||
parse = self._parse(resp.content)
|
||||
message = parse.error or 'Unknown HTTPError'
|
||||
|
||||
if resp.status_code in (401, 403):
|
||||
if 'CAPTCHA' in message:
|
||||
exception = CaptchaWrong if 'Wrong' in message else CaptchaRequired
|
||||
raise exception(message, CaptchaResponse.de_json(parse.result, self.client))
|
||||
elif resp.status_code in (401, 403):
|
||||
raise Unauthorized(message)
|
||||
elif resp.status_code == 400:
|
||||
raise BadRequest(message)
|
||||
|
|
読み込み中…
新しいイシューから参照