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