1340 lines
45 KiB
Python
1340 lines
45 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import re
|
|
import urllib
|
|
import urllib2
|
|
import cookielib
|
|
import base64
|
|
import mimetools
|
|
import json
|
|
import itertools
|
|
from StringIO import StringIO
|
|
import gzip
|
|
|
|
import xbmc
|
|
import xbmcgui
|
|
import xbmcvfs
|
|
|
|
os.sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
import dopal.main
|
|
|
|
__plugin__ = sys.modules["__main__"].__plugin__
|
|
__settings__ = sys.modules["__main__"].__settings__
|
|
ROOT = sys.modules["__main__"].__root__ # .decode('utf-8').encode(sys.getfilesystemencoding())
|
|
userStorageDirectory = __settings__.getSetting("storage")
|
|
USERAGENT = "Mozilla/5.0 (Windows NT 6.1; rv:5.0) Gecko/20100101 Firefox/5.0"
|
|
URL = 'http://torrenter.host.org'
|
|
torrentFilesDirectory = 'torrents'
|
|
__addonpath__ = __settings__.getAddonInfo('path')
|
|
icon = __addonpath__ + '/icon.png'
|
|
|
|
RE = {
|
|
'content-disposition': re.compile('attachment;\sfilename="*([^"\s]+)"|\s')
|
|
}
|
|
|
|
# ################################
|
|
#
|
|
# HTTP
|
|
#
|
|
# ################################
|
|
|
|
class HTTP:
|
|
def __init__(self):
|
|
self._dirname = xbmc.translatePath('special://temp') # .decode('utf-8').encode('cp1251')
|
|
for subdir in ('xbmcup', sys.argv[0].replace('plugin://', '').replace('/', '')):
|
|
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)
|
|
|
|
# Debug('XBMCup: HTTP: request: ' + str(self.request))
|
|
|
|
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:
|
|
if not self.response.headers.get('content-encoding') == 'gzip':
|
|
self.response.body = self.con.read()
|
|
else:
|
|
buf = StringIO(self.con.read())
|
|
f = gzip.GzipFile(fileobj=buf)
|
|
self.response.body = f.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=<data>'
|
|
else:
|
|
args += ',body=None'
|
|
return '%s(%s)' % (self.__class__.__name__, args)
|
|
|
|
|
|
class UTorrent:
|
|
def config(self, login, password, host, port, url=None):
|
|
self.login = login
|
|
self.password = password
|
|
|
|
self.url = 'http://' + host
|
|
if port:
|
|
self.url += ':' + str(port)
|
|
self.url += '/gui/'
|
|
|
|
self.http = HTTP()
|
|
|
|
self.re = {
|
|
'cookie': re.compile('GUID=([^;]+);'),
|
|
'token': re.compile("<div[^>]+id='token'[^>]*>([^<]+)</div>")
|
|
}
|
|
|
|
def listdirs(self):
|
|
obj = self.action('action=list-dirs')
|
|
if not obj:
|
|
return False
|
|
items = []
|
|
clean = []
|
|
for r in obj.get('download-dirs', []):
|
|
available = int(r['available'])
|
|
if available > 1024:
|
|
memory = '[%s GB]' % str(available / 1024)
|
|
else:
|
|
memory = '[%s MB]' % str(available)
|
|
items.append(r['path'] + ' ' + memory)
|
|
path = r['path']
|
|
if path[len(path) - 1:] != '\\': path += '\\'
|
|
clean.append(path)
|
|
return items, clean
|
|
|
|
def list(self):
|
|
obj = self.action('list=1')
|
|
if not obj:
|
|
return False
|
|
|
|
res = []
|
|
for r in obj.get('torrents', []):
|
|
res.append({
|
|
'id': r[0],
|
|
'status': self.get_status(r[1], r[4] / 10),
|
|
'name': r[2],
|
|
'size': r[3],
|
|
'progress': r[4] / 10,
|
|
'download': r[5],
|
|
'upload': r[6],
|
|
'ratio': float(r[7]) / 1000,
|
|
'upspeed': r[8],
|
|
'downspeed': r[9],
|
|
'eta': r[10],
|
|
'peer': r[12] + r[14],
|
|
'leach': r[12],
|
|
'seed': r[14],
|
|
'add': r[23],
|
|
'finish': r[24],
|
|
'dir': r[26]
|
|
})
|
|
|
|
return res
|
|
|
|
def listfiles(self, id):
|
|
obj = self.action('action=getfiles&hash=' + id)
|
|
if not obj:
|
|
return None
|
|
res = []
|
|
i = -1
|
|
|
|
for x in obj['files'][1]:
|
|
i += 1
|
|
if x[1] >= 1024 * 1024 * 1024:
|
|
size = str(x[1] / (1024 * 1024 * 1024)) + 'GB'
|
|
elif x[1] >= 1024 * 1024:
|
|
size = str(x[1] / (1024 * 1024)) + 'MB'
|
|
elif x[1] >= 1024:
|
|
size = str(x[1] / 1024) + 'KB'
|
|
else:
|
|
size = str(x[1]) + 'B'
|
|
res.append((x[0], (int(x[2] * 100 / x[1])), i, size))
|
|
return res
|
|
|
|
def dirid(self, dirname):
|
|
if __settings__.getSetting("torrent_save") == '0':
|
|
dirid = self.listdirs()[1].index(dirname)
|
|
else:
|
|
dirname = __settings__.getSetting("torrent_dir")
|
|
clean = self.listdirs()[1]
|
|
try:
|
|
dirid = clean.index(dirname)
|
|
except:
|
|
dirid = 0
|
|
return dirid
|
|
|
|
def add(self, torrent, dirname):
|
|
dirid = self.dirid(dirname)
|
|
res = self.action('action=add-file&download_dir=' + str(dirid),
|
|
{'name': 'torrent_file', 'download_dir': str(dirid),
|
|
'content-type': 'application/x-bittorrent', 'body': torrent})
|
|
return True if res else None
|
|
|
|
def add_url(self, torrent, dirname):
|
|
dirid = self.dirid(dirname)
|
|
res = self.action('action=add-url&download_dir=' + str(dirid) + '&s=' + urllib.quote(torrent))
|
|
return True if res else None
|
|
|
|
def setprio(self, id, ind):
|
|
obj = self.action('action=getfiles&hash=' + id)
|
|
|
|
if not obj or ind == None:
|
|
return None
|
|
|
|
i = -1
|
|
for x in obj['files'][1]:
|
|
i += 1
|
|
if x[3] == 2: self.setprio_simple(id, '0', i)
|
|
|
|
res = self.setprio_simple(id, '3', ind)
|
|
|
|
return True if res else None
|
|
|
|
def setprio_simple(self, id, prio, ind):
|
|
obj = self.action('action=setprio&hash=%s&p=%s&f=%s' % (id, prio, ind))
|
|
|
|
if not obj or ind == None:
|
|
return None
|
|
|
|
return True if obj else None
|
|
|
|
def setprio_simple_multi(self, menu):
|
|
for hash, action, ind in menu:
|
|
self.setprio_simple(hash, action, ind)
|
|
|
|
def delete(self, id):
|
|
pass
|
|
|
|
def action(self, uri, upload=None):
|
|
cookie, token = self.get_token()
|
|
if not cookie:
|
|
return None
|
|
|
|
req = HTTPRequest(self.url + '?' + uri + '&token=' + token, headers={'Cookie': cookie},
|
|
auth_username=self.login, auth_password=self.password)
|
|
if upload:
|
|
req.upload = upload
|
|
|
|
response = self.http.fetch(req)
|
|
if response.error:
|
|
return None
|
|
else:
|
|
try:
|
|
obj = json.loads(response.body)
|
|
except:
|
|
return None
|
|
else:
|
|
return obj
|
|
|
|
def action_simple(self, action, id):
|
|
obj = self.action('action=%s&hash=%s' % (action, id))
|
|
return True if obj else None
|
|
|
|
def get_token(self):
|
|
response = self.http.fetch(self.url + 'token.html', auth_username=self.login, auth_password=self.password)
|
|
if response.error:
|
|
return None, None
|
|
|
|
r = self.re['cookie'].search(response.headers.get('set-cookie', ''))
|
|
if r:
|
|
cookie = r.group(1).strip()
|
|
r = self.re['token'].search(response.body)
|
|
if r:
|
|
token = r.group(1).strip()
|
|
if cookie and token:
|
|
return 'GUID=' + cookie, token
|
|
|
|
return None, None
|
|
|
|
def get_status(self, status, progress):
|
|
mapping = {
|
|
'error': 'stopped',
|
|
'paused': 'stopped',
|
|
'forcepaused': 'stopped',
|
|
'stopped': 'stopped',
|
|
'notloaded': 'check_pending',
|
|
'checked': 'checking',
|
|
'queued': 'download_pending',
|
|
'downloading': 'downloading',
|
|
'forcedownloading': 'downloading',
|
|
'finished': 'seed_pending',
|
|
'queuedseed': 'seed_pending',
|
|
'seeding': 'seeding',
|
|
'forceseeding': 'seeding'
|
|
}
|
|
return mapping[self.get_status_raw(status, progress)]
|
|
|
|
def get_status_raw(self, status, progress):
|
|
"""
|
|
Return status: notloaded, error, checked,
|
|
paused, forcepaused,
|
|
queued,
|
|
downloading,
|
|
finished, forcedownloading
|
|
queuedseed, seeding, forceseeding
|
|
"""
|
|
|
|
started = bool(status & 1)
|
|
checking = bool(status & 2)
|
|
start_after_check = bool(status & 4)
|
|
checked = bool(status & 8)
|
|
error = bool(status & 16)
|
|
paused = bool(status & 32)
|
|
queued = bool(status & 64)
|
|
loaded = bool(status & 128)
|
|
|
|
if not loaded:
|
|
return 'notloaded'
|
|
|
|
if error:
|
|
return 'error'
|
|
|
|
if checking:
|
|
return 'checked'
|
|
|
|
if paused:
|
|
if queued:
|
|
return 'paused'
|
|
else:
|
|
return 'forcepaused'
|
|
|
|
if progress == 100:
|
|
|
|
if queued:
|
|
if started:
|
|
return 'seeding'
|
|
else:
|
|
return 'queuedseed'
|
|
|
|
else:
|
|
if started:
|
|
return 'forceseeding'
|
|
else:
|
|
return 'finished'
|
|
else:
|
|
|
|
if queued:
|
|
if started:
|
|
return 'downloading'
|
|
else:
|
|
return 'queued'
|
|
|
|
else:
|
|
if started:
|
|
return 'forcedownloading'
|
|
|
|
return 'stopped'
|
|
|
|
|
|
class Transmission:
|
|
def config(self, login, password, host, port, url):
|
|
self.login = login
|
|
self.password = password
|
|
|
|
self.url = 'http://' + host
|
|
if port:
|
|
self.url += ':' + str(port)
|
|
|
|
if url[0] != '/':
|
|
url = '/' + url
|
|
if url[-1] != '/':
|
|
url += '/'
|
|
|
|
self.url += url
|
|
|
|
self.http = HTTP()
|
|
|
|
self.token = '0'
|
|
|
|
def list(self):
|
|
obj = self.action({'method': 'torrent-get', 'arguments': {
|
|
'fields': ['id', 'status', 'name', 'totalSize', 'sizeWhenDone', 'leftUntilDone', 'downloadedEver',
|
|
'uploadedEver', 'uploadRatio', 'rateUpload', 'rateDownload', 'eta', 'peersConnected',
|
|
'peersFrom',
|
|
'addedDate', 'doneDate', 'downloadDir', 'fileStats', 'peersConnected', 'peersGettingFromUs',
|
|
'peersSendingToUs']}})
|
|
if obj is None:
|
|
return False
|
|
|
|
res = []
|
|
for r in obj['arguments'].get('torrents', []):
|
|
if len(r['fileStats']) > 1:
|
|
res.append({
|
|
'id': str(r['id']),
|
|
'status': self.get_status(r['status']),
|
|
'name': r['name'],
|
|
'size': r['totalSize'],
|
|
'progress': 0 if not r['sizeWhenDone'] else int(
|
|
100.0 * float(r['sizeWhenDone'] - r['leftUntilDone']) / float(r['sizeWhenDone'])),
|
|
'download': r['downloadedEver'],
|
|
'upload': r['uploadedEver'],
|
|
'upspeed': r['rateUpload'],
|
|
'downspeed': r['rateDownload'],
|
|
'ratio': float(r['uploadRatio']),
|
|
'eta': r['eta'],
|
|
'peer': r['peersConnected'],
|
|
'seed': r['peersSendingToUs'],
|
|
'leech': r['peersGettingFromUs'],
|
|
'add': r['addedDate'],
|
|
'finish': r['doneDate'],
|
|
'dir': os.path.join(r['downloadDir'], r['name'])
|
|
})
|
|
else:
|
|
res.append({
|
|
'id': str(r['id']),
|
|
'status': self.get_status(r['status']),
|
|
'name': r['name'],
|
|
'size': r['totalSize'],
|
|
'progress': 0 if not r['sizeWhenDone'] else int(
|
|
100.0 * float(r['sizeWhenDone'] - r['leftUntilDone']) / float(r['sizeWhenDone'])),
|
|
'download': r['downloadedEver'],
|
|
'upload': r['uploadedEver'],
|
|
'upspeed': r['rateUpload'],
|
|
'downspeed': r['rateDownload'],
|
|
'ratio': float(r['uploadRatio']),
|
|
'eta': r['eta'],
|
|
'peer': r['peersConnected'],
|
|
'seed': r['peersSendingToUs'],
|
|
'leech': r['peersGettingFromUs'],
|
|
'add': r['addedDate'],
|
|
'finish': r['doneDate'],
|
|
'dir': r['downloadDir']
|
|
})
|
|
|
|
return res
|
|
|
|
def listdirs(self):
|
|
obj = self.action({'method': 'session-get'})
|
|
if obj is None:
|
|
return False
|
|
|
|
res = [obj['arguments'].get('download-dir')]
|
|
# Debug('[Transmission][listdirs]: %s' % (str(res)))
|
|
return res, res
|
|
|
|
def listfiles(self, id):
|
|
obj = self.action({"method": "torrent-get", "arguments": {
|
|
"fields": ["id", "activityDate", "corruptEver", "desiredAvailable", "downloadedEver", "fileStats",
|
|
"haveUnchecked", "haveValid", "peers", "startDate", "trackerStats", "comment", "creator",
|
|
"dateCreated", "files", "hashString", "isPrivate", "pieceCount", "pieceSize"],
|
|
"ids": [int(id)]}})['arguments']['torrents'][0]
|
|
if obj is None:
|
|
return None
|
|
|
|
res = []
|
|
i = -1
|
|
|
|
lenf = len(obj['files'])
|
|
for x in obj['files']:
|
|
i += 1
|
|
if x['length'] >= 1024 * 1024 * 1024:
|
|
size = str(x['length'] / (1024 * 1024 * 1024)) + 'GB'
|
|
elif x['length'] >= 1024 * 1024:
|
|
size = str(x['length'] / (1024 * 1024)) + 'MB'
|
|
elif x['length'] >= 1024:
|
|
size = str(x['length'] / 1024) + 'KB'
|
|
else:
|
|
size = str(x['length']) + 'B'
|
|
if lenf > 1:
|
|
x['name'] = x['name'].strip('/\\').replace('\\', '/')
|
|
x['name'] = x['name'].replace(x['name'].split('/')[0] + '/', '')
|
|
res.append([x['name'], (int(x['bytesCompleted'] * 100 / x['length'])), i, size])
|
|
return res
|
|
|
|
def add(self, torrent, dirname):
|
|
if self.action({'method': 'torrent-add',
|
|
'arguments': {'download-dir': dirname, 'metainfo': base64.b64encode(torrent)}}) is None:
|
|
return None
|
|
return True
|
|
|
|
def add_url(self, torrent, dirname):
|
|
if self.action({'method': 'torrent-add', 'arguments': {'download-dir': dirname, 'filename': torrent}}) is None:
|
|
return None
|
|
return True
|
|
|
|
def delete(self, id):
|
|
pass
|
|
|
|
def setprio(self, id, ind):
|
|
obj = self.action({"method": "torrent-get", "arguments": {"fields": ["id", "fileStats", "files"],
|
|
"ids": [int(id)]}})['arguments']['torrents'][0]
|
|
if not obj or ind == None:
|
|
return None
|
|
|
|
inds = []
|
|
i = -1
|
|
|
|
for x in obj['fileStats']:
|
|
i += 1
|
|
if x['wanted'] == True and x['priority'] == 0:
|
|
inds.append(i)
|
|
|
|
if len(inds) > 1: self.action(
|
|
{"method": "torrent-set", "arguments": {"ids": [int(id)], "priority-high": inds, "files-unwanted": inds}})
|
|
|
|
res = self.setprio_simple(id, '3', ind)
|
|
|
|
# self.action_simple('start',id)
|
|
|
|
return True if res else None
|
|
|
|
def setprio_simple(self, id, prio, ind):
|
|
if ind == None:
|
|
return None
|
|
|
|
res = None
|
|
inds = [int(ind)]
|
|
|
|
if prio == '3':
|
|
res = self.action(
|
|
{"method": "torrent-set", "arguments": {"ids": [int(id)], "priority-high": inds, "files-wanted": inds}})
|
|
elif prio == '0':
|
|
res = self.action({"method": "torrent-set",
|
|
"arguments": {"ids": [int(id)], "priority-high": inds, "files-unwanted": inds}})
|
|
|
|
return True if res else None
|
|
|
|
def setprio_simple_multi(self, menu):
|
|
id = menu[0][0]
|
|
prio = menu[0][1]
|
|
res = None
|
|
|
|
inds = []
|
|
for hash, action, ind in menu:
|
|
inds.append(int(ind))
|
|
|
|
if prio == '3':
|
|
res = self.action(
|
|
{"method": "torrent-set", "arguments": {"ids": [int(id)], "priority-high": inds, "files-wanted": inds}})
|
|
elif prio == '0':
|
|
res = self.action({"method": "torrent-set",
|
|
"arguments": {"ids": [int(id)], "priority-high": inds, "files-unwanted": inds}})
|
|
return True if res else None
|
|
|
|
def action(self, request):
|
|
try:
|
|
jsobj = json.dumps(request)
|
|
except:
|
|
return None
|
|
else:
|
|
|
|
while True:
|
|
# пробуем сделать запрос
|
|
if self.login:
|
|
response = self.http.fetch(self.url + 'rpc/', method='POST', params=jsobj,
|
|
headers={'X-Transmission-Session-Id': self.token,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Content-Type': 'charset=UTF-8'}, auth_username=self.login,
|
|
auth_password=self.password)
|
|
else:
|
|
response = self.http.fetch(self.url + 'rpc/', method='POST', params=jsobj,
|
|
headers={'X-Transmission-Session-Id': self.token,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Content-Type': 'charset=UTF-8'})
|
|
|
|
if response.error:
|
|
|
|
# требуется авторизация?
|
|
if response.code == 401:
|
|
if not self.get_auth():
|
|
return None
|
|
|
|
# требуется новый токен?
|
|
elif response.code == 409:
|
|
if not self.get_token(response.error):
|
|
return None
|
|
|
|
else:
|
|
return None
|
|
|
|
else:
|
|
try:
|
|
obj = json.loads(response.body)
|
|
except:
|
|
return None
|
|
else:
|
|
return obj
|
|
|
|
def action_simple(self, action, id):
|
|
actions = {'start': {"method": "torrent-start", "arguments": {"ids": [int(id)]}},
|
|
'stop': {"method": "torrent-stop", "arguments": {"ids": [int(id)]}},
|
|
'remove': {"method": "torrent-remove", "arguments": {"ids": [int(id)], "delete-local-data": False}},
|
|
'removedata': {"method": "torrent-remove",
|
|
"arguments": {"ids": [int(id)], "delete-local-data": True}}}
|
|
obj = self.action(actions[action])
|
|
return True if obj else None
|
|
|
|
def get_auth(self):
|
|
response = self.http.fetch(self.url, auth_username=self.login, auth_password=self.password)
|
|
if response.error:
|
|
if response.code == 409:
|
|
return self.get_token(response.error)
|
|
return False
|
|
|
|
def get_token(self, error):
|
|
token = error.headers.get('x-transmission-session-id')
|
|
if not token:
|
|
return False
|
|
self.token = token
|
|
return True
|
|
|
|
def get_status(self, code):
|
|
mapping = {
|
|
0: 'stopped',
|
|
1: 'check_pending',
|
|
2: 'checking',
|
|
3: 'download_pending',
|
|
4: 'downloading',
|
|
5: 'seed_pending',
|
|
6: 'seeding'
|
|
}
|
|
return mapping[code]
|
|
|
|
|
|
class Deluge:
|
|
def config(self, login, password, host, port, url):
|
|
self.login = login
|
|
self.password = password
|
|
|
|
self.url = 'http://'+host
|
|
if port:
|
|
self.url += ':' + str(port)
|
|
self.url += url
|
|
log(str(self.url))
|
|
self.http = HTTP()
|
|
|
|
def get_info(self):
|
|
obj = self.action({"method": "web.update_ui",
|
|
"params": [[], {}], "id": 1})
|
|
return obj
|
|
|
|
def list(self):
|
|
obj = self.get_info()
|
|
if obj is None:
|
|
return False
|
|
|
|
res = []
|
|
if len(obj['result'].get('torrents')) > 0:
|
|
for k in obj['result'].get('torrents').keys():
|
|
r = obj['result']['torrents'][k]
|
|
add = {
|
|
'id': str(k),
|
|
'status': self.get_status(r['state']),
|
|
'name': r['name'],
|
|
'size': r['total_wanted'],
|
|
'progress': round(r['progress'], 2),
|
|
'download': r['total_done'],
|
|
'upload': r['total_uploaded'],
|
|
'upspeed': r['upload_payload_rate'],
|
|
'downspeed': r['download_payload_rate'],
|
|
'ratio': round(r['ratio'], 2),
|
|
'eta': r['eta'],
|
|
'peer': r['total_peers'],
|
|
'seed': r['num_seeds'],
|
|
'leech': r['num_peers'],
|
|
'add': r['time_added'],
|
|
'dir': r['save_path']
|
|
}
|
|
if len(r['files']) > 1: add['dir'] = os.path.join(r['save_path'], r['name'])
|
|
res.append(add)
|
|
return res
|
|
|
|
def listdirs(self):
|
|
obj = self.action({"method": "core.get_config", "params": [], "id": 5})
|
|
if obj is None:
|
|
return False
|
|
|
|
try:
|
|
res = [obj['result'].get('download_location')]
|
|
except:
|
|
res = [None]
|
|
return res, res
|
|
|
|
def listfiles(self, id):
|
|
obj = self.get_info()
|
|
i = 0
|
|
if obj is None:
|
|
return None
|
|
|
|
res = []
|
|
obj = obj['result']['torrents'][id]
|
|
# print str(obj)
|
|
if len(obj['files']) == 1:
|
|
strip_path = None
|
|
else:
|
|
strip_path = obj['name']
|
|
|
|
for x in obj['files']:
|
|
if x['size'] >= 1024 * 1024 * 1024:
|
|
size = str(x['size'] / (1024 * 1024 * 1024)) + 'GB'
|
|
elif x['size'] >= 1024 * 1024:
|
|
size = str(x['size'] / (1024 * 1024)) + 'MB'
|
|
elif x['size'] >= 1024:
|
|
size = str(x['size'] / 1024) + 'KB'
|
|
else:
|
|
size = str(x['size']) + 'B'
|
|
if strip_path:
|
|
path = x['path'].lstrip(strip_path).lstrip('/')
|
|
else:
|
|
path = x['path']
|
|
|
|
if x.get('progress'):
|
|
percent = int(x['progress'] * 100)
|
|
elif obj.get('file_progress') and len(obj['file_progress']) >= i:
|
|
percent = int(obj['file_progress'][i] * 100)
|
|
else:
|
|
percent = 0
|
|
|
|
i += 1
|
|
res.append([path, percent, x['index'], size])
|
|
|
|
return res
|
|
|
|
def get_prio(self, id):
|
|
obj = self.get_info()
|
|
if obj is None:
|
|
return None
|
|
res = obj['result']['torrents'][id]['file_priorities']
|
|
return res
|
|
|
|
def add(self, torrent, dirname):
|
|
torrentFile = os.path.join(self.http._dirname, 'deluge.torrent')
|
|
if self.action({'method': 'core.add_torrent_file',
|
|
'params': [torrentFile,
|
|
base64.b64encode(torrent), {"download_path": dirname}], "id": 3}) is None:
|
|
return None
|
|
return True
|
|
|
|
def add_url(self, torrent, dirname):
|
|
if re.match("^magnet\:.+$", torrent):
|
|
if self.action({'method': 'core.add_torrent_magnet', 'params': [torrent,
|
|
{'download_path': dirname}],
|
|
"id": 3}) is None:
|
|
return None
|
|
else:
|
|
if self.action({"method": "core.add_torrent_url", "params": [torrent, {'download_path': dirname}],
|
|
"id": 3}) is None:
|
|
return None
|
|
return True
|
|
|
|
def delete(self, id):
|
|
pass
|
|
|
|
def setprio(self, id, ind):
|
|
i = -1
|
|
prios = self.get_prio(id)
|
|
|
|
for p in prios:
|
|
i = i + 1
|
|
if p == 1:
|
|
prios.pop(i)
|
|
prios.insert(i, 0)
|
|
|
|
prios.pop(int(ind))
|
|
prios.insert(int(ind), 7)
|
|
|
|
if self.action({"method": "core.set_torrent_file_priorities", "params": [id, prios], "id": 6}) is None:
|
|
return None
|
|
|
|
return True
|
|
|
|
def setprio_simple(self, id, prio, ind):
|
|
prios = self.get_prio(id)
|
|
|
|
if ind != None:
|
|
prios.pop(int(ind))
|
|
if prio == '3':
|
|
prios.insert(int(ind), 7)
|
|
elif prio == '0':
|
|
prios.insert(int(ind), 0)
|
|
|
|
if self.action({"method": "core.set_torrent_file_priorities", "params": [id, prios], "id": 6}) is None:
|
|
return None
|
|
return True
|
|
|
|
def setprio_simple_multi(self, menu):
|
|
id = menu[0][0]
|
|
prios = self.get_prio(id)
|
|
|
|
for hash, action, ind in menu:
|
|
prios.pop(int(ind))
|
|
if action == '3':
|
|
prios.insert(int(ind), 7)
|
|
elif action == '0':
|
|
prios.insert(int(ind), 0)
|
|
|
|
if self.action({"method": "core.set_torrent_file_priorities", "params": [id, prios], "id": 6}) is None:
|
|
return None
|
|
|
|
def action(self, request):
|
|
cookie = self.get_auth()
|
|
if not cookie:
|
|
return None
|
|
|
|
try:
|
|
jsobj = json.dumps(request)
|
|
except:
|
|
return None
|
|
else:
|
|
response = self.http.fetch(self.url + '/json', method='POST', params=jsobj,
|
|
headers={'X-Requested-With': 'XMLHttpRequest', 'Cookie': cookie,
|
|
'Content-Type': 'application/json; charset=UTF-8'})
|
|
|
|
if response.error:
|
|
return None
|
|
|
|
else:
|
|
try:
|
|
obj = json.loads(response.body)
|
|
except:
|
|
return None
|
|
else:
|
|
return obj
|
|
|
|
def action_simple(self, action, id):
|
|
actions = {'start': {"method": "core.resume_torrent", "params": [[id]], "id": 4},
|
|
'stop': {"method": "core.pause_torrent", "params": [[id]], "id": 4},
|
|
'remove': {"method": "core.remove_torrent", "params": [id, False], "id": 4},
|
|
'removedata': {"method": "core.remove_torrent", "params": [id, True], "id": 4}}
|
|
obj = self.action(actions[action])
|
|
return True if obj else None
|
|
|
|
def get_auth(self):
|
|
params = json.dumps({"method": "auth.login", "params": [self.password], "id": 0})
|
|
response = self.http.fetch(self.url + '/json', method='POST', params=params, gzip=True,
|
|
headers={'X-Requested-With': 'XMLHttpRequest',
|
|
'Content-Type': 'application/json; charset=UTF-8'})
|
|
if response.error:
|
|
return None
|
|
|
|
auth = json.loads(response.body)
|
|
if auth["result"] == False:
|
|
return False
|
|
else:
|
|
r = re.compile('_session_id=([^;]+);').search(response.headers.get('set-cookie', ''))
|
|
if r:
|
|
cookie = r.group(1).strip()
|
|
return '_session_id=' + cookie
|
|
|
|
def get_status(self, code):
|
|
mapping = {
|
|
'Queued': 'stopped',
|
|
'Error': 'stopped',
|
|
'Checking': 'checking',
|
|
'Paused': 'seed_pending',
|
|
'Downloading': 'downloading',
|
|
'Active': 'seed_pending',
|
|
'Seeding': 'seeding'
|
|
}
|
|
return mapping[code]
|
|
|
|
|
|
class Vuze:
|
|
def config(self, login, password, host, port, url):
|
|
self.login = login
|
|
self.password = password
|
|
|
|
self.connection = dopal.main.make_connection(host=host, port=port, user=login, password=password)
|
|
try:
|
|
self.interface = self.connection.getPluginInterface()
|
|
self.downloads = self.interface.getDownloadManager().getDownloads()
|
|
except:
|
|
self.downloads = False
|
|
|
|
def list(self):
|
|
|
|
if self.downloads == False:
|
|
return self.downloads
|
|
|
|
i = -1
|
|
res = []
|
|
for r in self.downloads:
|
|
i += 1
|
|
res.append({
|
|
'id': str(i),
|
|
'status': self.get_status(int(getattr(r, 'state'))),
|
|
'name': getattr(getattr(r, 'torrent'), 'name'),
|
|
'size': getattr(getattr(r, 'torrent'), 'size'),
|
|
'progress': round(
|
|
float(long(r.stats.downloaded) + 1 / (long(r.stats.downloaded) + long(r.stats.remaining) + 1)),
|
|
4) * 100,
|
|
'download': float(getattr(getattr(r, 'stats'), 'downloaded')),
|
|
'upload': getattr(getattr(r, 'stats'), 'uploaded'),
|
|
# 'upspeed': r['rateUpload'],
|
|
# 'downspeed': r['rateDownload'],
|
|
'ratio': float(r.stats.share_ratio) / 1000,
|
|
'eta': getattr(getattr(r, 'stats'), 'eta'),
|
|
'peer': getattr(getattr(r, 'scrape_result'), 'non_seed_count') + getattr(getattr(r, 'scrape_result'),
|
|
'seed_count'),
|
|
'seed': getattr(getattr(r, 'scrape_result'), 'seed_count'),
|
|
'leech': getattr(getattr(r, 'scrape_result'), 'non_seed_count'),
|
|
'add': getattr(r, 'creation_time'),
|
|
'finish': getattr(r, 'creation_time') + getattr(getattr(r, 'stats'), 'seconds_downloading'),
|
|
'dir': getattr(r, 'save_path')
|
|
})
|
|
|
|
return res
|
|
|
|
def listdirs(self):
|
|
res = []
|
|
return res, res
|
|
|
|
def listfiles(self, id):
|
|
obj = self.downloads[int(id)].getDiskManagerFileInfo()
|
|
if obj is None:
|
|
return None
|
|
|
|
res = []
|
|
i = -1
|
|
|
|
for x in obj:
|
|
i += 1
|
|
if long(x.length) >= 1024 * 1024 * 1024:
|
|
size = str(long(x.length) / (1024 * 1024 * 1024)) + 'GB'
|
|
elif long(x.length) >= 1024 * 1024:
|
|
size = str(long(x.length) / (1024 * 1024)) + 'MB'
|
|
elif x.length >= 1024:
|
|
size = str(long(x.length) / 1024) + 'KB'
|
|
else:
|
|
size = str(long(x.length)) + 'B'
|
|
|
|
if '\\' in x.file:
|
|
title = x.file.split('\\')[-1]
|
|
elif '/' in x.file:
|
|
title = x.file.split('/')[-1]
|
|
else:
|
|
title = x.file
|
|
res.append([title, (long(x.downloaded * 100 / long(x.length))), i, size])
|
|
return res
|
|
|
|
def add(self, torrent, dirname):
|
|
torrent = self.interface.getTorrentManager().createFromBEncodedData(torrent)
|
|
obj = self.interface.getDownloadManager().addDownload(torrent)
|
|
return True if obj else None
|
|
|
|
def add_url(self, torrent, dirname):
|
|
|
|
obj = self.interface.getDownloadManager().addDownload(torrent)
|
|
return True if obj else None
|
|
|
|
def delete(self, id):
|
|
pass
|
|
|
|
def setprio(self, id, ind):
|
|
self.setprioobj = self.downloads[int(id)].getDiskManagerFileInfo()
|
|
# -1 low, 0 normal, 1 high
|
|
|
|
if not self.setprioobj or ind == None:
|
|
return None
|
|
|
|
i = -1
|
|
for x in self.setprioobj:
|
|
i += 1
|
|
if not x.isPriority(): self.setprio_simple_vuze(id, i, skip=True, prio=-1)
|
|
|
|
res = self.setprio_simple_vuze(id, ind, skip=False, prio=1)
|
|
|
|
return True if res else None
|
|
|
|
def setprio_simple(self, id, prio, ind):
|
|
if ind == None:
|
|
return None
|
|
|
|
res = None
|
|
|
|
if prio == '0':
|
|
res = self.setprio_simple_vuze(id, ind, skip=True, prio=-1)
|
|
elif prio == '3':
|
|
res = self.setprio_simple_vuze(id, ind, skip=False, prio=1)
|
|
|
|
def setprio_simple_vuze(self, id, ind, skip=None, prio=None):
|
|
obj = None
|
|
|
|
self.setprioobj = self.downloads[int(id)].getDiskManagerFileInfo()
|
|
|
|
if prio != None:
|
|
obj = self.setprioobj[int(ind)].setNumericPriority(prio)
|
|
|
|
if skip != None:
|
|
obj = self.setprioobj[int(ind)].setSkipped(skip)
|
|
|
|
if not obj or ind == None:
|
|
return None
|
|
time.sleep(0.1)
|
|
return True if obj else None
|
|
|
|
def setprio_simple_multi(self, menu):
|
|
for hash, action, ind in menu:
|
|
self.setprio_simple(hash, action, ind)
|
|
|
|
def action_simple(self, action, id):
|
|
torrent = self.downloads[int(id)]
|
|
obj = None
|
|
if action == 'start':
|
|
obj = torrent.restart()
|
|
if action == 'stop':
|
|
obj = torrent.stopDownload()
|
|
if action == 'remove':
|
|
obj = torrent.remove()
|
|
if action == 'removedata':
|
|
obj = torrent.remove(True, True)
|
|
|
|
return True if obj else None
|
|
|
|
def get_status(self, status):
|
|
mapping = ['stopped', 'download_pending', 'download_pending', 'download_pending', 'downloading', 'seeding',
|
|
'seed_pending', 'stopped', 'stopped', 'download_pending']
|
|
return mapping[status]
|
|
|
|
|
|
class Download():
|
|
def __init__(self):
|
|
self.handle()
|
|
|
|
def handle(self):
|
|
config = self.get_torrent_client()
|
|
|
|
if self.client == 'utorrent':
|
|
self.client = UTorrent()
|
|
|
|
elif self.client == 'transmission':
|
|
self.client = Transmission()
|
|
|
|
elif self.client == 'vuze':
|
|
self.client = Vuze()
|
|
|
|
elif self.client == 'deluge':
|
|
self.client = Deluge()
|
|
|
|
self.client.config(host=config['host'], port=config['port'], login=config['login'], password=config['password'],
|
|
url=config['url'])
|
|
# print(self.client.list())
|
|
return True
|
|
|
|
def get_torrent_client(self):
|
|
self.setting = __settings__
|
|
client = self.setting.getSetting("torrent")
|
|
config = {}
|
|
if client == '0':
|
|
self.client = 'utorrent'
|
|
config = {
|
|
'host': self.setting.getSetting("torrent_utorrent_host"),
|
|
'port': self.setting.getSetting("torrent_utorrent_port"),
|
|
'url': '',
|
|
'login': self.setting.getSetting("torrent_utorrent_login"),
|
|
'password': self.setting.getSetting("torrent_utorrent_password")
|
|
}
|
|
elif client == '1':
|
|
self.client = 'transmission'
|
|
config = {
|
|
'host': self.setting.getSetting("torrent_transmission_host"),
|
|
'port': self.setting.getSetting("torrent_transmission_port"),
|
|
'url': self.setting.getSetting("torrent_transmission_url"),
|
|
'login': self.setting.getSetting("torrent_transmission_login"),
|
|
'password': self.setting.getSetting("torrent_transmission_password")
|
|
}
|
|
elif client == '2':
|
|
self.client = 'vuze'
|
|
config = {
|
|
'host': self.setting.getSetting("torrent_vuze_host"),
|
|
'port': self.setting.getSetting("torrent_vuze_port"),
|
|
'url': '',
|
|
'login': self.setting.getSetting("torrent_vuze_login"),
|
|
'password': self.setting.getSetting("torrent_vuze_password")
|
|
}
|
|
elif client == '3':
|
|
self.client = 'deluge'
|
|
config = {
|
|
'host': self.setting.getSetting("torrent_deluge_host"),
|
|
'port': self.setting.getSetting("torrent_deluge_port"),
|
|
'url': self.setting.getSetting("torrent_deluge_path"),
|
|
'login': '',
|
|
'password': self.setting.getSetting("torrent_deluge_password")
|
|
}
|
|
|
|
return config
|
|
|
|
def add(self, torrent, dirname):
|
|
return self.client.add(torrent, dirname)
|
|
|
|
def add_url(self, torrent, dirname):
|
|
return self.client.add_url(torrent, dirname)
|
|
|
|
def list(self):
|
|
return self.client.list()
|
|
|
|
def listdirs(self):
|
|
return self.client.listdirs()
|
|
|
|
def listfiles(self, id):
|
|
return self.client.listfiles(id)
|
|
|
|
def delete(self, id):
|
|
return self.client.delete(id)
|
|
|
|
def setprio(self, id, ind):
|
|
return self.client.setprio(id, ind)
|
|
|
|
def setprio_simple(self, id, prio, ind):
|
|
# Debug('[setprio_simple] '+str((id, prio, ind)))
|
|
return self.client.setprio_simple(id, prio, ind)
|
|
|
|
def setprio_simple_multi(self, prio_list):
|
|
return self.client.setprio_simple_multi(prio_list)
|
|
|
|
def action_simple(self, action, id):
|
|
return self.client.action_simple(action, id)
|