sync webui 112 and translate

This commit is contained in:
Marcos Pinto 2007-10-28 01:35:27 +00:00
parent 019ea1f601
commit cd2f45da92
22 changed files with 810 additions and 332 deletions

View file

@ -29,7 +29,7 @@
# this exception statement from your version. If you delete this exception
plugin_name = _("Web User Interface")
plugin_author = _("Martijn Voncken")
plugin_author = "Martijn Voncken"
plugin_version = "rev."
plugin_description = _("""A Web based User Interface
@ -41,7 +41,7 @@ There is support for multiple templates, but just one is included.
Other contributors:
*somedude : template enhancements.
*markybob : stability : synced with his changes in deluge-svn.
""")
import deluge.common
@ -80,9 +80,10 @@ class plugin_WebUi(object):
self.web_server = None
if not deluge.common.windows_check():
import commands
status = commands.getstatusoutput('ps x |grep -v grep |grep run_webserver')
status = commands.getstatusoutput(
'ps x |grep -v grep |grep run_webserver')
if status[0] == 0:
os.kill(status[1].split()[0], 9)
os.kill(int(status[1].split()[0]), 9)
time.sleep(1) #safe time to wait for kill to finish.
self.config_file = deluge.common.CONFIG_DIR + "/webui.conf"
self.config = deluge.pref.Preferences(self.config_file, False)
@ -95,9 +96,6 @@ class plugin_WebUi(object):
if not self.config.get('port'): #ugly way to detect new config file.
#set default values:
self.config.set("port", 8112)
#future->use deluge-core setting for download_dir (if it is set)
self.config.set("download_dir", os.path.expanduser("~"))
self.config.set("torrent_dir", os.path.expanduser("~"))
self.config.set("button_style", 2)
self.config.set("auto_refresh", False)
self.config.set("auto_refresh_secs", 4)
@ -112,11 +110,9 @@ class plugin_WebUi(object):
self.config.set("cache_templates", True)
if deluge.common.windows_check():
if self.config.get("run_in_thread") == None:
self.config.set("run_in_thread", True)
self.config.set("run_in_thread", True)
else:
if self.config.get("run_in_thread") == None:
self.config.set("run_in_thread", False)
self.config.set("run_in_thread", False)
self.dbus_manager = get_dbus_manager(deluge_core, deluge_interface,
self.config, self.config_file)
@ -141,8 +137,8 @@ class plugin_WebUi(object):
self.kill_server()
if self.config.get("run_in_thread"):
print 'start Webui(inside gtk)..'
webserver_common.init() #reload changed config.
print 'Start Webui(inside gtk)..'
webserver_common.init_gtk_05() #reload changed config.
from deluge_webserver import WebServer #only import in threaded mode
@ -150,11 +146,9 @@ class plugin_WebUi(object):
self.web_server.start_gtk()
else:
print 'start Webui(in process)..'
path = os.path.dirname(__file__)
server_bin = path + '/run_webserver'
port = str(self.config.get('port'))
self.proc = Popen((server_bin, port),cwd=path)
print 'Start Webui(in process)..'
server_bin = os.path.dirname(__file__) + '/run_webserver'
self.proc = Popen((server_bin,'env=0.5'))
def kill_server(self):
if self.web_server:
@ -187,7 +181,8 @@ class ConfigDialog(gtk.Dialog):
template_path = os.path.join(os.path.dirname(__file__), 'templates')
self.templates = [dirname for dirname
in os.listdir(template_path)
if os.path.isdir(os.path.join(template_path, dirname))]
if os.path.isdir(os.path.join(template_path, dirname))
and not dirname.startswith('.')]
self.port = self.add_widget(_('Port Number'), gtk.SpinButton())
self.pwd1 = self.add_widget(_('New Password'), gtk.Entry())
@ -195,16 +190,11 @@ class ConfigDialog(gtk.Dialog):
self.template = self.add_widget(_('Template'), gtk.combo_box_new_text())
self.button_style = self.add_widget(_('Button Style'),
gtk.combo_box_new_text())
self.download_dir = self.add_widget(_('Download Directory'),
gtk.FileChooserButton(_('Download Directory')))
self.torrent_dir = self.add_widget(_('Torrent Directory'),
gtk.FileChooserButton(_('Torrent Directory')))
self.cache_templates = self.add_widget(_('Cache Templates'),
gtk.CheckButton())
self.run_in_thread = self.add_widget(_('Run inside GTK'), gtk.CheckButton())
#self.share_downloads = self.add_widget(_('Share Download Directory'),
# gtk.CheckButton())
self.download_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.torrent_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.port.set_range(80, 65536)
self.port.set_increments(1, 10)
self.pwd1.set_visibility(False)
@ -218,23 +208,16 @@ class ConfigDialog(gtk.Dialog):
for item in [_('Text and image'), _('Image Only'), _('Text Only')]:
self.button_style.append_text(item)
if not self.config.get("button_style"):
if self.config.get("button_style") == None:
self.config.set("button_style", 2)
self.port.set_value(int(self.config.get("port")))
self.template.set_active(
self.templates.index(self.config.get("template")))
self.button_style.set_active(self.config.get("button_style"))
#self.share_downloads.set_active(
# bool(self.config.get("share_downloads")))
self.torrent_dir.set_filename(self.config.get("torrent_dir"))
self.download_dir.set_filename(self.config.get("download_dir"))
if deluge.common.windows_check():
self.run_in_thread.set_active(True)
self.run_in_thread.set_sensitive(False)
else:
self.run_in_thread.set_active(False)
self.run_in_thread.set_sensitive(False)
self.cache_templates.set_active(self.config.get("cache_templates"))
self.vbox.pack_start(self.vb, True, True, 0)
@ -270,9 +253,7 @@ class ConfigDialog(gtk.Dialog):
self.config.set("port", int(self.port.get_value()))
self.config.set("template", self.template.get_active_text())
self.config.set("button_style", self.button_style.get_active())
self.config.set("torrent_dir", self.torrent_dir.get_filename())
self.config.set("download_dir",self.download_dir.get_filename())
self.config.set("cache_templates", self.cache_templates.get_active())
self.config.set("run_in_thread", self.run_in_thread.get_active())
#self.config.set("share_downloads", self.share_downloads.get_active())
self.config.save(self.plugin.config_file)
self.plugin.start_server() #restarts server

View file

@ -68,7 +68,7 @@ class DbusManager(dbus.service.Object):
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="",out_signature="as")
def get_torrent_state(self):
def get_session_state(self):
"""Returns a list of torrent_ids in the session.
same as 0.6, but returns type "as" instead of a pickle
"""
@ -129,18 +129,20 @@ class DbusManager(dbus.service.Object):
return status_subset
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s",out_signature="")
def pause_torrent(self, torrent_id):
in_signature="as",out_signature="")
def pause_torrent(self, torrents):
"""same as 0.6 interface"""
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,True)
for torrent_id in torrents:
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,True)
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="")
def resume_torrent(self, torrent_id):
in_signature="as", out_signature="")
def resume_torrent(self, torrents):
"""same as 0.6 interface"""
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,False)
for torrent_id in torrents:
torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,False)
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="sbb", out_signature="")
@ -157,7 +159,6 @@ class DbusManager(dbus.service.Object):
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="b")
def add_torrent_url(self, url):
"""not available in deluge 0.6 interface"""
filename = fetch_url(url)
self._add_torrent(filename)
return True
@ -182,8 +183,8 @@ class DbusManager(dbus.service.Object):
#name = fillename without directory
name = name.replace('\\','/')
name = 'deluge_' + str(random.random()) + '_' + name.split('/')[-1]
filename = os.path.join(self.core.config.get("default_download_path"), name)
filename = os.path.join(self.config.get("torrent_dir"),name)
filecontent = base64.b64decode(filecontent_b64)
f = open(filename,"wb") #no with statement, that's py 2.5+
f.write(filecontent)
@ -192,11 +193,42 @@ class DbusManager(dbus.service.Object):
self._add_torrent(filename)
return True
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="a{sv}")
def get_config(self):
return self.core.config.mapping
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="v")
def get_config_value(self,key):
return self.core.config.mapping[pythonize(key)] #ugly!
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="a{sv}", out_signature="")
def set_config(self, config):
"""Set the config with values from dictionary"""
config = deluge.common.pythonize(config)
# Load all the values into the configuration
for key in self.core.config.keys():
self.core.config[key] = config[key]
self.core.apply_prefs()
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="v")
def get_download_rate(self):
return self.core.get_state()['download_rate']
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="v")
def get_upload_rate(self):
return self.core.get_state()['upload_rate']
#internal
def _add_torrent(self, filename):
#dbus types break pickle, again.....
filename = unicode(filename)
target = self.config.get("download_dir")
target = self.core.config.get("default_download_path")
torrent_id = self.core.add_torrent(filename, target,
self.interface.config.get("use_compact_storage"))

View file

@ -31,67 +31,15 @@
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
from webserver_common import TORRENT_KEYS, STATE_MESSAGES
import webserver_common as ws
from webserver_framework import *
import webpy022 as web
from webpy022.http import seeother, url
from webpy022.utils import Storage
from md5 import md5
import base64
from deluge.common import fsize
from operator import attrgetter
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = ws.proxy.get_torrent_status(torrent_id,TORRENT_KEYS)
status["id"] = torrent_id
#for naming the status-images
status["calc_state_str"] = "downloading"
if status["paused"]:
status["calc_state_str"] = "inactive"
elif status["is_seed"]:
status["calc_state_str"] = "seeding"
#action for torrent_pause
if status["calc_state_str"] == "inactive":
status["action"] = "start"
else:
status["action"] = "stop"
if status["paused"]:
status["message"] = _("Paused %s%%") % status['progress']
else:
status["message"] = "%s %i%%" % (STATE_MESSAGES[status["state"]]
, status['progress'])
#add some pre-calculated values
status.update({
"calc_total_downloaded" : (fsize(status["total_done"])
+ " (" + fsize(status["total_download"]) + ")"),
"calc_total_uploaded": (fsize(status['uploaded_memory']
+ status["total_payload_upload"]) + " ("
+ fsize(status["total_upload"]) + ")"),
})
return Storage(status) #Storage for easy templating.
#/utils
import os
#routing:
urls = (
@ -107,15 +55,21 @@ urls = (
"/resume_all(.*)", "resume_all",
"/refresh/set(.*)", "refresh_set",
"/refresh/(.*)", "refresh",
"/config(.*)","config",
"/home(.*)", "home",
"/about(.*)", "about",
"/logout(.*)", "logout",
#default-pages
"/", "login",
"", "login",
"/", "home",
"", "home",
#remote-api:
"/remote/torrent/add(.*)", "remote_torrent_add"
)
"/remote/torrent/add(.*)", "remote_torrent_add",
#static:
"/static/(.*)","static",
"/template/static/(.*)","template_static",
#"/downloads/(.*)","downloads" disabled until it can handle large downloads.
)
#/routing
#pages:
@ -133,15 +87,10 @@ class login:
start_session()
do_redirect()
elif vars.redir:
seeother(url('/login',error=1,redir=vars.redir))
seeother(url('/login',error=1, redir=vars.redir))
else:
seeother('/login?error=1')
class home:
@check_session
def GET(self, name):
do_redirect()
class index:
"page containing the torrent list."
@auto_refreshed
@ -150,7 +99,7 @@ class index:
vars = web.input(sort=None, order=None)
status_rows = [get_torrent_status(torrent_id)
for torrent_id in ws.proxy.get_torrent_state()]
for torrent_id in ws.proxy.get_session_state()]
#sorting:
if vars.sort:
@ -164,21 +113,19 @@ class index:
return ws.render.index(status_rows)
class torrent_info:
"torrent details"
@auto_refreshed
@deluge_page
def GET(self, torrent_id):
return ws.render.torrent_info(get_torrent_status(torrent_id))
class torrent_pause:
"start/stop a torrent"
@check_session
def POST(self, name):
vars = web.input(stop = None, start = None, redir = None)
if vars.stop:
ws.proxy.pause_torrent(vars.stop)
ws.proxy.pause_torrent([vars.stop])
elif vars.start:
ws.proxy.resume_torrent(vars.start)
ws.proxy.resume_torrent([vars.start])
do_redirect()
@ -194,7 +141,7 @@ class torrent_add:
if vars.url and vars.torrent.filename:
error_page(_("Choose an url or a torrent, not both."))
if vars.url:
ws.proxy.add_torrent_url(vars.url)
ws.proxy.add_torrent_url(vars.url )
do_redirect()
elif vars.torrent.filename:
data = vars.torrent.file.read()
@ -227,8 +174,7 @@ class torrent_delete:
return ws.render.torrent_delete(get_torrent_status(torrent_id))
@check_session
def POST(self, name):
torrent_id = name
def POST(self, torrent_id):
vars = web.input(data_also = None, torrent_also = None)
data_also = bool(vars.data_also)
torrent_also = bool(vars.torrent_also)
@ -238,30 +184,26 @@ class torrent_delete:
class torrent_queue_up:
@check_session
def POST(self, name):
torrent_id = name
def POST(self, torrent_id):
ws.proxy.queue_up(torrent_id)
do_redirect()
class torrent_queue_down:
@check_session
def POST(self, name):
torrent_id = name
def POST(self, torrent_id):
ws.proxy.queue_down(torrent_id)
do_redirect()
class pause_all:
@check_session
def POST(self, name):
for torrent_id in ws.proxy.get_torrent_state():
ws.proxy.pause_torrent(torrent_id)
ws.proxy.pause_torrent(ws.proxy.get_session_state())
do_redirect()
class resume_all:
@check_session
def POST(self, name):
for torrent_id in ws.proxy.get_torrent_state():
ws.proxy.resume_torrent(torrent_id)
ws.proxy.resume_torrent(ws.proxy.get_session_state())
do_redirect()
class refresh:
@ -287,13 +229,61 @@ class refresh_set:
else:
error_page(_('refresh must be > 0'))
class config:
"""core config
TODO:good validation.
"""
cfg_form = web.form.Form(
web.form.Dropdown('max_download', ws.SPEED_VALUES,
description=_('Download Speed Limit'),
post='%s Kib/sec' % ws.proxy.get_config_value('max_download_speed')
)
,web.form.Dropdown('max_upload', ws.SPEED_VALUES,
description=_('Upload Speed Limit'),
post='%s Kib/sec' % ws.proxy.get_config_value('max_upload_speed')
)
)
@deluge_page
def GET(self, name):
return ws.render.config(self.cfg_form())
def POST(self, name):
vars = web.input(max_download=None, max_upload=None)
#self.config.set("max_download_speed", float(str_bwdown))
raise NotImplementedError('todo')
class home:
@check_session
def GET(self, name):
do_redirect()
class about:
@deluge_page_noauth
def GET(self, name):
return ws.render.about()
#/pages
class logout:
def POST(self, name):
end_session()
seeother('/login')
class static(static_handler):
base_dir = os.path.join(os.path.dirname(__file__),'static')
class template_static(static_handler):
def get_base_dir(self):
return os.path.join(os.path.dirname(__file__),
'templates/%s/static' % ws.config.get('template'))
class downloads(static_handler):
def GET(self, name):
self.base_dir = ws.proxy.get_config_value('default_download_path')
if not ws.config.get('share_downloads'):
raise Exception('Access to downloads is forbidden.')
return static_handler.GET(self, name)
#/pages
def WebServer():
return create_webserver(urls, globals())
@ -307,4 +297,3 @@ def run():
if __name__ == "__main__":
run()

View file

@ -1 +1 @@
87
112

View file

@ -1,3 +1,3 @@
#!/usr/bin/env python
from deluge_webserver import *
web.run(urls, globals())
import deluge_webserver
deluge_webserver.run()

View file

@ -0,0 +1,10 @@
#!/bin/bash
pwd=deluge
url=http://localhost:8112
for arg in "$@"
do
curl -F torrent=@"$arg" -F pwd=$pwd $url/remote/torrent/add
done

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

View file

@ -0,0 +1,136 @@
#!/usr/bin/env python
#(c) Martijn Voncken, mvoncken@gmail.com
#Same Licence as web.py 0.22 ->Public Domain
#
"""
static fileserving for web.py
without the need for wsgi wrapper magic.
"""
import webpy022 as web
from webpy022.http import seeother, url
import posixpath
import urlparse
import urllib
import mimetypes
import os
import datetime
import cgi
from StringIO import StringIO
mimetypes.init() # try to read system mime.types
class static_handler:
"""
mostly c&p from SimpleHttpServer
serves relative from start location
"""
base_dir = './'
extensions_map = mimetypes.types_map
def get_base_dir(self):
#override this if you have a config that changes the base dir at runtime
#deluge on windows :(
return self.base_dir
def GET(self, path):
path = self.translate_path(path)
if os.path.isdir(path):
if not path.endswith('/'):
path += "/"
return self.list_directory(path)
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except IOError:
raise Exception('file not found:%s' % path)
#web.header("404", "File not found")
#return
web.header("Content-type", ctype)
fs = os.fstat(f.fileno())
web.header("Content-Length", str(fs[6]))
web.lastmodified(datetime.datetime.fromtimestamp(fs.st_mtime))
print f.read()
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = self.get_base_dir()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def guess_type(self, path):
base, ext = posixpath.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return 'application/octet-stream'
def list_directory(self, path):
"""Helper to produce a directory listing (absent index.html).
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
#TODO ->use web.py +template!
"""
try:
list = os.listdir(path)
except os.error:
web.header('404', "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
f = StringIO()
displaypath = cgi.escape(urllib.unquote(path))
f.write("<title>Directory listing for %s</title>\n" % displaypath)
f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
f.write("<hr>\n<ul>\n")
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
f.write('<li><a href="%s">%s</a>\n'
% (urllib.quote(linkname), cgi.escape(displayname)))
f.write("</ul>\n<hr>\n")
length = f.tell()
f.seek(0)
web.header("Content-type", "text/html")
web.header("Content-Length", str(length))
print f.read()
if __name__ == '__main__':
#example:
class usr_static(static_handler):
base_dir = os.path.expanduser('~')
urls = ('/relative/(.*)','static_handler',
'/(.*)','usr_static')
web.run(urls,globals())

View file

@ -7,6 +7,7 @@ $:render.header(_('About'))
<li><a href="http://deluge-torrent.org">Deluge</a></li>
<li><a href="http://forum.deluge-torrent.org/viewtopic.php?f=9&t=425">
WebUi forum Thread</a>
</li>
<li><a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL v2
</a></li>
@ -18,6 +19,7 @@ $:render.header(_('About'))
<ul>
<li>Martijn Voncken</li>
</ul>
<h3>Template</h3>
<ul>
<li>Martijn Voncken</li>
@ -27,6 +29,7 @@ $:render.header(_('About'))
<ul>
<li>Zach Tibbitts</li>
<li>Alon Zakai</li>
<li>Alon Zakai</li>
<li>Marcos Pinto</li>
<li>Andrew Resch</li>
@ -36,4 +39,4 @@ $:render.header(_('About'))
*and all other authors/helpers/contributors I forgot to mention.
</div>
$:render.footer()
$:render.footer()

View file

@ -0,0 +1,10 @@
$def with (form)
$:render.header(_('Config'))
<div class="error">Not Implemented!</div>
<form method="POST">
$:form.render()
<input type="submit" value="$_('Apply')"/>
</form>
$:render.footer()

View file

@ -51,8 +51,10 @@ $for torrent in torrent_list:
$:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png')
$:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png')
$:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png')
$:render.part_button('POST', '/logout', _('Logout'), 'tango/system-log-out.png')
</div>
$:render.part_refresh()
$:part_stats()
$:render.footer()

View file

@ -2,21 +2,25 @@ $def with (method, url, title, image='')
<div class="deluge_button">
<form method="$method" action="$url" class="deluge_button">
<input type="hidden" name="redir" value="$self_url()">
<button type="submit" class="deluge_button">
$if (get_config('button_style') == 0):
<button type="submit" class="deluge_button" alt="$title">
$title
$if image:
<image src="/static/images/$image" class="button" alt="$title"/>
</button>
$if (get_config('button_style') == 1):
$if image:
<image src="/static/images/$image" class="button" alt="$title"/>
<input type="image" image src="/static/images/$image" class="img_button" alt="$title"/>
$else:
<button type="submit" class="deluge_button" alt="$title">
$title
</button>
$if (get_config('button_style') == 2):
<button type="submit" class="deluge_button" alt="$title">
$title
</button>
</button>
</form>
</div>

View file

@ -0,0 +1,30 @@
$def with (stats)
<div class="panel" id='refresh_panel'>
$_('Auto refresh:')
$if getcookie('auto_refresh') == '1':
($getcookie('auto_refresh_secs')) $_('seconds') &nbsp;
$:render.part_button('GET', '/refresh/set', _('Set'), 'tango/preferences-system.png')
$:render.part_button('POST', '/refresh/off', _('Disable'), 'tango/process-stop.png')
$else:
$_('Off') &nbsp;
$:render.part_button('POST', '/refresh/on', _('Enable'), 'tango/view-refresh.png')
$#end
</div>
<div class="panel" id='refresh_panel'>
<a href='/config'>
$_('Down Speed') : $stats.download_rate ($stats.max_download)
$_('Up Speed') : $stats.upload_rate ($stats.max_upload)
</a>
(<a href='/about'>$_('About')</a>)
</div>

View file

@ -84,6 +84,7 @@ $:render.part_button('GET', '/torrent/delete/' + str(torrent.id), _('Remove'), '
<br>
<!--
[<a onclick="javascript:toggle_dump()">$_('Debug:Data Dump')</a>]
<pre style="background-color:white;color:black;display:none" id="data_dump">
@ -102,6 +103,8 @@ function toggle_dump(){
}
}
</script>
-->
</div>
@ -114,6 +117,6 @@ $:render.part_button('POST', '/torrent/queue/up/' + str(torrent.id), _('Queue Up
$:render.part_button('POST', '/torrent/queue/down/' + str(torrent.id), _('Queue Down'), 'tango/down.png')
</div>
$:render.part_refresh()
$:part_stats()
$:render.footer()

View file

@ -0,0 +1,3 @@
</body>
</html>

View file

@ -0,0 +1,12 @@
$def with (title)
<html>
<head>
<title>Deluge(example) : $title</title>
<link rel="icon" href="/static/images/deluge_icon.gif" type="image/gif" />
<link rel="shortcut icon" href="/static/images/deluge_icon.gif" type="image/gif" />
</head>
<body>
<img src="/template/static/example.png">
<a href=/home>[HOME]</a>
<h1>$title</h1>

View file

@ -0,0 +1,42 @@
$def with (torrent_list)
$:render.header(_('Torrent list'))
<form action="/torrent/pause" method="POST">
<table class="torrent_list" border=1>
<tr>
$:(sort_head('calc_state_str', 'S'))
$:(sort_head('queue_pos', '#'))
$:(sort_head('name', _('Name')))
$:(sort_head('progress', _('Progress')))
</tr>
$#4-space indentation is mandatory for for-loops in templetor!
$for torrent in torrent_list:
<tr>
<td><input type="image"
src="/static/images/$(torrent.calc_state_str)16.png"
name="$torrent.action" value="$torrent.id">
</td>
<td>$torrent.queue_pos</td>
<td style="width:100px; overflow:hidden;white-space: nowrap">
<a href="/torrent/info/$torrent.id">$(crop(torrent.name, 40))</a></td>
<td class="progress_bar">
<div class="progress_bar_outer">
<div class="progress_bar" style="width:$(torrent.progress)%">
$torrent.message
</div>
</div>
</td>
</tr>
</table>
</form>
<div class="panel" bgcolor="5555AA">
$:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png')
$:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png')
$:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png')
$:render.part_button('POST', '/logout', _('Logout'), 'tango/system-log-out.png')
</div>
$:render.footer()

View file

@ -1,5 +1,5 @@
revision-id: mvoncken@gmail.com-20070930083408-sv8mo0mi1rbjnfvk
date: 2007-10-23 15:10:08 +0200
build-date: 2007-10-23 15:34:50 +0200
revno: 87
revno: 112
branch-nick: WebUi

View file

@ -29,40 +29,96 @@
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
"""
initializes config,render and proxy.
contains all hacks to support running in process0.5 ,run inside-gtk0.5 and
run in process0.6
"""
import os
import deluge
from deluge.common import INSTALL_PREFIX
import random
import pickle
import sys
from webpy022 import template
random.seed()
path = os.path.dirname(__file__)
config_file = deluge.common.CONFIG_DIR + "/webui.conf"
try:
_('translate something')
except:
import gettext
gettext.install('~/') #no translations :(
#a bit hacky way of detecting i'm in the deluge gui or in a process :(
if not hasattr(deluge,'pref'):
try:
config_dir = deluge.common.CONFIG_DIR
except:
config_dir = os.path.expanduser("~/.config/deluge")
config_file = os.path.join(config_dir,'webui.conf')
session_file = os.path.join(config_dir,'webui.sessions')
class subclassed_render(object):
"""
try to use the html template in configured dir.
not available : use template in /deluge/
"""
def __init__(self, template_dirname, cache=False):
self.base_template = template.render(
os.path.join(path, 'templates/deluge/'),
cache=cache)
self.sub_template = template.render(
os.path.join(path, 'templates/%s/' % template_dirname),
cache=cache)
def __getattr__(self, attr):
if hasattr(self.sub_template, attr):
return getattr(self.sub_template, attr)
else:
return getattr(self.base_template, attr)
def init_process():
globals()['config'] = pickle.load(open(config_file))
globals()['render'] = subclassed_render(config.get('template'),
config.get('cache_templates'))
def init_06():
import deluge.ui.client as proxy
proxy.set_core_uri('http://localhost:58846') #How to configure this?
init_process()
globals()['proxy'] = proxy
def init_05():
import dbus
init_process()
bus = dbus.SessionBus()
proxy = bus.get_object("org.deluge_torrent.dbusplugin"
, "/org/deluge_torrent/DelugeDbusPlugin")
config = pickle.load(open(config_file))
render = template.render('templates/%s/' % config.get('template'),
cache=config.get('cache_templates'))
globals()['proxy'] = proxy
def init():
def init_gtk_05():
#appy possibly changed config-vars, only called in when runing inside gtk.
path = os.path.dirname(__file__)
from dbus_interface import get_dbus_manager
globals()['proxy'] = get_dbus_manager()
globals()['config'] = deluge.pref.Preferences(config_file, False)
globals()['render'] = template.render(os.path.join(path, 'templates/%s/' %
config.get('template')), cache=config.get('cache_templates'))
globals()['render'] = subclassed_render(config.get('template'),
config.get('cache_templates'))
#hacks to determine environment, TODO: clean up.
if 'env=0.5' in sys.argv:
init_05()
elif not hasattr(deluge, 'common'):
init_06()
elif not hasattr(deluge,'pref'):
init_05()
REVNO = '0.56.stable.' + open(os.path.join(os.path.dirname(__file__),'revno')).read()
#constants
REVNO = open(os.path.join(os.path.dirname(__file__),'revno')).read()
VERSION = open(os.path.join(os.path.dirname(__file__),'version')).read()
TORRENT_KEYS = ['distributed_copies', 'download_payload_rate',
@ -82,3 +138,30 @@ STATE_MESSAGES = (_("Queued"),
_("Finished"),
_("Seeding"),
_("Allocating"))
SPEED_VALUES = [
(-1, 'Unlimited'),
(5, '5.0 Kib/sec'),
(10, '10.0 Kib/sec'),
(15, '15.0 Kib/sec'),
(25, '25.0 Kib/sec'),
(30, '30.0 Kib/sec'),
(50, '50.0 Kib/sec'),
(80, '80.0 Kib/sec'),
(300, '300.0 Kib/sec'),
(500, '500.0 Kib/sec')
]
COOKIE_DEFAULTS = {
'auto_refresh_secs':'10'
}
try:
SESSIONS = pickle.load(open(session_file))
except:
SESSIONS = []

View file

@ -45,15 +45,21 @@ import webpy022 as web
from webpy022.webapi import cookies, setcookie as w_setcookie
from webpy022.http import seeother, url
from webpy022 import template,changequery as self_url
from webpy022.utils import Storage
from static_handler import static_handler
from deluge.common import fsize,fspeed
import traceback
import random
from operator import attrgetter
import datetime
import pickle
from md5 import md5
from deluge import common
from webserver_common import REVNO, VERSION
from webserver_common import REVNO, VERSION, COOKIE_DEFAULTS
import webserver_common as ws
from debugerror import deluge_debugerror
#init:
@ -65,14 +71,22 @@ def setcookie(key, val):
"""add 30 days expires header for persistent cookies"""
return w_setcookie(key, val , expires=2592000)
SESSIONS = [] #dumb sessions.
#really simple sessions, to bad i had to implement them myself.
def start_session():
session_id = str(random.random())
SESSIONS.append(session_id)
ws.SESSIONS.append(session_id)
if len(ws.SESSIONS) > 20: #save max 20 sessions?
ws.SESSIONS = ws.SESSIONS[-20:]
#not thread safe! , but a verry rare bug.
pickle.dump(ws.SESSIONS, open(ws.session_file,'wb'))
setcookie("session_id", session_id)
if getcookie('auto_refresh_secs') == None:
setcookie('auto_refresh_secs','10')
def end_session():
session_id = getcookie("session_id")
if session_id in ws.SESSIONS:
ws.SESSIONS.remove(session_id)
#not thread safe! , but a verry rare bug.
pickle.dump(ws.SESSIONS, open(ws.session_file,'wb'))
def do_redirect():
"""for redirects after a POST"""
@ -92,7 +106,6 @@ def error_page(error):
print ws.render.error(error)
def getcookie(key, default=None):
COOKIE_DEFAULTS = {'auto_refresh_secs':'10'}
key = str(key).strip()
ck = cookies()
val = ck.get(key, default)
@ -120,9 +133,8 @@ def check_session(func):
"""
def deco(self, name):
vars = web.input(redir_after_login=None)
ck = cookies()
if ck.has_key("session_id") and ck["session_id"] in SESSIONS:
if ck.has_key("session_id") and ck["session_id"] in ws.SESSIONS:
return func(self, name) #ok, continue..
elif vars.redir_after_login:
seeother(url("/login",redir=self_url()))
@ -154,6 +166,77 @@ def remote(func):
print traceback.format_exc()
return deco
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_stats():
stats = Storage({
'download_rate':fspeed(ws.proxy.get_download_rate()),
'upload_rate':fspeed(ws.proxy.get_upload_rate()),
'max_download':ws.proxy.get_config_value('max_download_speed_bps'),
'max_upload':ws.proxy.get_config_value('max_upload_speed_bps'),
})
if stats.max_upload < 0:
stats.max_upload = _("Unlimited")
else:
stats.max_upload = fspeed(stats.max_upload)
if stats.max_download < 0:
stats.max_download = _("Unlimited")
else:
stats.max_download = fspeed(stats.max_download)
return stats
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = Storage(ws.proxy.get_torrent_status(torrent_id,ws.TORRENT_KEYS))
#add missing values for deluge 0.6:
for key in ws.TORRENT_KEYS:
if not key in status:
status[key] = 0
status["id"] = torrent_id
#for naming the status-images
status["calc_state_str"] = "downloading"
if status["paused"]:
status["calc_state_str"] = "inactive"
elif status["is_seed"]:
status["calc_state_str"] = "seeding"
#action for torrent_pause
if status["calc_state_str"] == "inactive":
status["action"] = "start"
else:
status["action"] = "stop"
if status["paused"]:
status["message"] = _("Paused %s%%") % status['progress']
else:
status["message"] = "%s %i%%" % (ws.STATE_MESSAGES[status["state"]]
, status['progress'])
#add some pre-calculated values
status.update({
"calc_total_downloaded" : (fsize(status["total_done"])
+ " (" + fsize(status["total_download"]) + ")"),
"calc_total_uploaded": (fsize(status['uploaded_memory']
+ status["total_payload_upload"]) + " ("
+ fsize(status["total_upload"]) + ")"),
})
return status
#/utils
#template-defs:
def template_crop(text, end):
if len(text) > end:
@ -176,12 +259,15 @@ def template_sort_head(id,name):
return ws.render.sort_column_head(id, name, order, active_up, active_down)
def template_part_stats():
return ws.render.part_stats(get_stats())
def get_config(var):
return ws.config.get(var)
template.Template.globals.update({
'sort_head': template_sort_head,
'part_stats':template_part_stats,
'crop': template_crop,
'_': _ , #gettext/translations
'str': str, #because % in templetor is broken.
@ -198,145 +284,20 @@ template.Template.globals.update({
})
#/template-defs
def create_webserver(urls, methods):
from webpy022.request import webpyfunc
from webpy022 import webapi
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
#------------------------------------------------------------------------------
#Some copy and paste from web.py
#mostly caused by /static
#TODO : FIX THIS.
#static-files serving should be moved to the normal webserver!
from SimpleHTTPServer import SimpleHTTPRequestHandler
from BaseHTTPServer import BaseHTTPRequestHandler
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
from BaseHTTPServer import BaseHTTPRequestHandler
from webpy022.request import webpyfunc
from webpy022 import webapi
import os
import posixpath
import urllib
import urlparse
class RelativeHandler(SimpleHTTPRequestHandler):
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.path.dirname(__file__)
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
class StaticApp(RelativeHandler):
"""WSGI application for serving static files."""
def __init__(self, environ, start_response):
self.headers = []
self.environ = environ
self.start_response = start_response
def send_response(self, status, msg=""):
self.status = str(status) + " " + msg
def send_header(self, name, value):
self.headers.append((name, value))
def end_headers(self):
pass
def log_message(*a): pass
def __iter__(self):
environ = self.environ
self.path = environ.get('PATH_INFO', '')
self.client_address = environ.get('REMOTE_ADDR','-'), \
environ.get('REMOTE_PORT','-')
self.command = environ.get('REQUEST_METHOD', '-')
from cStringIO import StringIO
self.wfile = StringIO() # for capturing error
f = self.send_head()
self.start_response(self.status, self.headers)
if f:
block_size = 16 * 1024
while True:
buf = f.read(block_size)
if not buf:
break
yield buf
f.close()
else:
value = self.wfile.getvalue()
yield value
class WSGIWrapper(BaseHTTPRequestHandler):
"""WSGI wrapper for logging the status and serving static files."""
def __init__(self, app):
self.app = app
self.format = '%s - - [%s] "%s %s %s" - %s'
def __call__(self, environ, start_response):
def xstart_response(status, response_headers, *args):
write = start_response(status, response_headers, *args)
self.log(status, environ)
return write
path = environ.get('PATH_INFO', '')
if path.startswith('/static/'):
return StaticApp(environ, xstart_response)
else:
return self.app(environ, xstart_response)
def log(self, status, environ):
#mvoncken,no logging..
return
outfile = environ.get('wsgi.errors', web.debug)
req = environ.get('PATH_INFO', '_')
protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
method = environ.get('REQUEST_METHOD', '-')
host = "%s:%s" % (environ.get('REMOTE_ADDR','-'),
environ.get('REMOTE_PORT','-'))
#@@ It is really bad to extend from
#@@ BaseHTTPRequestHandler just for this method
time = self.log_date_time_string()
print >> outfile, self.format % (host, time, protocol,
method, req, status)
def create_webserver(urls,methods):
func = webapi.wsgifunc(webpyfunc(urls,methods, False))
server_address=("0.0.0.0",ws.config.get('port'))
func = WSGIWrapper(func)
func = webapi.wsgifunc(webpyfunc(urls, methods, False))
server_address=("0.0.0.0", int(ws.config.get('port')))
server = CherryPyWSGIServer(server_address, func, server_name="localhost")
print "(created) http://%s:%d/" % server_address
print "http://%s:%d/" % server_address
return server
#------
__all__ = ['deluge_page_noauth', 'deluge_page', 'remote',
'auto_refreshed', 'check_session',
'do_redirect', 'error_page','start_session','getcookie'
,'create_webserver','setcookie']
,'setcookie','create_webserver','end_session',
'get_torrent_status', 'check_pwd','static_handler']

View file

@ -54,4 +54,7 @@ plugins/WebSeed/__init__.py
plugins/WebSeed/webseed.glade
plugins/Scheduler/__init__.py
plugins/Scheduler/plugin.py
plugins/WebUI/__init__.py
plugins/WebUi/__init__.py
plugins/WebUi/webserver_common.py
plugins/WebUi/deluge_webserver.py
plugins/WebUi/scripts/template_strings.py

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-25 21:56-0500\n"
"POT-Creation-Date: 2007-10-27 20:34-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -88,7 +88,7 @@ msgstr ""
msgid "<b>Torrent Info</b>"
msgstr ""
#: glade/delugegtk.glade:750
#: glade/delugegtk.glade:750 plugins/WebUi/scripts/template_strings.py:8
msgid "Details"
msgstr ""
@ -149,11 +149,13 @@ msgid "Status"
msgstr ""
#: glade/delugegtk.glade:965 src/interface.py:602
#: plugins/WebUi/scripts/template_strings.py:19
msgid "Seeders"
msgstr ""
#: glade/delugegtk.glade:974 src/interface.py:605
#: plugins/TorrentPeers/__init__.py:72
#: plugins/WebUi/scripts/template_strings.py:15
msgid "Peers"
msgstr ""
@ -171,11 +173,11 @@ msgstr ""
msgid "Time Remaining"
msgstr ""
#: glade/delugegtk.glade:1010
#: glade/delugegtk.glade:1010 plugins/WebUi/scripts/template_strings.py:4
msgid "Availability"
msgstr ""
#: glade/delugegtk.glade:1019
#: glade/delugegtk.glade:1019 plugins/WebUi/scripts/template_strings.py:21
msgid "Share Ratio"
msgstr ""
@ -211,7 +213,7 @@ msgstr ""
msgid "Remove Torrent"
msgstr ""
#: glade/delugegtk.glade:1157
#: glade/delugegtk.glade:1157 plugins/WebUi/scripts/template_strings.py:18
msgid "Remove"
msgstr ""
@ -278,7 +280,7 @@ msgstr ""
msgid "Delete downloaded files"
msgstr ""
#: glade/dgtkpopups.glade:88
#: glade/dgtkpopups.glade:88 plugins/WebUi/scripts/template_strings.py:6
msgid "Delete .torrent file"
msgstr ""
@ -294,7 +296,7 @@ msgstr ""
msgid "Clear Finished"
msgstr ""
#: glade/dgtkpopups.glade:241
#: glade/dgtkpopups.glade:241 plugins/WebUi/scripts/template_strings.py:22
msgid "Speed"
msgstr ""
@ -542,6 +544,7 @@ msgid "<b>Seeding</b>"
msgstr ""
#: glade/preferences_dialog.glade:964 src/core.py:91
#: plugins/WebUi/webserver_common.py:139
msgid "Seeding"
msgstr ""
@ -646,6 +649,7 @@ msgstr ""
#: glade/preferences_dialog.glade:1478 glade/preferences_dialog.glade:1672
#: glade/preferences_dialog.glade:1866 glade/preferences_dialog.glade:2060
#: plugins/WebUi/scripts/template_strings.py:13
msgid "Password"
msgstr ""
@ -1004,7 +1008,7 @@ msgstr ""
msgid "Name"
msgstr ""
#: src/interface.py:614
#: src/interface.py:614 plugins/WebUi/scripts/template_strings.py:10
msgid "ETA"
msgstr ""
@ -1088,31 +1092,32 @@ msgstr ""
msgid "Are you sure that you want to remove all seeding torrents?"
msgstr ""
#: src/core.py:85
#: src/core.py:85 plugins/WebUi/webserver_common.py:133
msgid "Queued"
msgstr ""
#: src/core.py:86
#: src/core.py:86 plugins/WebUi/webserver_common.py:134
msgid "Checking"
msgstr ""
#: src/core.py:87
#: src/core.py:87 plugins/WebUi/webserver_common.py:135
msgid "Connecting"
msgstr ""
#: src/core.py:88
#: src/core.py:88 plugins/WebUi/webserver_common.py:136
msgid "Downloading Metadata"
msgstr ""
#: src/core.py:89 plugins/BlocklistImport/ui.py:117
#: plugins/WebUi/webserver_common.py:137
msgid "Downloading"
msgstr ""
#: src/core.py:90
#: src/core.py:90 plugins/WebUi/webserver_common.py:138
msgid "Finished"
msgstr ""
#: src/core.py:92
#: src/core.py:92 plugins/WebUi/webserver_common.py:140
msgid "Allocating"
msgstr ""
@ -1390,66 +1395,66 @@ msgstr ""
msgid "Torrent Creator"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:103
#: plugins/TorrentCreator/torrentcreator.glade:102
msgid "This torrent will be made from a single file"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:104
#: plugins/TorrentCreator/torrentcreator.glade:103
msgid "File:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:120
#: plugins/TorrentCreator/torrentcreator.glade:119
msgid "This torrent will be made from a directory"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:121
#: plugins/TorrentCreator/torrentcreator.glade:120
msgid "Folder:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:138
#: plugins/TorrentCreator/torrentcreator.glade:137
msgid "<b>Source</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:184
#: plugins/TorrentCreator/torrentcreator.glade:182
msgid "Save Torrent File As:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:237
#: plugins/TorrentCreator/torrentcreator.glade:235
msgid "Load this torrent into Deluge for seeding"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:238
#: plugins/TorrentCreator/torrentcreator.glade:236
msgid "Add new torrent to queue"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:257
#: plugins/TorrentCreator/torrentcreator.glade:255
msgid "<b>Torrent File</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:305
#: plugins/TorrentCreator/torrentcreator.glade:302
msgid "<b>Trackers</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:352
#: plugins/TorrentCreator/torrentcreator.glade:348
msgid "<b>Comments</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:389
#: plugins/TorrentCreator/torrentcreator.glade:384
msgid "<b>Author</b>"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:425
#: plugins/TorrentCreator/torrentcreator.glade:423
msgid "Set Private Flag"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:441
#: plugins/TorrentCreator/torrentcreator.glade:461
#: plugins/TorrentCreator/torrentcreator.glade:436
#: plugins/TorrentCreator/torrentcreator.glade:457
msgid ""
"The smaller the piece sizes, the more efficient the transfers will be, but "
"the actual \".torrent\" file will be larger"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:442
#: plugins/TorrentCreator/torrentcreator.glade:437
msgid ""
"32 KiB\n"
"64 KiB\n"
@ -1457,13 +1462,14 @@ msgid ""
"256 KiB\n"
"512 KiB\n"
"1024 KiB\n"
"2048 KiB\n"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:463
#: plugins/TorrentCreator/torrentcreator.glade:459
msgid "Piece Size:"
msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:477
#: plugins/TorrentCreator/torrentcreator.glade:473
msgid "<b>Advanced</b>"
msgstr ""
@ -2102,3 +2108,171 @@ msgid ""
"When set to -1 (unlimited), the global limits in Deluge's preferences will "
"be obeyed."
msgstr ""
#: plugins/WebUi/__init__.py:31
msgid "Web User Interface"
msgstr ""
#: plugins/WebUi/__init__.py:34
msgid ""
"A Web based User Interface\n"
"\n"
"Firefox greasemonkey script: http://userscripts.org/scripts/show/12639\n"
"\n"
"Remotely add a file: \"curl -F torrent=@./test1.torrent -F pwd=deluge http://"
"localhost:8112/remote/torrent/add\"\n"
"\n"
"There is support for multiple templates, but just one is included.\n"
"\n"
"Other contributors:\n"
"*somedude : template enhancements.\n"
"*markybob : stability : synced with his changes in deluge-svn.\n"
msgstr ""
#: plugins/WebUi/__init__.py:179
msgid "WebUi Config"
msgstr ""
#: plugins/WebUi/__init__.py:187
msgid "Port Number"
msgstr ""
#: plugins/WebUi/__init__.py:188
msgid "New Password"
msgstr ""
#: plugins/WebUi/__init__.py:189
msgid "New Password(confirm)"
msgstr ""
#: plugins/WebUi/__init__.py:190
msgid "Template"
msgstr ""
#: plugins/WebUi/__init__.py:191
msgid "Button Style"
msgstr ""
#: plugins/WebUi/__init__.py:193
msgid "Cache Templates"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Text and image"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Image Only"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Text Only"
msgstr ""
#: plugins/WebUi/__init__.py:243
msgid "Confirmed Password <> New Password\n"
msgstr ""
#: plugins/WebUi/webserver_common.py:48
msgid "translate something"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:142
msgid "Choose an url or a torrent, not both."
msgstr ""
#: plugins/WebUi/deluge_webserver.py:153
msgid "no data."
msgstr ""
#: plugins/WebUi/deluge_webserver.py:230
msgid "refresh must be > 0"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:238
msgid "Download Speed Limit"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:242
msgid "Upload Speed Limit"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:1
msgid "# Of Files"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:2
msgid "About"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:3
msgid "Auto refresh:"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:5
msgid "Debug:Data Dump"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:7
msgid "Delete downloaded files."
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:9
msgid "Downloaded"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:11
msgid "Next Announce"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:12
msgid "Off"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:14
msgid "Password is invalid,try again"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:16
msgid "Pieces"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:17
msgid "Refresh page every:"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:20
msgid "Set"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:23
msgid "Submit"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:24
msgid "Total Size"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:25
msgid "Tracker"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:26
msgid "Tracker Status"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:27
msgid "Upload torrent"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:28
msgid "Uploaded"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:29
msgid "Url"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:30
msgid "seconds"
msgstr ""