4 delugent : Frontend for deluge.
6 This is meant to work with deluge 0.60 (and above, I am sure), which has
7 a core-ui split. This is a work in progress.
9 Copyright (C) 2007 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
11 This application is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version.
16 This application is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this application; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301
27 import deluge
.ui
.client
as client
28 from deluge
.log
import LOG
as log
29 from deluge
.configmanager
import ConfigManager
41 "total_size" : "Size",
42 "total_done" : "Downloaded",
43 "progress" : "Progress",
44 "num_seeds" : "# of seeds",
45 "total_seeds" : "Total seeds",
46 "num_peers" : "# of peers",
47 "total_peers" : "Total peers",
49 "download_payload_rate" : "Download speed",
50 "upload_payload_rate" : "Upload speed",
51 "ratio" : "Share Ratio",
52 "distributed_copies" : "Availability",
53 "total_uploaded" : "Uploaded",
54 "num_files" : "# of files",
62 elif size
> (1 << 20):
65 elif size
> (1 << 10):
68 return str(size
) + unit
70 def show_speed(speed
):
73 speed
= speed
/ (1 << 30)
75 elif speed
> (1 << 20):
76 speed
= speed
/ (1 << 20)
78 elif speed
> (1 << 10):
79 speed
= speed
/ (1 << 10)
81 return ("%.01f" + unit
) % speed
83 def show_state(state
):
88 class TorRow(gobject
.GObject
):
99 def __init__(self
, id):
100 self
.__gobject
_init
__()
107 status
= client
.get_torrent_status(self
.id, status_keys
.keys())
108 state
= int(status
.get("state", 0))
109 name
= str(status
.get("name", ""))
110 size
= int(status
.get("total_size", 0))
111 progress
= float(status
.get("progress", 0.0))
112 peers
= str(status
.get("num_peers", 0))
113 seeds
= str(status
.get("num_seeds", 0))
114 dlspeed
= float(status
.get("download_payload_rate", 0.0))
115 upspeed
= float(status
.get("upload_payload_rate", 0.0))
116 eta
= float(status
.get("eta", 0.0))
118 return [name
, show_state(state
), show_size(size
), str(progress
), str(seeds
), str(peers
),
119 show_speed(dlspeed
), show_speed(upspeed
), str(eta
)]
121 gobject
.type_register(TorRow
)
123 class TorList(gnt
.Tree
):
125 'info' : ('show_info', 'i')
128 gnt
.Tree
.__init
__(self
)
129 self
.set_property('columns', TorRow
.COLS
)
130 self
.set_show_title(True)
132 titles
= {TorRow
.COL_NAME
: "Name",
133 TorRow
.COL_STATE
: "State",
134 TorRow
.COL_SIZE
: "Size",
135 TorRow
.COL_PROG
: "Progr",
136 TorRow
.COL_SD
: "Sd",
137 TorRow
.COL_PR
: "Pr",
138 TorRow
.COL_DS
: "DL Sp",
139 TorRow
.COL_US
: "Up Sp",
140 TorRow
.COL_ETA
: "ETA"
142 widths
= {TorRow
.COL_NAME
: 30,
143 TorRow
.COL_STATE
: 7,
155 self
.set_column_title(col
, titles
[col
])
156 # Set column widths and alignments
158 self
.set_col_width(col
, widths
[col
])
159 for col
in [TorRow
.COL_ETA
, TorRow
.COL_SD
, TorRow
.COL_PR
, TorRow
.COL_DS
, TorRow
.COL_US
, TorRow
.COL_SIZE
, TorRow
.COL_PROG
]:
160 self
.set_column_resizable(col
, False)
161 for col
in [TorRow
.COL_SIZE
, TorRow
.COL_DS
, TorRow
.COL_SD
, TorRow
.COL_PR
, TorRow
.COL_US
, TorRow
.COL_PROG
]:
162 self
.set_column_is_right_aligned(col
, True)
164 def get_row(self
, id):
165 tors
= self
.get_rows()
171 def remove_torrent(self
, id):
172 tor
= self
.get_row(id)
173 if tor
: self
.remove(tor
)
175 def add_torrent(self
, id):
177 self
.add_row_after(row
, row
.info(), None)
179 def update_torrent(self
, id, tor
= None):
181 tor
= self
.get_row(id)
183 for i
in range(0, TorRow
.COLS
):
184 self
.change_text(tor
, i
, info
[i
])
186 def update_all(self
):
187 for tor
in self
.get_rows():
188 self
.update_torrent(tor
.id, tor
)
190 def show_info(self
, null
):
191 tor
= self
.get_selection_data()
192 if not tor
: return True
196 gobject
.type_register(TorList
)
197 gnt
.register_bindings(TorList
)
199 def show_details(tid
):
200 """Show detailed information about a torrent."""
201 status
= client
.get_torrent_status(tid
, status_keys
.keys())
202 status
['state'] = show_state(status
.get('state', ''))
203 status
['total_size'] = show_size(status
.get('total_size', 0))
204 status
['download_payload_rate'] = show_speed(status
.get('download_payload_rate', 0.0))
205 status
['upload_payload_rate'] = show_speed(status
.get('upload_payload_rate', 0.0))
206 win
= gnt
.Box(vert
= True, homo
= False)
207 win
.set_toplevel(True)
208 win
.set_alignment(gnt
.ALIGN_MID
)
211 win
.set_title("Details")
215 # This is the order of the info we want to see
228 "download_payload_rate",
229 "upload_payload_rate",
231 "distributed_copies",
235 for iter in range(0, len(keys
)):
237 tag
= ("%15s" % status_keys
[key
]) + ": "
238 value
= str(status
.get(key
, ''))
239 tv
.append_text_with_flags(tag
, gnt
.TEXT_FLAG_BOLD
)
240 tv
.append_text_with_flags(value
+ "\n", gnt
.TEXT_FLAG_NORMAL
)
241 string
= string
+ tag
+ value
+ "\n"
242 tv
.attach_scroll_widget(win
)
243 tv
.set_flag(gnt
.TEXT_VIEW_TOP_ALIGN | gnt
.TEXT_VIEW_NO_SCROLL
)
244 w
, h
= gnt
.get_text_bound(string
)
247 ok
= gnt
.Button("OK")
248 def close_info_dlg(b
, window
):
250 ok
.connect('activate', close_info_dlg
, win
)
251 box
= gnt
.Box(False, False)
257 def setup_deluge_core():
259 client
.set_core_uri("http://localhost:58846")
260 ConfigManager("ui.conf", {})
261 gntui
= ConfigManager("gntui.conf", {"Dummy": ""})
263 def pack_widget(vert
, widgets
):
264 box
= gnt
.Box(vert
= vert
, homo
= False)
265 for widget
in widgets
:
266 box
.add_widget(widget
)
272 def show_dl_preferences(item
):
275 def refine_config_value(config
, value
):
276 """Return the value of the configuration in correct type."""
278 "max_connections_global" : int,
279 "max_download_speed" : float,
280 "max_upload_speed" : float,
281 "max_upload_slots_global" : int,
282 "max_connections_per_torrent" : int,
283 "max_upload_slots_per_torrent" : int,
284 "listen_ports" : int,
285 "enc_in_policy" : int,
286 "enc_out_policy" : int,
289 if config
in config_convert
:
290 conv
= config_convert
[config
]
291 if isinstance(value
, list):
292 for iter in range(0, len(value
)):
293 value
[iter] = conv(value
[iter])
298 def pref_window(title
):
300 win
.set_property('vertical', True)
302 win
.set_alignment(gnt
.ALIGN_MID
)
304 save
= gnt
.Button("Save")
305 cancel
= gnt
.Button("Cancel")
306 def close_window(b
, window
):
308 cancel
.connect('activate', close_window
, win
)
309 return win
, save
, cancel
311 def add_section(win
, title
):
312 win
.add_widget(gnt
.gnt_label_new_with_format(title
, gnt
.TEXT_FLAG_BOLD | gnt
.TEXT_FLAG_UNDERLINE
))
314 def show_bandwidth_pref(item
):
315 """Bandwidth settings."""
316 win
, save
, cancel
= pref_window("Bandwidth Preference")
318 configs
= client
.get_config()
320 def hook_entry_with_config(entry
, config
):
321 value
= str(configs
[config
])
322 entry
.set_data("config", config
)
323 entry
.set_data("config-value", value
)
324 entry
.set_text(value
)
325 entries
.append(entry
)
327 win
.add_widget(gnt
.gnt_label_new_with_format("Global Bandwidth Usage", gnt
.TEXT_FLAG_BOLD | gnt
.TEXT_FLAG_UNDERLINE
))
328 maxcon
= gnt
.Entry("")
329 maxdlsp
= gnt
.Entry("")
330 maxupsp
= gnt
.Entry("")
331 maxupsl
= gnt
.Entry("")
333 for entry
, config
in ((maxcon
, "max_connections_global"),
334 (maxdlsp
, "max_download_speed"),
335 (maxupsp
, "max_upload_speed"),
336 (maxupsl
, "max_upload_slots_global"),
338 hook_entry_with_config(entry
, config
)
340 for label
, entry
in (("Maximum Connections:", maxcon
),
341 ("Maximum Download Speed (KB/s):", maxdlsp
),
342 ("Maximum Upload Speed (KB/s):", maxupsp
),
343 ("Maximum Upload Slots:", maxupsl
),
345 win
.add_widget(pack_widget(False, [gnt
.Label(label
), entry
]))
347 win
.add_widget(gnt
.gnt_label_new_with_format("Per Torrent Bandwidth Usage", gnt
.TEXT_FLAG_BOLD | gnt
.TEXT_FLAG_UNDERLINE
))
348 maxconpt
= gnt
.Entry("")
349 maxupslpt
= gnt
.Entry("")
350 for entry
, config
in ((maxconpt
, "max_connections_per_torrent"),
351 (maxupslpt
, "max_upload_slots_per_torrent"),
353 hook_entry_with_config(entry
, config
)
355 for label
, entry
in (("Maximum Connections:", maxconpt
),
356 ("Maximum Upload Slots:", maxupslpt
),
358 win
.add_widget(pack_widget(False, [gnt
.Label(label
), entry
]))
360 def save_prefs(b
, window
):
362 for entry
in entries
:
364 config
= entry
.get_data("config")
365 oldv
= entry
.get_data("config-value")
366 value
= entry
.get_text()
368 value
= refine_config_value(config
, value
)
369 newconfigs
[config
] = value
370 sys
.stderr
.write("Changing " + config
+ " to " + str(value
) + "\n")
372 client
.set_config(newconfigs
)
374 except Exception, exc
:
376 save
.connect('activate', save_prefs
, win
)
377 win
.add_widget(gnt
.Line(False))
378 win
.add_widget(pack_widget(False, [save
, cancel
]))
382 def show_network_pref(item
):
383 """Network settings."""
384 win
, save
, cancel
= pref_window("Network Preference")
386 configs
= client
.get_config()
389 def create_checkbox(label
, config
):
390 check
= gnt
.CheckBox(label
)
391 value
= configs
[config
]
392 check
.set_checked(value
)
393 check
.set_data("config", config
)
394 check
.set_data("config-value", value
)
399 add_section(win
, "Ports")
400 randport
= create_checkbox("Use Random Ports", "random_port")
401 win
.add_widget(pack_widget(False, [randport
, gnt
.Label(" (Active Port: " + str(client
.get_listen_port()) + ")")]))
403 add_section(win
, "DHT")
404 dht
= create_checkbox("Enable Mainline DHT", "dht")
407 add_section(win
, "Network Extras")
408 upnp
= create_checkbox("UPnP", "upnp")
409 nat
= create_checkbox("NAT-PMP", "natpmp")
410 pex
= create_checkbox("uTorrent-PeX", "utpex")
411 win
.add_widget(pack_widget(False, [upnp
, nat
, pex
]))
413 def save_callback(vt
, window
):
416 oldv
= check
.get_data("config-value")
417 config
= check
.get_data("config")
418 val
= check
.get_checked()
420 newconfigs
[config
] = val
421 client
.set_config(newconfigs
)
423 save
.connect('activate', save_callback
, win
)
424 win
.add_widget(gnt
.Line(False))
425 win
.add_widget(pack_widget(False, [save
, cancel
]))
429 """Create the menu for the main window."""
430 menu
= gnt
.Menu(gnt
.MENU_TOPLEVEL
)
432 file = gnt
.MenuItem("File")
435 filesub
= gnt
.Menu(gnt
.MENU_POPUP
)
436 file.set_submenu(filesub
)
438 qt
= gnt
.MenuItem("Quit")
442 qt
.connect('activate', qt_cb
)
445 edit
= gnt
.MenuItem("Edit")
448 editsub
= gnt
.Menu(gnt
.MENU_POPUP
)
449 edit
.set_submenu(editsub
)
451 pref
= gnt
.MenuItem("Preference")
452 prefsub
= gnt
.Menu(gnt
.MENU_POPUP
)
453 pref
.set_submenu(prefsub
)
454 editsub
.add_item(pref
)
456 for name
, cb
in (("Downloads", show_dl_preferences
),
457 ("Network", show_network_pref
),
458 ("Bandwidth", show_bandwidth_pref
),
462 dl
= gnt
.MenuItem(name
)
463 dl
.connect('activate', cb
)
466 conn
= gnt
.MenuItem("Connections")
467 editsub
.add_item(conn
)
475 win
.set_property('vertical', True)
476 win
.set_toplevel(True)
477 win
.set_title("Delugent")
479 win
.set_alignment(gnt
.ALIGN_MID
)
484 width
, height
= gnt
.screen_size()
485 list.set_size(width
, height
)
487 hbox
= gnt
.Box(homo
= False, vert
= False)
489 add
= gnt
.Button("Add")
491 def file_selected(widget
, path
, file):
492 files
= widget
.get_selected_multi_files()
494 client
.add_torrent_file(files
)
496 fl
.set_title("Select a Torrent")
497 fl
.set_multi_select(True)
498 fl
.connect("file_selected", file_selected
)
501 add
.connect('activate', add_clicked
)
503 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_INS
, add
)
505 rm
= gnt
.Button("Remove")
507 tor
= list.get_selection_data()
508 client
.remove_torrent([tor
.id])
509 rm
.connect('activate', rm_clicked
)
511 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_DEL
, rm
)
513 hbox
.add_widget(gnt
.Label("|"))
515 pause
= gnt
.Button("Pause")
516 def pause_clicked(b
):
517 tor
= list.get_selection_data()
518 client
.pause_torrent([tor
.id])
519 pause
.connect('activate', pause_clicked
)
520 hbox
.add_widget(pause
)
521 gnt
.gnt_util_set_trigger_widget(win
, 'p', pause
)
523 hbox
.add_widget(gnt
.Label("|"))
525 mup
= gnt
.Button("Up")
527 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_CTRL_UP
, mup
)
529 mdown
= gnt
.Button("Down")
530 hbox
.add_widget(mdown
)
531 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_CTRL_DOWN
, mdown
)
535 menu
= create_menu() # XXX: Investigate why this causes warnings on exit
540 # Add the torrents in the list
541 session_state
= client
.get_session_state()
542 for torrent_id
in session_state
:
543 list.add_torrent(torrent_id
)
545 signalhandler
= signals
.Signals(list)
552 if __name__
== "__main__":