2010-05-28 02:38:06 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2010-07-13 01:09:42 +00:00
|
|
|
# Copyright (c) 2010 Correl J. Roush
|
2010-05-28 02:38:06 +00:00
|
|
|
|
|
|
|
import sys
|
|
|
|
import base64
|
2013-03-09 09:58:36 +00:00
|
|
|
import threading
|
2010-05-28 02:38:06 +00:00
|
|
|
import xbmc
|
|
|
|
import xbmcgui
|
|
|
|
from basictypes.bytes import Bytes
|
2013-04-12 02:59:09 +00:00
|
|
|
import transmissionrpc
|
2011-09-13 05:27:04 +00:00
|
|
|
import search
|
2010-05-28 02:38:06 +00:00
|
|
|
|
|
|
|
_ = sys.modules[ "__main__" ].__language__
|
2010-06-10 17:10:31 +00:00
|
|
|
__settings__ = sys.modules[ "__main__" ].__settings__
|
2010-05-28 02:38:06 +00:00
|
|
|
|
|
|
|
KEY_BUTTON_BACK = 275
|
|
|
|
KEY_KEYBOARD_ESC = 61467
|
2012-03-12 12:05:21 +00:00
|
|
|
KEY_MENU_ID = 92
|
2010-05-28 02:38:06 +00:00
|
|
|
|
2010-06-10 17:10:31 +00:00
|
|
|
EXIT_SCRIPT = ( 6, 10, 247, 275, 61467, 216, 257, 61448, )
|
|
|
|
CANCEL_DIALOG = EXIT_SCRIPT + ( 216, 257, 61448, )
|
|
|
|
|
2013-03-09 09:58:36 +00:00
|
|
|
UPDATE_INTERVAL = 1.0
|
|
|
|
|
2013-03-14 05:26:20 +00:00
|
|
|
STATUS_ICONS = {'stopped': 'pause.png',
|
|
|
|
'seeding': 'ok.png',
|
|
|
|
'downloading': 'down.png'}
|
|
|
|
|
2010-05-28 02:38:06 +00:00
|
|
|
class TransmissionGUI(xbmcgui.WindowXMLDialog):
|
|
|
|
def __init__(self, strXMLname, strFallbackPath, strDefaultName, bforeFallback=0):
|
2010-07-15 19:58:42 +00:00
|
|
|
self.list = {}
|
|
|
|
self.torrents = {}
|
2013-03-09 09:58:36 +00:00
|
|
|
self.timer = None
|
2013-03-15 08:15:14 +00:00
|
|
|
def get_settings(self):
|
2010-05-28 02:38:06 +00:00
|
|
|
params = {
|
|
|
|
'address': __settings__.getSetting('rpc_host'),
|
|
|
|
'port': __settings__.getSetting('rpc_port'),
|
|
|
|
'user': __settings__.getSetting('rpc_user'),
|
|
|
|
'password': __settings__.getSetting('rpc_password')
|
|
|
|
}
|
2013-03-15 08:15:14 +00:00
|
|
|
return params
|
|
|
|
def set_settings(self, params):
|
|
|
|
__settings__.setSetting('rpc_host', params['address'])
|
|
|
|
__settings__.setSetting('rpc_port', params['port'])
|
|
|
|
__settings__.setSetting('rpc_user', params['user'])
|
|
|
|
__settings__.setSetting('rpc_password', params['password'])
|
|
|
|
def get_rpc_client(self):
|
|
|
|
params = self.get_settings()
|
|
|
|
return transmissionrpc.Client(**params)
|
|
|
|
def onInit(self):
|
|
|
|
p = xbmcgui.DialogProgress()
|
|
|
|
p.create(_(0), _(1)) # 'Transmission', 'Connecting to Transmission'
|
2010-07-15 19:58:42 +00:00
|
|
|
try:
|
2013-03-15 08:15:14 +00:00
|
|
|
self.transmission = self.get_rpc_client()
|
2011-03-29 03:22:40 +00:00
|
|
|
except:
|
|
|
|
p.close()
|
2011-03-29 17:07:23 +00:00
|
|
|
self.close()
|
2011-03-29 03:22:40 +00:00
|
|
|
(type, e, traceback) = sys.exc_info()
|
2011-03-29 16:53:53 +00:00
|
|
|
message = _(9000) # Unexpected error
|
|
|
|
if type is transmissionrpc.TransmissionError:
|
|
|
|
if e.original:
|
|
|
|
if e.original.code is 401:
|
|
|
|
message = _(9002) # Invalid auth
|
|
|
|
else:
|
|
|
|
message = _(9001) # Unable to connect
|
2011-09-13 05:27:04 +00:00
|
|
|
if xbmcgui.Dialog().yesno(_(2), message, _(3)):
|
2011-03-29 17:07:23 +00:00
|
|
|
__settings__.openSettings()
|
2011-03-29 16:53:53 +00:00
|
|
|
elif type is ValueError:
|
|
|
|
# In python 2.4, urllib2.HTTPDigestAuthHandler will barf up a lung
|
|
|
|
# if auth fails and the server wants non-digest authentication
|
|
|
|
message = _(9002) # Invalid auth
|
2011-09-13 05:27:04 +00:00
|
|
|
if xbmcgui.Dialog().yesno(_(2), message, _(3)):
|
2011-03-29 17:07:23 +00:00
|
|
|
__settings__.openSettings()
|
|
|
|
else:
|
|
|
|
message = _(9000) # Unexpected error
|
2011-09-13 05:27:04 +00:00
|
|
|
xbmcgui.Dialog().ok(_(2), message)
|
2010-07-15 19:58:42 +00:00
|
|
|
return False
|
2010-05-28 02:38:06 +00:00
|
|
|
self.updateTorrents()
|
2013-03-09 08:55:20 +00:00
|
|
|
p.close()
|
2013-03-09 09:58:36 +00:00
|
|
|
self.timer = threading.Timer(UPDATE_INTERVAL, self.updateTorrents)
|
|
|
|
self.timer.start()
|
2010-05-28 02:38:06 +00:00
|
|
|
def updateTorrents(self):
|
2011-12-01 03:24:50 +00:00
|
|
|
list = self.getControl(120)
|
2013-03-14 05:26:20 +00:00
|
|
|
self.torrents = self.transmission.info()
|
2013-04-12 02:34:58 +00:00
|
|
|
for i, torrent in self.torrents.iteritems():
|
2010-05-28 02:38:06 +00:00
|
|
|
statusline = "[%(status)s] %(down)s down (%(pct).2f%%), %(up)s up (Ratio: %(ratio).2f)" % \
|
|
|
|
{'down': Bytes.format(torrent.downloadedEver), 'pct': torrent.progress, \
|
|
|
|
'up': Bytes.format(torrent.uploadedEver), 'ratio': torrent.ratio, \
|
|
|
|
'status': torrent.status}
|
|
|
|
if i not in self.list:
|
|
|
|
# Create a new list item
|
|
|
|
l = xbmcgui.ListItem(label=torrent.name, label2=statusline)
|
|
|
|
list.addItem(l)
|
|
|
|
self.list[i] = l
|
|
|
|
else:
|
|
|
|
# Update existing list item
|
|
|
|
l = self.list[i]
|
|
|
|
l.setLabel(torrent.name)
|
|
|
|
l.setLabel2(statusline)
|
2013-03-14 05:26:20 +00:00
|
|
|
l.setProperty('TorrentStatusIcon', STATUS_ICONS.get(torrent.status, 'pending.png'))
|
2010-05-28 02:38:06 +00:00
|
|
|
l.setProperty('TorrentID', str(i))
|
2013-03-10 10:36:10 +00:00
|
|
|
l.setProperty('TorrentProgress', "%3d%%" % torrent.progress)
|
2010-05-28 02:38:06 +00:00
|
|
|
l.setInfo('torrent', torrent.fields)
|
|
|
|
l.setInfo('video', {'episode': int(torrent.progress)})
|
|
|
|
|
2013-04-12 02:34:58 +00:00
|
|
|
removed = [id for id in self.list.keys() if id not in self.torrents.keys()]
|
2010-05-28 02:38:06 +00:00
|
|
|
if len(removed) > 0:
|
|
|
|
# Clear torrents from the list that have been removed
|
|
|
|
for id in removed:
|
|
|
|
del self.list[id]
|
|
|
|
list.reset()
|
|
|
|
for id, item in self.list.iteritems():
|
|
|
|
list.addItem(item)
|
2013-04-12 02:34:58 +00:00
|
|
|
list.setEnabled(bool(self.torrents))
|
2010-06-10 17:10:31 +00:00
|
|
|
|
2013-04-16 07:13:50 +00:00
|
|
|
# Update again, after an interval, but only if the timer has not been cancelled
|
|
|
|
if self.timer:
|
|
|
|
self.timer = threading.Timer(UPDATE_INTERVAL, self.updateTorrents)
|
|
|
|
self.timer.start()
|
2010-05-28 02:38:06 +00:00
|
|
|
def onClick(self, controlID):
|
2011-12-01 03:24:50 +00:00
|
|
|
list = self.getControl(120)
|
|
|
|
if (controlID == 111):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Add torrent
|
2011-09-13 05:27:04 +00:00
|
|
|
engines = [
|
|
|
|
(_(200), None),
|
|
|
|
(_(202), search.TPB),
|
2011-09-13 12:32:14 +00:00
|
|
|
(_(203), search.Mininova),
|
2011-09-18 00:45:11 +00:00
|
|
|
(_(204), search.TorrentReactor),
|
2011-09-13 05:27:04 +00:00
|
|
|
]
|
|
|
|
selected = xbmcgui.Dialog().select(_(0), [i[0] for i in engines])
|
|
|
|
if selected < 0:
|
|
|
|
return
|
|
|
|
engine = engines[selected][1]
|
|
|
|
if not engine:
|
|
|
|
filename = xbmcgui.Dialog().browse(1, _(0), 'files', '.torrent')
|
|
|
|
try:
|
|
|
|
f = open(filename, 'r')
|
|
|
|
data = base64.b64encode(f.read())
|
|
|
|
self.transmission.add(data)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
kb = xbmc.Keyboard('', engines[selected][0])
|
|
|
|
kb.doModal()
|
|
|
|
if not kb.isConfirmed():
|
|
|
|
return
|
|
|
|
terms = kb.getText()
|
|
|
|
p = xbmcgui.DialogProgress()
|
|
|
|
p.create(_(0), _(290))
|
|
|
|
try:
|
|
|
|
results = engine().search(terms)
|
|
|
|
except:
|
|
|
|
p.close()
|
|
|
|
xbmcgui.Dialog().ok(_(0), _(292))
|
|
|
|
return
|
|
|
|
p.close()
|
|
|
|
if not results:
|
|
|
|
xbmcgui.Dialog().ok(_(0), _(291))
|
|
|
|
return
|
|
|
|
selected = xbmcgui.Dialog().select(_(0), ['[S:%d L:%d] %s' % (t['seeds'], t['leechers'], t['name']) for t in results])
|
|
|
|
if selected < 0:
|
|
|
|
return
|
|
|
|
try:
|
2012-03-13 23:51:09 +00:00
|
|
|
self.transmission.add_uri(results[selected]['url'])
|
2011-09-13 05:27:04 +00:00
|
|
|
except:
|
|
|
|
xbmcgui.Dialog().ok(_(0), _(293))
|
|
|
|
return
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 112):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Remove selected torrent
|
|
|
|
item = list.getSelectedItem()
|
|
|
|
if item and xbmcgui.Dialog().yesno(_(0), 'Remove \'%s\'?' % self.torrents[int(item.getProperty('TorrentID'))].name):
|
|
|
|
remove_data = xbmcgui.Dialog().yesno(_(0), 'Remove data as well?')
|
|
|
|
self.transmission.remove(int(item.getProperty('TorrentID')), remove_data)
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 113):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Stop selected torrent
|
|
|
|
item = list.getSelectedItem()
|
|
|
|
if item:
|
|
|
|
self.transmission.stop(int(item.getProperty('TorrentID')))
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 114):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Start selected torrent
|
|
|
|
item = list.getSelectedItem()
|
|
|
|
if item:
|
|
|
|
self.transmission.start(int(item.getProperty('TorrentID')))
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 115):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Stop all torrents
|
|
|
|
self.transmission.stop(self.torrents.keys())
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 116):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Start all torrents
|
|
|
|
self.transmission.start(self.torrents.keys())
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 117):
|
2010-05-28 02:38:06 +00:00
|
|
|
# Exit button
|
2011-03-29 03:22:40 +00:00
|
|
|
self.close()
|
2013-03-15 08:15:14 +00:00
|
|
|
if (controlID == 118):
|
|
|
|
# Settings button
|
|
|
|
prev_settings = self.get_settings()
|
|
|
|
__settings__.openSettings()
|
|
|
|
p = xbmcgui.DialogProgress()
|
|
|
|
p.create(_(0), _(1)) # 'Transmission', 'Connecting to Transmission'
|
|
|
|
try:
|
|
|
|
self.transmission = self.get_rpc_client()
|
|
|
|
self.updateTorrents()
|
|
|
|
p.close()
|
|
|
|
except:
|
|
|
|
p.close()
|
|
|
|
xbmcgui.Dialog().ok(_(2), _(9001))
|
|
|
|
# restore settings
|
|
|
|
self.set_settings(prev_settings)
|
|
|
|
try:
|
|
|
|
self.transmission = self.get_rpc_client()
|
|
|
|
except err:
|
|
|
|
xbmcgui.Dialog().ok(_(2), _(9001))
|
|
|
|
self.close()
|
2011-12-01 03:24:50 +00:00
|
|
|
if (controlID == 120):
|
2010-05-28 02:38:06 +00:00
|
|
|
# A torrent was chosen, show details
|
|
|
|
item = list.getSelectedItem()
|
2011-12-06 06:24:37 +00:00
|
|
|
w = TorrentInfoGUI("script-Transmission-details.xml", __settings__.getAddonInfo('path') ,"Default")
|
2010-07-16 19:36:06 +00:00
|
|
|
w.setTorrent(self.transmission, int(item.getProperty('TorrentID')))
|
2010-05-28 02:38:06 +00:00
|
|
|
w.doModal()
|
|
|
|
del w
|
|
|
|
def onFocus(self, controlID):
|
|
|
|
pass
|
|
|
|
|
2010-06-10 17:10:31 +00:00
|
|
|
def onAction( self, action ):
|
2012-03-12 12:05:21 +00:00
|
|
|
if ( action.getButtonCode() in CANCEL_DIALOG ) or (action.getId() == KEY_MENU_ID):
|
2011-03-29 03:22:40 +00:00
|
|
|
self.close()
|
|
|
|
def close(self):
|
2013-03-09 09:58:36 +00:00
|
|
|
if self.timer:
|
|
|
|
self.timer.cancel()
|
2013-04-16 07:13:50 +00:00
|
|
|
self.timer = None
|
2011-03-29 03:22:40 +00:00
|
|
|
super(TransmissionGUI, self).close()
|
|
|
|
|
2010-06-10 17:10:31 +00:00
|
|
|
|
2010-05-28 02:38:06 +00:00
|
|
|
class TorrentInfoGUI(xbmcgui.WindowXMLDialog):
|
|
|
|
def __init__(self, strXMLname, strFallbackPath, strDefaultName, bforeFallback=0):
|
2010-07-16 19:36:06 +00:00
|
|
|
self.transmission = None
|
2010-05-28 02:38:06 +00:00
|
|
|
self.torrent_id = None
|
2010-07-16 19:36:06 +00:00
|
|
|
self.list = {}
|
2013-03-09 09:58:36 +00:00
|
|
|
self.timer = None
|
2010-07-16 19:36:06 +00:00
|
|
|
def setTorrent(self, transmission, t_id):
|
|
|
|
self.transmission = transmission
|
2010-05-28 02:38:06 +00:00
|
|
|
self.torrent_id = t_id
|
2013-03-09 09:58:36 +00:00
|
|
|
self.timer = threading.Timer(UPDATE_INTERVAL, self.updateTorrent)
|
|
|
|
self.timer.start()
|
2010-07-16 19:36:06 +00:00
|
|
|
def updateTorrent(self):
|
|
|
|
pbar = self.getControl(219)
|
|
|
|
list = self.getControl(220)
|
|
|
|
labelName = self.getControl(1)
|
|
|
|
labelStatus = self.getControl(2)
|
2013-03-09 11:02:59 +00:00
|
|
|
labelProgress = self.getControl(11)
|
2010-07-16 19:36:06 +00:00
|
|
|
torrent = self.transmission.info()[self.torrent_id]
|
|
|
|
files = self.transmission.get_files(self.torrent_id)[self.torrent_id]
|
2011-09-07 23:24:13 +00:00
|
|
|
|
2013-03-09 11:02:59 +00:00
|
|
|
statusline = "[%(status)s] %(down)s down, %(up)s up (Ratio: %(ratio).2f)" % \
|
2010-07-16 19:36:06 +00:00
|
|
|
{'down': Bytes.format(torrent.downloadedEver), 'pct': torrent.progress, \
|
|
|
|
'up': Bytes.format(torrent.uploadedEver), 'ratio': torrent.ratio, \
|
|
|
|
'status': torrent.status}
|
|
|
|
if torrent.status is 'downloading':
|
|
|
|
statusline += " ETA: %(eta)s" % \
|
|
|
|
{'eta': torrent.eta}
|
2011-09-07 23:24:13 +00:00
|
|
|
|
2010-07-16 19:36:06 +00:00
|
|
|
labelName.setLabel(torrent.name)
|
|
|
|
labelStatus.setLabel(statusline)
|
2013-03-09 11:02:59 +00:00
|
|
|
labelProgress.setLabel('%3d%%' % (torrent.progress))
|
2010-07-16 19:36:06 +00:00
|
|
|
pbar.setPercent(torrent.progress)
|
2011-09-07 23:24:13 +00:00
|
|
|
|
2010-07-16 19:36:06 +00:00
|
|
|
for i, file in files.iteritems():
|
|
|
|
if i not in self.list:
|
|
|
|
# Create a new list item
|
|
|
|
l = xbmcgui.ListItem(label=file['name'])
|
|
|
|
list.addItem(l)
|
|
|
|
self.list[i] = l
|
|
|
|
else:
|
|
|
|
# Update existing list item
|
|
|
|
l = self.list[i]
|
|
|
|
l.setProperty('Progress', '[%3d%%]' % (file['completed'] * 100 / file['size']))
|
2013-03-09 09:58:36 +00:00
|
|
|
|
|
|
|
# Update again, after an interval
|
|
|
|
self.timer = threading.Timer(UPDATE_INTERVAL, self.updateTorrent)
|
|
|
|
self.timer.start()
|
2010-05-28 02:38:06 +00:00
|
|
|
def onInit(self):
|
2010-07-16 19:36:06 +00:00
|
|
|
self.updateTorrent()
|
|
|
|
def close(self):
|
2013-03-09 09:58:36 +00:00
|
|
|
if self.timer:
|
|
|
|
self.timer.cancel()
|
2010-07-16 19:36:06 +00:00
|
|
|
super(TorrentInfoGUI, self).close()
|
2010-05-28 02:38:06 +00:00
|
|
|
def onAction(self, action):
|
2012-03-12 12:05:21 +00:00
|
|
|
if (action.getButtonCode() in CANCEL_DIALOG) or (action.getId() == KEY_MENU_ID):
|
2010-05-28 02:38:06 +00:00
|
|
|
self.close()
|
2010-07-16 19:36:06 +00:00
|
|
|
pass
|
2010-05-28 02:38:06 +00:00
|
|
|
def onClick(self, controlID):
|
2013-03-09 08:44:03 +00:00
|
|
|
if controlID == 111:
|
|
|
|
self.close()
|
2010-05-28 02:38:06 +00:00
|
|
|
def onFocus(self, controlID):
|
2010-06-10 17:10:31 +00:00
|
|
|
pass
|