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:
parent
12d0e9574b
commit
dbad4684db
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue