mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
d97bb7174d
Change improve provider title processing. Change improve handling erroneous JSON responses. Change improve find show with unicode characters. Change improve result for providers Omgwtf, SpeedCD, Transmithenet, Zoogle. Change validate .torrent files that contain optional header data. Fix case where an episode status was not restored on failure. Add raise log error if no wanted qualities are found. Change add un/pw to Config/Media providers/Options for BTN API graceful fallback (can remove Api key for security). Change only download torrent once when using blackhole. Add Cloudflare module 1.6.8 (be0a536) to mitigate CloudFlare (IUAM) access validator. Add Js2Py 0.43 (c1442f1) Cloudflare dependency. Add pyjsparser 2.4.5 (cd5b829) Js2Py dependency.
327 lines
No EOL
8.3 KiB
Python
327 lines
No EOL
8.3 KiB
Python
import binascii
|
|
|
|
from pyjsparser import PyJsParser
|
|
import six
|
|
if six.PY3:
|
|
basestring = str
|
|
long = int
|
|
xrange = range
|
|
unicode = str
|
|
|
|
REGEXP_CONVERTER = PyJsParser()
|
|
|
|
def to_hex(s):
|
|
return binascii.hexlify(s.encode('utf8')).decode('utf8') # fucking python 3, I hate it so much
|
|
# wtf was wrong with s.encode('hex') ???
|
|
def indent(lines, ind=4):
|
|
return ind*' '+lines.replace('\n', '\n'+ind*' ').rstrip(' ')
|
|
|
|
def inject_before_lval(source, lval, code):
|
|
if source.count(lval)>1:
|
|
print()
|
|
print(lval)
|
|
raise RuntimeError('To many lvals (%s)' % lval)
|
|
elif not source.count(lval):
|
|
print()
|
|
print(lval)
|
|
assert lval not in source
|
|
raise RuntimeError('No lval found "%s"' % lval)
|
|
end = source.index(lval)
|
|
inj = source.rfind('\n', 0, end)
|
|
ind = inj
|
|
while source[ind+1]==' ':
|
|
ind+=1
|
|
ind -= inj
|
|
return source[:inj+1]+ indent(code, ind) + source[inj+1:]
|
|
|
|
|
|
def get_continue_label(label):
|
|
return CONTINUE_LABEL%to_hex(label)
|
|
|
|
def get_break_label(label):
|
|
return BREAK_LABEL%to_hex(label)
|
|
|
|
|
|
def is_valid_py_name(name):
|
|
try:
|
|
compile(name+' = 11', 'a','exec')
|
|
except:
|
|
return False
|
|
return True
|
|
|
|
def indent(lines, ind=4):
|
|
return ind*' '+lines.replace('\n', '\n'+ind*' ').rstrip(' ')
|
|
|
|
def compose_regex(val):
|
|
reg, flags = val
|
|
#reg = REGEXP_CONVERTER._unescape_string(reg)
|
|
return u'/%s/%s' % (reg, flags)
|
|
|
|
def float_repr(f):
|
|
if int(f)==f:
|
|
return repr(int(f))
|
|
return repr(f)
|
|
|
|
def argsplit(args, sep=','):
|
|
"""used to split JS args (it is not that simple as it seems because
|
|
sep can be inside brackets).
|
|
|
|
pass args *without* brackets!
|
|
|
|
Used also to parse array and object elements, and more"""
|
|
parsed_len = 0
|
|
last = 0
|
|
splits = []
|
|
for e in bracket_split(args, brackets=['()', '[]', '{}']):
|
|
if e[0] not in ('(', '[', '{'):
|
|
for i, char in enumerate(e):
|
|
if char==sep:
|
|
splits.append(args[last:parsed_len+i])
|
|
last = parsed_len + i + 1
|
|
parsed_len += len(e)
|
|
splits.append(args[last:])
|
|
return splits
|
|
|
|
|
|
def bracket_split(source, brackets=('()','{}','[]'), strip=False):
|
|
"""DOES NOT RETURN EMPTY STRINGS (can only return empty bracket content if strip=True)"""
|
|
starts = [e[0] for e in brackets]
|
|
in_bracket = 0
|
|
n = 0
|
|
last = 0
|
|
while n<len(source):
|
|
e = source[n]
|
|
if not in_bracket and e in starts:
|
|
in_bracket = 1
|
|
start = n
|
|
b_start, b_end = brackets[starts.index(e)]
|
|
elif in_bracket:
|
|
if e==b_start:
|
|
in_bracket += 1
|
|
elif e==b_end:
|
|
in_bracket -= 1
|
|
if not in_bracket:
|
|
if source[last:start]:
|
|
yield source[last:start]
|
|
last = n+1
|
|
yield source[start+strip:n+1-strip]
|
|
n+=1
|
|
if source[last:]:
|
|
yield source[last:]
|
|
|
|
|
|
def js_comma(a, b):
|
|
return 'PyJsComma('+a+','+b+')'
|
|
|
|
def js_or(a, b):
|
|
return '('+a+' or '+b+')'
|
|
|
|
def js_bor(a, b):
|
|
return '('+a+'|'+b+')'
|
|
|
|
def js_bxor(a, b):
|
|
return '('+a+'^'+b+')'
|
|
|
|
def js_band(a, b):
|
|
return '('+a+'&'+b+')'
|
|
|
|
def js_and(a, b):
|
|
return '('+a+' and '+b+')'
|
|
def js_strict_eq(a, b):
|
|
return 'PyJsStrictEq('+a+','+b+')'
|
|
|
|
def js_strict_neq(a, b):
|
|
return 'PyJsStrictNeq('+a+','+b+')'
|
|
|
|
#Not handled by python in the same way like JS. For example 2==2==True returns false.
|
|
# In JS above would return true so we need brackets.
|
|
def js_abstract_eq(a, b):
|
|
return '('+a+'=='+b+')'
|
|
|
|
#just like ==
|
|
def js_abstract_neq(a, b):
|
|
return '('+a+'!='+b+')'
|
|
|
|
def js_lt(a, b):
|
|
return '('+a+'<'+b+')'
|
|
|
|
def js_le(a, b):
|
|
return '('+a+'<='+b+')'
|
|
|
|
def js_ge(a, b):
|
|
return '('+a+'>='+b+')'
|
|
|
|
def js_gt(a, b):
|
|
return '('+a+'>'+b+')'
|
|
|
|
def js_in(a, b):
|
|
return b+'.contains('+a+')'
|
|
|
|
def js_instanceof(a, b):
|
|
return a+'.instanceof('+b+')'
|
|
|
|
def js_lshift(a, b):
|
|
return '('+a+'<<'+b+')'
|
|
|
|
def js_rshift(a, b):
|
|
return '('+a+'>>'+b+')'
|
|
|
|
def js_shit(a, b):
|
|
return 'PyJsBshift('+a+','+b+')'
|
|
|
|
def js_add(a, b): # To simplify later process of converting unary operators + and ++
|
|
return '(%s+%s)'%(a, b)
|
|
|
|
def js_sub(a, b): # To simplify
|
|
return '(%s-%s)'%(a, b)
|
|
|
|
def js_mul(a, b):
|
|
return '('+a+'*'+b+')'
|
|
|
|
def js_div(a, b):
|
|
return '('+a+'/'+b+')'
|
|
|
|
def js_mod(a, b):
|
|
return '('+a+'%'+b+')'
|
|
|
|
def js_typeof(a):
|
|
cand = list(bracket_split(a, ('()',)))
|
|
if len(cand)==2 and cand[0]=='var.get':
|
|
return cand[0]+cand[1][:-1]+',throw=False).typeof()'
|
|
return a+'.typeof()'
|
|
|
|
def js_void(a):
|
|
# eval and return undefined
|
|
return 'PyJsComma(%s, Js(None))' % a
|
|
|
|
def js_new(a):
|
|
cands = list(bracket_split(a, ('()',)))
|
|
lim = len(cands)
|
|
if lim < 2:
|
|
return a + '.create()'
|
|
n = 0
|
|
while n < lim:
|
|
c = cands[n]
|
|
if c[0]=='(':
|
|
if cands[n-1].endswith('.get') and n+1>=lim: # last get operation.
|
|
return a + '.create()'
|
|
elif cands[n-1][0]=='(':
|
|
return ''.join(cands[:n])+'.create' + c + ''.join(cands[n+1:])
|
|
elif cands[n-1]=='.callprop':
|
|
beg = ''.join(cands[:n-1])
|
|
args = argsplit(c[1:-1],',')
|
|
prop = args[0]
|
|
new_args = ','.join(args[1:])
|
|
create = '.get(%s).create(%s)' % (prop, new_args)
|
|
return beg + create + ''.join(cands[n+1:])
|
|
n+=1
|
|
return a + '.create()'
|
|
|
|
|
|
def js_delete(a):
|
|
#replace last get with delete.
|
|
c = list(bracket_split(a, ['()']))
|
|
beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip() #strips just to make sure... I will remove it later
|
|
if beg[-4:]!='.get':
|
|
print(a)
|
|
raise SyntaxError('Invalid delete operation')
|
|
return beg[:-3]+'delete'+arglist
|
|
|
|
|
|
def js_neg(a):
|
|
return '(-'+a+')'
|
|
|
|
def js_pos(a):
|
|
return '(+'+a+')'
|
|
|
|
def js_inv(a):
|
|
return '(~'+a+')'
|
|
|
|
def js_not(a):
|
|
return a+'.neg()'
|
|
|
|
def js_postfix(a, inc, post):
|
|
bra = list(bracket_split(a, ('()',)))
|
|
meth = bra[-2]
|
|
if not meth.endswith('get'):
|
|
raise SyntaxError('Invalid ++ or -- operation.')
|
|
bra[-2] = bra[-2][:-3] + 'put'
|
|
bra[-1] = '(%s,Js(%s.to_number())%sJs(1))' % (bra[-1][1:-1], a, '+' if inc else '-')
|
|
res = ''.join(bra)
|
|
return res if not post else '(%s%sJs(1))' % (res, '-' if inc else '+')
|
|
|
|
def js_pre_inc(a):
|
|
return js_postfix(a, True, False)
|
|
|
|
def js_post_inc(a):
|
|
return js_postfix(a, True, True)
|
|
|
|
def js_pre_dec(a):
|
|
return js_postfix(a, False, False)
|
|
|
|
def js_post_dec(a):
|
|
return js_postfix(a, False, True)
|
|
|
|
|
|
|
|
CONTINUE_LABEL = 'JS_CONTINUE_LABEL_%s'
|
|
BREAK_LABEL = 'JS_BREAK_LABEL_%s'
|
|
PREPARE = '''HOLDER = var.own.get(NAME)\nvar.force_own_put(NAME, PyExceptionToJs(PyJsTempException))\n'''
|
|
RESTORE = '''if HOLDER is not None:\n var.own[NAME] = HOLDER\nelse:\n del var.own[NAME]\ndel HOLDER\n'''
|
|
TRY_CATCH = '''%stry:\nBLOCKfinally:\n%s''' % (PREPARE, indent(RESTORE))
|
|
|
|
|
|
|
|
OR = {'||': js_or}
|
|
AND = {'&&': js_and}
|
|
BOR = {'|': js_bor}
|
|
BXOR = {'^': js_bxor}
|
|
BAND = {'&': js_band}
|
|
|
|
EQS = {'===': js_strict_eq,
|
|
'!==': js_strict_neq,
|
|
'==': js_abstract_eq, # we need == and != too. Read a note above method
|
|
'!=': js_abstract_neq}
|
|
|
|
#Since JS does not have chained comparisons we need to implement all cmp methods.
|
|
COMPS = {'<': js_lt,
|
|
'<=': js_le,
|
|
'>=': js_ge,
|
|
'>': js_gt,
|
|
'instanceof': js_instanceof, #todo change to validitate
|
|
'in': js_in}
|
|
|
|
BSHIFTS = {'<<': js_lshift,
|
|
'>>': js_rshift,
|
|
'>>>': js_shit}
|
|
|
|
ADDS = {'+': js_add,
|
|
'-': js_sub}
|
|
|
|
MULTS = {'*': js_mul,
|
|
'/': js_div,
|
|
'%': js_mod}
|
|
BINARY = {}
|
|
BINARY.update(ADDS)
|
|
BINARY.update(MULTS)
|
|
BINARY.update(BSHIFTS)
|
|
BINARY.update(COMPS)
|
|
BINARY.update(EQS)
|
|
BINARY.update(BAND)
|
|
BINARY.update(BXOR)
|
|
BINARY.update(BOR)
|
|
BINARY.update(AND)
|
|
BINARY.update(OR)
|
|
#Note they dont contain ++ and -- methods because they both have 2 different methods
|
|
# correct method will be found automatically in translate function
|
|
UNARY = {'typeof': js_typeof,
|
|
'void': js_void,
|
|
'new': js_new,
|
|
'delete': js_delete,
|
|
'!': js_not,
|
|
'-': js_neg,
|
|
'+': js_pos,
|
|
'~': js_inv,
|
|
'++': None,
|
|
'--': None
|
|
} |