diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/addon.xml b/addon.xml deleted file mode 100644 index f1f1208..0000000 --- a/addon.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - Kodi CZ/SK plugins common python modules mainly for resolving stream URLs 2025 - Common python modules pack that is used by kodi cz/sk plugins - GNU GPL - all - - diff --git a/changelog.txt b/changelog.txt deleted file mode 100644 index b8e90ff..0000000 --- a/changelog.txt +++ /dev/null @@ -1,208 +0,0 @@ -[B]1.6.62:[/B] -- fix dailymotion auto quality stream -[B]1.6.61:[/B] -- fix youtube signature -[B]1.6.60:[/B] -- fix cloudflare scrape protection -[B]1.6.59:[/B] -- fix youtube signature -[B]1.6.58:[/B] -- preserve compatibility with older kodi versions -[B]1.6.57:[/B] -- youtube: age content -[B]1.6.56:[/B] -- fix youtube signature -- recognize youtu.be scheme -[B]1.6.55:[/B] -- duration is now recognized as infolabels attribute -[B]1.6.54:[/B] -- add dailymotion resolver -[B]1.6.52:[/B] -- fix streamujtv resolver -[B]1.6.48:[/B] -- fixed video download code which was failing when there were subtitles to -download -[B]1.6.45:[/B] -- support for filtering by language (needs to be passed by addon as 'lang' setting) -[B]1.6.40:[/B] -- add on_init hook to ContentProvider -- fix hqq & nahnoji parsers, added sk translations (jose1711) -[B]1.6.39:[/B] -- Fixed Exashare, Openload and Youwatch resolvers -- New resolver for Videram -[B]1.6.37:[/B] -- (for all plugins) search items can be now edited (Search Similar) -[B]1.6.36:[/B] -- fix streamujtv resolver to play videos with missing lang -[B]1.6.34:[/B] -- fix usage tracker issue on windows -[B]1.6.30:[/B] -- fix froto compatibility issue -[B]1.6.29:[/B] -- providers have now injected 'lang' - current language of xbmc/kodi user -[B]1.6.24:[/B] -- resolvers: support for displaying additional info (audio track language, etc) -- streamujtv: return all possible streams (incl. audio track combinations) #223 -[B]1.6.16:[/B] -- support for disabled subtitles by plugin setting -- #217 - incorrect item title on some skins -[B]1.6.15:[/B] -- fix streamujtv resolver to correctly handle subtitles -- another possible fix of showing captcha on windows -[B]1.6.14:[/B] -- fixed playedto resolver -[B]1.6.12:[/B] -- fixed streamujtv resolver -- better support for dynamic context menu items -[B]1.6.11:[/B] -- fixed putlocker and flashx resolvers (ibv) -[B]1.6.10:[/B] -- attempt to fix showing captcha image on windows (when there are special -chars in profile path) -[B]1.6.9:[/B] -- attempt to fix showing captcha image on windows (when there are special -chars in xbmc profile path) -- added anyfiles resolver -[B]1.6.8:[/B] -- added streamujtv resolver -[B]1.6.7:[/B] -- add ContentProvider.findstreams - wrapper for resolver.findstreams -[B]1.6.6:[/B] -- added koukejse resolver -- improved @cached decorator -- captcha image is now downloaded to file before showing up captcha dialog -[B]1.6.5:[/B] -- fixed youtube resolver (mx3L) -- download is now more robust #174 -[B]1.6.4:[/B] -- fixed youtube resolver (by mx3L) -- added concurrent function (by mx3L) -[B]1.6.2:[/B] -- updated zkouknito resolver, added kset, publicvideohost resolvers (by mx3L) -[B]1.6.1:[/B] -- fixed downloading of files with special chars -- support for @cached decorator for provider methods -[B]1.5.6:[/B] -- fixed koukni - works back with mp4 -[B]1.5.5:[/B] -- contentprovider fixes (does not fail when item title starts with $, supports -infolabels -[B]1.5.4:[/B] -- fixed koukni resolver to properly resolve rtmp -- removed providers - moved to plugins -[B]1.5.2:[/B] -- fixed youtube resolver -[B]1.5.1:[/B] -- when downloaded file does not have extension, it is autoappened (.mp4) -[B]1.5.0:[/B] -- added resolvers flashx, videomail (ivo, mx3L) -- resolvers can now return headers (if required for playing/downloading) -- ContentProviders can now raise ResolveException -- ulozto provider raises ResolveException when ulozto server is overloaded -[B]1.4.6:[/B] -- fixed youtube resolver -- subtitles now work in munkvideo resolver -[B]1.4.5:[/B] -- ulozto provider can now search for all file types -[B]1.4.4:[/B] -- added munkvideo resolver -[B]1.4.3:[/B] -- fixed eserial resolver to load subs correctly #136 -[B]1.3.12:[/B] -- fixed eserial resolver to support subtitles and work for more streams -- fixed quality filtering: we always return exactly 1 stream unless user wants to select himself -[B]1.3.11:[/B] -- sledujuserialy resolver resolves mp4 videos as well -- fixed #127 - searching does not work on frodo beta1 -- added eserialresolver -- resolvers have now priority (higher=resolver is asked fist) -[B]1.3.10:[/B] -- fixed #127 - searching does not work on frodo beta1 -- fixed ulozto to show search results again -- fixed sledujuserialy resolver (to resolve mp4 videos) -[B]1.3.9:[/B] -- got rid of old resolver API -- fixed ulozto provider to be able to retrieve non-VIP files again -- hellspy provider has now configurable server url and supports http://stiahnito.sk (slovak mirror) -[B]1.3.8:[/B] -- resolver.findstreams - prevent multiple resolving same url -- added sledujuserialy resolver -[B]1.3.7:[/B] -- fixed vkontakte resolver -[B]1.3.6:[/B] -- fixed vkontakte resolver -[B]1.3.4:[/B] -- fixed youtube resolver -- added streamcloud & gosuparg resolvers (Ivo) -[B]1.3.3:[/B] -- ulozto and hellspy fixed: login() failed when called repeatedly -[B]1.3.2:[/B] -- fixed bezvadata content provider -- added support for delayed file download (bezvadata uses it) -[B]1.3.1:[/B] -- vkontakte resolver now supports 720p videos -- added videonet resolver (thanks to Ivo Brhel), rutube resolver -[B]1.3.0:[/B] -- introduced ContentProvider API -- util.py splitted to util.py and xbmcutil.py (so we're now able to run tests in non-xbmc environment) -[B]1.2.8:[/B] -- added nahnoji and moevideo resolvers by Ivo Brhel -- fixed putlocker (by Ivo), videozer & videobb resolvers -[B]1.2.7:[/B] -- youtube resolver fixes -- all resolvers now return non-empty 'subs' key within resolve2 or findstreams -[B]1.2.6:[/B] -- resolvers can now retrieve subtitles -- added koukni.cz resovler (supports subtitles) -[B]1.2.2:[/B] -- bugfixes -- search.py now supports multiple search items within one addon -[B]1.2.0:[/B] -- added method to prelace czech diaktitic chars -- fixed divByZero when downloading, made notifications more robust -[B]1.1.7:[/B] -- new API for stream resolving - quality aware API -- youtube and vkontakte now implement quality aware API, others have dummy -impl only -[B]1.1.6:[/B] -- another small fix of youtuberesolver -- fixed videobb and videozer resovlers, thanks to Lynx187 -[B]1.1.5:[/B] -- added higher level searching API -- fixed youtuberesolver -[B]1.1.4:[/B] -- added dependency on script.usage.tracker -[B]1.1.3:[/B] -- download notification: added EST -[B]1.1.2:[/B] -- saved searches can be removed now -[B]1.1.0:[/B] -- added new API for retrieving common icons from github -- fixed downloading to report dialog on finish (when not playing) -[B]1.0.8:[/B] -- added new API for downloading, browsing local folders -- fixed string encoding issues -- fixed zideo resolver -[B]1.0.7:[/B] -- added servertip.cz resolver -- fixed youtube -- added api for search history -- added simplejson dependency -[B]1.0.6:[/B] -- added zideo.nl -- stagevu - do not fail if movie was removed -- videobb - proper resolving -[B]1.0.5:[/B] -- added novamov, youtube, trivial - special does nothing -- fixed megavideo and videozer -[B]1.0.4:[/B] -- added megavideo, videozer resolvers -- fixed zkouknito -[B]1.0.3:[/B] -- added other resolvers -- added simple debug logging -- all resovlers renamed not to collide with other 3rd party on Camelot -[B]1.0.1:[/B] - - added putlocker resolver -[B]1.0.0:[/B] -Initial version diff --git a/install.sh b/install.sh deleted file mode 100644 index dd30da9..0000000 --- a/install.sh +++ /dev/null @@ -1,7 +0,0 @@ -#/bin/sh -# do NOT use this script from Kodi addons directory, it is intented for development only -DESTDIR=~/.kodi/addons/script.module.stream.resolver - -rm -rf ${DESTDIR} -mkdir -p ${DESTDIR} -cp -a * ${DESTDIR} diff --git a/lib/__init__.py b/lib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/__pycache__/aadecode.cpython-38.opt-1.pyc b/lib/__pycache__/aadecode.cpython-38.opt-1.pyc deleted file mode 100644 index e82e104..0000000 Binary files a/lib/__pycache__/aadecode.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/__pycache__/cloudflare.cpython-38.opt-1.pyc b/lib/__pycache__/cloudflare.cpython-38.opt-1.pyc deleted file mode 100644 index dbf9106..0000000 Binary files a/lib/__pycache__/cloudflare.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/__pycache__/resolver.cpython-38.opt-1.pyc b/lib/__pycache__/resolver.cpython-38.opt-1.pyc deleted file mode 100644 index f9334f9..0000000 Binary files a/lib/__pycache__/resolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/__pycache__/search.cpython-38.opt-1.pyc b/lib/__pycache__/search.cpython-38.opt-1.pyc deleted file mode 100644 index f201d3c..0000000 Binary files a/lib/__pycache__/search.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/__pycache__/util.cpython-38.opt-1.pyc b/lib/__pycache__/util.cpython-38.opt-1.pyc deleted file mode 100644 index 9383f77..0000000 Binary files a/lib/__pycache__/util.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc b/lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc deleted file mode 100644 index bdcf05a..0000000 Binary files a/lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/aadecode.py b/lib/aadecode.py deleted file mode 100644 index 92135a2..0000000 --- a/lib/aadecode.py +++ /dev/null @@ -1,209 +0,0 @@ -#-*- coding: utf-8 -*- -# -# author : Djeman -# Updated by Shani-08 (https://github.com/Shani-08/ShaniXBMCWork2) - -import re - -class AADecoder(object): - def __init__(self, aa_encoded_data): - self.encoded_str = aa_encoded_data.replace('/*´∇`*/','') - - self.b = ["(c^_^o)", "(゚Θ゚)", "((o^_^o) - (゚Θ゚))", "(o^_^o)", - "(゚ー゚)", "((゚ー゚) + (゚Θ゚))", "((o^_^o) +(o^_^o))", "((゚ー゚) + (o^_^o))", - "((゚ー゚) + (゚ー゚))", "((゚ー゚) + (゚ー゚) + (゚Θ゚))", "(゚Д゚) .゚ω゚ノ", "(゚Д゚) .゚Θ゚ノ", - "(゚Д゚) ['c']", "(゚Д゚) .゚ー゚ノ", "(゚Д゚) .゚Д゚ノ", "(゚Д゚) [゚Θ゚]"] - - def is_aaencoded(self): - idx = self.encoded_str.find("゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); ") - if idx == -1: - return False - - if self.encoded_str.find("(゚Д゚)[゚o゚]) (゚Θ゚)) ('_');", idx) == -1: - return False - - return True - - def base_repr(self, number, base=2, padding=0): - digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' - if base > len(digits): - base = len(digits) - - num = abs(number) - res = [] - while num: - res.append(digits[num % base]) - num //= base - if padding: - res.append('0' * padding) - if number < 0: - res.append('-') - return ''.join(reversed(res or '0')) - - def decode_char(self, enc_char, radix): - end_char = "+ " - str_char = "" - while enc_char != '': - found = False - - if not found: - for i in range(len(self.b)): - enc_char=enc_char.replace(self.b[i], str(i)) - - startpos=0 - findClose=True - balance=1 - result=[] - if enc_char.startswith('('): - l=0 - - for t in enc_char[1:]: - l+=1 - if findClose and t==')': - balance-=1; - if balance==0: - result+=[enc_char[startpos:l+1]] - findClose=False - continue - elif not findClose and t=='(': - startpos=l - findClose=True - balance=1 - continue - elif t=='(': - balance+=1 - - - if result is None or len(result)==0: - return "" - else: - - for r in result: - value = self.decode_digit(r, radix) - if value == "": - return "" - else: - str_char += value - - return str_char - - enc_char = enc_char[len(end_char):] - - return str_char - - - - def decode_digit(self, enc_int, radix): - - rr = '(\(.+?\)\))\+' - rerr=enc_int.split('))+') - v = '' - - #new mode - - for c in rerr: - - if len(c)>0: - if c.strip().endswith('+'): - c=c.strip()[:-1] - - startbrackets=len(c)-len(c.replace('(','')) - endbrackets=len(c)-len(c.replace(')','')) - - if startbrackets>endbrackets: - c+=')'*startbrackets-endbrackets - - c = c.replace('!+[]','1') - c = c.replace('-~','1+') - c = c.replace('[]','0') - - v+=str(eval(c)) - - return v - - mode = 0 - value = 0 - - while enc_int != '': - found = False - for i in range(len(self.b)): - if enc_int.find(self.b[i]) == 0: - if mode == 0: - value += i - else: - value -= i - enc_int = enc_int[len(self.b[i]):] - found = True - break - - if not found: - return "" - - enc_int = re.sub('^\s+|\s+$', '', enc_int) - if enc_int.find("+") == 0: - mode = 0 - else: - mode = 1 - - enc_int = enc_int[1:] - enc_int = re.sub('^\s+|\s+$', '', enc_int) - - return self.base_repr(value, radix) - - def decode(self): - - self.encoded_str = re.sub('^\s+|\s+$', '', self.encoded_str) - - # get data - pattern = (r"\(゚Д゚\)\[゚o゚\]\+ (.+?)\(゚Д゚\)\[゚o゚\]\)") - result = re.search(pattern, self.encoded_str, re.DOTALL) - if result is None: - print("AADecoder: data not found") - return False - - data = result.group(1) - - # hex decode string - begin_char = "(゚Д゚)[゚ε゚]+" - alt_char = "(o゚ー゚o)+ " - - out = '' - - while data != '': - # Check new char - if data.find(begin_char) != 0: - print("AADecoder: data not found") - return False - - data = data[len(begin_char):] - - # Find encoded char - enc_char = "" - if data.find(begin_char) == -1: - enc_char = data - data = "" - else: - enc_char = data[:data.find(begin_char)] - data = data[len(enc_char):] - - - radix = 8 - # Detect radix 16 for utf8 char - if enc_char.find(alt_char) == 0: - enc_char = enc_char[len(alt_char):] - radix = 16 - - str_char = self.decode_char(enc_char, radix) - - if str_char == "": - print("no match : ") - print(data + "\nout = " + out + "\n") - return False - - out += chr(int(str_char, radix)) - - if out == "": - print("no match : " + data) - return False - - return out diff --git a/lib/cloudflare.py b/lib/cloudflare.py deleted file mode 100644 index 855d90d..0000000 --- a/lib/cloudflare.py +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 tknorris (Derived from Mikey1234's & Lambda's) -# -# This Program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# This Program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with XBMC; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# http://www.gnu.org/copyleft/gpl.html -# -# This code is a derivative of the YouTube plugin for XBMC and associated works -# released under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 3 - -import re -import urllib.request, urllib.parse, urllib.error -import urllib.parse -import util -import xbmc - -MAX_TRIES = 3 -COMPONENT = __name__ - - -class NoRedirection(urllib.request.HTTPErrorProcessor): - - def http_response(self, request, response): - util.info('[CF] Stopping Redirect') - return response - - https_response = http_response - -def solve_equation(equation): - try: - offset = (1 if equation[0] == '+' else 0) - ev = equation.replace('!+[]', '1').replace('!![]', - '1').replace('[]', '0').replace('(', 'str(')[offset:] - ev = re.sub(r'^str', 'float', re.sub(r'\/(.)str', r'/\1float', ev)) - # util.debug('[CF] eval: {0}'.format(ev)) - return float(eval(ev)) - except: - pass - - -def solve(url, cj, user_agent=None, wait=True): - if user_agent is None: - user_agent = util.UA - headers = {'User-Agent': user_agent, 'Referer': url} - if cj is not None: - try: - cj.load(ignore_discard=True) - except: - pass - opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) - urllib.request.install_opener(opener) - - scheme = urllib.parse.urlparse(url).scheme - domain = urllib.parse.urlparse(url).hostname - request = urllib.request.Request(url) - for key in headers: - request.add_header(key, headers[key]) - try: - response = urllib.request.urlopen(request) - html = response.read() - except urllib.error.HTTPError as e: - html = e.read() - - tries = 0 - while tries < MAX_TRIES: - solver_pattern = \ - 'var (?:s,t,o,p,b,r,e,a,k,i,n,g|t,r,a),f,\s*([^=]+)' - solver_pattern += \ - '={"([^"]+)":([^}]+)};.+challenge-form\'\);' - vc_pattern = \ - 'input type="hidden" name="jschl_vc" value="([^"]+)' - pass_pattern = 'input type="hidden" name="pass" value="([^"]+)' - s_pattern = 'input type="hidden" name="s" value="([^"]+)' - init_match = re.search(solver_pattern, html, re.DOTALL) - vc_match = re.search(vc_pattern, html) - pass_match = re.search(pass_pattern, html) - s_match = re.search(s_pattern, html) - - if not init_match or not vc_match or not pass_match or not s_match: - msg = \ - "[CF] Couldn't find attribute: init: |%s| vc: |%s| pass: |%s| No cloudflare check?" - util.info(msg % (init_match, vc_match, pass_match)) - return False - - (init_dict, init_var, init_equation) = \ - init_match.groups() - vc = vc_match.group(1) - password = pass_match.group(1) - s = s_match.group(1) - - equations = re.compile(r"challenge-form\'\);\s*(.*)a.v").findall(html)[0] - # util.info("[CF] VC is: %s" % (vc)) - varname = (init_dict, init_var) - # util.info('[CF] init: [{0}]'.format((init_equation.rstrip()))) - result = float(solve_equation(init_equation.rstrip())) - util.info('[CF] Initial value: [ {0} ] Result: [ {1} ]'.format(init_equation, - result)) - - for equation in equations.split(';'): - equation = equation.rstrip() - if len(equation) > len('.'.join(varname)): - # util.debug('[CF] varname {0} line {1}'.format('.'.join(varname), equation)) - if equation[:len('.'.join(varname))] != '.'.join(varname): - util.info('[CF] Equation does not start with varname |%s|' - % equation) - else: - equation = equation[len('.'.join(varname)):] - - expression = equation[2:] - operator = equation[0] - if operator not in ['+', '-', '*', '/']: - util.info('[CF] Unknown operator: |%s|' % equation) - continue - - result = float(str(eval(str(result) + operator + str(solve_equation( - expression))))) - #util.info('[CF] intermediate: %s = %s' % (equation, result)) - - #util.debug('[CF] POCET: {0} {1}'.format(result, len(domain))) - result = '{0:.10f}'.format(eval('float({0} + {1})'.format(result, len(domain)))) - util.info('[CF] Final Result: |%s|' % result) - - if wait: - util.info('[CF] Sleeping for 5 Seconds') - xbmc.sleep(5000) - - url = \ - '%s://%s/cdn-cgi/l/chk_jschl?s=%s&jschl_vc=%s&pass=%s&jschl_answer=%s' \ - % (scheme, domain, urllib.parse.quote(s), urllib.parse.quote(vc), urllib.parse.quote(password), urllib.parse.quote(result)) - # util.info('[CF] url: %s' % url) - # util.debug('[CF] headers: {0}'.format(headers)) - request = urllib.request.Request(url) - for key in headers: - request.add_header(key, headers[key]) - - try: - opener = urllib.request.build_opener(NoRedirection) - urllib.request.install_opener(opener) - response = urllib.request.urlopen(request) - # util.info('[CF] code: {}'.format(response.getcode())) - while response.getcode() in [301, 302, 303, 307]: - if cj is not None: - cj.extract_cookies(response, request) - - redir_url = response.info().getheader('location') - if not redir_url.startswith('http'): - base_url = '%s://%s' % (scheme, domain) - redir_url = urllib.parse.urljoin(base_url, redir_url) - - request = urllib.request.Request(redir_url) - for key in headers: - request.add_header(key, headers[key]) - if cj is not None: - cj.add_cookie_header(request) - - response = urllib.request.urlopen(request) - final = response.read() - if 'cf-browser-verification' in final: - util.info('[CF] Failure: html: %s url: %s' % (html, url)) - tries += 1 - html = final - else: - break - except urllib.error.HTTPError as e: - util.info('[CF] HTTP Error: %s on url: %s' % (e.code, - url)) - return False - except urllib.error.URLError as e: - util.info('[CF] URLError Error: %s on url: %s' % (e, - url)) - return False - - if cj is not None: - util.cache_cookies() - - return final diff --git a/lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc b/lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc deleted file mode 100644 index a2599ca..0000000 Binary files a/lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/contentprovider/__pycache__/xbmcprovider.cpython-38.opt-1.pyc b/lib/contentprovider/__pycache__/xbmcprovider.cpython-38.opt-1.pyc deleted file mode 100644 index 11d95fb..0000000 Binary files a/lib/contentprovider/__pycache__/xbmcprovider.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/contentprovider/provider.py b/lib/contentprovider/provider.py deleted file mode 100644 index 8536a39..0000000 --- a/lib/contentprovider/provider.py +++ /dev/null @@ -1,230 +0,0 @@ -# * Copyright (C) 2012 Libor Zoubek -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ -from collections import defaultdict - -import util -import resolver - - -try: - import StorageServer -except ImportError: - print('Using dummy storage server') - import storageserverdummy as StorageServer - - -class ResolveException(Exception): - pass - - -class ContentProvider(object): - """ - ContentProvider class provides an internet content. It should NOT have any xbmc-related imports - and must be testable without XBMC runtime. This is a basic/dummy implementation. - """ - - def __init__(self, name='dummy', base_url='/', username=None, password=None, filter=None, tmp_dir='.'): - """ - ContentProvider constructor - Args: - name (str): name of provider - base_url (str): base url of site being accessed - username (str): login username - password (str): login password - filter (func{item}): function to filter results returned by search or list methods - tmp_dir (str): temporary dir where provider can store/cache files - """ - self.name = name - self.username = username - self.password = password - if not base_url[-1] == '/': - base_url += '/' - self.base_url = base_url - self.filter = filter - self.tmp_dir = tmp_dir - self.cache = StorageServer.StorageServer(self.name, 24) - self.lang = 'cs' # initialize, current language could be set by XBMContentProvider - - def __str__(self): - return 'ContentProvider' + self.name - - def on_init(self): - """ - This function gets called by XbmcContentProvider after it initializes itself - and sets eveything up (especially 'lang' property of ContentProvider which gets detected - from kodi) - """ - pass - - def capabilities(self): - """ - This way class defines which capabilities it provides ['login','search','resolve','categories'] - It may also contain '!download' when provider does not support downloading - """ - return [] - - def video_item(self, url='', img='', quality='???'): - """ - Returns empty video item - contains all required fields - """ - return {'type': 'video', 'title': '', 'rating': 0, 'year': 0, 'size': '0MB', 'url': url, 'img': img, - 'length': '', 'quality': quality, 'subs': '', 'surl': '', 'lang': ''} - - def dir_item(self, title='', url='', type='dir'): - """ - Returns empty directory item - """ - return {'type': type, 'title': title, 'size': '0', 'url': url} - - def login(self): - """ - A login method returns True on successfull login, False otherwise - """ - return False - - def search(self, keyword): - """ - Search for a keyword on a site - Args: - keyword (str) - - returns: - array of video or directory items - """ - return [] - - def list(self, url): - """ - Lists content on given url - Args: - url (str): either relative or absolute provider URL - - Returns: - array of video or directory items - - """ - return [] - - def categories(self): - """ - Lists categories on provided site - - Returns: - array of video or directory items - """ - return [] - - def findstreams(self, data, regexes=None): - """ - Finds streams in given data (see resovler.findstreams for more details) - - :param data: A string (piece of HTML, for example) or an array of URLs - :param regexes: An array of regexes to be used for extracting URLs from - 'data' of type 'string' - :returns: An array of video items - """ - resolved = resolver.findstreams(data, regexes) - if resolved is None: - raise ResolveException( - 'Nelze ziskat video link [CR]zkontrolujte jestli video nebylo odstraneno') - elif isinstance(resolved, list) and not resolved: - raise ResolveException('Video je na serveru, ktery neni podporovan') - elif not resolved: - raise ResolveException( - 'Nebyl nalezen zadny video embed [CR]zkontrolujte stranku pripadne nahlaste chybu pluginu') - result = [] - for j in resolved: - i = defaultdict(lambda: '', j) - item = self.video_item() - item['title'] = i['name'] - item['url'] = i['url'] - item['quality'] = i['quality'] - item['surl'] = i['surl'] - item['subs'] = i['subs'] - item['headers'] = i['headers'] - item['lang'] = i['lang'] - item['info'] = i['info'] - result.append(item) - return result - - def resolve(self, item, captcha_cb=None, select_cb=None, wait_cb=None): - """ - Resolves given video item to a downloable/playable file/stream URL - - Args: - url (str): relative or absolute URL to be resolved - captcha_cb(func{obj}): callback function when user input is required (captcha, one-time passwords etc). - function implementation must be Provider-specific - select_cb(func{array}): callback function for cases when given url resolves to multiple streams, - provider class may call this function and require user interaction - wait_cb(func{obj}): callback function for cases when url resolves to stream which becomes available - somewhere in future (typically in several seconds). Provider may call this and require waiting. - Returns: - None - if ``url`` was not resolved. Video item with 'url' key pointing to resolved target - """ - return None - - def _url(self, url): - """ - Transforms relative to absolute url based on ``base_url`` class property - """ - if url.startswith('http'): - return url - return self.base_url + url.lstrip('./') - - def _filter(self, result, item): - """ - Applies filter, if filter passes `item` is appended to `result` - - Args: - result (array) : target array - item (obj) : item that is being applied filter on - """ - if self.filter: - if self.filter(item): - result.append(item) - else: - result.append(item) - - def info(self, msg): - util.info('[%s] %s' % (self.name, msg)) - - def error(self, msg): - util.error('[%s] %s' % (self.name, msg)) - - -class cached(object): - """ - A method decorator that can be used on any ContentProvider method - Having this decorator means that results of such method are going - to be cached for 24hours by default. You can pass number argument - to decorator, for example @cached(1) would cache for 1 hour. - """ - - def __init__(self, ttl=24): - self.ttl = ttl - - def __call__(self, f): - def wrap(*args): - provider = args[0] - cache = StorageServer.StorageServer(provider.name + str(self.ttl), self.ttl) - return cache.cacheFunction(f, *args) - - return wrap diff --git a/lib/contentprovider/xbmcprovider.py b/lib/contentprovider/xbmcprovider.py deleted file mode 100644 index f7e1401..0000000 --- a/lib/contentprovider/xbmcprovider.py +++ /dev/null @@ -1,464 +0,0 @@ -# * Copyright (C) 2012 Libor Zoubek -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ - -import sys -import os -import re -import traceback -import util -import xbmcutil -import resolver -import time -import xbmcplugin -import xbmc -import xbmcvfs -import xbmcgui -import urllib.parse -import urllib.request, urllib.parse, urllib.error -from collections import defaultdict -from provider import ResolveException - - -class XBMContentProvider(object): - ''' - ContentProvider class provides an internet content. It should NOT have any xbmc-related imports - and must be testable without XBMC runtime. This is a basic/dummy implementation. - ''' - - def __init__(self, provider, settings, addon): - ''' - XBMContentProvider constructor - Args: - name (str): name of provider - ''' - self.provider = provider - # inject current user language - try: # not fully supported on Frodo - provider.lang = xbmc.getLanguage(xbmc.ISO_639_1) - except: - provider.lang = None - pass - self.settings = settings - # lang setting is optional for plugins - if not 'lang' in self.settings: - self.settings['lang'] = '0' - - util.info('Initializing provider %s with settings %s' % (provider.name, settings)) - self.addon = addon - self.addon_id = addon.getAddonInfo('id') - if '!download' not in self.provider.capabilities(): - self.check_setting_keys(['downloads']) - self.cache = provider.cache - provider.on_init() - - def check_setting_keys(self, keys): - for key in keys: - if not key in list(self.settings.keys()): - raise Exception('Invalid settings passed - [' + key + '] setting is required') - - def params(self): - return {'cp': self.provider.name} - - def run(self, params): - if params == {} or params == self.params(): - return self.root() - if 'list' in list(params.keys()): - self.list(self.provider.list(params['list'])) - return xbmcplugin.endOfDirectory(int(sys.argv[1])) - if 'down' in list(params.keys()): - return self.download({'url': params['down'], 'title': params['title']}) - if 'play' in list(params.keys()): - return self.play({'url': params['play'], 'info': params}) - if 'search-list' in list(params.keys()): - return self.search_list() - if 'search' in list(params.keys()): - return self.do_search(params['search']) - if 'search-remove' in list(params.keys()): - return self.search_remove(params['search-remove']) - if 'search-edit' in list(params.keys()): - return self.search_edit(params['search-edit']) - if self.run_custom: - return self.run_custom(params) - - def search_list(self): - params = self.params() - params.update({'search': '#'}) - menu1 = self.params() - menu2 = self.params() - xbmcutil.add_dir(xbmcutil.__lang__(30004), params, xbmcutil.icon('search.png')) - for what in xbmcutil.search_list(self.cache): - params['search'] = what - menu1['search-remove'] = what - menu2['search-edit'] = what - xbmcutil.add_dir(what, params, menuItems={xbmcutil.__lang__( - 30016): menu2, xbmc.getLocalizedString(117): menu1}) - xbmcplugin.endOfDirectory(int(sys.argv[1])) - - def search_remove(self, what): - xbmcutil.search_remove(self.cache, what) - xbmc.executebuiltin('Container.Refresh') - - def search_edit(self, what): - kb = xbmc.Keyboard(what, xbmcutil.__lang__(30003), False) - kb.doModal() - if kb.isConfirmed(): - replacement = kb.getText() - xbmcutil.search_replace(self.cache, what, replacement) - params = self.params() - params.update({'search': replacement}) - action = xbmcutil._create_plugin_url(params) - xbmc.executebuiltin('Container.Update(%s)' % action) - - def do_search(self, what): - if what == '' or what == '#': - kb = xbmc.Keyboard('', xbmcutil.__lang__(30003), False) - kb.doModal() - if kb.isConfirmed(): - what = kb.getText() - if not what == '': - maximum = 20 - try: - maximum = int(self.settings['keep-searches']) - except: - util.error('Unable to parse convert addon setting to number') - pass - xbmcutil.search_add(self.cache, what, maximum) - self.search(what) - - def root(self): - searches = xbmcutil.get_searches(self.addon, self.provider.name) - if len(searches) > 0: - self.provider.info('Upgrading to new saved search storage...') - for s in searches: - self.provider.info('Moving item %s' % s) - xbmcutil.search_add(self.cache, s, 9999999) - xbmcutil.delete_search_history(self.addon, self.provider.name) - - if 'search' in self.provider.capabilities(): - params = self.params() - params.update({'search-list': '#'}) - xbmcutil.add_dir(xbmcutil.__lang__(30003), params, xbmcutil.icon('search.png')) - if not '!download' in self.provider.capabilities(): - xbmcutil.add_local_dir(xbmcutil.__lang__(30006), self.settings[ - 'downloads'], xbmcutil.icon('download.png')) - self.list(self.provider.categories()) - return xbmcplugin.endOfDirectory(int(sys.argv[1])) - - def download(self, item): - downloads = self.settings['downloads'] - if '' == downloads: - xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30009)) - return - stream = self.resolve(item['url']) - if stream: - if not 'headers' in list(stream.keys()): - stream['headers'] = {} - xbmcutil.reportUsage(self.addon_id, self.addon_id + '/download') - # clean up \ and / - name = item['title'].replace('/', '_').replace('\\', '_') - if not stream['subs'] == '': - xbmcutil.save_to_file(stream['subs'], os.path.join( - downloads, name + '.srt'), stream['headers']) - dot = name.find('.') - if dot <= 0: - # name does not contain extension, append some - name += '.mp4' - xbmcutil.download(self.addon, name, self.provider._url( - stream['url']), os.path.join(downloads, name), headers=stream['headers']) - - def play(self, item): - stream = self.resolve(item['url']) - if stream: - xbmcutil.reportUsage(self.addon_id, self.addon_id + '/play') - if 'headers' in list(stream.keys()): - headerStr = '|' + urllib.parse.urlencode(stream['headers']) - if len(headerStr) > 1: - stream['url'] += headerStr.encode('utf-8') - print('Sending %s to player' % stream['url']) - li = xbmcgui.ListItem(path=stream['url']) - li.setArt({'icon': 'DefaulVideo.png'}) - il = self._extract_infolabels(item['info']) - if len(il) > 0: # only set when something was extracted - li.setInfo('video', il) - try: - local_subs = xbmcutil.set_subtitles(li, stream['subs'], stream.get('headers')) - except: - xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li) - xbmcutil.load_subtitles(stream['subs'], stream.get('headers')) - else: - xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li) - - def _handle_exc(self, e): - msg = e.message - if msg.find('$') == 0: - try: - msg = self.addon.getLocalizedString(int(msg[1:])) - except: - try: - msg = xbmcutil.__lang__(int(msg[1:])) - except: - pass - xbmcgui.Dialog().ok(self.provider.name, msg) - - def resolve(self, url): - item = self.provider.video_item() - item.update({'url': url}) - try: - return self.provider.resolve(item) - except ResolveException as e: - self._handle_exc(e) - - def search(self, keyword): - self.list(self.provider.search(keyword)) - return xbmcplugin.endOfDirectory(int(sys.argv[1])) - - def list(self, items): - params = self.params() - for item in items: - if item['type'] == 'dir': - self.render_dir(item) - elif item['type'] == 'next': - params.update({'list': item['url']}) - xbmcutil.add_dir(xbmcutil.__lang__(30007), params, xbmcutil.icon('next.png')) - elif item['type'] == 'prev': - params.update({'list': item['url']}) - xbmcutil.add_dir(xbmcutil.__lang__(30008), params, xbmcutil.icon('prev.png')) - elif item['type'] == 'new': - params.update({'list': item['url']}) - xbmcutil.add_dir(xbmcutil.__lang__(30012), params, xbmcutil.icon('new.png')) - elif item['type'] == 'top': - params.update({'list': item['url']}) - xbmcutil.add_dir(xbmcutil.__lang__(30013), params, xbmcutil.icon('top.png')) - elif item['type'] == 'video': - self.render_video(item) - else: - self.render_default(item) - - def render_default(self, item): - raise Exception("Unable to render item " + str(item)) - - def render_dir(self, item): - params = self.params() - params.update({'list': item['url']}) - title = item['title'] - img = None - if 'img' in list(item.keys()): - img = item['img'] - if title.find('$') == 0: - try: - title = self.addon.getLocalizedString(int(title[1:])) - except: - pass - menuItems = {} - if 'menu' in list(item.keys()): - for ctxtitle, value in item['menu'].items(): - if ctxtitle.find('$') == 0: - try: - ctxtitle = self.addon.getLocalizedString(int(ctxtitle[1:])) - except: - pass - menuItems[ctxtitle] = value - xbmcutil.add_dir(title, params, img, infoLabels=self._extract_infolabels( - item), menuItems=menuItems) - - def _extract_infolabels(self, item): - infoLabels = {} - for label in ['title', 'plot', 'year', 'genre', 'rating', 'director', - 'votes', 'cast', 'trailer', 'tvshowtitle', 'season', - 'episode', 'duration']: - if label in list(item.keys()): - infoLabels[label] = util.decode_html(item[label]) - return infoLabels - - def render_video(self, item): - params = self.params() - params.update({'play': item['url']}) - downparams = self.params() - downparams.update({'title': item['title'], 'down': item['url']}) - def_item = self.provider.video_item() - if item['size'] == def_item['size']: - item['size'] = '' - else: - item['size'] = ' (%s)' % item['size'] - title = '%s%s' % (item['title'], item['size']) - menuItems = {} - if "!download" not in self.provider.capabilities(): - menuItems[xbmc.getLocalizedString(33003)] = downparams - if 'menu' in list(item.keys()): - for ctxtitle, value in item['menu'].items(): - if ctxtitle.find('$') == 0: - try: - ctxtitle = self.addon.getLocalizedString(int(ctxtitle[1:])) - except: - pass - menuItems[ctxtitle] = value - xbmcutil.add_video(title, - params, - item['img'], - infoLabels=self._extract_infolabels(item), - menuItems=menuItems - ) - - def categories(self): - self.list(self.provider.categories(keyword)) - return xbmcplugin.endOfDirectory(int(sys.argv[1])) - - -class XBMCMultiResolverContentProvider(XBMContentProvider): - - def __init__(self, provider, settings, addon): - XBMContentProvider.__init__(self, provider, settings, addon) - self.check_setting_keys(['quality']) - - def resolve(self, url): - item = self.provider.video_item() - item.update({'url': url}) - - def select_cb(resolved): - - quality = self.settings['quality'] or '0' - filtered = resolver.filter_by_quality(resolved, quality) - lang = self.settings['lang'] or '0' - filtered = resolver.filter_by_language(filtered, lang) - # if user requested something but 'ask me' or filtered result is exactly 1 - if len(filtered) == 1 or (int(quality) > 0 and int(lang) == 0): - return filtered[0] - # if user requested particular language and we have it - if len(filtered) > 0 and int(lang) > 0: - return filtered[0] - dialog = xbmcgui.Dialog() - opts = [] - for r in resolved: - d = defaultdict(lambda: '', r) - opts.append('%s [%s] %s' % (d['title'], d['quality'], d['lang'])) - ret = dialog.select(xbmcutil.__lang__(30005), opts) - if ret >= 0: - return resolved[ret] - try: - return self.provider.resolve(item, select_cb=select_cb) - except ResolveException as e: - self._handle_exc(e) - - -class XBMCLoginRequiredContentProvider(XBMContentProvider): - - def root(self): - if not self.provider.login(): - xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011)) - else: - return XBMContentProvider.root(self) - - -class XBMCLoginOptionalContentProvider(XBMContentProvider): - - def __init__(self, provider, settings, addon): - XBMContentProvider.__init__(self, provider, settings, addon) - self.check_setting_keys(['vip']) - - def ask_for_captcha(self, params): - img = os.path.join(str(xbmcvfs.translatePath( - self.addon.getAddonInfo('profile'))), 'captcha.png') - util.save_to_file(params['img'], img) - cd = CaptchaDialog('captcha-dialog.xml', - xbmcutil.__addon__.getAddonInfo('path'), 'default', '0') - cd.image = img - xbmc.sleep(3000) - cd.doModal() - del cd - kb = xbmc.Keyboard('', self.addon.getLocalizedString(200), False) - kb.doModal() - if kb.isConfirmed(): - print('got code ' + kb.getText()) - return kb.getText() - - def ask_for_account_type(self): - if len(self.provider.username) == 0: - util.info('Username is not set, NOT using VIP account') - return False - if self.settings['vip'] == '0': - util.info('Asking user whether to use VIP account') - ret = xbmcgui.Dialog().yesno(self.provider.name, xbmcutil.__lang__(30010)) - return ret == 1 - return self.settings['vip'] == '1' - - def resolve(self, url): - item = self.provider.video_item() - item.update({'url': url}) - if not self.ask_for_account_type(): - # set user/pass to null - user does not want to use VIP at this time - self.provider.username = None - self.provider.password = None - else: - if not self.provider.login(): - xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011)) - return - try: - return self.provider.resolve(item, captcha_cb=self.ask_for_captcha) - except ResolveException as e: - self._handle_exc(e) - - -class XBMCLoginOptionalDelayedContentProvider(XBMCLoginOptionalContentProvider): - - def wait_cb(self, wait): - left = wait - msg = xbmcutil.__lang__(30014).encode('utf-8') - while left > 0: - xbmc.executebuiltin("XBMC.Notification(%s,%s,1000,%s)" % - (self.provider.name, msg % str(left), '')) - left -= 1 - time.sleep(1) - - def resolve(self, url): - item = self.video_item() - item.update({'url': url}) - if not self.ask_for_account_type(): - # set user/pass to null - user does not want to use VIP at this time - self.provider.username = None - self.provider.password = None - else: - if not self.provider.login(): - xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011)) - return - try: - return self.provider.resolve(item, captcha_cb=self.ask_for_captcha, wait_cb=self.wait_cb) - except ResolveException as e: - self._handle_exc(e) - - -class CaptchaDialog (xbmcgui.WindowXMLDialog): - - def __init__(self, *args, **kwargs): - super(xbmcgui.WindowXMLDialog, self).__init__(args, kwargs) - self.image = None - - def onFocus(self, controlId): - self.controlId = controlId - - def onInit(self): - self.getControl(101).setImage(self.image) - - def onAction(self, action): - if action.getId() in [9, 10]: - self.close() - - def onClick(self, controlId): - if controlId == 102: - self.close() diff --git a/lib/crypto/__init__.py b/lib/crypto/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc b/lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc deleted file mode 100644 index ea61905..0000000 Binary files a/lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc b/lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc deleted file mode 100644 index dcc6efa..0000000 Binary files a/lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/crypto/md5crypt.py b/lib/crypto/md5crypt.py deleted file mode 100644 index 8023187..0000000 --- a/lib/crypto/md5crypt.py +++ /dev/null @@ -1,75 +0,0 @@ -import hashlib - -MAGIC = '$1$' -ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - -def to64(v, n): - ret = '' - while n > 0: - ret += ITOA64[v & 0x3f] - v >>= 6 - n -= 1 - return ret - -def apache_md5_crypt(pw, salt): - return unix_md5_crypt(pw, salt, '$apr1$') - -def unix_md5_crypt(pw, salt, magic=None): - if magic is None: - magic = MAGIC - - if salt.startswith(magic): - salt = salt[len(magic):] - - salt = salt.split('$', 1)[0][:8] - - pw_bytes = pw.encode('utf-8') - salt_bytes = salt.encode('utf-8') - magic_bytes = magic.encode('utf-8') - - ctx = pw_bytes + magic_bytes + salt_bytes - final = hashlib.md5(pw_bytes + salt_bytes + pw_bytes).digest() - - for pl in range(len(pw_bytes), 0, -16): - ctx += final[:min(16, pl)] - - i = len(pw_bytes) - while i: - if i & 1: - ctx += b'\x00' - else: - ctx += pw_bytes[:1] - i >>= 1 - - final = hashlib.md5(ctx).digest() - - for i in range(1000): - ctx1 = b'' - if i & 1: - ctx1 += pw_bytes - else: - ctx1 += final - if i % 3: - ctx1 += salt_bytes - if i % 7: - ctx1 += pw_bytes - if i & 1: - ctx1 += final - else: - ctx1 += pw_bytes - final = hashlib.md5(ctx1).digest() - - passwd = '' - passwd += to64((final[0] << 16) | (final[6] << 8) | final[12], 4) - passwd += to64((final[1] << 16) | (final[7] << 8) | final[13], 4) - passwd += to64((final[2] << 16) | (final[8] << 8) | final[14], 4) - passwd += to64((final[3] << 16) | (final[9] << 8) | final[15], 4) - passwd += to64((final[4] << 16) | (final[10] << 8) | final[5], 4) - passwd += to64(final[11], 2) - - return magic + salt + '$' + passwd - -md5crypt = unix_md5_crypt - -if __name__ == "__main__": - print(unix_md5_crypt("cat", "hat")) diff --git a/lib/resolver.py b/lib/resolver.py deleted file mode 100644 index 063f107..0000000 --- a/lib/resolver.py +++ /dev/null @@ -1,289 +0,0 @@ -# -*- coding: UTF-8 -*- -# * Copyright (C) 2011 Libor Zoubek -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ - -import sys -import os -import re -import traceback - -import util - -sys.path.append(os.path.join(os.path.dirname(__file__), 'server')) - -RESOLVERS = [] -util.debug('%s searching for modules' % __name__) -for module in os.listdir(os.path.join(os.path.dirname(__file__), 'server')): - if module == '__init__.py' or module[-3:] != '.py': - continue - module = module[:-3] - exec('import %s' % module) - resolver = eval(module) - util.debug('found %s %s' % (resolver, dir(resolver))) - - if not hasattr(resolver, '__priority__'): - resolver.__priority__ = 0 - RESOLVERS.append(resolver) - del module -RESOLVERS = sorted(RESOLVERS, key=lambda m: -m.__priority__) -util.debug('done') - - -def item(): - return {'name': '', 'url': '', 'quality': '???', 'surl': '', 'subs': '', 'headers': {}} - - -def resolve(url): - """ - resolves given url by asking all resolvers - - returns None if no resolver advised to be able to resolve this url - returns False if resolver did his job, but did not return any value (thus failed) - returns Array of resolved objects in positive usecase - """ - url = util.decode_html(url) - util.info('Resolving ' + url) - resolver = _get_resolver(url) - value = None - if resolver is None: - return None - util.info('Using resolver \'%s\'' % str(resolver.__name__)); - try: - value = resolver.resolve(url) - except: - traceback.print_exc() - if value is None: - return False - default = item() - - def fix_stream(i, url, resolver, default): - """ fix missing but required values """ - if 'name' not in list(i.keys()): - i['name'] = resolver.__name__ - if 'surl' not in list(i.keys()): - i['surl'] = url - for key in list(default.keys()): - if key not in list(i.keys()): - i[key] = default[key] - - [fix_stream(i, url, resolver, default) for i in value] - return sorted(value, key=lambda i: i['quality']) - - -def _get_resolver(url): - util.debug('Get resolver for ' + url) - for r in RESOLVERS: - util.debug('querying %s' % r) - if r.supports(url): - return r - - -def can_resolve(url): - """ Returns true if we are able to resolve stream by given URL """ - return _get_resolver(url) is not None - - -def filter_resolvable(url): - if url.find('facebook') > 0 or url.find('yield') > 0: - return - return url.strip('\'\"') - - -def findstreams(data, regexes=None): - """ - Finds streams in given data. Respects caller add-on settings about - quality and asks user if necessary. - - :param data: A string (piece of text / HTML code), an array of URLs or an - array of dictionaries, where 'url' key stores actual URL and - all other keys not present in item() are being copied to the - resolved stream dictionary - :param regexes: An array of strings - regular expressions, each MUST define - named group called 'url', which retrieves resolvable URL - (that one is passed to resolve operation); only used - with 'data' of type 'string' - :returns: An array of resolved objects, None if at least 1 resolver failed - to resolve and nothing else was found, an empty array if no - resolvers for URLs has been found or False if none of regexes - found anything - """ - - def get_url(obj): - return obj['url'] if isinstance(obj, dict) else obj - - urls = [] - resolvables = [] - resolved = [] - not_found = False - if isinstance(data, str) and regexes: - for regex in regexes: - for match in re.finditer(regex, data, re.IGNORECASE | re.DOTALL): - urls.append(match.group('url')) - elif isinstance(data, list): - urls = data - else: - raise TypeError - for url in urls: - if isinstance(url, dict): - url['url'] = filter_resolvable(url['url']) - else: - url = filter_resolvable(url) - if url and url not in resolvables: - util.info('Found resolvable ' + get_url(url)) - resolvables.append(url) - if len(resolvables) == 0: - util.info('No resolvables found!') - return False - for url in resolvables: - streams = resolve(get_url(url)) - if streams is None: - util.info('No resolver found for ' + get_url(url)) - not_found = True - elif not streams: - util.info('There was an error resolving ' + get_url(url)) - elif len(streams) > 0: - for stream in streams: - if isinstance(url, dict): - for key in list(url.keys()): - if key not in stream: - stream[key] = url[key] - elif key not in item(): - if isinstance(stream[key], str) and \ - isinstance(url[key], str): - stream[key] = url[key] + ' +' + stream[key] - elif isinstance(stream[key], list) and \ - isinstance(url[key], list): - stream[key] = url[key] + stream[key] - elif isinstance(stream[key], dict) and \ - isinstance(url[key], dict): - stream[key].update(url[key]) - resolved.append(stream) - if len(resolved) == 0: - if not_found: - return [] - return None - resolved = sorted(resolved, key=lambda i: i['quality']) - resolved = sorted(resolved, key=lambda i: len(i['quality'])) - resolved.reverse() - return resolved - - -q_map = {'3': '720p', '4': '480p', '5': '360p'} - - -def filter_by_quality(resolved, q): - util.info('filtering by quality setting ' + q) - if q == '0': - return resolved - sources = {} - ret = [] - # first group streams by source url - for item in resolved: - if item['surl'] in list(sources.keys()): - sources[item['surl']].append(item) - else: - sources[item['surl']] = [item] - if q == '1': - # always return best quality from each source - for key in list(sources.keys()): - ret.append(sources[key][0]) - elif q == '2': - # always return worse quality from each source - for key in list(sources.keys()): - ret.append(sources[key][-1]) - else: - # we try to select sources of desired qualities - quality = q_map[q] - # 3,4,5 are 720,480,360 - for key in list(sources.keys()): - added = False - for item in sources[key]: - if quality == item['quality']: - ret.append(item) - added = True - if not added: - util.debug('Desired quality %s not found, adding best found' % quality) - ret.append(sources[key][-1]) - # sort results again, so best quality streams appear first - ret = sorted(ret, key=lambda i: i['quality']) - if not q == '2': - ret.reverse() - return ret - -def filter_by_language(resolved, lang): - util.info('filtering by language setting ' + lang) - if lang == '0': - return resolved - ret = [] - # first group streams by source url - for item in resolved: - if 'lang' in item and item['lang'] != '': - util.info(item) - if lang == '1' and re.match('en', item['lang'], re.IGNORECASE): - ret.append(item) - if lang == '2' and re.match('cs|cz|čeština', item['lang'], re.IGNORECASE): - ret.append(item) - return ret - -def findstreams_multi(data, regexes): - """ - Finds streams in given data according to given regexes - respects caller addon's setting about desired quality, asks user if needed - assumes, that all resolvables need to be returned, but in particular quality - @param data piece of text (HTML code) to search in - @param regexes - array of strings - regular expressions, each MUST define named group called 'url' - which retrieves resolvable URL (that one is passsed to resolve operation) - @return array of dictionaries with keys: name,url,quality,surl - @return None if at least 1 resoler failed to resolve and nothing else has been found - @return [] if no resolvable URLs or no resolvers for URL has been found - """ - resolved = [] - # keep list of found urls to aviod having duplicates - urls = [] - error = False - for regex in regexes: - for match in re.finditer(regex, data, re.IGNORECASE | re.DOTALL): - print('Found resolvable %s ' % match.group('url')) - streams = resolve(match.group('url')) - if isinstance(streams, list) and streams: - util.debug('There was an error resolving ' + match.group('url')) - error = True - if streams is not None: - if len(streams) > 0: - for stream in streams: - resolved.append(stream) - if error and len(resolved) == 0: - return None - if len(resolved) == 0: - return [] - resolved = sorted(resolved, key=lambda i: i['quality']) - resolved = sorted(resolved, key=lambda i: len(i['quality'])) - resolved2 = resolved - resolved2.reverse() - qualities = {} - for item in resolved2: - if item['quality'] in list(qualities.keys()): - qualities[item['quality']].append(item) - else: - qualities[item['quality']] = [item] - # now .. we must sort items to be in same order as they were found on page - for q in list(qualities.keys()): - qualities[q] = sorted(qualities[q], key=lambda i: resolved.index(i)) - return qualities diff --git a/lib/search.py b/lib/search.py deleted file mode 100644 index 69b727f..0000000 --- a/lib/search.py +++ /dev/null @@ -1,76 +0,0 @@ - -# -*- coding: UTF-8 -*- -#/* -# * Copyright (C) 2011 Libor Zoubek -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ -import os,re,sys -import xbmcplugin,xbmcaddon,xbmc -import xbmcutil,util - -def _list(addon,history,key,value): - params = {} - menuItems = {} - if key: - params[key] = value - menuItems[key] = value - params['search'] = '#' - xbmcutil.add_dir(xbmcutil.__lang__(30004),params,xbmcutil.icon('search.png')) - for what in xbmcutil.get_searches(addon,history): - params['search'] = what - menuItems['search-remove'] = what - xbmcutil.add_dir(what,params,menuItems={xbmc.getLocalizedString(117):menuItems}) - xbmcplugin.endOfDirectory(int(sys.argv[1])) - -def _remove(addon,history,search): - xbmcutil.remove_search(addon,history,search) - xbmc.executebuiltin('Container.Refresh') - -def _search(addon,history,what,update_history,callback): - if what == '' or what == '#': - kb = xbmc.Keyboard('',xbmcutil.__lang__(30003),False) - kb.doModal() - if kb.isConfirmed(): - what = kb.getText() - if not what == '': - maximum = 20 - try: - maximum = int(addon.getSetting('keep-searches')) - except: - util.error('Unable to parse convert addon setting to number') - pass - if update_history: - xbmcutil.add_search(addon,history,what,maximum) - callback(what) - -def item(items={},label=xbmcutil.__lang__(30003)): - items['search-list'] = '#' - xbmcutil.add_dir(label,items,xbmcutil.icon('search.png')) - -def main(addon,history,p,callback,key=None,value=None): - if (key==None) or (key in p and p[key] == value): - if 'search-list' in list(p.keys()): - _list(addon,history,key,value) - if 'search' in list(p.keys()): - update_history=True - if 'search-no-history' in list(p.keys()): - update_history=False - _search(addon,history,p['search'],update_history,callback) - if 'search-remove' in list(p.keys()): - _remove(addon,history,p['search-remove']) diff --git a/lib/server/__init__.py b/lib/server/__init__.py deleted file mode 100644 index 56e0887..0000000 --- a/lib/server/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -#/* -# * Copyright (C) 2011 Libor Zoubek -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ - - -##########################################################3 -# all resolvers modules in this directory must have following methods: - -# __name__ - name of the resolver module - can override module filename -# def supports(url) - returns true iff resolver is able to resolve url to stream otherwise false -# def resolve(url) - returns array of all hashmaps that were resolved -# - if resolving fails, nothing is returned -# - a hash MUST contain key 'url' - it's value is stream URL -# - optional keys are 'subs' (link to subtitle), 'quality' (quality string like '240p' or just 'HD' diff --git a/lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 5d00482..0000000 Binary files a/lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc deleted file mode 100644 index a522930..0000000 Binary files a/lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 503d788..0000000 Binary files a/lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/eserialresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/eserialresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 0335f11..0000000 Binary files a/lib/server/__pycache__/eserialresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 3fe3e38..0000000 Binary files a/lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/flashxresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/flashxresolver.cpython-38.opt-1.pyc deleted file mode 100644 index aef6817..0000000 Binary files a/lib/server/__pycache__/flashxresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/gosuparkresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/gosuparkresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 9394260..0000000 Binary files a/lib/server/__pycache__/gosuparkresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 2b06755..0000000 Binary files a/lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/koukejseresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/koukejseresolver.cpython-38.opt-1.pyc deleted file mode 100644 index da93aaf..0000000 Binary files a/lib/server/__pycache__/koukejseresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/koukniresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/koukniresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 73fdbb3..0000000 Binary files a/lib/server/__pycache__/koukniresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 5e7dd9b..0000000 Binary files a/lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/letwatch.cpython-38.opt-1.pyc b/lib/server/__pycache__/letwatch.cpython-38.opt-1.pyc deleted file mode 100644 index 600508d..0000000 Binary files a/lib/server/__pycache__/letwatch.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index a7cf04c..0000000 Binary files a/lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index a9409da..0000000 Binary files a/lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/mojevideosk.cpython-38.opt-1.pyc b/lib/server/__pycache__/mojevideosk.cpython-38.opt-1.pyc deleted file mode 100644 index e42f064..0000000 Binary files a/lib/server/__pycache__/mojevideosk.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc deleted file mode 100644 index aed5b98..0000000 Binary files a/lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 24adaeb..0000000 Binary files a/lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc deleted file mode 100644 index f538c42..0000000 Binary files a/lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/nahnojiresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/nahnojiresolver.cpython-38.opt-1.pyc deleted file mode 100644 index d7f5911..0000000 Binary files a/lib/server/__pycache__/nahnojiresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc b/lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc deleted file mode 100644 index 944d12d..0000000 Binary files a/lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/openload.cpython-38.opt-1.pyc b/lib/server/__pycache__/openload.cpython-38.opt-1.pyc deleted file mode 100644 index fd96ad2..0000000 Binary files a/lib/server/__pycache__/openload.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/playedtoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/playedtoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 04ba62f..0000000 Binary files a/lib/server/__pycache__/playedtoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/playmdresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/playmdresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 481c619..0000000 Binary files a/lib/server/__pycache__/playmdresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/publicvideohostresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/publicvideohostresolver.cpython-38.opt-1.pyc deleted file mode 100644 index c216658..0000000 Binary files a/lib/server/__pycache__/publicvideohostresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/putlockerresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/putlockerresolver.cpython-38.opt-1.pyc deleted file mode 100644 index df68aca..0000000 Binary files a/lib/server/__pycache__/putlockerresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc deleted file mode 100644 index d117486..0000000 Binary files a/lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/servertipczresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/servertipczresolver.cpython-38.opt-1.pyc deleted file mode 100644 index d3f48c5..0000000 Binary files a/lib/server/__pycache__/servertipczresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/sledujuserialyresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/sledujuserialyresolver.cpython-38.opt-1.pyc deleted file mode 100644 index a0fb6cc..0000000 Binary files a/lib/server/__pycache__/sledujuserialyresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 4aa8c81..0000000 Binary files a/lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 592973e..0000000 Binary files a/lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 5f8114b..0000000 Binary files a/lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 9b170a8..0000000 Binary files a/lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 4e20ed5..0000000 Binary files a/lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/videobbresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videobbresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 16ce114..0000000 Binary files a/lib/server/__pycache__/videobbresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 35820c9..0000000 Binary files a/lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/videonetresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videonetresolver.cpython-38.opt-1.pyc deleted file mode 100644 index da71741..0000000 Binary files a/lib/server/__pycache__/videonetresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/videoweedresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videoweedresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 2e4d672..0000000 Binary files a/lib/server/__pycache__/videoweedresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 547c3f2..0000000 Binary files a/lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/videram.cpython-38.opt-1.pyc b/lib/server/__pycache__/videram.cpython-38.opt-1.pyc deleted file mode 100644 index bfeed34..0000000 Binary files a/lib/server/__pycache__/videram.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/vimeoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/vimeoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 13ba13f..0000000 Binary files a/lib/server/__pycache__/vimeoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/vkontakteresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/vkontakteresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 5cf3ed8..0000000 Binary files a/lib/server/__pycache__/vkontakteresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 1970332..0000000 Binary files a/lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc deleted file mode 100644 index 7fa170c..0000000 Binary files a/lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc b/lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc deleted file mode 100644 index 63e4e6e..0000000 Binary files a/lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 08a0b43..0000000 Binary files a/lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/__pycache__/zkouknitoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/zkouknitoresolver.cpython-38.opt-1.pyc deleted file mode 100644 index 53df311..0000000 Binary files a/lib/server/__pycache__/zkouknitoresolver.cpython-38.opt-1.pyc and /dev/null differ diff --git a/lib/server/anyfilesresolver.py b/lib/server/anyfilesresolver.py deleted file mode 100644 index c6fec63..0000000 --- a/lib/server/anyfilesresolver.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: UTF-8 -*- -# * GNU General Public License for more details. -# * -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# * -# * thanks to http://code.google.com/p/sd-xbmc/ -# */ - -import re -import urllib.request, urllib.parse, urllib.error -import urllib.request, urllib.error, urllib.parse -import random -import decimal - -import util - -__name__='anyfiles' - -BASE_URL = 'http://video.anyfiles.pl' - -def supports(url): - return not _regex(url) == None - -def _gen_random_decimal(i, d): - return decimal.Decimal('%d.%d' % (random.randint(0, i), random.randint(0, d))) - - -def _decode(param): - #-- define variables - loc_3 = [0,0,0,0] - loc_4 = [0,0,0] - loc_2 = '' - #-- define hash parameters for decoding - dec = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' - hash1 = ["L", "y", "c", "X", "2", "M", "a", "l", "p", "5", "Q", "e", "R", "t", "Z", "Y", "9", "m", "d", "0", "s", "V", "b", "3", "7", "="] - hash2 = ["i", "B", "v", "U", "H", "4", "D", "n", "k", "8", "x", "T", "u", "G", "w", "f", "N", "J", "6", "W", "1", "g", "z", "o", "I", "r"] - hash1 = ["c", "u", "4", "V", "z", "5", "k", "m", "y", "p", "L", "J", "I", "d", "0", "M", "9", "e", "3", "8", "v", "l", "i", "7", "n", "="]; - hash2 = ["t", "Y", "T", "x", "B", "g", "G", "b", "2", "X", "1", "R", "a", "N", "w", "Q", "f", "W", "U", "D", "Z", "s", "6", "H", "o", "r"] - - #-- decode - for i in range(0, len(hash1)): - re1 = hash1[i] - re2 = hash2[i] - - param = param.replace(re1, '___') - param = param.replace(re2, re1) - param = param.replace('___', re2) - - i = 0 - while i < len(param): - j = 0 - while j < 4 and i+j < len(param): - loc_3[j] = dec.find(param[i+j]) - j = j + 1 - - loc_4[0] = (loc_3[0] << 2) + ((loc_3[1] & 48) >> 4); - loc_4[1] = ((loc_3[1] & 15) << 4) + ((loc_3[2] & 60) >> 2); - loc_4[2] = ((loc_3[2] & 3) << 6) + loc_3[3]; - - j = 0 - while j < 3: - if loc_3[j + 1] == 64: - break - try: - loc_2 += chr(loc_4[j]) - except: - pass - j = j + 1 - - i = i + 4; - - return loc_2 - -def resolve(url): - m = _regex(url) - if m: - resp = urllib.request.urlopen(url) - sessc = resp.headers.get('Set-Cookie').split(';')[0] - resp.close() - furl = "%s/w.jsp?id=%s&width=620&height=349&pos=&skin=0" % (BASE_URL,m.group('id')) - headers = {'Cookie':sessc, 'Referer':url} - data = util.request(furl,headers) - m1 = re.search('document.cookie = "([^"]+?)"',data) - m2 = re.search('src="(\/pcsevlet\?code=[^"]+)', data) - if m1 and m2: - headers['Cookie'] = headers['Cookie'] + '; ' + m1.group(1) - headers['Referer'] = BASE_URL + '/flowplayer/flowplayer.commercial-3.2.16.swf' - data = util.request(BASE_URL + m2.group(1),headers) - m_vurl = re.search("'url':.*?'(http[^']+?mp4)'", data, re.DOTALL) - m_surl = re.search("'captionUrl':.*?'(http[^']+)'",data, re.DOTALL) - if m_vurl: - resolved = {'url':m_vurl.group(1).strip(),'quality':'???'} - if m_surl: - resolved['subs'] = m_surl.group(1).strip() - return [resolved] - else: - return [] -def _regex(url): - return re.search('video\.anyfiles\.pl/w\.jsp\?id=(?P\d+)',url,re.IGNORECASE | re.DOTALL) diff --git a/lib/server/dailymotionresolver.py b/lib/server/dailymotionresolver.py deleted file mode 100644 index 538db52..0000000 --- a/lib/server/dailymotionresolver.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: UTF-8 -*- -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * - -import re -from xml.etree import ElementTree -import util -from copy import deepcopy -import json - -__name__ = 'dailymotion' - - -def supports(url): - return re.search(r'dailymotion.com/embed', url) is not None - - -def resolve(url): - print('The url is ::', url) - id = re.search(r'dailymotion.com/embed/video/(.+)', url).group(1) - print('The id is ::', id) - headers = {'User-Agent': 'Android'} - cookie = {'Cookie': "lang=en; ff=off"} - r = util.request("http://www.dailymotion.com/player/metadata/video/" + id, - headers) - content = json.loads(r) - cc = content['qualities'] - cc = list(cc.items()) - - cc = sorted(cc, reverse=True) - m_url = '' - other_playable_url = [] - - items = [] - result = [] - - for source, json_source in cc: - source = source.split("@")[0] - for item in json_source: - - m_url = item.get('url', None) - # xbmc.log("DAILYMOTION - m_url = %s" % m_url, xbmc.LOGNOTICE) - if m_url: - if source == "auto": - continue - - elif '.mnft' in m_url: - continue - - if 'video' in item.get('type', None): - item = {} - item['url'] = m_url - item['quality'] = source - item['title'] = 'video' - items.append(item) - - other_playable_url.append(m_url) - - if items: - for item in items: - newitem = deepcopy(item) - item['lang'] = '???' - item['headers'] = headers - result.append(newitem) - if not result and cc[0][0]=='auto': - json_source=cc[0][1] - m_url=json_source[0].get('url', None) - r = util.request(m_url) - streams = re.compile(r'RESOLUTION=\d+x(\d+).*\n([^\s]+)').findall(r) - for quality, url in streams: - item = {} - item['url'] = url - item['quality'] = quality + 'p' - item['title'] = 'video' - result.append(item) - return result diff --git a/lib/server/divxstageresolver.py b/lib/server/divxstageresolver.py deleted file mode 100644 index 44d520d..0000000 --- a/lib/server/divxstageresolver.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: UTF-8 -*- -#/* -# * Copyright (C) 2011 Libor Zoubek -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ -import re,util,resolver -__name__ = 'divxstage' -def supports(url): - return not _regex(url) == None - -# returns the steam url -def resolve(url): - if not _regex(url) == None: - data = util.substr(util.request(url),'') - link = re.search('src=\"([^\"]+)',data,re.IGNORECASE | re.DOTALL) - if link: - return [{'url':link.group(1)}] - -def _regex(url): - return re.search('embed.divxstage.eu/(.+?)',url,re.IGNORECASE | re.DOTALL) diff --git a/lib/server/eserialresolver.py b/lib/server/eserialresolver.py deleted file mode 100644 index 0b8dc80..0000000 --- a/lib/server/eserialresolver.py +++ /dev/null @@ -1,27 +0,0 @@ -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ -import re -__name__='eserial' -def supports(url): - return not _regex(url) == None - -# returns the steam url -def resolve(url): - m = _regex(url) - if m: - stream = re.search('(?P.+?)(\&|$)',m.group('url')).group('url') - show = re.search('serial=(?P.+?)(\&|$)',m.group('url')) - tit = re.search('srt=(?P.+?)(\&|$)',m.group('url')) - if show and tit: - return [{'url':stream,'subs':'http://www.eserial.cz/titulky/%s/%s.srt' % (show.group('url'),tit.group('url'))}] - return [{'url':stream}] - -def _regex(url): - return re.search('eserial\.cz/video\.php\?file=(?P.+?)$',url,re.IGNORECASE | re.DOTALL) - diff --git a/lib/server/exashareresolver.py b/lib/server/exashareresolver.py deleted file mode 100644 index 5ccccd0..0000000 --- a/lib/server/exashareresolver.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: UTF-8 -*- -# /* -# * Copyright (C) 2015 Lubomir Kucera -# * -# * -# * This Program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License as published by -# * the Free Software Foundation; either version 2, or (at your option) -# * any later version. -# * -# * This Program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# * -# * You should have received a copy of the GNU General Public License -# * along with this program; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# */ -import re -import util -from demjson import demjson - -__author__ = 'Jose Riha/Lubomir Kucera' -__name__ = 'exashare' - - -def supports(url): - return re.search(r'exashare\.com/embed\-[^\.]+\.html', url) is not None - - -def resolve(url): - realurl = re.search(r'