diff --git a/.gitignore b/.gitignore index 59e2119..ca7919e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .idea/ -*.py[cod] -bin/ \ No newline at end of file +*.py[cod] \ No newline at end of file diff --git a/bin/__init__.py b/bin/__init__.py new file mode 100644 index 0000000..44fc62e --- /dev/null +++ b/bin/__init__.py @@ -0,0 +1,25 @@ +#-*- coding: utf-8 -*- +''' + Torrenter v2 plugin for XBMC/Kodi + Copyright (C) 2015 srg70, RussakHH, DiMartino +''' + +from functions import * +import xbmc, xbmcaddon +import sys +import os + +__settings__ = xbmcaddon.Addon(id='script.module.libtorrent') +__version__ = __settings__.getAddonInfo('version') +__plugin__ = __settings__.getAddonInfo('name') + " v." + __version__ + +lm=LibraryManager(dest_path, platform) +if not lm.check_exist(): + ok=lm.download() + xbmc.sleep(2000) + + +if __settings__.getSetting('plugin_name')!=__plugin__: + __settings__.setSetting('plugin_name', __plugin__) + lm.update() + diff --git a/bin/android_arm/torrent2http b/bin/android_arm/torrent2http new file mode 100644 index 0000000..f4785ac Binary files /dev/null and b/bin/android_arm/torrent2http differ diff --git a/bin/darwin_x64/torrent2http b/bin/darwin_x64/torrent2http new file mode 100644 index 0000000..02ab529 Binary files /dev/null and b/bin/darwin_x64/torrent2http differ diff --git a/bin/functions.py b/bin/functions.py new file mode 100644 index 0000000..161b5b3 --- /dev/null +++ b/bin/functions.py @@ -0,0 +1,66 @@ +#import sys +import os +import xbmc, xbmcgui, xbmcvfs, xbmcaddon +from net import HTTP + +__libbaseurl__ = "https://github.com/DiMartinoXBMC/script.module.torrent2http/tree/master/bin" +__settings__ = xbmcaddon.Addon(id='script.module.torrent2http') +__version__ = __settings__.getAddonInfo('version') +__plugin__ = __settings__.getAddonInfo('name') + " v." + __version__ + +def get_libname(platform): + return ["torrent2http" + (".exe" if 'windows' in platform else "")] + +def log(msg): + try: + xbmc.log("### [%s]: %s" % (__plugin__,msg,), level=xbmc.LOGNOTICE ) + except UnicodeEncodeError: + xbmc.log("### [%s]: %s" % (__plugin__,msg.encode("utf-8", "ignore"),), level=xbmc.LOGNOTICE ) + except: + xbmc.log("### [%s]: %s" % (__plugin__,'ERROR LOG',), level=xbmc.LOGNOTICE ) + +def getSettingAsBool(setting): + return __settings__.getSetting(setting).lower() == "true" + +class LibraryManager(): + def __init__(self, dest_path, platform): + self.dest_path = dest_path + self.platform = platform + self.root=os.path.dirname(__file__) + + def check_update(self): + need_update=False + if __settings__.getSetting('plugin_name')!=__plugin__: + __settings__.setSetting('plugin_name', __plugin__) + for libname in get_libname(self.platform): + self.libpath = os.path.join(self.dest_path, libname) + self.sizepath=os.path.join(self.root, self.platform, libname+'.size.txt') + size=str(os.path.getsize(self.libpath)) + size_old=open( self.sizepath, "r" ).read() + if size_old!=size: + need_update=True + return need_update + + def update(self): + if self.check_update(): + for libname in get_libname(self.platform): + self.libpath = os.path.join(self.dest_path, libname) + xbmcvfs.delete(self.libpath) + self.download() + + def download(self): + xbmcvfs.mkdirs(self.dest_path) + for libname in get_libname(self.platform): + dest = os.path.join(self.dest_path, libname) + log("try to fetch %s" % libname) + url = "%s/%s/%s.zip" % (__libbaseurl__, self.platform, libname) + try: + self.http = HTTP() + self.http.fetch(url, download=dest + ".zip", progress=True) + log("%s -> %s" % (url, dest)) + xbmc.executebuiltin('XBMC.Extract("%s.zip","%s")' % (dest, self.dest_path), True) + xbmcvfs.delete(dest + ".zip") + except: + text = 'Failed download %s!' % libname + xbmc.executebuiltin("XBMC.Notification(%s,%s,%s)" % (__plugin__,text,750)) + return True diff --git a/bin/linux_arm/torrent2http b/bin/linux_arm/torrent2http new file mode 100644 index 0000000..6e7dafb Binary files /dev/null and b/bin/linux_arm/torrent2http differ diff --git a/bin/linux_x64/torrent2http b/bin/linux_x64/torrent2http new file mode 100644 index 0000000..3221768 Binary files /dev/null and b/bin/linux_x64/torrent2http differ diff --git a/bin/linux_x86/torrent2http b/bin/linux_x86/torrent2http new file mode 100644 index 0000000..5cfff30 Binary files /dev/null and b/bin/linux_x86/torrent2http differ diff --git a/bin/net.py b/bin/net.py new file mode 100644 index 0000000..0836c4b --- /dev/null +++ b/bin/net.py @@ -0,0 +1,297 @@ +# -*- coding: utf-8 -*- + +import os +import time +import re +import urllib +import urllib2 +import cookielib +import base64 +import mimetools +import itertools + +import xbmc +import xbmcgui +import xbmcvfs + +RE = { + 'content-disposition': re.compile('attachment;\sfilename="*([^"\s]+)"|\s') +} + +# ################################ +# +# HTTP +# +# ################################ + +class HTTP: + def __init__(self): + self._dirname = xbmc.translatePath('special://temp') + for subdir in ('xbmcup', 'script.module.libtorrent'): + self._dirname = os.path.join(self._dirname, subdir) + if not xbmcvfs.exists(self._dirname): + xbmcvfs.mkdir(self._dirname) + + def fetch(self, request, **kwargs): + self.con, self.fd, self.progress, self.cookies, self.request = None, None, None, None, request + + if not isinstance(self.request, HTTPRequest): + self.request = HTTPRequest(url=self.request, **kwargs) + + self.response = HTTPResponse(self.request) + + xbmc.log('XBMCup: HTTP: request: ' + str(self.request), xbmc.LOGDEBUG) + + try: + self._opener() + self._fetch() + except Exception, e: + xbmc.log('XBMCup: HTTP: ' + str(e), xbmc.LOGERROR) + if isinstance(e, urllib2.HTTPError): + self.response.code = e.code + self.response.error = e + else: + self.response.code = 200 + + if self.fd: + self.fd.close() + self.fd = None + + if self.con: + self.con.close() + self.con = None + + if self.progress: + self.progress.close() + self.progress = None + + self.response.time = time.time() - self.response.time + + xbmc.log('XBMCup: HTTP: response: ' + str(self.response), xbmc.LOGDEBUG) + + return self.response + + def _opener(self): + + build = [urllib2.HTTPHandler()] + + if self.request.redirect: + build.append(urllib2.HTTPRedirectHandler()) + + if self.request.proxy_host and self.request.proxy_port: + build.append(urllib2.ProxyHandler( + {self.request.proxy_protocol: self.request.proxy_host + ':' + str(self.request.proxy_port)})) + + if self.request.proxy_username: + proxy_auth_handler = urllib2.ProxyBasicAuthHandler() + proxy_auth_handler.add_password('realm', 'uri', self.request.proxy_username, + self.request.proxy_password) + build.append(proxy_auth_handler) + + if self.request.cookies: + self.request.cookies = os.path.join(self._dirname, self.request.cookies) + self.cookies = cookielib.MozillaCookieJar() + if os.path.isfile(self.request.cookies): + self.cookies.load(self.request.cookies) + build.append(urllib2.HTTPCookieProcessor(self.cookies)) + + urllib2.install_opener(urllib2.build_opener(*build)) + + def _fetch(self): + params = {} if self.request.params is None else self.request.params + + if self.request.upload: + boundary, upload = self._upload(self.request.upload, params) + req = urllib2.Request(self.request.url) + req.add_data(upload) + else: + + if self.request.method == 'POST': + if isinstance(params, dict) or isinstance(params, list): + params = urllib.urlencode(params) + req = urllib2.Request(self.request.url, params) + else: + req = urllib2.Request(self.request.url) + + for key, value in self.request.headers.iteritems(): + req.add_header(key, value) + + if self.request.upload: + req.add_header('Content-type', 'multipart/form-data; boundary=%s' % boundary) + req.add_header('Content-length', len(upload)) + + if self.request.auth_username and self.request.auth_password: + req.add_header('Authorization', 'Basic %s' % base64.encodestring( + ':'.join([self.request.auth_username, self.request.auth_password])).strip()) + + self.con = urllib2.urlopen(req, timeout=self.request.timeout) + # self.con = urllib2.urlopen(req) + self.response.headers = self._headers(self.con.info()) + + if self.request.download: + self._download() + else: + self.response.body = self.con.read() + + if self.request.cookies: + self.cookies.save(self.request.cookies) + + def _download(self): + fd = open(self.request.download, 'wb') + if self.request.progress: + self.progress = xbmcgui.DialogProgress() + self.progress.create(u'Download') + + bs = 1024 * 8 + size = -1 + read = 0 + name = None + + if self.request.progress: + if 'content-length' in self.response.headers: + size = int(self.response.headers['content-length']) + if 'content-disposition' in self.response.headers: + r = RE['content-disposition'].search(self.response.headers['content-disposition']) + if r: + name = urllib.unquote(r.group(1)) + + while 1: + buf = self.con.read(bs) + if buf == '': + break + read += len(buf) + fd.write(buf) + + if self.request.progress: + self.progress.update(*self._progress(read, size, name)) + + self.response.filename = self.request.download + + def _upload(self, upload, params): + res = [] + boundary = mimetools.choose_boundary() + part_boundary = '--' + boundary + + if params: + for name, value in params.iteritems(): + res.append([part_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', value]) + + if isinstance(upload, dict): + upload = [upload] + + for obj in upload: + name = obj.get('name') + filename = obj.get('filename', 'default') + content_type = obj.get('content-type') + try: + body = obj['body'].read() + except AttributeError: + body = obj['body'] + + if content_type: + res.append([part_boundary, + 'Content-Disposition: file; name="%s"; filename="%s"' % (name, urllib.quote(filename)), + 'Content-Type: %s' % content_type, '', body]) + else: + res.append([part_boundary, + 'Content-Disposition: file; name="%s"; filename="%s"' % (name, urllib.quote(filename)), '', + body]) + + result = list(itertools.chain(*res)) + result.append('--' + boundary + '--') + result.append('') + return boundary, '\r\n'.join(result) + + def _headers(self, raw): + headers = {} + for line in raw.headers: + pair = line.split(':', 1) + if len(pair) == 2: + tag = pair[0].lower().strip() + value = pair[1].strip() + if tag and value: + headers[tag] = value + return headers + + def _progress(self, read, size, name): + res = [] + if size < 0: + res.append(1) + else: + res.append(int(float(read) / (float(size) / 100.0))) + if name: + res.append(u'File: ' + name) + if size != -1: + res.append(u'Size: ' + self._human(size)) + res.append(u'Load: ' + self._human(read)) + return res + + def _human(self, size): + human = None + for h, f in (('KB', 1024), ('MB', 1024 * 1024), ('GB', 1024 * 1024 * 1024), ('TB', 1024 * 1024 * 1024 * 1024)): + if size / f > 0: + human = h + factor = f + else: + break + if human is None: + return (u'%10.1f %s' % (size, u'byte')).replace(u'.0', u'') + else: + return u'%10.2f %s' % (float(size) / float(factor), human) + + +class HTTPRequest: + def __init__(self, url, method='GET', headers=None, cookies=None, params=None, upload=None, download=None, + progress=False, auth_username=None, auth_password=None, proxy_protocol='http', proxy_host=None, + proxy_port=None, proxy_username=None, proxy_password='', timeout=20.0, redirect=True, gzip=False): + if headers is None: + headers = {} + + self.url = url + self.method = method + self.headers = headers + + self.cookies = cookies + + self.params = params + + self.upload = upload + self.download = download + self.progress = progress + + self.auth_username = auth_username + self.auth_password = auth_password + + self.proxy_protocol = proxy_protocol + self.proxy_host = proxy_host + self.proxy_port = proxy_port + self.proxy_username = proxy_username + self.proxy_password = proxy_password + + self.timeout = timeout + + self.redirect = redirect + + self.gzip = gzip + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, ','.join('%s=%r' % i for i in self.__dict__.iteritems())) + + +class HTTPResponse: + def __init__(self, request): + self.request = request + self.code = None + self.headers = {} + self.error = None + self.body = None + self.filename = None + self.time = time.time() + + def __repr__(self): + args = ','.join('%s=%r' % i for i in self.__dict__.iteritems() if i[0] != 'body') + if self.body: + args += ',body=' + else: + args += ',body=None' + return '%s(%s)' % (self.__class__.__name__, args) diff --git a/bin/public.py b/bin/public.py new file mode 100644 index 0000000..753ec4c --- /dev/null +++ b/bin/public.py @@ -0,0 +1,63 @@ +#-*- coding: utf-8 -*- +''' + Torrenter v2 plugin for XBMC/Kodi + Copyright (C) 2015 srg70, RussakHH, DiMartino +''' + +import os + +def get_libname(platform): + return ["torrent2http" + (".exe" if 'windows' in platform else "")] + +class Public: + def __init__( self ): + self.platforms=[] + self.root=os.path.dirname(__file__) + for dir in os.listdir(self.root): + if os.path.isdir(os.path.join(self.root,dir)): + self.platforms.append(dir) + self._generate_size_file() + + def _generate_size_file( self ): + for platform in self.platforms: + for libname in get_libname(platform): + self.libname=libname + self.platform=platform + self.libdir = os.path.join(self.root, self.platform) + self.libpath = os.path.join(self.libdir, self.libname) + self.sizepath=self.libpath+'.size.txt' + self.zipname=self.libname+'.zip' + zippath=os.path.join(self.libdir, self.zipname) + system=platform+'/' + if os.path.exists(self.libpath): + if not os.path.exists(self.sizepath): + print system+self.libname+' NO SIZE' + self._makezip() + elif not os.path.exists(zippath): + print system+self.libname+' NO ZIP' + self._makezip() + else: + size=str(os.path.getsize(self.libpath)) + size_old=open( self.sizepath, "r" ).read() + if size_old!=size: + print system+self.libname+' NOT EQUAL' + self._makezip() + else: + print system+self.libname+' NO ACTION' + else: + print system+self.libname+' NO LIB' + + def _makezip(self): + open( self.sizepath, "w" ).write( str(os.path.getsize(self.libpath)) ) + os.chdir(self.libdir) + os.system('del %s' % (self.zipname)) + os.system('"C:\\Program Files\\7-Zip\\7z.exe" a %s.zip %s' % + (self.libname, self.libname)) + os.chdir(self.root) + #os.system('"C:\\Program Files\\7-Zip\\7z.exe" a %s.zip %s' % + # (self.platform['system']+os.sep+self.libname, self.platform['system']+os.sep+self.libname)) + +if ( __name__ == "__main__" ): + # start + #TODO: publicate + Public() \ No newline at end of file diff --git a/bin/windows_x86/torrent2http.exe b/bin/windows_x86/torrent2http.exe new file mode 100644 index 0000000..4f20cd8 Binary files /dev/null and b/bin/windows_x86/torrent2http.exe differ diff --git a/lib/torrent2http/engine.py b/lib/torrent2http/engine.py index ba542cb..450cb95 100644 --- a/lib/torrent2http/engine.py +++ b/lib/torrent2http/engine.py @@ -9,6 +9,7 @@ import time import urllib2 import httplib from os.path import dirname +from bin.functions import * import logpipe import mimetypes @@ -56,9 +57,14 @@ class Engine: binary = "torrent2http" + (".exe" if self.platform.system == 'windows' else "") binary_dir = os.path.join(binaries_path, "%s_%s" % (self.platform.system, self.platform.arch)) binary_path = os.path.join(binary_dir, binary) + lm=LibraryManager(binary_dir, "%s_%s" % (self.platform.system, self.platform.arch)) if not os.path.isfile(binary_path): - raise Error("Can't find torrent2http binary for %s" % self.platform, - Error.UNKNOWN_PLATFORM, platform=str(self.platform)) + success=lm.download() + if not success: + raise Error("Can't find torrent2http or download binary for %s" % self.platform, + Error.UNKNOWN_PLATFORM, platform=str(self.platform)) + else: + lm.update() if not self._ensure_binary_executable(binary_path): if self.platform.system == "android":