This commit is contained in:
Pavel Suha
2025-04-25 16:30:00 +02:00
parent 9d43f8f476
commit ef3ef9f859
34 changed files with 2550 additions and 0 deletions

469
resources/lib/ulozto.py Normal file
View File

@@ -0,0 +1,469 @@
# -*- 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 sys
import urllib
import ulozto_search
query = "your_query"
file_type = "documents|videos|images|archives|audios" # optional
kwargs = {
"insecure": False, # disables SSL check, optional, default False
"includeApproximate": False # also return approximate results
}
#Python 2
try:
import cookielib
import urllib2
#import sys
reload(sys) # Reload does the trick!
sys.setdefaultencoding('UTF8')
#Python 3
except:
import http.cookiejar
cookielib = http.cookiejar
urllib2 = urllib.request
import re,random,util,os,traceback
import json
from base64 import b64decode
from provider import ContentProvider
from provider import ResolveException
from provider import cached
def loadurl(url, req, headers=None):
print (url)
req = urllib.parse.urlencode(req).encode('utf-8')
if headers:
req = urllib.request.Request(url, req, headers=headers)
else:
req = urllib.request.Request(url, req)
with urllib.request.urlopen(req) as f:
try:
if f.getcode()==200:
response = f.read()
return response
except:
return False
class UloztoContentProvider(ContentProvider):
def __init__(self,username=None,password=None,filter=None):
ContentProvider.__init__(self,'ulozto.cz','https://www.ulozto.cz/',username,password,filter)
self.search_type=''
self.cp = urllib2.HTTPCookieProcessor(cookielib.LWPCookieJar())
self.rh = UloztoHTTPRedirectHandler()
self.rh.throw = False
self.rh.location = None
self.init_urllib()
def init_urllib(self):
opener = urllib2.build_opener(self.cp,self.rh)
urllib2.install_opener(opener)
def capabilities(self):
return ['login','search','resolve','categories']
def categories(self):
result = []
if not self.login():
return result
data = util.request(self.base_url+'m/'+self.username)
fav = re.search('<li id=\"fmFavoritesFolder.+?href=\"(?P<url>[^\"]+)[^>]*>(?P<title>[^<]+)',data,re.IGNORECASE|re.DOTALL)
if fav:
item = self.dir_item()
item['url'] = '#fm#'+fav.group('url')
item['title'] = fav.group('title')
result.append(item)
myfiles = re.search('<a class=\"fmHomeFolder.+?href=\"(?P<url>[^\"]+)[^>]*>(?P<title>[^<]+)',data,re.IGNORECASE|re.DOTALL)
if myfiles:
item = self.dir_item()
item['url'] = '#fm#' + myfiles.group('url')
item['title'] = myfiles.group('title')
result.append(item)
return result
def search(self, keyword):
#https://ulozto.sk/hledej?type=videos&q=matrix
# brb = self.base_url+'hledej?'+self.search_type+'q='+urllib.parse.quote(keyword)
# self.info (brb)
# return self.list('https://ulozto.cz/hledej?'+self.search_type+'q='+urllib.parse.quote(keyword))
query = keyword
l = ulozto_search.search(query, 1, file_type, **kwargs)
return self.list( l )
def login(self):
return True
# if self.username and self.password and len(self.username)>0 and len(self.password)>0:
# self.info('Login user=%s, pass=*****' % self.username)
# self.rh.throw = False
# page = util.request(self.base_url+'login?key=logreg')#.decode('utf-8-sig')
# # headers = util.headers
# self.info(page)
# self.info(type(page))
# if page.find('href="/?do=web-logout') > 0:
# self.info('Already logged in')
# return True
# data = util.substr(page,'<li class=\"menu-username','</li')
# m = re.search('key=(?P<key>[^\"]+)\"',data,re.IGNORECASE | re.DOTALL)
# token = re.search('<input type=\"hidden\" name=\"_token_\".+?value=\"([^\"]+)"',page,re.IGNORECASE | re.DOTALL)
# self.info (token)
# #if m and token:
# if True:
# #login_url = self.base_url+'login?key='+m.group('key')+'&do=loginForm-submit'
# #login_url = self.base_url+'login?key=logreg'
# # urllib.parse.urljoin("https://ulozto.net/login", url),
# # post=inputs)
# inputs = {'username' : self.username,
# 'password' : self.password,
# 'fakeUsername':'',
# 'fakePassword':'',
# 'login': '',
# '_do': 'loginComponent-loginForm-form-submit',
# }
# data = util.post(self.base_url+'login&do=loginForm-submit', inputs)
# #data = util.post('https://uloz.to/login&do=loginForm-submit', inputs)
# #data = loadurl(self._url('login'), inputs, headers=headers)
# self.info (data)
# if data.find('href="/?do=web-logout') > 0:
# self.info('Login successfull')
# return True
# self.info('Login failed')
# return False
def list_folder(self,url):
self.login()
result = []
page = util.request(self._url(url))
page = util.substr(page,'<div id=\"fmItems','</ul')
for m in re.finditer('<div class=\"fmFolder(.+?)</em',page,re.IGNORECASE | re.DOTALL):
data = m.group(1)
item = self.dir_item()
item['url'] = '#fm#' + re.search('data-href=\"([^\"]+)',data).group(1)
item['title'] = re.search('data-name=\"([^\"]+)',data).group(1)
item['img'] = re.search('<img src=\"([^\"]+)',data).group(1)
result.append(item)
for m in re.finditer('<div class=\"fmFile(.+?)</em>',page,re.IGNORECASE | re.DOTALL):
data = m.group(1)
item = self.video_item()
item['url'] = re.search('data-href=\"([^\"]+)',data).group(1)
item['title'] = '%s.%s' % (re.search('data-name=\"([^\"]+)',data).group(1),re.search('data-ext=\"([^\"]+)',data).group(1))
item['img'] = re.search('<img src=\"([^\"]+)',data).group(1)
result.append(item)
return result
@cached(1)
def list(self, url):
self.info(url)
# if url.find('#fm#') == 0:
# return self.list_folder(url[5:])
# url = self._url(url)
# # page = util.request(url,headers={'X-Requested-With':'XMLHttpRequest','Referer':url,'Cookie':'uloz-to-id=1561277170;'})#.decode('string-escape')
# self.info(page)
# # script = util.substr(page,'var kn','</script>')
# keymap = None
# key = None
# # self.info(script)
# # k = re.search(r'({.+?})',script)
# # if k:
# # keymap = util.json.loads(k.group(1))
# # j = re.search(r'ad.push\(\[kn, kn\["([^"]+)', script)
# # if j:
# # key = j.group(1)
# # if not (j and k):
# # self.error('error parsing page - unable to locate keys')
# # return []
# keymap = json.loads(page)
# burl = b64decode('I2h0dHA6Ly9kZWNyLWNlY2gucmhjbG91ZC5jb20vZGVjcnlwdC8/a2V5PSVzJnZhbHVlPSVz')
# murl = b64decode('aHR0cDovL2RlY3ItY2VjaC5yaGNsb3VkLmNvbS9kZWNyeXB0Lw==')
# result = []
# req = {'seed':keymap[key], 'values':keymap}
# decr = json.loads(util.post_json(murl, req))
page = url
result = []
for p in page:
item = self.video_item()
item['title'] = p['name']
item['url'] = p['link']
item['length'] = p['length']
item['size'] = p['size']
# div_media = util.substr(body, 'div class="media"', '<div class="tools">')
# img_match = re.search(r'img src="([^"]+)', div_media)
# if img_match:
# item['img'] = "http:" + img_match.group(1)
# time_match = re.search(r'<span>Čas</span>(.+)', div_media)
# if time_match:
# item['length'] = time_match.group(1).strip()
# size_match = re.search(r'<span>Velikost</span>([^<]+)', div_media)
# if size_match:
# item['size'] = size_match.group(1).strip()
self._filter(result, item)
# for li in re.finditer('<div data-icon=\"(?P<key>[^\"]+)',page, re.IGNORECASE | re.DOTALL):
# body = urllib.unquote(b64decode(decr[li.group('key')]))
# div_name = util.substr(body, '<div class="name"', '</div>')
# title_url_match = re.search(r'<a href="(?P<url>[^"]+)" title="(?P<title>[^"]+)', div_name)
# if not title_url_match:
# continue
# item = self.video_item()
# item['title'] = title_url_match.group('title')
# item['url'] = title_url_match.group('url')
# div_media = util.substr(body, 'div class="media"', '<div class="tools">')
# img_match = re.search(r'img src="([^"]+)', div_media)
# if img_match:
# item['img'] = "http:" + img_match.group(1)
# time_match = re.search(r'<span>Čas</span>(.+)', div_media)
# if time_match:
# item['length'] = time_match.group(1).strip()
# size_match = re.search(r'<span>Velikost</span>([^<]+)', div_media)
# if size_match:
# item['size'] = size_match.group(1).strip()
# self._filter(result,item)
# page navigation
# data = util.substr(page,'<div class=\"paginator','</div')
# mnext = re.search('<a href=\"(?P<url>[^\"]+)\" class="next',data)
# ulozto_search.search(query, file_type, **kwargs)
# if mnext:
# item = self.dir_item()
# item['type'] = 'next'
# item['url'] = util.decode_html(mnext.group('url'))
# result.append(item)
# mnext = re.search('page=(?P<id>[\d]+)', url)
# # idx = url.find('page=')
# # if idx != -1:
# # st = url[idx + len('page='):]
# if mnext:
# s = int(mnext.group('id'))
# url = re.sub(mnext.group(), 'page={}'.format(s+1), url)
# item = self.dir_item()
# item['type'] = 'next'
# item['url'] = util.decode_html(url)
# result.append(item)
# return result
@cached(48)
def decr_url(self,url):
if url.startswith('#'):
ret = json.loads(util.request(url[1:]))
#if ret.has_key('result'):
if 'result' in ret.keys():
url = b64decode(ret['result'])
url = self._url(url)
return url
def resolve(self,item,captcha_cb=None):
item = item.copy()
url = item['url']
if url.startswith('http://www.ulozto.sk'):
url = self.base_url + url[20:]
url = self.decr_url(url)
url = self._url(url)
if url.startswith('#'):
util.error('[uloz.to] - url was not correctly decoded')
return
self.init_urllib()
self.login()
self.info('Resolving %s'% url)
# if not item.has_key('vip'):
if not 'vip' in item.keys():
item['vip'] = False
vip = item['vip']
if vip:
page = util.request(url)
else:
try:
request = urllib2.Request(url)
response = urllib2.urlopen(request)
page = response.read().decode('utf-8')
response.close()
except urllib2.HTTPError as e:
traceback.print_exc()
return
self.info( page )
if page.find('Stránka nenalezena!') > 0:
self.error('page with movie was not found on server')
return
if vip:
url = self._url('quickDownload/' + 'e2ZbEck4nD1E')
self.info( url )
# data = util.substr(page,'<h3>Neomezené stahování</h3>','</div')
# m = re.search('<a(.+?)href=\"(?P<url>[^\"#]+)\"',data,re.IGNORECASE | re.DOTALL)
if url:
try:
self.rh.throw = True
resp = urllib2.urlopen(urllib2.Request(url))
except RedirectionException:
# this is what we need, our redirect handler raises this
pass
except urllib2.HTTPError:
# this is not OK, something went wrong
traceback.print_exc()
self.error('Cannot resolve stream url, server did not redirected us')
self.info('POST url:'+post_url)
return
stream = self.rh.location
item['url'] = self._fix_stream_url(stream)
item['surl'] = url
return item
else:
# m = re.search('<form action="(?P<action>[^"]+)[^>]+class="jsFreeDownloadForm"', page)
m = re.search('data-href=\"(/download-dialog/free/download\?fileSlug=\S+)\"', page)
self.info (m)
if m:
self.info (m.group(1))
self.rh.throw = True
stream_url = self._get_file_url_anonymous(page,self._url(m.group(1)),response.headers,captcha_cb)
#stream_url = self._get_file_url_anonymous(page,self._url(m.group('action')),response.headers,captcha_cb)
if stream_url:
item['url'] = stream_url
# free ulozto allows seeking but doesn't allow multiple connections.
# kodi does this when seeking is possible so playback doesn't work.
# To prevent from use of multiple connections we set header special for kodi
# which disables seeking -> only one connection -> playback works, though we lose
# seeking possibility.
# more info - http://forum.kodi.tv/showthread.php?tid=236411
item['headers'] = {'seekable':'0'}
item['surl'] = url
return item
def _get_file_url_anonymous(self,page,post_url,headers,captcha_cb):
data = util.request(self._url('reloadXapca.php'))
capdata = json.loads(data)
captcha = capdata['image']
if not captcha.startswith('http'):
captcha = 'http:' + captcha
sound = capdata['sound']
if not sound.startswith('http'):
sound = 'http:' + sound
# ask callback to provide captcha code
self.info('Asking for captcha img %s' % captcha)
code = captcha_cb({'id':captcha,'img': captcha,'snd':sound})
if not code:
self.info('Captcha not provided, done')
return
self.info( page )
ts = re.search('<input type=\"hidden\" name=\"ts\".+?value=\"([^\"]+)"',page,re.IGNORECASE | re.DOTALL)
cid = re.search('<input type=\"hidden\" name=\"cid\".+?value=\"([^\"]+)"',page,re.IGNORECASE | re.DOTALL)
sign = re.search('<input type=\"hidden\" name=\"sign\".+?value=\"([^\"]+)"',page,re.IGNORECASE | re.DOTALL)
sign_a = re.search('<input type=\"hidden\" name=\"sign_a\".+?value=\"([^\"]+)"',page,re.IGNORECASE | re.DOTALL)
has = capdata['hash']
salt = capdata['salt']
timestamp = capdata['timestamp']
token = re.search('<input type=\"hidden\" name=\"_token_\".+?value=\"([^\"]+)"',page,re.IGNORECASE | re.DOTALL)
if not (sign and ts and cid and has and token):
util.error('[uloz.to] - unable to parse required params from page, plugin needs fix')
return
request = {
'captcha_type':'xapca',
'hash':has,
'salt':salt,
'timestamp':timestamp,
'ts':ts.group(1),
'cid':'',
'sign':sign.group(1),
'sign_a':sign_a.group(1),
'captcha_value':code,
'_do':'download-freeDownloadTab-freeDownloadForm-submit',
'_token_':token.group(1),
'adi':'f'
}
req = urllib2.Request(post_url, urllib.urlencode(request))
req.add_header('User-Agent',util.UA)
req.add_header('Referer',post_url)
req.add_header('Accept','application/json')
req.add_header('X-Requested-With','XMLHttpRequest')
sessid=[]
for cookie in re.finditer('(ULOSESSID=[^\;]+)',headers.get('Set-Cookie'),re.IGNORECASE | re.DOTALL):
sessid.append(cookie.group(1))
req.add_header('Cookie','nomobile=1; uloztoid='+cid.group(1)+'; uloztoid2='+cid.group(1)+'; '+sessid[-1])
util.info(req.headers)
util.info(request)
try:
resp = urllib2.urlopen(req)
page = resp.read()
headers = resp.headers
except urllib2.HTTPError:
# this is not OK, something went wrong
traceback.print_exc()
util.error('[uloz.to] cannot resolve stream url, server did not redirected us')
util.info('[uloz.to] POST url:'+post_url)
return
try:
result = json.loads(page)
except:
raise ResolveException('Unexpected error, addon needs fix')
if not 'status' in result.keys():
raise ResolveException('Unexpected error, addon needs fix')
if result['status'] == 'ok':
return self._fix_stream_url(result['url'])
elif result['status'] == 'error':
# the only known state is wrong captcha for now
util.error('Captcha validation failed, please try playing/downloading again')
util.error(result)
raise ResolveException('Captcha failed, try again')
def _fix_stream_url(self,stream):
index = stream.rfind('/')
if index > 0:
fn = stream[index:]
index2 = fn.find('?')
if index2 > 0:
fn = urllib.quote(fn[:index2])+fn[index2:]
else:
fn = urllib.quote(fn)
stream = stream[:index]+fn
return stream
def _regex(url):
return re.search('(#(.*)|ulozto\.cz|uloz\.to)',url,re.IGNORECASE | re.DOTALL)
class UloztoHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
def http_error_302(self, req, fp, code, msg, headers):
if self.throw:
self.location = headers.getheader('Location')
raise RedirectionException()
else:
return urllib2.HTTPRedirectHandler.http_error_302(self,req,fp,code,msg,headers)
class RedirectionException(Exception):
pass