mirror of
https://git.deluge-torrent.org/deluge
synced 2025-08-08 01:18:39 +00:00
switch over to using twisted-web
This commit is contained in:
parent
ea734931d7
commit
e96b4c4f58
5 changed files with 118 additions and 98 deletions
|
@ -7,11 +7,14 @@ html, body {
|
||||||
height:100%;
|
height:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.deluge-torrents td {
|
||||||
|
height: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.deluge-torrents .torrent-name {
|
.deluge-torrents .torrent-name {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
height: 16px;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.deluge-torrents .deluge-torrent-progress {
|
.deluge-torrents .deluge-torrent-progress {
|
||||||
|
@ -64,3 +67,4 @@ html, body {
|
||||||
|
|
||||||
.deluge-status dd.torrent_name, .deluge-status dd.tracker, .deluge-status dd.path {
|
.deluge-status dd.torrent_name, .deluge-status dd.tracker, .deluge-status dd.path {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
|
}
|
|
@ -3,55 +3,55 @@ Deluge.ToolBar = new Ext.Toolbar({
|
||||||
{
|
{
|
||||||
id: 'create',
|
id: 'create',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Create',
|
text: _('Create'),
|
||||||
icon: '/icons/16/create.png',
|
icon: '/icons/16/create.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'add',
|
id: 'add',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Add',
|
text: _('Add'),
|
||||||
icon: '/icons/16/add.png',
|
icon: '/icons/16/add.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'remove',
|
id: 'remove',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Remove',
|
text: _('Remove'),
|
||||||
icon: '/icons/16/remove.png',
|
icon: '/icons/16/remove.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'pause',
|
id: 'pause',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Pause',
|
text: _('Pause'),
|
||||||
icon: '/icons/16/pause.png',
|
icon: '/icons/16/pause.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'resume',
|
id: 'resume',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Resume',
|
text: _('Resume'),
|
||||||
icon: '/icons/16/start.png',
|
icon: '/icons/16/start.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'up',
|
id: 'up',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Up',
|
text: _('Up'),
|
||||||
icon: '/icons/16/up.png',
|
icon: '/icons/16/up.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'down',
|
id: 'down',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Down',
|
text: _('Down'),
|
||||||
icon: '/icons/16/down.png',
|
icon: '/icons/16/down.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'preferences',
|
id: 'preferences',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Preferences',
|
text: _('Preferences'),
|
||||||
icon: '/icons/16/preferences.png',
|
icon: '/icons/16/preferences.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
},{
|
},{
|
||||||
id: 'connectionman',
|
id: 'connectionman',
|
||||||
cls: 'x-btn-text-icon',
|
cls: 'x-btn-text-icon',
|
||||||
text: 'Connection Manager',
|
text: _('Connection Manager'),
|
||||||
icon: '/icons/16/connection_manager.png',
|
icon: '/icons/16/connection_manager.png',
|
||||||
handler: torrentAction
|
handler: torrentAction
|
||||||
}
|
}
|
||||||
|
@ -105,10 +105,11 @@ function torrentAction(item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Deluge.SideBar = {
|
Deluge.SideBar = {
|
||||||
region:'west',
|
region: 'west',
|
||||||
id:'west-panel',
|
id: 'sidebar',
|
||||||
title:'Sidebar',
|
cls: 'deluge-sidebar',
|
||||||
split:true,
|
title: _('Sidebar'),
|
||||||
|
split: true,
|
||||||
width: 200,
|
width: 200,
|
||||||
minSize: 175,
|
minSize: 175,
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
|
|
@ -36,4 +36,8 @@ Deluge.Details.Status.add({
|
||||||
id: 'status-details',
|
id: 'status-details',
|
||||||
cls: 'deluge-status',
|
cls: 'deluge-status',
|
||||||
border: false
|
border: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deluge.Details.update = function(torrentId) {
|
||||||
|
Deluge.Details.getActiveTab().update(torrent);
|
||||||
|
}
|
|
@ -13,6 +13,12 @@ Deluge.Ui = {
|
||||||
bbar: Deluge.StatusBar
|
bbar: Deluge.StatusBar
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deluge.SideBar = this.MainPanel.items.get('sidebar');
|
||||||
|
Deluge.SideBar.on('collapse', function(bar) {
|
||||||
|
|
||||||
|
//alert(JSON.encode($('sidebar').getSize()));
|
||||||
|
});
|
||||||
|
|
||||||
this.Viewport = new Ext.Viewport({
|
this.Viewport = new Ext.Viewport({
|
||||||
layout: 'fit',
|
layout: 'fit',
|
||||||
items: [this.MainPanel]
|
items: [this.MainPanel]
|
||||||
|
|
|
@ -37,10 +37,9 @@ if sys.version_info > (2, 6):
|
||||||
else:
|
else:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
|
from twisted.application import service, internet
|
||||||
from twisted.internet.defer import Deferred
|
from twisted.internet.defer import Deferred
|
||||||
from twisted.application import service, strports
|
from twisted.web import http, resource, server, static
|
||||||
from twisted.web2 import static, wsgi, resource, responsecode
|
|
||||||
from twisted.web2 import stream, http, http_headers, server, channel
|
|
||||||
|
|
||||||
from mako.template import Template as MakoTemplate
|
from mako.template import Template as MakoTemplate
|
||||||
|
|
||||||
|
@ -60,14 +59,14 @@ class Template(MakoTemplate):
|
||||||
|
|
||||||
class JSONException(Exception): pass
|
class JSONException(Exception): pass
|
||||||
|
|
||||||
class JSON(resource.PostableResource):
|
class JSON(resource.Resource):
|
||||||
"""
|
"""
|
||||||
A Twisted Web2 resource that exposes a JSON-RPC interface for web clients
|
A Twisted Web resource that exposes a JSON-RPC interface for web clients
|
||||||
to use.
|
to use.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
resource.PostableResource.__init__(self)
|
resource.Resource.__init__(self)
|
||||||
self._remote_methods = []
|
self._remote_methods = []
|
||||||
self._local_methods = {
|
self._local_methods = {
|
||||||
"web.update_ui": self.update_ui,
|
"web.update_ui": self.update_ui,
|
||||||
|
@ -76,12 +75,16 @@ class JSON(resource.PostableResource):
|
||||||
"web.add_torrents": self.add_torrents
|
"web.add_torrents": self.add_torrents
|
||||||
}
|
}
|
||||||
for entry in open(common.get_default_config_dir("auth")):
|
for entry in open(common.get_default_config_dir("auth")):
|
||||||
username, password = entry.split(":")
|
parts = entry.split(":")
|
||||||
|
if len(parts) > 1:
|
||||||
|
username, password = parts[0], parts[1]
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
if username != "localclient":
|
if username != "localclient":
|
||||||
continue
|
continue
|
||||||
self.local_username = username
|
self.local_username = username
|
||||||
self.local_password = password
|
self.local_password = password
|
||||||
print username, password
|
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|
||||||
def connect(self, host="localhost", username=None, password=None):
|
def connect(self, host="localhost", username=None, password=None):
|
||||||
|
@ -159,21 +162,22 @@ class JSON(resource.PostableResource):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise JSONException(str(e))
|
raise JSONException(str(e))
|
||||||
|
|
||||||
def _on_rpc_request_finished(self, result, response):
|
def _on_rpc_request_finished(self, result, response, request):
|
||||||
"""
|
"""
|
||||||
Sends the response of any rpc calls back to the json-rpc client.
|
Sends the response of any rpc calls back to the json-rpc client.
|
||||||
"""
|
"""
|
||||||
response["result"] = result
|
response["result"] = result
|
||||||
return self._send_response(response)
|
return self._send_response(request, response)
|
||||||
|
|
||||||
def _on_rpc_request_failed(self, reason, response):
|
def _on_rpc_request_failed(self, reason, response, request):
|
||||||
"""
|
"""
|
||||||
Handles any failures that occured while making an rpc call.
|
Handles any failures that occured while making an rpc call.
|
||||||
"""
|
"""
|
||||||
print reason
|
print reason
|
||||||
return http.Response(responsecode.INTERNAL_SERVER_ERROR)
|
request.setResponseCode(http.INTERNAL_SERVER_ERROR)
|
||||||
|
return ""
|
||||||
|
|
||||||
def _on_json_request(self, result, request):
|
def _on_json_request(self, request):
|
||||||
"""
|
"""
|
||||||
Handler to take the json data as a string and pass it on to the
|
Handler to take the json data as a string and pass it on to the
|
||||||
_handle_request method for further processing.
|
_handle_request method for further processing.
|
||||||
|
@ -181,8 +185,8 @@ class JSON(resource.PostableResource):
|
||||||
log.debug("json-request: %s", request.json)
|
log.debug("json-request: %s", request.json)
|
||||||
response = {"result": None, "error": None, "id": None}
|
response = {"result": None, "error": None, "id": None}
|
||||||
d, response["id"] = self._handle_request(request.json)
|
d, response["id"] = self._handle_request(request.json)
|
||||||
d.addCallback(self._on_rpc_request_finished, response)
|
d.addCallback(self._on_rpc_request_finished, response, request)
|
||||||
d.addErrback(self._on_rpc_request_failed, response)
|
d.addErrback(self._on_rpc_request_failed, response, request)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _on_json_request_failed(self, reason, request):
|
def _on_json_request_failed(self, reason, request):
|
||||||
|
@ -190,38 +194,31 @@ class JSON(resource.PostableResource):
|
||||||
Errback handler to return a HTTP code of 500.
|
Errback handler to return a HTTP code of 500.
|
||||||
"""
|
"""
|
||||||
print reason
|
print reason
|
||||||
return http.Response(responsecode.INTERNAL_SERVER_ERROR)
|
request.setResponseCode(http.INTERNAL_SERVER_ERROR)
|
||||||
|
return ""
|
||||||
|
|
||||||
def _send_response(self, response):
|
def _send_response(self, request, response):
|
||||||
response = json.dumps(response)
|
response = json.dumps(response)
|
||||||
return http.Response(responsecode.OK,
|
request.setHeader("content-type", "application/x-json")
|
||||||
{"content-type": http_headers.MimeType("application", "x-json")},
|
request.write(response)
|
||||||
stream=response)
|
request.finish()
|
||||||
|
|
||||||
def http_POST(self, request):
|
def render(self, request):
|
||||||
"""
|
"""
|
||||||
Handles all the POST requests made to the /json controller.
|
Handles all the POST requests made to the /json controller.
|
||||||
"""
|
"""
|
||||||
request.json = ""
|
|
||||||
def handle_data(data):
|
|
||||||
request.json += data
|
|
||||||
|
|
||||||
d = stream.readStream(request.stream, handle_data)
|
|
||||||
d.addCallbacks(
|
|
||||||
self._on_json_request,
|
|
||||||
self._on_json_request_failed,
|
|
||||||
callbackArgs=(request,),
|
|
||||||
errbackArgs=(request,)
|
|
||||||
)
|
|
||||||
|
|
||||||
d.addErrback(self._on_json_request_failed, request)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def render(self, request):
|
if request.method != "POST":
|
||||||
"""
|
request.setResponseCode(http.NOT_ALLOWED)
|
||||||
Block all other HTTP methods.
|
return ""
|
||||||
"""
|
|
||||||
return http.Response(responsecode.NOT_ALLOWED)
|
try:
|
||||||
|
request.content.seek(0)
|
||||||
|
request.json = request.content.read()
|
||||||
|
d = self._on_json_request(request)
|
||||||
|
return server.NOT_DONE_YET
|
||||||
|
except Exception, e:
|
||||||
|
return self._on_json_request_failed(e, request)
|
||||||
|
|
||||||
def update_ui(self, keys, filter_dict, cache_id=None):
|
def update_ui(self, keys, filter_dict, cache_id=None):
|
||||||
|
|
||||||
|
@ -302,16 +299,14 @@ class JSON(resource.PostableResource):
|
||||||
|
|
||||||
|
|
||||||
class GetText(resource.Resource):
|
class GetText(resource.Resource):
|
||||||
headers = {
|
|
||||||
"content-type": http_headers.MimeType("text", "javascript")
|
|
||||||
}
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
|
request.setHeader("content-type", "text/javascript")
|
||||||
template = Template(filename="gettext.js")
|
template = Template(filename="gettext.js")
|
||||||
return http.Response(responsecode.OK, self.headers, template.render())
|
return template.render()
|
||||||
|
|
||||||
class Upload(resource.PostableResource):
|
class Upload(resource.Resource):
|
||||||
"""
|
"""
|
||||||
Twisted Web2 resource to handle file uploads
|
Twisted Web resource to handle file uploads
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def http_POST(self, request):
|
def http_POST(self, request):
|
||||||
|
@ -330,72 +325,81 @@ class Upload(resource.PostableResource):
|
||||||
f = open(fn, upload[2].mode)
|
f = open(fn, upload[2].mode)
|
||||||
shutil.copyfileobj(upload[2], f)
|
shutil.copyfileobj(upload[2], f)
|
||||||
filenames.append(fn)
|
filenames.append(fn)
|
||||||
return http.Response(responsecode.OK, stream="\n".join(filenames))
|
return http.Response(http.OK, stream="\n".join(filenames))
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
"""
|
"""
|
||||||
Block all other HTTP methods.
|
Block all other HTTP methods.
|
||||||
"""
|
"""
|
||||||
return http.Response(responsecode.NOT_ALLOWED)
|
return http.Response(http.NOT_ALLOWED)
|
||||||
|
|
||||||
class Render(resource.Resource):
|
class Render(resource.Resource):
|
||||||
|
|
||||||
headers = {
|
|
||||||
"Content-type": http_headers.MimeType("text", "html")
|
|
||||||
}
|
|
||||||
|
|
||||||
def locateChild(self, request, segments):
|
def getChild(self, path, request):
|
||||||
request.render_file = segments[0]
|
request.render_file = path
|
||||||
return self, ()
|
return self
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
if not hasattr(request, "render_file"):
|
if not hasattr(request, "render_file"):
|
||||||
return http.Response(responsecode.INTERNAL_SERVER_ERROR)
|
request.setResponseCode(http.INTERNAL_SERVER_ERROR)
|
||||||
|
return ""
|
||||||
|
|
||||||
filename = os.path.join("render", request.render_file)
|
filename = os.path.join("render", request.render_file)
|
||||||
template = Template(filename=filename)
|
template = Template(filename=filename)
|
||||||
return http.Response(responsecode.OK, self.headers, template.render())
|
request.setHeader("content-type", "text/html")
|
||||||
|
request.setResponseCode(http.OK)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
class Tracker(resource.Resource):
|
class Tracker(resource.Resource):
|
||||||
tracker_icons = TrackerIcons()
|
tracker_icons = TrackerIcons()
|
||||||
|
|
||||||
def locateChild(self, request, segments):
|
def getChild(self, path, request):
|
||||||
request.tracker_name = "/".join(segments)
|
request.tracker_name = path
|
||||||
return self, ()
|
return self
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
headers = {}
|
headers = {}
|
||||||
filename = self.tracker_icons.get(request.tracker_name)
|
filename = self.tracker_icons.get(request.tracker_name)
|
||||||
if filename:
|
if filename:
|
||||||
http_headers.He
|
request.setHeader("cache-control", "public, must-revalidate, max-age=86400")
|
||||||
#headers["cache-control"] = "public, must-revalidate, max-age=86400"
|
|
||||||
if filename.endswith(".ico"):
|
if filename.endswith(".ico"):
|
||||||
headers["content-type"] = http_headers.MimeType("image", "x-icon")
|
request.setHeader("content-type", "image/x-icon")
|
||||||
elif filename.endwith(".png"):
|
elif filename.endwith(".png"):
|
||||||
headers["content-type"] = http_headers.MimeType("image", "png")
|
request.setHeader("content-type", "image/png")
|
||||||
data = open(filename, "rb")
|
data = open(filename, "rb")
|
||||||
return http.Response(responsecode.OK, headers, data.read())
|
request.setResponseCode(http.OK)
|
||||||
|
return data.read()
|
||||||
else:
|
else:
|
||||||
return http.Response(responsecode.NOT_FOUND)
|
request.setResponseCode(http.NOT_FOUND)
|
||||||
|
return ""
|
||||||
|
|
||||||
class TopLevel(resource.Resource):
|
class TopLevel(resource.Resource):
|
||||||
|
|
||||||
addSlash = True
|
addSlash = True
|
||||||
child_json = JSON()
|
|
||||||
child_upload = Upload()
|
def __init__(self):
|
||||||
child_test = static.File("test.html")
|
resource.Resource.__init__(self)
|
||||||
child_js = static.File("js")
|
self.putChild("css", static.File("css"))
|
||||||
child_images = static.File("images")
|
self.putChild("gettext.js", GetText())
|
||||||
child_icons = static.File("icons")
|
self.putChild("icons", static.File("icons"))
|
||||||
child_css = static.File("css")
|
self.putChild("images", static.File("images"))
|
||||||
child_themes = static.File("themes")
|
self.putChild("js", static.File("js"))
|
||||||
child_gettext = GetText()
|
self.putChild("json", JSON())
|
||||||
child_render = Render()
|
self.putChild("upload", Upload())
|
||||||
child_tracker = Tracker()
|
self.putChild("render", Render())
|
||||||
|
self.putChild("test", static.File("test.html"))
|
||||||
|
self.putChild("themes", static.File("themes"))
|
||||||
|
self.putChild("tracker", Tracker())
|
||||||
|
|
||||||
|
def getChild(self, path, request):
|
||||||
|
if path == "":
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return resource.Resource.getChild(self, path, request)
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
template = Template(filename="index.html")
|
template = Template(filename="index.html")
|
||||||
return http.Response(responsecode.OK, stream=template.render())
|
request.setHeader("content-type", "text/html; charset=utf-8")
|
||||||
|
return template.render()
|
||||||
|
|
||||||
setupLogger(level="debug")
|
setupLogger(level="debug")
|
||||||
|
|
||||||
|
@ -411,8 +415,9 @@ try:
|
||||||
gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.error("Unable to initialize gettext/locale: %s", e)
|
log.error("Unable to initialize gettext/locale: %s", e)
|
||||||
|
|
||||||
site = server.Site(TopLevel())
|
site = server.Site(TopLevel())
|
||||||
application = service.Application("DelugeWeb")
|
application = service.Application("DelugeWeb")
|
||||||
s = strports.service("tcp:8112", channel.HTTPFactory(site))
|
sc = service.IServiceCollection(application)
|
||||||
s.setServiceParent(application)
|
i = internet.TCPServer(8112, site)
|
||||||
|
i.setServiceParent(sc)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue