Create torrent remotely progress information.

Support progress information when creating torrents remotely. For this to be possible, a method was added to the `RPCServer`, `emit_event_for_session_id()`, which does exactly what is says. This is needed because the event created, `CreateTorrentProgressEvent` needs to be addressed to a single session id, not all session ids interested in that event.
Fixed a bug that apparently was not found yet. When creating torrents locally, we defer that task to a thread. Since this thread updates UI widgets, namely the progress bar info and since we can't guarantee that it's the main thread, updating the widgets must be done by calling `gobject.iddle_add()`.
This commit is contained in:
Pedro Algarvio 2011-07-06 16:45:02 +01:00
parent 12d0e9574b
commit dbad4684db
4 changed files with 90 additions and 23 deletions

View File

@ -427,14 +427,14 @@ class RPCServer(component.Component):
def deregister_object(self, obj):
"""
Deregisters an objects exported rpc methods.
:param obj: the object that was previously registered
"""
for key, value in self.factory.methods.items():
if value.im_self == obj:
del self.factory.methods[key]
def get_object_method(self, name):
"""
Returns a registered method.
@ -535,6 +535,29 @@ class RPCServer(component.Component):
(RPC_EVENT, event.name, event.args)
)
def emit_event_for_session_id(self, session_id, event):
"""
Emits the event to specified session_id.
:param session_id: the event to emit
:type session_id: int
:param event: the event to emit
:type event: :class:`deluge.event.DelugeEvent`
"""
if not self.is_session_valid(session_id):
log.debug("Session ID %s is not valid. Not sending event \"%s\".", session_id, event.name)
return
if session_id not in self.factory.interested_events:
log.debug("Session ID %s is not interested in any events. Not sending event \"%s\".", session_id, event.name)
return
if event.name not in self.factory.interested_events[session_id]:
log.debug("Session ID %s is not interested in event \"%s\". Not sending it.", session_id, event.name)
return
log.debug("Sending event \"%s\" with args \"%s\" to session id \"%s\".",
event.name, event.args, session_id)
self.factory.session_protocols[session_id].sendData((RPC_EVENT, event.name, event.args))
def check_ssl_keys():
"""
Check for SSL cert/key and create them if necessary

View File

@ -197,6 +197,13 @@ class TorrentFileCompletedEvent(DelugeEvent):
"""
self._args = [torrent_id, index]
class CreateTorrentProgressEvent(DelugeEvent):
"""
Emitted when creating a torrent file remotely.
"""
def __init__(self, piece_count, num_pieces):
self._args = [piece_count, num_pieces]
class NewVersionAvailableEvent(DelugeEvent):
"""
Emitted when a more recent version of Deluge is available.

View File

@ -16,14 +16,15 @@
# Written by Bram Cohen
# Modifications for use in Deluge by Andrew Resch 2008
import os
import os.path
import sys
import time
import logging
from hashlib import sha1 as sha
import deluge.component as component
from deluge.bencode import bencode
from deluge.event import CreateTorrentProgressEvent
log = logging.getLogger(__name__)
@ -57,10 +58,18 @@ def decode_from_filesystem(path):
def dummy(*v):
pass
def make_meta_file(path, url, piece_length, progress=dummy,
title=None, comment=None, safe=None, content_type=None,
target=None, webseeds=None, name=None, private=False,
created_by=None, trackers=None):
class RemoteFileProgress(object):
def __init__(self, session_id):
self.session_id = session_id
def __call__(self, piece_count, num_pieces):
component.get("RPCServer").emit_event_for_session_id(
self.session_id, CreateTorrentProgressEvent(piece_count, num_pieces)
)
def make_meta_file(path, url, piece_length, progress=None, title=None, comment=None,
safe=None, content_type=None, target=None, webseeds=None, name=None,
private=False, created_by=None, trackers=None):
data = {'creation date': int(gmtime())}
if url:
data['announce'] = url.strip()
@ -72,6 +81,14 @@ def make_meta_file(path, url, piece_length, progress=dummy,
f = os.path.join(a, b + '.torrent')
else:
f = target
if progress is None:
session_id = component.get("RPCServer").get_session_id()
if not session_id:
progress = dummy
else:
progress = RemoteFileProgress(component.get("RPCServer").get_session_id())
info = makeinfo(path, piece_length, progress, name, content_type, private)
#check_info(info)
@ -118,18 +135,18 @@ def calcsize(path):
def makeinfo(path, piece_length, progress, name = None,
content_type = None, private=False): # HEREDAVE. If path is directory,
# how do we assign content type?
# how do we assign content type?
def to_utf8(name):
if isinstance(name, unicode):
u = name
else:
try:
u = decode_from_filesystem(name)
except Exception, e:
except Exception:
raise Exception('Could not convert file/directory name %r to '
'Unicode. Either the assumed filesystem '
'encoding "%s" is wrong or the filename contains '
'illegal bytes.' % (name, get_filesystem_encoding()))
'Unicode. Either the assumed filesystem '
'encoding "%s" is wrong or the filename contains '
'illegal bytes.' % (name, get_filesystem_encoding()))
if u.translate(noncharacter_translate) != u:
raise Exception('File/directory name "%s" contains reserved '
@ -181,6 +198,7 @@ def makeinfo(path, piece_length, progress, name = None,
h.close()
if done > 0:
pieces.append(sh.digest())
piece_count += 1
progress(piece_count, num_pieces)
if name is not None:

View File

@ -241,13 +241,12 @@ class CreateTorrentDialog:
return
is_remote = self.files_treestore[0][1] == gtk.STOCK_NETWORK
torrent_filename = "%s.torrent" % os.path.split(self.files_treestore[0][0].rstrip('/'))[-1]
if is_remote:
# This is a remote path
dialog = self.builder.get_object("remote_save_dialog")
dialog.set_transient_for(self.dialog)
self.builder.get_object("entry_save_path").set_text(
os.path.split(self.files_treestore[0][0])[-1] + ".torrent"
)
self.builder.get_object("entry_save_path").set_text(torrent_filename)
response = dialog.run()
if response == gtk.RESPONSE_OK:
result = self.builder.get_object("entry_save_path").get_text()
@ -276,7 +275,7 @@ class CreateTorrentDialog:
file_filter.add_pattern("*")
chooser.add_filter(file_filter)
chooser.set_current_name(os.path.split(self.files_treestore[0][0])[-1] + ".torrent")
chooser.set_current_name(torrent_filename)
# Run the dialog
response = chooser.run()
@ -327,6 +326,18 @@ class CreateTorrentDialog:
add_to_session = self.builder.get_object("chk_add_to_session").get_active()
if is_remote:
def torrent_created():
self.builder.get_object("progress_dialog").hide_all()
client.deregister_event_handler("CreateTorrentProgressEvent", on_create_torrent_progress_event)
def on_create_torrent_progress_event(piece_count, num_pieces):
self._on_create_torrent_progress(piece_count, num_pieces)
if piece_count == num_pieces:
from twisted.internet import reactor
reactor.callLater(0.5, torrent_created)
client.register_event_handler("CreateTorrentProgressEvent", on_create_torrent_progress_event)
client.core.create_torrent(
path,
tracker,
@ -340,9 +351,6 @@ class CreateTorrentDialog:
add_to_session)
else:
# Setup progress dialog
self.builder.get_object("progress_dialog").set_transient_for(component.get("MainWindow").window)
self.builder.get_object("progress_dialog").show_all()
def hide_progress(result):
self.builder.get_object("progress_dialog").hide_all()
@ -360,6 +368,10 @@ class CreateTorrentDialog:
trackers,
add_to_session).addCallback(hide_progress)
# Setup progress dialog
self.builder.get_object("progress_dialog").set_transient_for(component.get("MainWindow").window)
self.builder.get_object("progress_dialog").show_all()
self.dialog.destroy()
def create_torrent(self, path, tracker, piece_length, progress, comment, target,
@ -385,10 +397,17 @@ class CreateTorrentDialog:
def _on_create_torrent_progress(self, value, num_pieces):
percent = float(value)/float(num_pieces)
pbar = self.builder.get_object("progressbar")
pbar.set_text("%.2f%%" % (percent*100))
if percent >= 0 and percent <= 1.0:
def update_pbar_with_gobject(percent):
pbar = self.builder.get_object("progressbar")
pbar.set_text("%.2f%%" % (percent*100))
pbar.set_fraction(percent)
return False
if percent >= 0 and percent <= 1.0:
# Make sure there are no threads race conditions that can
# crash the UI while updating it.
gobject.idle_add(update_pbar_with_gobject, percent)
def _on_button_up_clicked(self, widget):
log.debug("_on_button_up_clicked")