test
This commit is contained in:
17
addon.xml
Normal file
17
addon.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<addon id="script.module.stream.resolver" name="Stream Resolver" provider-name="Gald" version="1.7.4">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="3.0.0" />
|
||||
<import addon="script.common.plugin.cache" version="2.5.5"/>
|
||||
<import addon="script.module.demjson" version="2.3.0" />
|
||||
<import addon="script.module.beautifulsoup4" version="4.3.1"/>
|
||||
<import addon="script.module.html5lib" version="0.999.0" />
|
||||
</requires>
|
||||
<extension library="lib" point="xbmc.python.module" />
|
||||
<extension point="xbmc.addon.metadata">
|
||||
<summary lang="cs">Kodi CZ/SK plugins common python modules mainly for resolving stream URLs 2025</summary>
|
||||
<description lang="cs">Common python modules pack that is used by kodi cz/sk plugins</description>
|
||||
<license>GNU GPL</license>
|
||||
<platform>all</platform>
|
||||
</extension>
|
||||
</addon>
|
||||
208
changelog.txt
Normal file
208
changelog.txt
Normal file
@@ -0,0 +1,208 @@
|
||||
[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
|
||||
7
install.sh
Normal file
7
install.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#/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}
|
||||
0
lib/__init__.py
Normal file
0
lib/__init__.py
Normal file
BIN
lib/__pycache__/aadecode.cpython-38.opt-1.pyc
Normal file
BIN
lib/__pycache__/aadecode.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/__pycache__/cloudflare.cpython-38.opt-1.pyc
Normal file
BIN
lib/__pycache__/cloudflare.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/__pycache__/resolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/__pycache__/resolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/__pycache__/search.cpython-38.opt-1.pyc
Normal file
BIN
lib/__pycache__/search.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/__pycache__/util.cpython-38.opt-1.pyc
Normal file
BIN
lib/__pycache__/util.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc
Normal file
BIN
lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
209
lib/aadecode.py
Normal file
209
lib/aadecode.py
Normal file
@@ -0,0 +1,209 @@
|
||||
#-*- 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
|
||||
189
lib/cloudflare.py
Normal file
189
lib/cloudflare.py
Normal file
@@ -0,0 +1,189 @@
|
||||
# -*- 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
|
||||
BIN
lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc
Normal file
BIN
lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
Binary file not shown.
230
lib/contentprovider/provider.py
Normal file
230
lib/contentprovider/provider.py
Normal file
@@ -0,0 +1,230 @@
|
||||
# * 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
|
||||
464
lib/contentprovider/xbmcprovider.py
Normal file
464
lib/contentprovider/xbmcprovider.py
Normal file
@@ -0,0 +1,464 @@
|
||||
# * 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()
|
||||
0
lib/crypto/__init__.py
Normal file
0
lib/crypto/__init__.py
Normal file
BIN
lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc
Normal file
BIN
lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc
Normal file
BIN
lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
75
lib/crypto/md5crypt.py
Normal file
75
lib/crypto/md5crypt.py
Normal file
@@ -0,0 +1,75 @@
|
||||
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"))
|
||||
289
lib/resolver.py
Normal file
289
lib/resolver.py
Normal file
@@ -0,0 +1,289 @@
|
||||
# -*- 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
|
||||
76
lib/search.py
Normal file
76
lib/search.py
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
# -*- 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'])
|
||||
31
lib/server/__init__.py
Normal file
31
lib/server/__init__.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#/*
|
||||
# * 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'
|
||||
BIN
lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/eserialresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/eserialresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/flashxresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/flashxresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/gosuparkresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/gosuparkresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/koukejseresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/koukejseresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/koukniresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/koukniresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/letwatch.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/letwatch.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/mojevideosk.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/mojevideosk.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/nahnojiresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/nahnojiresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/openload.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/openload.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/playedtoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/playedtoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/playmdresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/playmdresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
lib/server/__pycache__/putlockerresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/putlockerresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/servertipczresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/servertipczresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/videobbresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/videobbresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/videonetresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/videonetresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/videoweedresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/videoweedresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/videram.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/videram.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/vimeoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/vimeoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/vkontakteresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/vkontakteresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
BIN
lib/server/__pycache__/zkouknitoresolver.cpython-38.opt-1.pyc
Normal file
BIN
lib/server/__pycache__/zkouknitoresolver.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
104
lib/server/anyfilesresolver.py
Normal file
104
lib/server/anyfilesresolver.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# -*- 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<id>\d+)',url,re.IGNORECASE | re.DOTALL)
|
||||
91
lib/server/dailymotionresolver.py
Normal file
91
lib/server/dailymotionresolver.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# -*- 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
|
||||
36
lib/server/divxstageresolver.py
Normal file
36
lib/server/divxstageresolver.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- 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),'<embed type=\"video/divx','>')
|
||||
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)
|
||||
27
lib/server/eserialresolver.py
Normal file
27
lib/server/eserialresolver.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# * 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<url>.+?)(\&|$)',m.group('url')).group('url')
|
||||
show = re.search('serial=(?P<url>.+?)(\&|$)',m.group('url'))
|
||||
tit = re.search('srt=(?P<url>.+?)(\&|$)',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>.+?)$',url,re.IGNORECASE | re.DOTALL)
|
||||
|
||||
53
lib/server/exashareresolver.py
Normal file
53
lib/server/exashareresolver.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# -*- 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'<iframe src="([^"]+)".*', util.request(url), re.I | re.S).group(1)
|
||||
data = re.search(r'<script[^\.]+?\.setup\((.+?)\);', util.request(realurl), re.I | re.S)
|
||||
if data:
|
||||
data = data.group(1).decode('string_escape')
|
||||
data = re.sub(r'\w+\(([^\)]+?)\)', r'\1', data) # Strip JS functions
|
||||
data = re.sub(r': *([^"][a-zA-Z]+)',r':"\1"', data) # Fix incorrect JSON
|
||||
data = demjson.decode(data)
|
||||
if 'sources' in data:
|
||||
result = []
|
||||
for source in data['sources']:
|
||||
if 'tracks' in data:
|
||||
for track in data['tracks']:
|
||||
result.append({
|
||||
'url': source['file'],
|
||||
'subs': track['file'],
|
||||
'lang': ' %s subtitles' % track['label']
|
||||
})
|
||||
return result
|
||||
return None
|
||||
60
lib/server/flashxresolver.py
Normal file
60
lib/server/flashxresolver.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- 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
|
||||
|
||||
__name__ = 'flashx'
|
||||
|
||||
|
||||
def supports(url):
|
||||
return re.search(r'flashx\.tv/embed\-[^\.]+\.html', url) is not None
|
||||
|
||||
|
||||
def resolve(url):
|
||||
data = util.extract_jwplayer_setup(util.request(url))
|
||||
if data and 'sources' in data:
|
||||
result = []
|
||||
for source in data['sources']:
|
||||
items = []
|
||||
if source['file'].endswith('.smil'):
|
||||
tree = ElementTree.fromstring(util.request(source['file']))
|
||||
base_path = tree.find('./head/meta').get('base')
|
||||
for video in tree.findall('./body/switch/video'):
|
||||
items.append({
|
||||
'url': '%s playpath=%s pageUrl=%s swfUrl=%s swfVfy=true' %
|
||||
(base_path, video.get('src'), url,
|
||||
'http://static.flashx.tv/player6/jwplayer.flash.swf'),
|
||||
'quality': video.get('height') + 'p'
|
||||
})
|
||||
else:
|
||||
items.append({'url': source['file']})
|
||||
if len(data['tracks']) > 0:
|
||||
for item in items:
|
||||
for track in data['tracks']:
|
||||
new_item = deepcopy(item)
|
||||
new_item['subs'] = track['file']
|
||||
new_item['lang'] = ' %s subtitles' % track['label']
|
||||
result.append(new_item)
|
||||
else:
|
||||
result += items
|
||||
return result
|
||||
return None
|
||||
42
lib/server/gosuparkresolver.py
Normal file
42
lib/server/gosuparkresolver.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- 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
|
||||
__name__ = 'gosupark'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
#http://gosupark.com/embed-j3erxu8i2o30-630x320.html
|
||||
data = util.request('http://gosupark.com/'+m.group('url'))
|
||||
n = re.search('file: \"(.+?)\"',data,re.IGNORECASE | re.DOTALL)
|
||||
quality = '???'
|
||||
q = re.search('x(\d+)\.html',url)
|
||||
if q:
|
||||
quality = q.group(1)+'p'
|
||||
if not n == None:
|
||||
return [{'quality':quality,'url':n.group(1).strip()}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('gosupark\.com/(?P<url>.+?)$',url,re.IGNORECASE | re.DOTALL)
|
||||
220
lib/server/hqqresolver.py
Normal file
220
lib/server/hqqresolver.py
Normal file
@@ -0,0 +1,220 @@
|
||||
# -*- 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
|
||||
# *
|
||||
# *
|
||||
# * original based on https://gitorious.org/iptv-pl-dla-openpli/ urlresolver
|
||||
# * update based on https://github.com/LordVenom/
|
||||
# */
|
||||
from io import StringIO
|
||||
import json
|
||||
import util
|
||||
import re
|
||||
import base64
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
|
||||
__name__ = 'hqq'
|
||||
|
||||
def supports(url):
|
||||
return _regex(url) is not None
|
||||
|
||||
|
||||
def _decode(data):
|
||||
def O1l(string):
|
||||
ret = ""
|
||||
i = len(string) - 1
|
||||
while i >= 0:
|
||||
ret += string[i]
|
||||
i -= 1
|
||||
return ret
|
||||
|
||||
def l0I(string):
|
||||
enc = ""
|
||||
dec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
||||
i = 0
|
||||
while True:
|
||||
h1 = dec.find(string[i])
|
||||
i += 1
|
||||
h2 = dec.find(string[i])
|
||||
i += 1
|
||||
h3 = dec.find(string[i])
|
||||
i += 1
|
||||
h4 = dec.find(string[i])
|
||||
i += 1
|
||||
bits = h1 << 18 | h2 << 12 | h3 << 6 | h4
|
||||
o1 = bits >> 16 & 0xff
|
||||
o2 = bits >> 8 & 0xff
|
||||
o3 = bits & 0xff
|
||||
if h3 == 64:
|
||||
enc += chr(o1)
|
||||
else:
|
||||
if h4 == 64:
|
||||
enc += chr(o1) + chr(o2)
|
||||
else:
|
||||
enc += chr(o1) + chr(o2) + chr(o3)
|
||||
if i >= len(string):
|
||||
break
|
||||
return enc
|
||||
|
||||
escape = re.search("var _escape=\'([^\']+)", l0I(O1l(data))).group(1)
|
||||
return escape.replace('%', '\\').decode('unicode-escape')
|
||||
|
||||
|
||||
def _decode2(file_url):
|
||||
def K12K(a, typ='b'):
|
||||
codec_a = ["G", "L", "M", "N", "Z", "o", "I", "t", "V", "y", "x", "p", "R", "m", "z", "u",
|
||||
"D", "7", "W", "v", "Q", "n", "e", "0", "b", "="]
|
||||
codec_b = ["2", "6", "i", "k", "8", "X", "J", "B", "a", "s", "d", "H", "w", "f", "T", "3",
|
||||
"l", "c", "5", "Y", "g", "1", "4", "9", "U", "A"]
|
||||
if 'd' == typ:
|
||||
tmp = codec_a
|
||||
codec_a = codec_b
|
||||
codec_b = tmp
|
||||
idx = 0
|
||||
while idx < len(codec_a):
|
||||
a = a.replace(codec_a[idx], "___")
|
||||
a = a.replace(codec_b[idx], codec_a[idx])
|
||||
a = a.replace("___", codec_b[idx])
|
||||
idx += 1
|
||||
return a
|
||||
|
||||
def _xc13(_arg1):
|
||||
_lg27 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
||||
_local2 = ""
|
||||
_local3 = [0, 0, 0, 0]
|
||||
_local4 = [0, 0, 0]
|
||||
_local5 = 0
|
||||
while _local5 < len(_arg1):
|
||||
_local6 = 0
|
||||
while _local6 < 4 and (_local5 + _local6) < len(_arg1):
|
||||
_local3[_local6] = _lg27.find(_arg1[_local5 + _local6])
|
||||
_local6 += 1
|
||||
_local4[0] = ((_local3[0] << 2) + ((_local3[1] & 48) >> 4))
|
||||
_local4[1] = (((_local3[1] & 15) << 4) + ((_local3[2] & 60) >> 2))
|
||||
_local4[2] = (((_local3[2] & 3) << 6) + _local3[3])
|
||||
|
||||
_local7 = 0
|
||||
while _local7 < len(_local4):
|
||||
if _local3[_local7 + 1] == 64:
|
||||
break
|
||||
_local2 += chr(_local4[_local7])
|
||||
_local7 += 1
|
||||
_local5 += 4
|
||||
return _local2
|
||||
|
||||
return _xc13(K12K(file_url, 'e'))
|
||||
|
||||
|
||||
def _decode3(w, i, s, e):
|
||||
var1 = 0
|
||||
var2 = 0
|
||||
var3 = 0
|
||||
var4 = []
|
||||
var5 = []
|
||||
while (True):
|
||||
if (var1 < 5):
|
||||
var5.append(w[var1])
|
||||
elif (var1 < len(w)):
|
||||
var4.append(w[var1])
|
||||
var1 += 1
|
||||
if (var2 < 5):
|
||||
var5.append(i[var2])
|
||||
elif (var2 < len(i)):
|
||||
var4.append(i[var2])
|
||||
var2 += 1
|
||||
if (var3 < 5):
|
||||
var5.append(s[var3])
|
||||
elif (var3 < len(s)):
|
||||
var4.append(s[var3])
|
||||
var3 += 1
|
||||
if (len(w) + len(i) + len(s) + len(e) == len(var4) + len(var5) + len(e)):
|
||||
break
|
||||
var6 = ''.join(var4)
|
||||
var7 = ''.join(var5)
|
||||
var2 = 0
|
||||
result = []
|
||||
for var1 in range(0, len(var4), 2):
|
||||
ll11 = -1
|
||||
if (ord(var7[var2]) % 2):
|
||||
ll11 = 1
|
||||
result.append(chr(int(var6[var1:var1 + 2], 36) - ll11))
|
||||
var2 += 1
|
||||
if (var2 >= len(var5)):
|
||||
var2 = 0
|
||||
return ''.join(result)
|
||||
|
||||
def _decode_data(data):
|
||||
valuesPattern = r";}\('(\w+)','(\w*)','(\w*)','(\w*)'\)\)"
|
||||
values = re.search(valuesPattern, data, re.DOTALL)
|
||||
return _decode3(values.group(1), values.group(2), values.group(3), values.group(4))
|
||||
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
vid = m.group('vid')
|
||||
headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Content-Type': 'text/html; charset=utf-8'}
|
||||
|
||||
player_url = "http://hqq.tv/player/embed_player.php?vid=%s&autoplay=no" % vid
|
||||
data = util.request(player_url, headers)
|
||||
|
||||
data = _decode_data(data)
|
||||
data = _decode_data(data)
|
||||
blocs = data.split(';; ')
|
||||
data = _decode_data(blocs[1])
|
||||
|
||||
jsonInfo = util.request("http://hqq.tv/player/ip.php?type=json", headers)
|
||||
jsonIp = json.loads(jsonInfo)['ip']
|
||||
at = re.search(r'at = "(\w+)";', data, re.DOTALL)
|
||||
if jsonIp and at:
|
||||
get_data = {'iss': jsonIp, 'vid': vid, 'at': at.group(1), 'autoplayed': 'yes', 'referer': 'on',
|
||||
'http_referer': '', 'pass': '', 'embed_from' : '', 'need_captcha' : '0' }
|
||||
|
||||
data = urllib.parse.unquote(util.request("http://hqq.tv/sec/player/embed_player.php?" +
|
||||
urllib.parse.urlencode(get_data), headers))
|
||||
|
||||
l = re.search(r'link_1: ([a-zA-Z]+), server_1: ([a-zA-Z]+)', data)
|
||||
vid_server = re.search(r'var ' + l.group(2) + ' = "([^"]+)"', data).group(1)
|
||||
vid_link = re.search(r'var ' + l.group(1) + ' = "([^"]+)"', data).group(1)
|
||||
|
||||
if vid_server and vid_link:
|
||||
get_data = {'server_1': vid_server, 'link_1': vid_link, 'at': at.group(1), 'adb': '0/',
|
||||
'b': '1', 'vid': vid }
|
||||
headers['x-requested-with'] = 'XMLHttpRequest'
|
||||
data = util.request("http://hqq.tv/player/get_md5.php?" + urllib.parse.urlencode(get_data), headers)
|
||||
jsonData = json.loads(data)
|
||||
encodedm3u = jsonData['file']
|
||||
decodedm3u = _decode2(encodedm3u.replace('\\', ''))
|
||||
|
||||
agent = 'User-Agent=Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X)'
|
||||
return [{'url': decodedm3u + '|' + agent, 'quality': '360p'}]
|
||||
return None
|
||||
|
||||
|
||||
def _regex(url):
|
||||
match = re.search("(hqq|netu)\.tv/watch_video\.php\?v=(?P<vid>[0-9A-Za-z]+)", url)
|
||||
if match:
|
||||
return match
|
||||
match = re.search(r'(hqq|netu)\.tv/player/embed_player\.php\?vid=(?P<vid>[0-9A-Za-z]+)', url)
|
||||
if match:
|
||||
return match
|
||||
match = re.search(r'(hqq|netu)\.tv/player/hash\.php\?hash=\d+', url)
|
||||
if match:
|
||||
match = re.search(r'var\s+vid\s*=\s*\'(?P<vid>[^\']+)\'', urllib.parse.unquote(util.request(url)))
|
||||
if match:
|
||||
return match
|
||||
b64enc = re.search(r'data:text/javascript\;charset\=utf\-8\;base64([^\"]+)', url)
|
||||
b64dec = b64enc and base64.decodestring(b64enc.group(1))
|
||||
enc = b64dec and re.search(r"\'([^']+)\'", b64dec).group(1)
|
||||
if enc:
|
||||
decoded = _decode(enc)
|
||||
match = re.search(r'<input name="vid"[^>]+? value="(?P<vid>[^"]+?)">', decoded)
|
||||
if re.search(r'<form(.+?)action="[^"]*(hqq|netu)\.tv/player/embed_player\.php"[^>]*>',
|
||||
decoded) and match:
|
||||
return match
|
||||
return None
|
||||
36
lib/server/koukejseresolver.py
Normal file
36
lib/server/koukejseresolver.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#/*
|
||||
# * Copyright (C) 2013 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,urllib.request,urllib.error,urllib.parse,traceback
|
||||
__name__ = 'koukejse.cz'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if not m == None:
|
||||
data = util.request(url)
|
||||
stream = re.search('_video_file = \'(?P<url>[^\']+)',data)
|
||||
if stream:
|
||||
return [{'name':__name__,'quality':'360p','url':stream.group('url'),'surl':url}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('koukejse\.cz/(.+?)',url,re.IGNORECASE | re.DOTALL)
|
||||
88
lib/server/koukniresolver.py
Normal file
88
lib/server/koukniresolver.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# -*- 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, urllib.request, urllib.error, urllib.parse, traceback
|
||||
__name__ = 'koukni.cz'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def url(url):
|
||||
m = _regex(url)
|
||||
if not m == None:
|
||||
iframe = _iframe(url)
|
||||
if iframe:
|
||||
return iframe[0]['url']
|
||||
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if not m == None:
|
||||
try:
|
||||
iframe = _iframe(url)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return
|
||||
if iframe:
|
||||
return iframe
|
||||
# else:
|
||||
# return [{'name':__name__,'quality':'720p','url':url,'surl':url}]
|
||||
|
||||
def _furl(url):
|
||||
if url.startswith('http'):
|
||||
return url
|
||||
url = url.lstrip('./')
|
||||
return 'http://www.koukni.cz/' + url
|
||||
|
||||
def _iframe(url):
|
||||
index = url.find('&')
|
||||
if index > 0:
|
||||
url = url[:index]
|
||||
iframe = re.search('(\d+)$', url, re.IGNORECASE | re.DOTALL)
|
||||
if iframe:
|
||||
data = util.request(url)
|
||||
ress = re.search('var api = flowplayer\(\),\s+resolutions = \{([^\}]+)\}', data, re.IGNORECASE | re.DOTALL)
|
||||
valid_ress = re.compile("<span[^>]*>([^<]+)</span>", re.IGNORECASE | re.DOTALL).findall(data)
|
||||
subs = re.search('<track.+?src=\"(?P<url>[^\"]+)', data, re.IGNORECASE | re.DOTALL)
|
||||
if ress:
|
||||
ret = []
|
||||
ress = ress.group(1).strip().split(',')
|
||||
for r in ress:
|
||||
r = r.replace('"', '').split(':')
|
||||
res = r[0].strip()
|
||||
vurl = _furl(r[1].strip())
|
||||
if res in valid_ress:
|
||||
v = {'name':__name__, 'url':vurl, 'quality':res, 'surl':url,'subs':''}
|
||||
if subs:
|
||||
v['subs'] = _furl(subs.group('url'))
|
||||
ret.append(v)
|
||||
if len(ret)>0:
|
||||
return ret
|
||||
video = re.search('url\: \'(?P<url>mp4[^\']+)', data, re.IGNORECASE | re.DOTALL)
|
||||
subs = re.search('captionUrl\: \'(?P<url>[^\']+)', data, re.IGNORECASE | re.DOTALL)
|
||||
if video:
|
||||
ret = {'name':__name__, 'quality':'720p', 'surl':url}
|
||||
ret['url'] = 'rtmp://koukni.cz/mp4 playpath=%s' % video.group('url')
|
||||
if subs:
|
||||
ret['subs'] = _furl(subs.group('url'))
|
||||
return [ret]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('(www\.)?koukni.cz/(.+?)', url, re.IGNORECASE | re.DOTALL)
|
||||
54
lib/server/ksetresolver.py
Normal file
54
lib/server/ksetresolver.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#/*
|
||||
# * Copyright (C) 2013 mx3L
|
||||
# *
|
||||
# *
|
||||
# * 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, decimal, random, base64, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
__name__ = 'kset'
|
||||
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)))
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
id = int(m.group('id'))
|
||||
headers = {
|
||||
"Referer":"http://st.kset.kz/pl/pl.swf"
|
||||
}
|
||||
|
||||
params = {
|
||||
'id':id,
|
||||
'ref':'http://kset.kz/video_frame.php?id=%d' % id,
|
||||
'r':gen_random_decimal(0, 99999999999999)
|
||||
}
|
||||
quality = "480p"
|
||||
data = util.request("http://kset.kz/v.php?" + urllib.parse.urlencode(params), headers=headers)
|
||||
item = util.json.loads(base64.decodestring(data))
|
||||
return [{'quality':quality, 'url':item['file']}]
|
||||
|
||||
|
||||
def _regex(url):
|
||||
m = re.search('http://kset\.kz/video_frame\.php\?id=(?P<id>[0-9]+)', url, re.IGNORECASE | re.DOTALL)
|
||||
return m
|
||||
|
||||
43
lib/server/letwatch.py
Normal file
43
lib/server/letwatch.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- 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__ = 'Lubomir Kucera'
|
||||
__name__ = 'letwatch'
|
||||
|
||||
|
||||
def supports(url):
|
||||
return re.search(r'letwatch\.us/embed\-[^\.]+\.html', url) is not None
|
||||
|
||||
|
||||
def resolve(url):
|
||||
data = re.search(r'<script[^\.]+?\.setup\(([^\)]+?)\);', util.request(url), re.I | re.S)
|
||||
if data:
|
||||
data = demjson.decode(data.group(1).decode('string_escape'))
|
||||
if 'sources' in data:
|
||||
result = []
|
||||
for source in data['sources']:
|
||||
result.append({'url': source['file'], 'quality': source['label']})
|
||||
return result
|
||||
return None
|
||||
68
lib/server/mixturevideoresolver.py
Normal file
68
lib/server/mixturevideoresolver.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# -*- 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,urllib.request,urllib.error,urllib.parse,traceback,resolver
|
||||
|
||||
__name__ = 'mixturecloud'
|
||||
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
class MyHTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
|
||||
|
||||
def http_error_302(self, req, fp, code, msg, headers):
|
||||
self.location = headers.getheader('Location')
|
||||
return urllib.request.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
defrhandler = urllib.request.HTTPRedirectHandler
|
||||
cookieprocessor = urllib.request.HTTPCookieProcessor()
|
||||
redirecthandler = MyHTTPRedirectHandler()
|
||||
opener = urllib.request.build_opener(redirecthandler, cookieprocessor)
|
||||
urllib.request.install_opener(opener)
|
||||
req = urllib.request.Request(url)
|
||||
response = urllib.request.urlopen(req)
|
||||
response.close()
|
||||
urllib.request.install_opener(urllib.request.build_opener(defrhandler,cookieprocessor))
|
||||
# mixturevideo uses REDIRECT to 'secret url' ;-)
|
||||
url = redirecthandler.location
|
||||
item = resolver.item()
|
||||
item['surl'] = url
|
||||
item['name'] = __name__
|
||||
try:
|
||||
ishd = re.search('hd\.state=([^\&]+)',url).group(1)
|
||||
streamer = re.search('streamer=([^$]+)',url).group(1)+'&start=0&'
|
||||
if ishd == 'true':
|
||||
item['url'] = streamer + 'hd.file=' +re.search('hd\.file=([^\&]+)',url).group(1)
|
||||
item['quality'] = 'hd'
|
||||
else:
|
||||
item['url'] = streamer + 'file=' +re.search('file=([^\&]+)',url).group(1)
|
||||
return [item]
|
||||
except:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
def _regex(url):
|
||||
return re.search('player\.mixturecloud\.com',url,re.IGNORECASE | re.DOTALL)
|
||||
|
||||
41
lib/server/moevideoresolver.py
Normal file
41
lib/server/moevideoresolver.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# -*- 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,urllib.request,urllib.error,urllib.parse,urllib.parse
|
||||
__name__ = 'moevideo'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def url(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
id = m.group('id')
|
||||
post = {'r':'["tVL0gjqo5",["preview/flv_image",{"uid":"'+id+'"}],["preview/flv_link",{"uid":"'+id+'"}]]'}
|
||||
data = util.post('http://api.letitbit.net',post)
|
||||
data = data.replace('\\','')
|
||||
print(data)
|
||||
link = re.search('link\"\:\"(?P<link>[^\"]+)',data)
|
||||
if link:
|
||||
return [{'url':link.group('link')}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('moevideo.net/video\.php\?file=(?P<id>[^\&]+)',url,re.IGNORECASE | re.DOTALL)
|
||||
47
lib/server/mojevideosk.py
Normal file
47
lib/server/mojevideosk.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
# /*
|
||||
# * Copyright (C) 2016 Jose Riha
|
||||
# *
|
||||
# *
|
||||
# * 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
|
||||
import time
|
||||
import pickle
|
||||
|
||||
__author__ = 'Jose Riha'
|
||||
__name__ = 'mojevideo.sk'
|
||||
|
||||
|
||||
def supports(url):
|
||||
return re.search(r'mojevideo\.sk/video/\w+/.+\.html', url) is not None
|
||||
|
||||
|
||||
def resolve(url):
|
||||
cookies = {}
|
||||
util.init_urllib(cookies)
|
||||
data = util.request(url)
|
||||
view = list(pickle.loads(util._cookie_jar.dump())[
|
||||
'.mojevideo.sk']['/'].keys())[0]
|
||||
st = re.search(r'vHash=\[\'([^\']+)', data)
|
||||
if not st:
|
||||
return None
|
||||
st = st.group(1)
|
||||
tim = int(time.time())
|
||||
base = 'http://fs5.mojevideo.sk:8080/securevd/'
|
||||
return [{'url': base + view.replace('view', '') + '.mp4?st=%s&e=%s|Cookie=%s=1' % (st, tim, view)}]
|
||||
35
lib/server/movshareresolver.py
Normal file
35
lib/server/movshareresolver.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- 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
|
||||
__name__='movshare'
|
||||
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),'<embed type=\"video/divx','>')
|
||||
stream = re.search('src=\"([^\"]+)',data,re.IGNORECASE | re.DOTALL).group(1)
|
||||
return [{'url':stream}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('movshare.net/(.+?)',url,re.IGNORECASE | re.DOTALL)
|
||||
45
lib/server/munkvideoresolver.py
Normal file
45
lib/server/munkvideoresolver.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# * 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
|
||||
|
||||
__name__='munkvideo'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
data = util.request(url)
|
||||
streams = re.search('res0\:[^\"]*\"([^\"]+)',data,re.IGNORECASE|re.DOTALL)
|
||||
subs = re.search('sub0\:[^\"]*\".*?(http[^\"]*)',data,re.IGNORECASE|re.DOTALL)
|
||||
rn = re.search('rn\:[^\"]*\"([^\"]*)',data,re.IGNORECASE|re.DOTALL)
|
||||
if streams and subs and rn:
|
||||
streams = streams.group(1).split(',')
|
||||
subs = subs.group(1)
|
||||
rn = rn.group(1).split(',')
|
||||
index = 0
|
||||
result = []
|
||||
headers = {'Referer':'me'}
|
||||
for stream in streams:
|
||||
q = rn[index]
|
||||
if q == 'HD':
|
||||
q = '720p'
|
||||
else:
|
||||
q = '???'
|
||||
if len(subs) > 0:
|
||||
result.append({'url':stream,'quality':q,'subs':subs,'headers':headers})
|
||||
else:
|
||||
result.append({'url':stream,'quality':q,'headers':headers})
|
||||
index+=1
|
||||
return result
|
||||
|
||||
def _regex(url):
|
||||
return re.search('munkvideo\.cz/video/',url,re.IGNORECASE | re.DOTALL)
|
||||
|
||||
59
lib/server/myviruresolver.py
Normal file
59
lib/server/myviruresolver.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python
|
||||
# -*- 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
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import pickle
|
||||
from demjson import demjson
|
||||
|
||||
__author__ = 'Jose Riha'
|
||||
__name__ = 'myviru'
|
||||
|
||||
UA = urllib.parse.quote(util.UA)
|
||||
|
||||
|
||||
def supports(url):
|
||||
return re.search(r'http://myvi.ru/player/flash', url) is not None
|
||||
|
||||
|
||||
def resolve(url):
|
||||
cookies = {}
|
||||
result = []
|
||||
util.init_urllib(cookies)
|
||||
id = re.search(r'.*player/flash/(?P<url>.+)', url).group('url')
|
||||
r = util.request('http://myvi.ru/player/api/Video/Get/%s?sig' % id)
|
||||
jsondata = demjson.decode(r)
|
||||
playlist = jsondata['sprutoData']['playlist'][0]
|
||||
uuid = pickle.loads(util._cookie_jar.dump())[
|
||||
'.myvi.ru']['/']['UniversalUserID']
|
||||
for f in playlist['video']:
|
||||
streamurl = f['url']
|
||||
streamurl += '|Cookie=UniversalUserID%3D' + urllib.parse.quote(uuid.value)
|
||||
streamurl += '&User-Agent=' + UA
|
||||
result.append({'url': streamurl})
|
||||
if result:
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
29
lib/server/nahnojiresolver.py
Normal file
29
lib/server/nahnojiresolver.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# * 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
|
||||
__name__='nahnoji'
|
||||
__priiority__=-1
|
||||
|
||||
def supports(url):
|
||||
return re.search(r'nahnoji\.cz/.+', url) is not None
|
||||
|
||||
def resolve(url):
|
||||
# returns the stream url
|
||||
stream = []
|
||||
if url.endswith('.flv'):
|
||||
stream = [url]
|
||||
else:
|
||||
page = util.parse_html(url)
|
||||
stream = ['http://nahnoji.cz'+x['src'] for x in page.select('source[type=video/mp4]')]
|
||||
if stream:
|
||||
result=[]
|
||||
for streamurl in stream:
|
||||
result.append({'name':__name__,'quality':'360p','url':streamurl,'surl':url})
|
||||
return result
|
||||
|
||||
36
lib/server/novamovresovler.py
Normal file
36
lib/server/novamovresovler.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# * 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 util,re
|
||||
__name__ = 'novamov'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
def resolve(url):
|
||||
if supports(url):
|
||||
data = util.request(url)
|
||||
m = re.search('flashvars.file=\"([^\"]+)',data,re.IGNORECASE | re.DOTALL)
|
||||
n = re.search('flashvars.filekey=\"([^\"]+)',data,re.IGNORECASE | re.DOTALL)
|
||||
if not m == None and not n == None:
|
||||
data = util.request('http://www.novamov.com/api/player.api.php?key=%s&file=%s&user=undefined&pass=undefined&codes=1' % (n.group(1),m.group(1)))
|
||||
stream = re.search('url=([^\&]+)',data).group(1)
|
||||
return [{'url':stream}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('novamov\.com',url,re.IGNORECASE | re.DOTALL)
|
||||
276
lib/server/openload.py
Normal file
276
lib/server/openload.py
Normal file
@@ -0,0 +1,276 @@
|
||||
# -*- 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
|
||||
# *
|
||||
# */
|
||||
# uses code fragments from https://github.com/LordVenom/venom-xbmc-addons
|
||||
import re
|
||||
import util
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
from aadecode import AADecoder
|
||||
|
||||
class cRequestHandler:
|
||||
REQUEST_TYPE_GET = 0
|
||||
REQUEST_TYPE_POST = 1
|
||||
|
||||
def __init__(self, sUrl):
|
||||
self.__sUrl = sUrl
|
||||
self.__sRealUrl = ''
|
||||
self.__cType = 0
|
||||
self.__aParamaters = {}
|
||||
self.__aHeaderEntries = []
|
||||
self.removeBreakLines(True)
|
||||
self.removeNewLines(True)
|
||||
self.__setDefaultHeader()
|
||||
|
||||
def removeNewLines(self, bRemoveNewLines):
|
||||
self.__bRemoveNewLines = bRemoveNewLines
|
||||
|
||||
def removeBreakLines(self, bRemoveBreakLines):
|
||||
self.__bRemoveBreakLines = bRemoveBreakLines
|
||||
|
||||
def setRequestType(self, cType):
|
||||
self.__cType = cType
|
||||
|
||||
def addHeaderEntry(self, sHeaderKey, sHeaderValue):
|
||||
aHeader = {sHeaderKey : sHeaderValue}
|
||||
self.__aHeaderEntries.append(aHeader)
|
||||
|
||||
def addParameters(self, sParameterKey, mParameterValue):
|
||||
self.__aParamaters[sParameterKey] = mParameterValue
|
||||
|
||||
def getResponseHeader(self):
|
||||
return self.__sResponseHeader
|
||||
|
||||
# url after redirects
|
||||
def getRealUrl(self):
|
||||
return self.__sRealUrl;
|
||||
|
||||
def request(self):
|
||||
self.__sUrl = self.__sUrl.replace(' ', '+')
|
||||
return self.__callRequest()
|
||||
|
||||
def getRequestUri(self):
|
||||
return self.__sUrl + '?' + urllib.parse.urlencode(self.__aParamaters)
|
||||
|
||||
def __setDefaultHeader(self):
|
||||
UA = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3'
|
||||
self.addHeaderEntry('User-Agent', UA)
|
||||
self.addHeaderEntry('Accept-Language', 'de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4')
|
||||
self.addHeaderEntry('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')
|
||||
|
||||
def __callRequest(self):
|
||||
sParameters = urllib.parse.urlencode(self.__aParamaters)
|
||||
|
||||
if (self.__cType == cRequestHandler.REQUEST_TYPE_GET):
|
||||
if (len(sParameters) > 0):
|
||||
if (self.__sUrl.find('?') == -1):
|
||||
self.__sUrl = self.__sUrl + '?' + str(sParameters)
|
||||
sParameters = ''
|
||||
else:
|
||||
self.__sUrl = self.__sUrl + '&' + str(sParameters)
|
||||
sParameters = ''
|
||||
|
||||
if (len(sParameters) > 0):
|
||||
oRequest = urllib.request.Request(self.__sUrl, sParameters)
|
||||
else:
|
||||
oRequest = urllib.request.Request(self.__sUrl)
|
||||
|
||||
for aHeader in self.__aHeaderEntries:
|
||||
for sHeaderKey, sHeaderValue in list(aHeader.items()):
|
||||
oRequest.add_header(sHeaderKey, sHeaderValue)
|
||||
|
||||
sContent = ''
|
||||
|
||||
try:
|
||||
oResponse = urllib.request.urlopen(oRequest, timeout=30)
|
||||
sContent = oResponse.read()
|
||||
|
||||
self.__sResponseHeader = oResponse.info()
|
||||
self.__sRealUrl = oResponse.geturl()
|
||||
|
||||
oResponse.close()
|
||||
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 503:
|
||||
if cloudflare.CheckIfActive(e.headers):
|
||||
cookies = e.headers['Set-Cookie']
|
||||
cookies = cookies.split(';')[0]
|
||||
from resources.lib.cloudflare import CloudflareBypass
|
||||
sContent = CloudflareBypass().GetHtml(self.__sUrl,e.read(),cookies)
|
||||
|
||||
self.__sResponseHeader = ''
|
||||
self.__sRealUrl = ''
|
||||
|
||||
if not sContent:
|
||||
cConfig().error("%s,%s" % (cConfig().getlanguage(30205), self.__sUrl))
|
||||
return ''
|
||||
|
||||
if (self.__bRemoveNewLines == True):
|
||||
sContent = sContent.replace("\n","")
|
||||
sContent = sContent.replace("\r\t","")
|
||||
|
||||
if (self.__bRemoveBreakLines == True):
|
||||
sContent = sContent.replace(" ","")
|
||||
|
||||
return sContent
|
||||
|
||||
def getHeaderLocationUrl(self):
|
||||
opened = urllib.request.urlopen(self.__sUrl)
|
||||
return opened.geturl()
|
||||
|
||||
class cParser:
|
||||
|
||||
def parseSingleResult(self, sHtmlContent, sPattern):
|
||||
aMatches = re.compile(sPattern).findall(sHtmlContent)
|
||||
if (len(aMatches) == 1):
|
||||
aMatches[0] = self.__replaceSpecialCharacters(aMatches[0])
|
||||
return True, aMatches[0]
|
||||
return False, aMatches
|
||||
|
||||
def __replaceSpecialCharacters(self, sString):
|
||||
res=sString.replace('\\/','/').replace('&','&').replace('\xc9','E').replace('–', '-')
|
||||
res=res.replace('&', '&').replace('’','\'').replace('\r','').replace('\n','')
|
||||
res=res.replace('\t','').replace(''',"'")
|
||||
return res
|
||||
|
||||
def parse(self, sHtmlContent, sPattern, iMinFoundValue = 1):
|
||||
sHtmlContent = self.__replaceSpecialCharacters(str(sHtmlContent))
|
||||
aMatches = re.compile(sPattern, re.IGNORECASE).findall(sHtmlContent)
|
||||
if (len(aMatches) >= iMinFoundValue):
|
||||
return True, aMatches
|
||||
return False, aMatches
|
||||
|
||||
def replace(self, sPattern, sReplaceString, sValue):
|
||||
return re.sub(sPattern, sReplaceString, sValue)
|
||||
|
||||
def escape(self, sValue):
|
||||
return re.escape(sValue)
|
||||
|
||||
def getNumberFromString(self, sValue):
|
||||
sPattern = "\d+"
|
||||
aMatches = re.findall(sPattern, sValue)
|
||||
if (len(aMatches) > 0):
|
||||
return aMatches[0]
|
||||
return 0
|
||||
|
||||
|
||||
__author__ = 'Jose Riha/Lubomir Kucera'
|
||||
__name__ = 'openload'
|
||||
|
||||
|
||||
def supports(url):
|
||||
return re.search(r'openload\.\w+/embed/.+', url) is not None
|
||||
|
||||
def base10toN(num, n):
|
||||
"""Change a to a base-n number.
|
||||
Up to base-36 is supported without special notation."""
|
||||
|
||||
new_num_string = ''
|
||||
current = num
|
||||
|
||||
while current != 0:
|
||||
remainder = current % n
|
||||
if 36 > remainder > 9:
|
||||
remainder_string = chr(remainder + 87)
|
||||
elif remainder >= 36:
|
||||
remainder_string = '(' + str(remainder) + ')'
|
||||
else:
|
||||
remainder_string = str(remainder)
|
||||
new_num_string = remainder_string + new_num_string
|
||||
current = current / n
|
||||
return new_num_string
|
||||
|
||||
def resolve(url):
|
||||
|
||||
oRequest = cRequestHandler(url)
|
||||
sHtmlContent = oRequest.request()
|
||||
|
||||
oParser = cParser()
|
||||
string = ''
|
||||
|
||||
sPattern = '<script type="text\/javascript">(゚ω゚.+?)<\/script>'
|
||||
aResult = oParser.parse(sHtmlContent, sPattern)
|
||||
|
||||
vid = 'XXXXXX'
|
||||
string2=[]
|
||||
|
||||
for aEntry in aResult[1]:
|
||||
s = AADecoder(aEntry).decode()
|
||||
string2.append(s)
|
||||
|
||||
if 'welikekodi_ya_rly' in s:
|
||||
c0 = re.search('welikekodi_ya_rly = ([^<>;"]+);', s)
|
||||
if c0:
|
||||
c = c0.group(1)
|
||||
c = c.replace('Math.round','int')
|
||||
cc = str(eval(c))
|
||||
vid = '[' + cc + ']'
|
||||
|
||||
for string3 in string2:
|
||||
if ('toString' in string3) and (vid in string3):
|
||||
base = int(re.findall('toString\(a\+([0-9]+)\)',string3)[0])
|
||||
table = re.findall('(\([0-9][^)]+\))',string3)
|
||||
|
||||
for str1 in table:
|
||||
val = re.findall('([0-9]+),([0-9]+)',str1)
|
||||
base2 = base + int(val[0][0])
|
||||
str2 = base10toN(int(val[0][1]), base2)
|
||||
string3 = string3.replace(str1, str2)
|
||||
|
||||
#clean up
|
||||
string3 = string3.replace('+', '')
|
||||
string3 = string3.replace('"', '')
|
||||
string3 = string3.replace('', '')
|
||||
|
||||
#a hack for not having to recode everything
|
||||
url = re.findall('(http[^<>}]+)',string3)[0]
|
||||
string = 'src="' + url + '?mime=true"'
|
||||
|
||||
if (string):
|
||||
sContent = string.replace('\\','')
|
||||
|
||||
api_call = ''
|
||||
|
||||
sPattern = 'src=\s*?"(.*?)\?'
|
||||
aResult = oParser.parse(sContent, sPattern)
|
||||
|
||||
if (aResult[0] == True):
|
||||
api_call = aResult[1][0]
|
||||
|
||||
if not api_call:
|
||||
sPattern = 'window\.vr *=["\'](.+?)["\']'
|
||||
aResult = oParser.parse(sContent, sPattern)
|
||||
if (aResult[0] == True):
|
||||
api_call = aResult[1][0]
|
||||
|
||||
if (api_call):
|
||||
|
||||
if 'openload.co/stream' in api_call:
|
||||
UA = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0'
|
||||
headers = {'User-Agent': UA }
|
||||
|
||||
req = urllib.request.Request(api_call,None,headers)
|
||||
res = urllib.request.urlopen(req)
|
||||
finalurl = res.geturl()
|
||||
api_call = finalurl
|
||||
|
||||
return [{'url': api_call}]
|
||||
|
||||
42
lib/server/playedtoresolver.py
Normal file
42
lib/server/playedtoresolver.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#/*
|
||||
# * Copyright (C) 2013 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
|
||||
__name__ = 'played.to'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
surl = m.group('url').replace('embed','iframe')
|
||||
data = util.request('http://played.to/%s' % surl)
|
||||
n = re.search('file: \"(.+?)\"',data,re.IGNORECASE | re.DOTALL)
|
||||
quality = '???'
|
||||
q = re.search('x(\d+)\.html',url)
|
||||
if q:
|
||||
quality = q.group(1)+'p'
|
||||
if not n == None:
|
||||
return [{'quality':quality,'url':n.group(1).strip()}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('played\.to/(?P<url>(embed|iframe)-.+?)$',url,re.IGNORECASE | re.DOTALL)
|
||||
28
lib/server/playmdresolver.py
Normal file
28
lib/server/playmdresolver.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# * 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
|
||||
|
||||
__name__='play.md'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
|
||||
def resolve(link):
|
||||
if _regex(link):
|
||||
data = util.request(link)
|
||||
url = re.search('base_url\: \"(?P<url>[^\"]+)',data)
|
||||
file = re.search('file_name\: \"(?P<url>[^\"]+)',data)
|
||||
res = re.search('resolutions\: \"(?P<url>[^\"]+)',data)
|
||||
if url and file and res:
|
||||
url = '%s/%s/%s' % (url.group('url'),res.group('url'),file.group('url'))
|
||||
return [{'quality':res.group('url'),'url':url}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('play\.md',url,re.IGNORECASE | re.DOTALL)
|
||||
|
||||
48
lib/server/publicvideohostresolver.py
Normal file
48
lib/server/publicvideohostresolver.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#/*
|
||||
# * Copyright (C) 2013 mx3L
|
||||
# *
|
||||
# *
|
||||
# * 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, decimal, random, base64, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
__name__ = 'publicvideohost'
|
||||
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)))
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
id = int(m.group('v'))
|
||||
params = {
|
||||
'v':id,
|
||||
}
|
||||
quality = "480p"
|
||||
data = util.request("http://embed.publicvideohost.org/v.php?" + urllib.parse.urlencode(params))
|
||||
vurl = re.search('file\:(.*?)\"(?P<url>[^"]+)',data, re.IGNORECASE | re.DOTALL).group('url')
|
||||
return [{'quality':quality, 'url':vurl}]
|
||||
|
||||
|
||||
def _regex(url):
|
||||
m = re.search('http://embed\.publicvideohost\.org/v\.php\?(.+?)v=(?P<v>[0-9]+)', url, re.IGNORECASE | re.DOTALL)
|
||||
return m
|
||||
|
||||
159
lib/server/putlockerresolver.py
Normal file
159
lib/server/putlockerresolver.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# -*- 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
|
||||
# *
|
||||
# *
|
||||
# */
|
||||
|
||||
# thanks to:
|
||||
# https://github.com/Eldorados
|
||||
|
||||
import re,util,os
|
||||
__name__ = 'putlocker'
|
||||
|
||||
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
|
||||
def get_host_and_id(url):
|
||||
r = re.search('//(.+?)/(?:file|embed)/([0-9A-Z]+)', url)
|
||||
if r:
|
||||
return r.groups()
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_host(host,media_id):
|
||||
#host,media_id=get_host_and_id(url)
|
||||
if 'putlocker' in host:
|
||||
host = 'www.putlocker.com'
|
||||
else:
|
||||
host = 'www.sockshare.com'
|
||||
return 'http://%s/file/%s' % (host, media_id)
|
||||
|
||||
|
||||
|
||||
def login_stale():
|
||||
url = 'http://www.putlocker.com/cp.php'
|
||||
if not os.path.exists(cookie_file):
|
||||
return True
|
||||
#self.net.set_cookies(cookie_file)
|
||||
source = util.request(url)
|
||||
if re.search('(?:<span class=pro_user>\( Pro \)</span>|<span class="free_user">\( Free \)</span>)', source):
|
||||
print ('Putlocker account appears to be logged in.')
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
|
||||
# returns the steam url
|
||||
def url(url):
|
||||
if supports(url):
|
||||
|
||||
'''
|
||||
if self.get_setting('login') == 'true':
|
||||
if login_stale():
|
||||
login()
|
||||
#self.net.set_cookies(cookie_file)
|
||||
'''
|
||||
|
||||
host,media_id=get_host_and_id(url)
|
||||
web_url = get_host(host,media_id)
|
||||
|
||||
#find session_hash
|
||||
try:
|
||||
html = util.request(web_url)
|
||||
except urllib.error.URLError as e:
|
||||
print(('putlocker: got http error %d fetching %s' % (e.code, web_url)))
|
||||
return False
|
||||
|
||||
#Shortcut for logged in users
|
||||
pattern = '<a href="(/.+?)" class="download_file_link" style="margin:0px 0px;">Download File</a>'
|
||||
link = re.search(pattern, html)
|
||||
if link:
|
||||
print('Direct link found: %s' %link.group(1))
|
||||
return 'http://www.putlocker.com%s' %link.group(1)
|
||||
|
||||
r = re.search('value="([0-9a-f]+?)" name="hash"', html)
|
||||
if r:
|
||||
session_hash = r.group(1)
|
||||
else:
|
||||
print ('putlocker: session hash not found')
|
||||
return False
|
||||
|
||||
#post session_hash
|
||||
try:
|
||||
html = util.post(web_url, {'hash': session_hash,'confirm': 'Continue as Free User'})
|
||||
except urllib.error.URLError as e:
|
||||
print(('putlocker: got http error %d posting %s' %(e.code, web_url)))
|
||||
return False
|
||||
|
||||
#find playlist code
|
||||
r = re.search('\?stream=(.+?)\'', html)
|
||||
if r:
|
||||
playlist_code = r.group(1)
|
||||
else:
|
||||
r = re.search('key=(.+?)&',html)
|
||||
playlist_code = r.group(1)
|
||||
|
||||
#find download link
|
||||
#q = self.get_setting('quality')
|
||||
q = '1'
|
||||
|
||||
#Try to grab highest quality link available
|
||||
if q == '1':
|
||||
#download & return link.
|
||||
if 'putlocker' in host:
|
||||
Avi = "http://putlocker.com/get_file.php?stream=%s&original=1"%playlist_code
|
||||
html = util.request(Avi)
|
||||
final=re.compile('url="(.+?)"').findall(html)[0].replace('&','&')
|
||||
return [final]
|
||||
else:
|
||||
Avi = "http://sockshare.com/get_file.php?stream=%s&original=1"%playlist_code
|
||||
html = util.request(Avi)
|
||||
final=re.compile('url="(.+?)"').findall(html)[0].replace('&','&')
|
||||
return [final]
|
||||
|
||||
#Else grab standard flv link
|
||||
else:
|
||||
xml_url = re.sub('/(file|embed)/.+', '/get_file.php?stream=', web_url)
|
||||
xml_url += playlist_code
|
||||
try:
|
||||
html = util.request(xml_url)
|
||||
except urllib.error.URLError as e:
|
||||
pritn ('putlocker: got http error %d fetching %s'(e.code, xml_url))
|
||||
return False
|
||||
|
||||
r = re.search('url="(.+?)"', html)
|
||||
if r:
|
||||
flv_url = r.group(1)
|
||||
else:
|
||||
print ('putlocker: stream url not found')
|
||||
return False
|
||||
|
||||
return [flv_url.replace('&','&')]
|
||||
|
||||
|
||||
def resolve(u):
|
||||
stream = url(u)
|
||||
if stream:
|
||||
return [{'name':__name__,'quality':'360p','url':stream[0],'surl':u}]
|
||||
|
||||
|
||||
def _regex(url):
|
||||
return re.search('http://(www.)?(putlocker|sockshare).com/' + '(file|embed)/[0-9A-Z]+', url,re.IGNORECASE | re.DOTALL)
|
||||
41
lib/server/rutuberesolver.py
Normal file
41
lib/server/rutuberesolver.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# -*- 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
|
||||
__name__ = 'rutube'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def url(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
data = util.request('http://rutube.ru/trackinfo/'+m.group('id')+'.xml')
|
||||
n = re.search('<m3u8>([^<]+)',data,re.IGNORECASE | re.DOTALL)
|
||||
if not n == None:
|
||||
return [n.group(1).strip()]
|
||||
|
||||
def resolve(u):
|
||||
stream = url(u)
|
||||
if stream:
|
||||
return [{'name':__name__,'quality':'640p','url':stream[0],'surl':u}]
|
||||
def _regex(url):
|
||||
return re.search('rutube\.ru/(video/embed|embed)/(?P<id>[^$]+)',url,re.IGNORECASE | re.DOTALL)
|
||||
40
lib/server/servertipczresolver.py
Normal file
40
lib/server/servertipczresolver.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- 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,urllib.request,urllib.parse,urllib.error
|
||||
__name__ = 'servertip'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
if supports(url):
|
||||
data = util.request(url)
|
||||
data = util.substr(data,'<div id=\"player_code','</div')
|
||||
try:
|
||||
video_id = re.search('flv\|\|([^\|]+)',data).group(1)
|
||||
return [{'url':'http://servertip.cz/cgi-bin/dl.cgi/%s/video.flv' % video_id}]
|
||||
except:
|
||||
pass
|
||||
|
||||
def _regex(url):
|
||||
return re.search('servertip\.cz',url,re.IGNORECASE | re.DOTALL)
|
||||
|
||||
50
lib/server/sledujuserialyresolver.py
Normal file
50
lib/server/sledujuserialyresolver.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# -*- 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,urllib.request,urllib.error,urllib.parse,traceback
|
||||
__name__ = 'zkouknito.cz'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if not m == None:
|
||||
data = util.request(url)
|
||||
if data.find('jwplayer(\'mediaplayer') > 0:
|
||||
video = re.search('\'file\'\: \'(?P<url>.+?[flv|mp4])\'',data)
|
||||
if video:
|
||||
|
||||
item = {}
|
||||
item['url'] = video.group('url')
|
||||
subs = re.search('\'file\'\: \'(?P<url>.+?srt)',data)
|
||||
if subs:
|
||||
item['subs'] = _furl(subs.group('url'))
|
||||
print(item)
|
||||
return [item]
|
||||
|
||||
def _furl(url):
|
||||
if url.startswith('http'):
|
||||
return url
|
||||
url = url.lstrip('./')
|
||||
return 'http://www.sledujuserialy.cz/'+url
|
||||
|
||||
def _regex(url):
|
||||
return re.search('(www\.)?sledujuserialy.cz/(.+?)',url,re.IGNORECASE | re.DOTALL)
|
||||
36
lib/server/stagevuresolver.py
Normal file
36
lib/server/stagevuresolver.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- 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
|
||||
__name__ = 'stagevu'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
if supports(url):
|
||||
data = util.substr(util.request(url),'<body>','</script>')
|
||||
m = re.search('url\[[\d]+\] = \'([^\']+)',data,re.IGNORECASE | re.DOTALL)
|
||||
if not m == None:
|
||||
return [{'url':m.group(1)}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('(www\.)?stagevu.com/(.+?)uid=(?P<id>(.+?))',url,re.IGNORECASE | re.DOTALL)
|
||||
61
lib/server/streamcloudresolver.py
Normal file
61
lib/server/streamcloudresolver.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# -*- 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
|
||||
__name__ = 'streamcloud'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
|
||||
|
||||
|
||||
# returns the steam url
|
||||
def url(url):
|
||||
m = _regex(url)
|
||||
if not m == None:
|
||||
|
||||
data = util.substr(util.request(url),'class=\"proform\"','</form>')
|
||||
#print data
|
||||
form_values = {}
|
||||
pattern = '<input.+?name="(?P<name>.*?)".+?value="(?P<value>.*?)"'
|
||||
for n in re.finditer(pattern,data,re.IGNORECASE | re.DOTALL ):
|
||||
form_values[n.group('name')] = n.group('value')
|
||||
#print form_values
|
||||
try:
|
||||
#time.sleep(10)
|
||||
resp = util.post(url,form_values)
|
||||
except:
|
||||
util.error('streamcloud: got http error fetching %s' % (url))
|
||||
return False
|
||||
|
||||
r = re.search('file: "(.+?)",', resp)
|
||||
if r:
|
||||
return [r.group(1)]
|
||||
|
||||
|
||||
def resolve(u):
|
||||
stream = url(u)
|
||||
if stream:
|
||||
return [{'name':__name__,'quality':'640p','url':stream[0],'surl':u}]
|
||||
|
||||
|
||||
def _regex(url):
|
||||
return re.search('http://(www.)?streamcloud.eu/[0-9A-Za-z]+',url,re.IGNORECASE | re.DOTALL)
|
||||
43
lib/server/streamintoresolver.py
Normal file
43
lib/server/streamintoresolver.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#/*
|
||||
# * Copyright (C) 2013 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
|
||||
__name__ = 'streamin.to'
|
||||
def supports(url):
|
||||
return not _regex(url) == None
|
||||
|
||||
# returns the steam url
|
||||
def resolve(url):
|
||||
m = _regex(url)
|
||||
if m:
|
||||
data = util.request('http://streamin.to/'+m.group('url'))
|
||||
n = re.search('config:{file:\'(.+?)\'',data,re.IGNORECASE | re.DOTALL)
|
||||
k = re.search('streamer: \"(.+?)\"',data,re.IGNORECASE | re.DOTALL)
|
||||
quality = '???'
|
||||
q = re.search('x(\d+)\.html',url)
|
||||
if q:
|
||||
quality = q.group(1)+'p'
|
||||
if n and k:
|
||||
url = '%s playpath=%s' % (k.group(1).strip(),n.group(1).strip())
|
||||
return [{'quality':quality,'url':url}]
|
||||
|
||||
def _regex(url):
|
||||
return re.search('streamin\.to/(?P<url>.+?)$',url,re.IGNORECASE | re.DOTALL)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user