webui rev-80

This commit is contained in:
Marcos Pinto 2007-10-07 04:56:01 +00:00
parent 278c6c9f3e
commit bf8727f7bf
12 changed files with 407 additions and 2204 deletions

View File

@ -115,7 +115,7 @@ class DbusManager(dbus.service.Object):
for key in ["total_seeds", "total_peers","is_seed", "total_done",
"total_download", "total_upload", "download_rate",
"upload_rate", "num_files", "piece_length", "distributed_copies"
,"next_announce","tracker"]:
,"next_announce","tracker","queue_pos"]:
status[key] = state[key]
#print 'all_keys:',sorted(status.keys())
@ -162,6 +162,19 @@ class DbusManager(dbus.service.Object):
self._add_torrent(filename)
return True
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="b")
def queue_up(self, torrent_id):
print 'UP!'
self.core.queue_up(int(torrent_id))
return True
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="b")
def queue_down(self, torrent_id):
self.core.queue_down(int(torrent_id))
return True
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="ss", out_signature="b")
def add_torrent_filecontent(self, name, filecontent_b64):

View File

@ -102,6 +102,8 @@ urls = (
"/torrent/pause(.*)", "torrent_pause",
"/torrent/add(.*)", "torrent_add",
"/torrent/delete/(.*)", "torrent_delete",
"/torrent/queue/up/(.*)", "torrent_queue_up",
"/torrent/queue/down/(.*)", "torrent_queue_down",
"/pause_all(.*)", "pause_all",
"/resume_all(.*)", "resume_all",
"/refresh/set(.*)", "refresh_set",
@ -234,6 +236,21 @@ class torrent_delete:
ws.proxy.remove_torrent(torrent_id, data_also, torrent_also)
do_redirect()
class torrent_queue_up:
@check_session
def POST(self, name):
torrent_id = name
ws.proxy.queue_up(torrent_id)
do_redirect()
class torrent_queue_down:
@check_session
def POST(self, name):
torrent_id = name
ws.proxy.queue_down(torrent_id)
do_redirect()
class pause_all:
@check_session
def POST(self, name):

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
#!/bin/sh
cd ~/prj/WebUi
bzr revno > revno
bzr version-info > version
rm ~/prj/WebUi/WebUi.tgz
cd ~/prj
tar -zcvf ~/prj/WebUi/WebUi.tgz WebUi/ --exclude '.*' --exclude '*.pyc' --exclude '*.tgz' --exclude 'attic' --exclude 'xul' --exclude '*.sh' --exclude '*.*~'

View File

@ -0,0 +1,24 @@
from __future__ import with_statement
import os
import re
template_dir = '~/prj/WebUi/templates/deluge'
template_dir = os.path.expanduser(template_dir )
files = [os.path.join(template_dir,fname)
for fname in os.listdir(template_dir)
if fname.endswith('.html')]
all_strings = []
for filename in files:
with open(filename,'r') as f:
content = f.read()
all_strings += re.findall("\$\_\(\'(.*)\'\)",content)
all_strings += re.findall("\$\_\(\"(.*)\"\)",content)
all_strings = sorted(set(all_strings))
with open ('./template_strings.py','w') as f:
for value in all_strings:
f.write("_('%s')\n" % value )

View File

@ -0,0 +1,30 @@
_('# Of Files')
_('About')
_('Auto refresh:')
_('Availability')
_('Debug:Data Dump')
_('Delete .torrent file')
_('Delete downloaded files.')
_('Details')
_('Downloaded')
_('ETA')
_('Next Announce')
_('Off')
_('Password')
_('Password is invalid,try again')
_('Peers')
_('Pieces')
_('Refresh page every:')
_('Remove')
_('Seeders')
_('Set')
_('Share Ratio')
_('Speed')
_('Submit')
_('Total Size')
_('Tracker')
_('Tracker Status')
_('Upload torrent')
_('Uploaded')
_('Url')
_('seconds')

View File

@ -5,7 +5,7 @@ $:render.header(_('Torrent list'))
<table class="torrent_list" border=1>
<tr>
$:(sort_head('calc_state_str', 'S'))
$:(sort_head('id', '#'))
$:(sort_head('queue_pos', '#'))
$:(sort_head('name', _('Name')))
$:(sort_head('total_size', _('Size')))
$:(sort_head('progress', _('Progress')))
@ -24,7 +24,7 @@ $for torrent in torrent_list:
src="/static/images/$(torrent.calc_state_str)16.png"
name="$torrent.action" value="$torrent.id">
</td>
<td>$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>$fsize(torrent.total_size)</td>

View File

@ -1,8 +1,8 @@
$def with (method, url, title, image='')
<div class="deluge_button">
<form method="$method" action="$url" class="deluge_button">
<button type="submit" class="deluge_button">
<input type="hidden" name="redir" value="$self_url()">
<button type="submit" class="deluge_button">
$if (get_config('button_style') == 0):
$title
$if image:

View File

@ -82,6 +82,7 @@ class="deluge_button">
$:render.part_button('GET', '/torrent/delete/' + str(torrent.id), _('Remove'), 'tango/user-trash.png')
<br>
[<a onclick="javascript:toggle_dump()">$_('Debug:Data Dump')</a>]
@ -103,6 +104,16 @@ function toggle_dump(){
</script>
</div>
<div class='panel'>
Queue pos: $torrent.queue_pos
$:render.part_button('POST', '/torrent/queue/up/' + str(torrent.id), _('Queue Up'), 'tango/up.png')
$:render.part_button('POST', '/torrent/queue/down/' + str(torrent.id), _('Queue Down'), 'tango/down.png')
</div>
$:render.part_refresh()
$:render.footer()

View File

@ -52,11 +52,14 @@ if not hasattr(deluge,'pref'):
def init():
#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('templates/%s/' % config.get('template'),
cache=config.get('cache_templates'))
globals()['render'] = template.render(os.path.join(path, 'templates/%s/' %
config.get('template')), cache=config.get('cache_templates'))
REVNO = open(os.path.join(os.path.dirname(__file__),'revno')).read()
@ -69,7 +72,7 @@ TORRENT_KEYS = ['distributed_copies', 'download_payload_rate',
'total_payload_download', 'total_payload_upload', 'total_peers',
'total_seeds', 'total_size', 'total_upload', 'total_wanted',
'tracker_status', 'upload_payload_rate', 'upload_rate',
'uploaded_memory','tracker','state']
'uploaded_memory','tracker','state','queue_pos']
STATE_MESSAGES = (_("Queued"),
_("Checking"),

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# webserver_framework.py
#
# Copyright (C) Martijn Voncken 2007 <mvoncken@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
@ -29,53 +31,303 @@
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
import os
import deluge
from deluge.common import INSTALL_PREFIX
"""
Todo's before stable:
-__init__:kill->restart is not waiting for kill to be finished.
--later/features:---
-alternating rows?
-set prio
-clear finished?
-torrent files.
"""
import webpy022 as web
from webpy022.webapi import cookies, setcookie
from webpy022.http import seeother, url
from webpy022 import template,changequery as self_url
import traceback
import random
import pickle
from webpy022 import template
from operator import attrgetter
random.seed()
from deluge import common
from webserver_common import REVNO, VERSION
import webserver_common as ws
config_file = deluge.common.CONFIG_DIR + "/webui.conf"
#a bit hacky way of detecting i'm in the deluge gui or in a process :(
if not hasattr(deluge,'pref'):
import dbus
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'))
def init():
#appy possibly changed config-vars, only called in when runing inside gtk.
from dbus_interface import get_dbus_manager
globals()['proxy'] = get_dbus_manager()
globals()['config'] = deluge.pref.Preferences(config_file, False)
globals()['render'] = template.render('templates/%s/' % config.get('template'),
cache=config.get('cache_templates'))
#init:
web.webapi.internalerror = web.debugerror
REVNO = open(os.path.join(os.path.dirname(__file__),'revno')).read()
VERSION = open(os.path.join(os.path.dirname(__file__),'version')).read()
#/init
#methods:
SESSIONS = [] #dumb sessions.
def start_session():
session_id = str(random.random())
SESSIONS.append(session_id)
setcookie("session_id", session_id)
if getcookie('auto_refresh_secs') == None:
setcookie('auto_refresh_secs','10')
def do_redirect():
"""for redirects after a POST"""
vars = web.input(redir = None)
ck = cookies()
if vars.redir:
seeother(vars.redir)
elif ("order" in ck and "sort" in ck):
seeother(url("/index", sort=ck['sort'], order=ck['order']))
else:
seeother(url("/index"))
def error_page(error):
web.header("Content-Type", "text/html; charset=utf-8")
web.header("Cache-Control", "no-cache, must-revalidate")
print ws.render.error(error)
def getcookie(key, default=None):
ck = cookies()
return str(ck.get(key, default))
#deco's:
def deluge_page_noauth(func):
"""
add http headers
print result of func
"""
def deco(self, name=None):
web.header("Content-Type", "text/html; charset=utf-8")
web.header("Cache-Control", "no-cache, must-revalidate")
res = func(self, name)
print res
return deco
def check_session(func):
"""
a decorator
return func if session is valid, else redirect to login page.
"""
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:
return func(self, name) #ok, continue..
elif vars.redir_after_login:
seeother(url("/login",redir=self_url()))
else:
seeother("/login") #do not continue, and redirect to login page
return deco
def deluge_page(func):
return check_session(deluge_page_noauth(func))
#combi-deco's:
def auto_refreshed(func):
"decorator:adds a refresh header"
def deco(self, name):
if getcookie('auto_refresh') == '1':
web.header("Refresh", "%i ; url=%s" %
(int(getcookie('auto_refresh_secs',10)),self_url()))
return func(self, name)
return deco
def remote(func):
"decorator for remote api's"
def deco(self, name):
try:
print func(self, name)
except Exception, e:
print 'error:' + e.message
print '-'*20
print traceback.format_exc()
return deco
#template-defs:
def template_crop(text, end):
if len(text) > end:
return text[0:end - 3] + '...'
return text
def template_sort_head(id,name):
#got tired of doing these complex things inside templetor..
vars = web.input(sort=None, order=None)
active_up = False
active_down = False
order = 'down'
if vars.sort == id:
if vars.order == 'down':
order = 'up'
active_down = True
else:
active_up = True
return ws.render.sort_column_head(id, name, order, active_up, active_down)
def get_config(var):
return ws.config.get(var)
template.Template.globals.update({
'sort_head': template_sort_head,
'crop': template_crop,
'_': _ , #gettext/translations
'str': str, #because % in templetor is broken.
'sorted': sorted,
'get_config': get_config,
'self_url': self_url,
'fspeed': common.fspeed,
'fsize': common.fsize,
'render': ws.render, #for easy resuse of templates
'rev': 'rev.%s' % (REVNO, ),
'version': VERSION,
'getcookie':getcookie,
'get': lambda (var): getattr(web.input(**{var:None}),var) # unreadable :-(
})
#/template-defs
#------------------------------------------------------------------------------
#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)
server = CherryPyWSGIServer(server_address, func, server_name="localhost")
print "(created) 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']
TORRENT_KEYS = ['distributed_copies', 'download_payload_rate',
'download_rate', 'eta', 'is_seed', 'name', 'next_announce',
'num_files', 'num_peers', 'num_pieces', 'num_seeds', 'paused',
'piece_length','progress', 'ratio', 'total_done', 'total_download',
'total_payload_download', 'total_payload_upload', 'total_peers',
'total_seeds', 'total_size', 'total_upload', 'total_wanted',
'tracker_status', 'upload_payload_rate', 'upload_rate',
'uploaded_memory','tracker','state']
STATE_MESSAGES = (_("Queued"),
_("Checking"),
_("Connecting"),
_("Downloading Metadata"),
_("Downloading"),
_("Finished"),
_("Seeding"),
_("Allocating"))