Skip to content

Commit

Permalink
Merge pull request #7582 from pymedusa/release/release-0.3.10
Browse files Browse the repository at this point in the history
Release 0.3.10
  • Loading branch information
medariox authored Jan 13, 2020
2 parents d0c136d + 22234ab commit 942040a
Show file tree
Hide file tree
Showing 101 changed files with 4,830 additions and 2,161 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ aliases:
- pip install --upgrade pip
- pip install dredd_hooks
- pip install 'PyYAML>=5.1'
- pip install six
- pip install 'six>=1.13.0'
- *nvm_install
- *install_yarn_bin
- yarn install --ignore-scripts
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
## 0.3.10 (2020-01-13)

#### New Features
- Add Opensubtitles VIP, aRGENTeaM and Subtitulamos subtitle providers ([#7555](https://github.com/pymedusa/Medusa/pull/7555), [#7518](https://github.com/pymedusa/Medusa/pull/7518))

#### Improvements
- Add `uniqueid` to Kodi 12+ show metadata ([#7483](https://github.com/pymedusa/Medusa/pull/7483))
- Update AppLink to enable native mouse navigation ([#7498](https://github.com/pymedusa/Medusa/pull/7498))

#### Fixes
- Fix Emby notifier error on Python 3 ([#7497](https://github.com/pymedusa/Medusa/pull/7497))
- Fix more qBittorrent authentication bugs ([#7501](https://github.com/pymedusa/Medusa/pull/7501))
- Fix `torrents.verifyCert` config patch ignored warning ([#7501](https://github.com/pymedusa/Medusa/pull/7501))
- Fix dragging and saving Anime / Series list handles in Home - Poster layout ([#7502](https://github.com/pymedusa/Medusa/pull/7502))
- Fix adding Anime with white/black listed release groups ([#7507](https://github.com/pymedusa/Medusa/pull/7507))
- Fix Schedule page and Forced Search on Schedule page ([#7512](https://github.com/pymedusa/Medusa/pull/7512))
- Fix manual search page release name bug ([#7517](https://github.com/pymedusa/Medusa/pull/7517))
- Fix being unable to save post-processing config ([#7526](https://github.com/pymedusa/Medusa/pull/7526))
- Fix qBittorrent error when torrent queueing is disabled ([#7541](https://github.com/pymedusa/Medusa/pull/7541))

-----

## 0.3.9 (2019-12-12)

#### Improvements
Expand Down
6 changes: 1 addition & 5 deletions dredd/dredd_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@
# Python 2
from __builtin__ import print as real_print

try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping

current_dir = os.path.abspath(os.path.dirname(__file__))
root_dir = os.path.abspath(os.path.join(current_dir, '..'))
sys.path.insert(1, os.path.join(root_dir, 'ext'))
Expand All @@ -31,6 +26,7 @@
import dredd_hooks as hooks

from six import string_types
from six.moves.collections_abc import Mapping
from six.moves.urllib.parse import parse_qs, urlencode, urlparse

import yaml
Expand Down
104 changes: 74 additions & 30 deletions ext/cfscrape/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def __init__(self, *args, **kwargs):

# Define headers to force using an OrderedDict and preserve header order
self.headers = headers
self.org_method = None

self.mount("https://", CloudflareAdapter())

Expand Down Expand Up @@ -152,75 +153,112 @@ def solve_cf_challenge(self, resp, **original_kwargs):
body = resp.text
parsed_url = urlparse(resp.url)
domain = parsed_url.netloc
submit_url = "%s://%s/cdn-cgi/l/chk_jschl" % (parsed_url.scheme, domain)
challenge_form = re.search(r'\<form.*?id=\"challenge-form\".*?\/form\>',body, flags=re.S).group(0) # find challenge form
method = re.search(r'method=\"(.*?)\"', challenge_form, flags=re.S).group(1)
if self.org_method is None:
self.org_method = resp.request.method
submit_url = "%s://%s%s" % (parsed_url.scheme,
domain,
re.search(r'action=\"(.*?)\"', challenge_form, flags=re.S).group(1).split('?')[0])

cloudflare_kwargs = copy.deepcopy(original_kwargs)

headers = cloudflare_kwargs.setdefault("headers", {})
headers["Referer"] = resp.url

try:
params = cloudflare_kwargs["params"] = OrderedDict(
re.findall(r'name="(s|jschl_vc|pass)"(?: [^<>]*)? value="(.+?)"', body)
)
cloudflare_kwargs["params"] = dict()
cloudflare_kwargs["data"] = dict()
if len(re.search(r'action=\"(.*?)\"', challenge_form, flags=re.S).group(1).split('?')) != 1:
for param in re.search(r'action=\"(.*?)\"', challenge_form, flags=re.S).group(1).split('?')[1].split('&'):
cloudflare_kwargs["params"].update({param.split('=')[0]:param.split('=')[1]})

for input_ in re.findall(r'\<input.*?(?:\/>|\<\/input\>)', challenge_form, flags=re.S):
if re.search(r'name=\"(.*?)\"',input_, flags=re.S).group(1) != 'jschl_answer':
if method == 'POST':
cloudflare_kwargs["data"].update({re.search(r'name=\"(.*?)\"',input_, flags=re.S).group(1):
re.search(r'value=\"(.*?)\"',input_, flags=re.S).group(1)})
elif method == 'GET':
cloudflare_kwargs["params"].update({re.search(r'name=\"(.*?)\"',input_, flags=re.S).group(1):
re.search(r'value=\"(.*?)\"',input_, flags=re.S).group(1)})
if method == 'POST':
for k in ("jschl_vc", "pass"):
if k not in cloudflare_kwargs["data"]:
raise ValueError("%s is missing from challenge form" % k)
elif method == 'GET':
for k in ("jschl_vc", "pass"):
if k not in cloudflare_kwargs["params"]:
raise ValueError("%s is missing from challenge form" % k)

for k in ("jschl_vc", "pass"):
if k not in params:
raise ValueError("%s is missing from challenge form" % k)
except Exception as e:
# Something is wrong with the page.
# This may indicate Cloudflare has changed their anti-bot
# technique. If you see this and are running the latest version,
# please open a GitHub issue so I can update the code accordingly.
raise ValueError(
"Unable to parse Cloudflare anti-bot IUAM page: %s %s"
% (e.message, BUG_REPORT)
% (e, BUG_REPORT)
)

# Solve the Javascript challenge
answer, delay = self.solve_challenge(body, domain)
params["jschl_answer"] = answer
if method == 'POST':
cloudflare_kwargs["data"]["jschl_answer"] = answer
elif method == 'GET':
cloudflare_kwargs["params"]["jschl_answer"] = answer

# Requests transforms any request into a GET after a redirect,
# so the redirect has to be handled manually here to allow for
# performing other types of requests even as the first request.
method = resp.request.method
cloudflare_kwargs["allow_redirects"] = False

# Cloudflare requires a delay before solving the challenge
time.sleep(max(delay - (time.time() - start_time), 0))

# Send the challenge response and handle the redirect manually
redirect = self.request(method, submit_url, **cloudflare_kwargs)
redirect_location = urlparse(redirect.headers["Location"])

if not redirect_location.netloc:
redirect_url = urlunparse(
(
parsed_url.scheme,
domain,
redirect_location.path,
redirect_location.params,
redirect_location.query,
redirect_location.fragment,
if "Location" in redirect.headers:
redirect_location = urlparse(redirect.headers["Location"])

if not redirect_location.netloc:
redirect_url = urlunparse(
(
parsed_url.scheme,
domain,
redirect_location.path,
redirect_location.params,
redirect_location.query,
redirect_location.fragment,
)
)
)
return self.request(method, redirect_url, **original_kwargs)
return self.request(method, redirect.headers["Location"], **original_kwargs)
return self.request(method, redirect_url, **original_kwargs)
return self.request(method, redirect.headers["Location"], **original_kwargs)
elif 'cf_clearance' in redirect.headers['Set-Cookie']:
resp = self.request(self.org_method, submit_url, cookies = redirect.cookies)
return resp
else:
resp = self.request(self.org_method, submit_url, **cloudflare_kwargs)
return resp


def solve_challenge(self, body, domain):
try:
javascript = re.search(r'\<script type\=\"text\/javascript\"\>\n(.*?)\<\/script\>',body, flags=re.S).group(1) # find javascript

challenge, ms = re.search(
r"setTimeout\(function\(\){\s*(var "
r"s,t,o,p,b,r,e,a,k,i,n,g,f.+?\r?\n[\s\S]+?a\.value\s*=.+?)\r?\n"
r"(?:[^{<>]*},\s*(\d{4,}))?",
body,
javascript, flags=re.S
).groups()

# The challenge requires `document.getElementById` to get this content.
# Future proofing would require escaping newlines and double quotes
innerHTML = re.search(r"<div(?: [^<>]*)? id=\"cf-dn.*?\">([^<>]*)", body)
innerHTML = innerHTML.group(1) if innerHTML else ""
innerHTML = ''
for i in javascript.split(';'):
if i.strip().split('=')[0].strip() == 'k': # from what i found out from pld example K var in
k = i.strip().split('=')[1].strip(' \'') # javafunction is for innerHTML this code to find it
innerHTML = re.search(r'\<div.*?id\=\"'+k+r'\".*?\>(.*?)\<\/div\>',body).group(1) #find innerHTML

# Prefix the challenge with a fake document object.
# Interpolate the domain, div contents, and JS challenge.
Expand Down Expand Up @@ -274,11 +312,17 @@ def solve_challenge(self, body, domain):
"""
% challenge
)
stderr = ''

try:
result = subprocess.check_output(
["node", "-e", js], stdin=subprocess.PIPE, stderr=subprocess.PIPE
)
node = subprocess.Popen(
["node", "-e", js], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True
)
result, stderr = node.communicate()
if node.returncode != 0:
stderr = "Node.js Exception:\n%s" % (stderr or None)
raise subprocess.CalledProcessError(node.returncode, "node -e ...", stderr)
except OSError as e:
if e.errno == 2:
raise EnvironmentError(
Expand Down
2 changes: 1 addition & 1 deletion ext/js2py/internals/constructors/jsfunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

def Function(this, args):
# convert arguments to python list of strings
a = map(to_string, tuple(args))
a = list(map(to_string, tuple(args)))
_body = u';'
_args = ()
if len(a):
Expand Down
Loading

0 comments on commit 942040a

Please sign in to comment.