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
42 "total_size" : "Size",
43 "total_done" : "Downloaded",
44 "progress" : "Progress",
45 "num_seeds" : "# of seeds",
46 "total_seeds" : "Total seeds",
47 "num_peers" : "# of peers",
48 "total_peers" : "Total peers",
50 "download_payload_rate" : "Download speed",
51 "upload_payload_rate" : "Upload speed",
52 "ratio" : "Share Ratio",
53 "distributed_copies" : "Availability",
54 "total_uploaded" : "Uploaded",
55 "num_files" : "# of files",
63 elif size
> (1 << 20):
66 elif size
> (1 << 10):
69 return str(size
) + unit
71 def show_speed(speed
):
74 speed
= speed
/ (1 << 30)
76 elif speed
> (1 << 20):
77 speed
= speed
/ (1 << 20)
79 elif speed
> (1 << 10):
80 speed
= speed
/ (1 << 10)
82 return ("%.01f" + unit
) % speed
84 def show_state(state
):
89 class TorRow(gobject
.GObject
):
100 def __init__(self
, id):
101 self
.__gobject
_init
__()
108 status
= client
.get_torrent_status(self
.id, status_keys
.keys())
109 state
= int(status
.get("state", 0))
110 name
= str(status
.get("name", ""))
111 size
= int(status
.get("total_size", 0))
112 progress
= float(status
.get("progress", 0.0))
113 peers
= str(status
.get("num_peers", 0))
114 seeds
= str(status
.get("num_seeds", 0))
115 dlspeed
= float(status
.get("download_payload_rate", 0.0))
116 upspeed
= float(status
.get("upload_payload_rate", 0.0))
117 eta
= float(status
.get("eta", 0.0))
119 return [name
, show_state(state
), show_size(size
), str(progress
), str(seeds
), str(peers
),
120 show_speed(dlspeed
), show_speed(upspeed
), str(eta
)]
122 gobject
.type_register(TorRow
)
124 class TorList(gnt
.Tree
):
126 'info' : ('show_info', 'i')
129 gnt
.Tree
.__init
__(self
)
130 self
.set_property('columns', TorRow
.COLS
)
131 self
.set_show_title(True)
133 titles
= {TorRow
.COL_NAME
: "Name",
134 TorRow
.COL_STATE
: "State",
135 TorRow
.COL_SIZE
: "Size",
136 TorRow
.COL_PROG
: "Progr",
137 TorRow
.COL_SD
: "Sd",
138 TorRow
.COL_PR
: "Pr",
139 TorRow
.COL_DS
: "DL Sp",
140 TorRow
.COL_US
: "Up Sp",
141 TorRow
.COL_ETA
: "ETA"
143 widths
= {TorRow
.COL_NAME
: 30,
144 TorRow
.COL_STATE
: 7,
156 self
.set_column_title(col
, titles
[col
])
157 # Set column widths and alignments
159 self
.set_col_width(col
, widths
[col
])
160 for col
in [TorRow
.COL_ETA
, TorRow
.COL_SD
, TorRow
.COL_PR
, TorRow
.COL_DS
, TorRow
.COL_US
, TorRow
.COL_SIZE
, TorRow
.COL_PROG
]:
161 self
.set_column_resizable(col
, False)
162 for col
in [TorRow
.COL_SIZE
, TorRow
.COL_DS
, TorRow
.COL_SD
, TorRow
.COL_PR
, TorRow
.COL_US
, TorRow
.COL_PROG
]:
163 self
.set_column_is_right_aligned(col
, True)
165 def get_row(self
, id):
166 tors
= self
.get_rows()
172 def remove_torrent(self
, id):
173 tor
= self
.get_row(id)
174 if tor
: self
.remove(tor
)
176 def add_torrent(self
, id):
178 self
.add_row_after(row
, row
.info(), None)
180 def update_torrent(self
, id, tor
= None):
182 tor
= self
.get_row(id)
184 for i
in range(0, TorRow
.COLS
):
185 self
.change_text(tor
, i
, info
[i
])
187 def update_all(self
):
188 for tor
in self
.get_rows():
189 self
.update_torrent(tor
.id, tor
)
191 def show_info(self
, null
):
192 tor
= self
.get_selection_data()
193 if not tor
: return True
197 gobject
.type_register(TorList
)
198 gnt
.register_bindings(TorList
)
200 def show_details(tid
):
201 """Show detailed information about a torrent."""
202 status
= client
.get_torrent_status(tid
, status_keys
.keys())
203 status
['state'] = show_state(status
.get('state', ''))
204 status
['total_size'] = show_size(status
.get('total_size', 0))
205 status
['download_payload_rate'] = show_speed(status
.get('download_payload_rate', 0.0))
206 status
['upload_payload_rate'] = show_speed(status
.get('upload_payload_rate', 0.0))
207 win
= gnt
.Box(vert
= True, homo
= False)
208 win
.set_toplevel(True)
209 win
.set_alignment(gnt
.ALIGN_MID
)
212 win
.set_title("Details")
216 # This is the order of the info we want to see
229 "download_payload_rate",
230 "upload_payload_rate",
232 "distributed_copies",
236 for iter in range(0, len(keys
)):
238 tag
= ("%15s" % status_keys
[key
]) + ": "
239 value
= str(status
.get(key
, ''))
240 tv
.append_text_with_flags(tag
, gnt
.TEXT_FLAG_BOLD
)
241 tv
.append_text_with_flags(value
+ "\n", gnt
.TEXT_FLAG_NORMAL
)
242 string
= string
+ tag
+ value
+ "\n"
243 tv
.attach_scroll_widget(win
)
244 tv
.set_flag(gnt
.TEXT_VIEW_TOP_ALIGN | gnt
.TEXT_VIEW_NO_SCROLL
)
245 w
, h
= gnt
.get_text_bound(string
)
248 ok
= gnt
.Button("OK")
249 def close_info_dlg(b
, window
):
251 ok
.connect('activate', close_info_dlg
, win
)
252 box
= gnt
.Box(False, False)
259 def setup_deluge_core():
260 """Do the foreplay with the deluge daemon."""
262 "load_torrent_location" : "/tmp",
265 client
.set_core_uri("http://localhost:58846")
266 gntui
= ConfigManager("gntui.conf", DEFAULT_PREFS
)
268 def pack_widget(vert
, widgets
):
269 box
= gnt
.Box(vert
= vert
, homo
= False)
270 for widget
in widgets
:
271 box
.add_widget(widget
)
277 def show_dl_preferences(item
):
280 def refine_config_value(config
, value
):
281 """Return the value of the configuration in correct type."""
283 "max_connections_global" : int,
284 "max_download_speed" : float,
285 "max_upload_speed" : float,
286 "max_upload_slots_global" : int,
287 "max_connections_per_torrent" : int,
288 "max_upload_slots_per_torrent" : int,
289 "listen_ports" : int,
290 "enc_in_policy" : int,
291 "enc_out_policy" : int,
294 if config
in config_convert
:
295 conv
= config_convert
[config
]
296 if isinstance(value
, list):
297 for iter in range(0, len(value
)):
298 value
[iter] = conv(value
[iter])
303 def pref_window(title
):
305 win
.set_property('vertical', True)
307 win
.set_alignment(gnt
.ALIGN_MID
)
309 save
= gnt
.Button("Save")
310 cancel
= gnt
.Button("Cancel")
311 def close_window(b
, window
):
313 cancel
.connect('activate', close_window
, win
)
314 return win
, save
, cancel
316 def add_section(win
, title
):
317 win
.add_widget(gnt
.gnt_label_new_with_format(title
, gnt
.TEXT_FLAG_BOLD | gnt
.TEXT_FLAG_UNDERLINE
))
319 def show_bandwidth_pref(item
):
320 """Bandwidth settings."""
321 win
, save
, cancel
= pref_window("Bandwidth Preference")
323 configs
= client
.get_config()
325 def hook_entry_with_config(entry
, config
):
326 value
= str(configs
[config
])
327 entry
.set_data("config", config
)
328 entry
.set_data("config-value", value
)
329 entry
.set_text(value
)
330 entries
.append(entry
)
332 win
.add_widget(gnt
.gnt_label_new_with_format("Global Bandwidth Usage", gnt
.TEXT_FLAG_BOLD | gnt
.TEXT_FLAG_UNDERLINE
))
333 maxcon
= gnt
.Entry("")
334 maxdlsp
= gnt
.Entry("")
335 maxupsp
= gnt
.Entry("")
336 maxupsl
= gnt
.Entry("")
338 for entry
, config
in ((maxcon
, "max_connections_global"),
339 (maxdlsp
, "max_download_speed"),
340 (maxupsp
, "max_upload_speed"),
341 (maxupsl
, "max_upload_slots_global"),
343 hook_entry_with_config(entry
, config
)
345 for label
, entry
in (("Maximum Connections:", maxcon
),
346 ("Maximum Download Speed (KB/s):", maxdlsp
),
347 ("Maximum Upload Speed (KB/s):", maxupsp
),
348 ("Maximum Upload Slots:", maxupsl
),
350 win
.add_widget(pack_widget(False, [gnt
.Label(label
), entry
]))
352 win
.add_widget(gnt
.gnt_label_new_with_format("Per Torrent Bandwidth Usage", gnt
.TEXT_FLAG_BOLD | gnt
.TEXT_FLAG_UNDERLINE
))
353 maxconpt
= gnt
.Entry("")
354 maxupslpt
= gnt
.Entry("")
355 for entry
, config
in ((maxconpt
, "max_connections_per_torrent"),
356 (maxupslpt
, "max_upload_slots_per_torrent"),
358 hook_entry_with_config(entry
, config
)
360 for label
, entry
in (("Maximum Connections:", maxconpt
),
361 ("Maximum Upload Slots:", maxupslpt
),
363 win
.add_widget(pack_widget(False, [gnt
.Label(label
), entry
]))
365 def save_prefs(b
, window
):
367 for entry
in entries
:
369 config
= entry
.get_data("config")
370 oldv
= entry
.get_data("config-value")
371 value
= entry
.get_text()
373 value
= refine_config_value(config
, value
)
374 newconfigs
[config
] = value
375 sys
.stderr
.write("Changing " + config
+ " to " + str(value
) + "\n")
377 client
.set_config(newconfigs
)
379 except Exception, exc
:
381 save
.connect('activate', save_prefs
, win
)
382 win
.add_widget(gnt
.Line(False))
383 win
.add_widget(pack_widget(False, [save
, cancel
]))
387 def show_network_pref(item
):
388 """Network settings."""
389 win
, save
, cancel
= pref_window("Network Preference")
391 configs
= client
.get_config()
394 def create_checkbox(label
, config
):
395 check
= gnt
.CheckBox(label
)
396 value
= configs
[config
]
397 check
.set_checked(value
)
398 check
.set_data("config", config
)
399 check
.set_data("config-value", value
)
404 add_section(win
, "Ports")
405 randport
= create_checkbox("Use Random Ports", "random_port")
406 win
.add_widget(pack_widget(False, [randport
, gnt
.Label(" (Active Port: " + str(client
.get_listen_port()) + ")")]))
408 add_section(win
, "DHT")
409 dht
= create_checkbox("Enable Mainline DHT", "dht")
412 add_section(win
, "Network Extras")
413 upnp
= create_checkbox("UPnP", "upnp")
414 nat
= create_checkbox("NAT-PMP", "natpmp")
415 pex
= create_checkbox("uTorrent-PeX", "utpex")
416 win
.add_widget(pack_widget(False, [upnp
, nat
, pex
]))
418 def save_callback(vt
, window
):
421 oldv
= check
.get_data("config-value")
422 config
= check
.get_data("config")
423 val
= check
.get_checked()
425 newconfigs
[config
] = val
426 client
.set_config(newconfigs
)
428 save
.connect('activate', save_callback
, win
)
429 win
.add_widget(gnt
.Line(False))
430 win
.add_widget(pack_widget(False, [save
, cancel
]))
434 """Create the menu for the main window."""
435 menu
= gnt
.Menu(gnt
.MENU_TOPLEVEL
)
437 file = gnt
.MenuItem("File")
440 filesub
= gnt
.Menu(gnt
.MENU_POPUP
)
441 file.set_submenu(filesub
)
443 qt
= gnt
.MenuItem("Quit")
447 qt
.connect('activate', qt_cb
)
450 edit
= gnt
.MenuItem("Edit")
453 editsub
= gnt
.Menu(gnt
.MENU_POPUP
)
454 edit
.set_submenu(editsub
)
456 pref
= gnt
.MenuItem("Preference")
457 prefsub
= gnt
.Menu(gnt
.MENU_POPUP
)
458 pref
.set_submenu(prefsub
)
459 editsub
.add_item(pref
)
461 for name
, cb
in (("Downloads", show_dl_preferences
),
462 ("Network", show_network_pref
),
463 ("Bandwidth", show_bandwidth_pref
),
467 dl
= gnt
.MenuItem(name
)
468 dl
.connect('activate', cb
)
471 conn
= gnt
.MenuItem("Connections")
472 editsub
.add_item(conn
)
480 win
.set_property('vertical', True)
481 win
.set_toplevel(True)
482 win
.set_title("Delugent")
484 win
.set_alignment(gnt
.ALIGN_MID
)
489 width
, height
= gnt
.screen_size()
490 list.set_size(width
, height
)
492 hbox
= gnt
.Box(homo
= False, vert
= False)
494 add
= gnt
.Button("Add")
496 def file_selected(widget
, path
, file):
497 files
= widget
.get_selected_multi_files()
499 client
.add_torrent_file(files
)
500 path
= os
.path
.dirname(files
[0])
501 gntui
['load_torrent_location'] = path
503 fl
.set_title("Select a Torrent")
504 fl
.set_multi_select(True)
505 fl
.set_current_location(gntui
['load_torrent_location'])
506 fl
.connect("file_selected", file_selected
)
509 add
.connect('activate', add_clicked
)
511 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_INS
, add
)
513 rm
= gnt
.Button("Remove")
515 tor
= list.get_selection_data()
516 client
.remove_torrent([tor
.id])
517 rm
.connect('activate', rm_clicked
)
519 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_DEL
, rm
)
521 hbox
.add_widget(gnt
.Label("|"))
523 pause
= gnt
.Button("Pause")
524 def pause_clicked(b
):
525 tor
= list.get_selection_data()
526 client
.pause_torrent([tor
.id])
527 pause
.connect('activate', pause_clicked
)
528 hbox
.add_widget(pause
)
529 gnt
.gnt_util_set_trigger_widget(win
, 'p', pause
)
531 hbox
.add_widget(gnt
.Label("|"))
533 mup
= gnt
.Button("Up")
535 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_CTRL_UP
, mup
)
537 mdown
= gnt
.Button("Down")
538 hbox
.add_widget(mdown
)
539 gnt
.gnt_util_set_trigger_widget(win
, gnt
.KEY_CTRL_DOWN
, mdown
)
543 menu
= create_menu() # XXX: Investigate why this causes warnings on exit
548 # Add the torrents in the list
549 session_state
= client
.get_session_state()
550 for torrent_id
in session_state
:
551 list.add_torrent(torrent_id
)
553 signalhandler
= signals
.Signals(list)
560 if __name__
== "__main__":