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

@ -535,6 +535,29 @@ class RPCServer(component.Component):
(RPC_EVENT, event.name, event.args) (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(): def check_ssl_keys():
""" """
Check for SSL cert/key and create them if necessary Check for SSL cert/key and create them if necessary

View File

@ -197,6 +197,13 @@ class TorrentFileCompletedEvent(DelugeEvent):
""" """
self._args = [torrent_id, index] 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): class NewVersionAvailableEvent(DelugeEvent):
""" """
Emitted when a more recent version of Deluge is available. Emitted when a more recent version of Deluge is available.

View File

@ -16,14 +16,15 @@
# Written by Bram Cohen # Written by Bram Cohen
# Modifications for use in Deluge by Andrew Resch 2008 # Modifications for use in Deluge by Andrew Resch 2008
import os
import os.path import os.path
import sys import sys
import time import time
import logging import logging
from hashlib import sha1 as sha from hashlib import sha1 as sha
import deluge.component as component
from deluge.bencode import bencode from deluge.bencode import bencode
from deluge.event import CreateTorrentProgressEvent
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -57,10 +58,18 @@ def decode_from_filesystem(path):
def dummy(*v): def dummy(*v):
pass pass
def make_meta_file(path, url, piece_length, progress=dummy, class RemoteFileProgress(object):
title=None, comment=None, safe=None, content_type=None, def __init__(self, session_id):
target=None, webseeds=None, name=None, private=False, self.session_id = session_id
created_by=None, trackers=None):
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())} data = {'creation date': int(gmtime())}
if url: if url:
data['announce'] = url.strip() 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') f = os.path.join(a, b + '.torrent')
else: else:
f = target 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) info = makeinfo(path, piece_length, progress, name, content_type, private)
#check_info(info) #check_info(info)
@ -125,7 +142,7 @@ def makeinfo(path, piece_length, progress, name = None,
else: else:
try: try:
u = decode_from_filesystem(name) u = decode_from_filesystem(name)
except Exception, e: except Exception:
raise Exception('Could not convert file/directory name %r to ' raise Exception('Could not convert file/directory name %r to '
'Unicode. Either the assumed filesystem ' 'Unicode. Either the assumed filesystem '
'encoding "%s" is wrong or the filename contains ' 'encoding "%s" is wrong or the filename contains '
@ -181,6 +198,7 @@ def makeinfo(path, piece_length, progress, name = None,
h.close() h.close()
if done > 0: if done > 0:
pieces.append(sh.digest()) pieces.append(sh.digest())
piece_count += 1
progress(piece_count, num_pieces) progress(piece_count, num_pieces)
if name is not None: if name is not None:

View File

@ -241,13 +241,12 @@ class CreateTorrentDialog:
return return
is_remote = self.files_treestore[0][1] == gtk.STOCK_NETWORK 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: if is_remote:
# This is a remote path # This is a remote path
dialog = self.builder.get_object("remote_save_dialog") dialog = self.builder.get_object("remote_save_dialog")
dialog.set_transient_for(self.dialog) dialog.set_transient_for(self.dialog)
self.builder.get_object("entry_save_path").set_text( self.builder.get_object("entry_save_path").set_text(torrent_filename)
os.path.split(self.files_treestore[0][0])[-1] + ".torrent"
)
response = dialog.run() response = dialog.run()
if response == gtk.RESPONSE_OK: if response == gtk.RESPONSE_OK:
result = self.builder.get_object("entry_save_path").get_text() result = self.builder.get_object("entry_save_path").get_text()
@ -276,7 +275,7 @@ class CreateTorrentDialog:
file_filter.add_pattern("*") file_filter.add_pattern("*")
chooser.add_filter(file_filter) 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 # Run the dialog
response = chooser.run() response = chooser.run()
@ -327,6 +326,18 @@ class CreateTorrentDialog:
add_to_session = self.builder.get_object("chk_add_to_session").get_active() add_to_session = self.builder.get_object("chk_add_to_session").get_active()
if is_remote: 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( client.core.create_torrent(
path, path,
tracker, tracker,
@ -340,9 +351,6 @@ class CreateTorrentDialog:
add_to_session) add_to_session)
else: 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): def hide_progress(result):
self.builder.get_object("progress_dialog").hide_all() self.builder.get_object("progress_dialog").hide_all()
@ -360,6 +368,10 @@ class CreateTorrentDialog:
trackers, trackers,
add_to_session).addCallback(hide_progress) 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() self.dialog.destroy()
def create_torrent(self, path, tracker, piece_length, progress, comment, target, 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): def _on_create_torrent_progress(self, value, num_pieces):
percent = float(value)/float(num_pieces) percent = float(value)/float(num_pieces)
def update_pbar_with_gobject(percent):
pbar = self.builder.get_object("progressbar") pbar = self.builder.get_object("progressbar")
pbar.set_text("%.2f%%" % (percent*100)) pbar.set_text("%.2f%%" % (percent*100))
if percent >= 0 and percent <= 1.0:
pbar.set_fraction(percent) 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): def _on_button_up_clicked(self, widget):
log.debug("_on_button_up_clicked") log.debug("_on_button_up_clicked")