Skip to content

Commit

Permalink
Merge pull request #9443 from pymedusa/release/release-0.5.11
Browse files Browse the repository at this point in the history
Release/release 0.5.11
  • Loading branch information
p0psicles authored Apr 17, 2021
2 parents 55cecb9 + 6e3ac19 commit 0286e89
Show file tree
Hide file tree
Showing 125 changed files with 4,077 additions and 2,393 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## 0.5.11 (17-04-2021)

#### New Features
- Added new provicer TvRoad. (credits to IamMika23) ([9424](https://github.com/pymedusa/Medusa/pull/9424))

#### Improvements
- Vueify history page. ([9201](https://github.com/pymedusa/Medusa/pull/9201))
- Nebulance: Prevent duplicate results for provider. ([9333](https://github.com/pymedusa/Medusa/pull/9333))
- Add Cloudflare BFM detection. ([9407](https://github.com/pymedusa/Medusa/pull/9407))

#### Fixes
- AnimeBytes: Fix exception when processing multi-ep BD specials. ([9396](https://github.com/pymedusa/Medusa/pull/9396))
- Fix issue with sending torrents to Synology downloadstation. ([9401](https://github.com/pymedusa/Medusa/pull/9401))
- Fix a number of issues with trakt sync. ([9319](https://github.com/pymedusa/Medusa/pull/9319))
- Fix shows enriched with wrong IMDB show data. ([9435](https://github.com/pymedusa/Medusa/pull/9435))
- Fix configured provider ratio getting lost after restart. ([9413](https://github.com/pymedusa/Medusa/pull/9413))
- Fix sending torrents to Synology Download Station from version 3.8.16.-3566. (credits to BenjV). ([9401](https://github.com/pymedusa/Medusa/pull/9401))

-----

## 0.5.10 (01-03-2021)

#### Fixes
Expand Down
80 changes: 45 additions & 35 deletions ext/cloudscraper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@

# ------------------------------------------------------------------------------- #

__version__ = '1.2.46'
__version__ = '1.2.58'

# ------------------------------------------------------------------------------- #

Expand Down Expand Up @@ -197,9 +197,9 @@ def simpleException(self, exception, msg):
@staticmethod
def debugRequest(req):
try:
print(dump.dump_all(req).decode('utf-8'))
print(dump.dump_all(req).decode('utf-8', errors='backslashreplace'))
except ValueError as e:
print("Debug Error: {}".format(getattr(e, 'message', e)))
print(f"Debug Error: {getattr(e, 'message', e)}")

# ------------------------------------------------------------------------------- #
# Unescape / decode html entities
Expand All @@ -225,10 +225,10 @@ def decodeBrotli(self, resp):
resp._content = brotli.decompress(resp.content)
else:
logging.warning(
'You\'re running urllib3 {}, Brotli content detected, '
f'You\'re running urllib3 {requests.packages.urllib3.__version__}, Brotli content detected, '
'Which requires manual decompression, '
'But option allow_brotli is set to False, '
'We will not continue to decompress.'.format(requests.packages.urllib3.__version__)
'We will not continue to decompress.'
)

return resp
Expand Down Expand Up @@ -290,7 +290,7 @@ def request(self, method, url, *args, **kwargs):
_ = self._solveDepthCnt
self.simpleException(
CloudflareLoopProtection,
"!!Loop Protection!! We have tried to solve {} time(s) in a row.".format(_)
f"!!Loop Protection!! We have tried to solve {_} time(s) in a row."
)

self._solveDepthCnt += 1
Expand All @@ -302,6 +302,27 @@ def request(self, method, url, *args, **kwargs):

return response

# ------------------------------------------------------------------------------- #
# check if the response contains a valid Cloudflare Bot Fight Mode challenge
# ------------------------------------------------------------------------------- #

@staticmethod
def is_BFM_Challenge(resp):
try:
return (
resp.headers.get('Server', '').startswith('cloudflare')
and re.search(
r"\/cdn-cgi\/bm\/cv\/\d+\/api\.js.*?"
r"window\['__CF\$cv\$params'\]\s*=\s*{",
resp.text,
re.M | re.S
)
)
except AttributeError:
pass

return False

# ------------------------------------------------------------------------------- #
# check if the response contains a valid Cloudflare challenge
# ------------------------------------------------------------------------------- #
Expand Down Expand Up @@ -334,11 +355,11 @@ def is_New_IUAM_Challenge(resp):
resp.headers.get('Server', '').startswith('cloudflare')
and resp.status_code in [429, 503]
and re.search(
r'cpo.src\s*=\s*"/cdn-cgi/challenge-platform/orchestrate/jsch/v1"',
r'cpo.src\s*=\s*"/cdn-cgi/challenge-platform/\S+orchestrate/jsch/v1',
resp.text,
re.M | re.S
)
and re.search(r'window._cf_chl_enter\(', resp.text, re.M | re.S)
and re.search(r'window._cf_chl_enter\s*[\(=]', resp.text, re.M | re.S)
)
except AttributeError:
pass
Expand All @@ -355,11 +376,11 @@ def is_New_Captcha_Challenge(resp):
return (
CloudScraper.is_Captcha_Challenge(resp)
and re.search(
r'cpo.src\s*=\s*"/cdn-cgi/challenge-platform/orchestrate/captcha/v1"',
r'cpo.src\s*=\s*"/cdn-cgi/challenge-platform/\S+orchestrate/captcha/v1',
resp.text,
re.M | re.S
)
and re.search(r'window._cf_chl_enter\(', resp.text, re.M | re.S)
and re.search(r'\s*id="trk_captcha_js"', resp.text, re.M | re.S)
)
except AttributeError:
pass
Expand Down Expand Up @@ -422,13 +443,13 @@ def is_Challenge_Request(self, resp):
if self.is_New_Captcha_Challenge(resp):
self.simpleException(
CloudflareChallengeError,
'Detected a Cloudflare version 2 challenge, This feature is not available in the opensource (free) version.'
'Detected a Cloudflare version 2 Captcha challenge, This feature is not available in the opensource (free) version.'
)

if self.is_New_IUAM_Challenge(resp):
self.simpleException(
CloudflareChallengeError,
'Detected a Cloudflare version 2 Captcha challenge, This feature is not available in the opensource (free) version.'
'Detected a Cloudflare version 2 challenge, This feature is not available in the opensource (free) version.'
)

if self.is_Captcha_Challenge(resp) or self.is_IUAM_Challenge(resp):
Expand Down Expand Up @@ -479,17 +500,11 @@ def IUAM_Challenge_Response(self, body, url, interpreter):
except Exception as e:
self.simpleException(
CloudflareIUAMError,
'Unable to parse Cloudflare anti-bots page: {}'.format(
getattr(e, 'message', e)
)
f"Unable to parse Cloudflare anti-bots page: {getattr(e, 'message', e)}"
)

return {
'url': '{}://{}{}'.format(
hostParsed.scheme,
hostParsed.netloc,
self.unescape(formPayload['challengeUUID'])
),
'url': f"{hostParsed.scheme}://{hostParsed.netloc}{self.unescape(formPayload['challengeUUID'])}",
'data': payload
}

Expand Down Expand Up @@ -570,11 +585,7 @@ def captcha_Challenge_Response(self, provider, provider_params, body, url):
hostParsed = urlparse(url)

return {
'url': '{}://{}{}'.format(
hostParsed.scheme,
hostParsed.netloc,
self.unescape(formPayload['challengeUUID'])
),
'url': f"{hostParsed.scheme}://{hostParsed.netloc}{self.unescape(formPayload['challengeUUID'])}",
'data': dataPayload
}

Expand Down Expand Up @@ -684,7 +695,7 @@ def updateAttr(obj, name, newValue):
cloudflare_kwargs,
'headers',
{
'Origin': '{}://{}'.format(urlParsed.scheme, urlParsed.netloc),
'Origin': f'{urlParsed.scheme}://{urlParsed.netloc}',
'Referer': resp.url
}
)
Expand Down Expand Up @@ -768,11 +779,12 @@ def get_tokens(cls, url, **kwargs):
'browser',
'debug',
'delay',
'interpreter',
'doubleDown',
'captcha',
'requestPreHook',
'requestPostHook',
'interpreter',
'source_address'
'requestPreHook',
'requestPostHook'
] if field in kwargs
}
)
Expand All @@ -781,15 +793,15 @@ def get_tokens(cls, url, **kwargs):
resp = scraper.get(url, **kwargs)
resp.raise_for_status()
except Exception:
logging.error('"{}" returned an error. Could not collect tokens.'.format(url))
logging.error(f'"{url}" returned an error. Could not collect tokens.')
raise

domain = urlparse(resp.url).netloc
# noinspection PyUnusedLocal
cookie_domain = None

for d in scraper.cookies.list_domains():
if d.startswith('.') and d in ('.{}'.format(domain)):
if d.startswith('.') and d in (f'.{domain}'):
cookie_domain = d
break
else:
Expand Down Expand Up @@ -822,11 +834,9 @@ def get_cookie_string(cls, url, **kwargs):

if ssl.OPENSSL_VERSION_INFO < (1, 1, 1):
print(
"DEPRECATION: The OpenSSL being used by this python install ({}) does not meet the minimum supported "
f"DEPRECATION: The OpenSSL being used by this python install ({ssl.OPENSSL_VERSION}) does not meet the minimum supported "
"version (>= OpenSSL 1.1.1) in order to support TLS 1.3 required by Cloudflare, "
"You may encounter an unexpected Captcha or cloudflare 1020 blocks.".format(
ssl.OPENSSL_VERSION
)
"You may encounter an unexpected Captcha or cloudflare 1020 blocks."
)

# ------------------------------------------------------------------------------- #
Expand Down
51 changes: 20 additions & 31 deletions ext/cloudscraper/captcha/2captcha.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,9 @@
)

try:
import polling
import polling2
except ImportError:
raise ImportError(
"Please install the python module 'polling' via pip or download it from "
"https://github.com/justiniso/polling/"
)
raise ImportError("Please install the python module 'polling2' via pip")

from . import Captcha

Expand All @@ -38,7 +35,7 @@ def __init__(self):
@staticmethod
def checkErrorStatus(response, request_type):
if response.status_code in [500, 502]:
raise CaptchaServiceUnavailable('2Captcha: Server Side Error {}'.format(response.status_code))
raise CaptchaServiceUnavailable(f'2Captcha: Server Side Error {response.status_code}')

errors = {
'in.php': {
Expand Down Expand Up @@ -84,12 +81,10 @@ def checkErrorStatus(response, request_type):
}
}

if response.json().get('status') == 0 and response.json().get('request') in errors.get(request_type):
rPayload = response.json()
if rPayload.get('status') == 0 and rPayload.get('request') in errors.get(request_type):
raise CaptchaAPIError(
'{} {}'.format(
response.json().get('request'),
errors.get(request_type).get(response.json().get('request'))
)
f"{rPayload['request']} {errors.get(request_type).get(rPayload['request'])}"
)

# ------------------------------------------------------------------------------- #
Expand All @@ -101,16 +96,14 @@ def reportJob(self, jobID):
)

def _checkRequest(response):
self.checkErrorStatus(response, 'res.php')
if response.ok and response.json().get('status') == 1:
return response

self.checkErrorStatus(response, 'res.php')

return None

response = polling.poll(
response = polling2.poll(
lambda: self.session.get(
'{}/res.php'.format(self.host),
f'{self.host}/res.php',
params={
'key': self.api_key,
'action': 'reportbad',
Expand Down Expand Up @@ -138,16 +131,14 @@ def requestJob(self, jobID):
raise CaptchaBadJobID("2Captcha: Error bad job id to request Captcha.")

def _checkRequest(response):
self.checkErrorStatus(response, 'res.php')
if response.ok and response.json().get('status') == 1:
return response

self.checkErrorStatus(response, 'res.php')

return None

response = polling.poll(
response = polling2.poll(
lambda: self.session.get(
'{}/res.php'.format(self.host),
f'{self.host}/res.php',
params={
'key': self.api_key,
'action': 'get',
Expand All @@ -172,18 +163,16 @@ def _checkRequest(response):

def requestSolve(self, captchaType, url, siteKey):
def _checkRequest(response):
self.checkErrorStatus(response, 'in.php')
if response.ok and response.json().get("status") == 1 and response.json().get('request'):
return response

self.checkErrorStatus(response, 'in.php')

return None

data = {
'key': self.api_key,
'pageurl': url,
'json': 1,
'soft_id': 5507698
'soft_id': 2905
}

data.update(
Expand All @@ -204,9 +193,9 @@ def _checkRequest(response):
}
)

response = polling.poll(
response = polling2.poll(
lambda: self.session.post(
'{}/in.php'.format(self.host),
f'{self.host}/in.php',
data=data,
allow_redirects=False,
timeout=30
Expand Down Expand Up @@ -252,17 +241,17 @@ def getCaptchaAnswer(self, captchaType, url, siteKey, captchaParams):
try:
jobID = self.requestSolve(captchaType, url, siteKey)
return self.requestJob(jobID)
except polling.TimeoutException:
except polling2.TimeoutException:
try:
if jobID:
self.reportJob(jobID)
except polling.TimeoutException:
except polling2.TimeoutException:
raise CaptchaTimeout(
"2Captcha: Captcha solve took to long and also failed reporting the job the job id {}.".format(jobID)
f"2Captcha: Captcha solve took to long and also failed reporting the job the job id {jobID}."
)

raise CaptchaTimeout(
"2Captcha: Captcha solve took to long to execute job id {}, aborting.".format(jobID)
f"2Captcha: Captcha solve took to long to execute job id {jobID}, aborting."
)


Expand Down
4 changes: 2 additions & 2 deletions ext/cloudscraper/captcha/9kw.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self):
def checkErrorStatus(response):
if response.status_code in [500, 502]:
raise reCaptchaServiceUnavailable(
'9kw: Server Side Error {}'.format(response.status_code)
f'9kw: Server Side Error {response.status_code}'
)

error_codes = {
Expand Down Expand Up @@ -203,7 +203,7 @@ def getCaptchaAnswer(self, captchaType, url, siteKey, reCaptchaParams):
return self.requestJob(jobID)
except polling.TimeoutException:
raise reCaptchaTimeout(
"9kw: reCaptcha solve took to long to execute 'captchaid' {}, aborting.".format(jobID)
f"9kw: reCaptcha solve took to long to execute 'captchaid' {jobID}, aborting."
)

# ------------------------------------------------------------------------------- #
Expand Down
7 changes: 4 additions & 3 deletions ext/cloudscraper/captcha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ def __init__(self, name):
def dynamicImport(cls, name):
if name not in captchaSolvers:
try:
__import__('{}.{}'.format(cls.__module__, name))
__import__(f'{cls.__module__}.{name}')
if not isinstance(captchaSolvers.get(name), Captcha):
raise ImportError('The anti captcha provider was not initialized.')
except ImportError:
logging.error("Unable to load {} anti captcha provider".format(name))
except ImportError as e:
sys.tracebacklimit = 0
logging.error(f'Unable to load {name} anti captcha provider -> {e}')
raise

return captchaSolvers[name]
Expand Down
Loading

0 comments on commit 0286e89

Please sign in to comment.