Implement Events (formerly Signals) which are emitted from the daemon to interested clients.
This commit is contained in:
parent
aa89c653f9
commit
a24738a9ff
|
@ -40,7 +40,7 @@ from deluge.log import LOG as log
|
||||||
class AlertManager(component.Component):
|
class AlertManager(component.Component):
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
log.debug("AlertManager initialized..")
|
log.debug("AlertManager initialized..")
|
||||||
component.Component.__init__(self, "AlertManager", interval=50)
|
component.Component.__init__(self, "AlertManager", interval=0.05)
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
self.session.set_alert_mask(
|
self.session.set_alert_mask(
|
||||||
|
|
|
@ -44,6 +44,7 @@ except ImportError:
|
||||||
import deluge.configmanager
|
import deluge.configmanager
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
|
from deluge.event import *
|
||||||
from deluge.core.torrentmanager import TorrentManager
|
from deluge.core.torrentmanager import TorrentManager
|
||||||
from deluge.core.pluginmanager import PluginManager
|
from deluge.core.pluginmanager import PluginManager
|
||||||
from deluge.core.alertmanager import AlertManager
|
from deluge.core.alertmanager import AlertManager
|
||||||
|
@ -212,7 +213,7 @@ class Core(component.Component):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if VersionSplit(self.new_release) > VersionSplit(deluge.common.get_version()):
|
if VersionSplit(self.new_release) > VersionSplit(deluge.common.get_version()):
|
||||||
self.signalmanager.emit("new_version_available", self.new_release)
|
component.get("RPCServer").emit_event(NewVersionAvailableEvent(self.new_release))
|
||||||
return self.new_release
|
return self.new_release
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -240,9 +241,9 @@ class Core(component.Component):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.error("There was an error adding the torrent file %s", filename)
|
log.error("There was an error adding the torrent file %s", filename)
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
|
else:
|
||||||
# Run the plugin hooks for 'post_torrent_add'
|
# Run the plugin hooks for 'post_torrent_add'
|
||||||
self.pluginmanager.run_post_torrent_add(torrent_id)
|
self.pluginmanager.run_post_torrent_add(torrent_id)
|
||||||
|
|
||||||
@export()
|
@export()
|
||||||
def add_torrent_url(self, url, options):
|
def add_torrent_url(self, url, options):
|
||||||
|
@ -364,7 +365,7 @@ class Core(component.Component):
|
||||||
def resume_all_torrents(self):
|
def resume_all_torrents(self):
|
||||||
"""Resume all torrents in the session"""
|
"""Resume all torrents in the session"""
|
||||||
self.session.resume()
|
self.session.resume()
|
||||||
self.signalmanager.emit("torrent_all_resumed")
|
component.get("RPCServer").emit_event(SessionResumedEvent())
|
||||||
|
|
||||||
@export()
|
@export()
|
||||||
def resume_torrent(self, torrent_ids):
|
def resume_torrent(self, torrent_ids):
|
||||||
|
@ -674,7 +675,7 @@ class Core(component.Component):
|
||||||
try:
|
try:
|
||||||
# If the queue method returns True, then we should emit a signal
|
# If the queue method returns True, then we should emit a signal
|
||||||
if self.torrentmanager.queue_top(torrent_id):
|
if self.torrentmanager.queue_top(torrent_id):
|
||||||
self.signalmanager.emit("torrent_queue_changed")
|
component.get("RPCServer").emit_event(TorrentQueueChangedEvent())
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||||
|
|
||||||
|
@ -687,7 +688,7 @@ class Core(component.Component):
|
||||||
try:
|
try:
|
||||||
# If the queue method returns True, then we should emit a signal
|
# If the queue method returns True, then we should emit a signal
|
||||||
if self.torrentmanager.queue_up(torrent_id):
|
if self.torrentmanager.queue_up(torrent_id):
|
||||||
self.signalmanager.emit("torrent_queue_changed")
|
component.get("RPCServer").emit_event(TorrentQueueChangedEvent())
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||||
|
|
||||||
|
@ -700,7 +701,7 @@ class Core(component.Component):
|
||||||
try:
|
try:
|
||||||
# If the queue method returns True, then we should emit a signal
|
# If the queue method returns True, then we should emit a signal
|
||||||
if self.torrentmanager.queue_down(torrent_id):
|
if self.torrentmanager.queue_down(torrent_id):
|
||||||
self.signalmanager.emit("torrent_queue_changed")
|
component.get("RPCServer").emit_event(TorrentQueueChangedEvent())
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||||
|
|
||||||
|
@ -711,7 +712,7 @@ class Core(component.Component):
|
||||||
try:
|
try:
|
||||||
# If the queue method returns True, then we should emit a signal
|
# If the queue method returns True, then we should emit a signal
|
||||||
if self.torrentmanager.queue_bottom(torrent_id):
|
if self.torrentmanager.queue_bottom(torrent_id):
|
||||||
self.signalmanager.emit("torrent_queue_changed")
|
component.get("RPCServer").emit_event(TorrentQueueChangedEvent())
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ except ImportError:
|
||||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||||
|
|
||||||
|
from deluge.event import *
|
||||||
import deluge.configmanager
|
import deluge.configmanager
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
|
@ -214,7 +215,7 @@ class PreferencesManager(component.Component):
|
||||||
|
|
||||||
# Config set functions
|
# Config set functions
|
||||||
def _on_config_value_change(self, key, value):
|
def _on_config_value_change(self, key, value):
|
||||||
self.signals.emit("config_value_changed", key, value)
|
component.get("RPCServer").emit_event(ConfigValueChangedEvent(key, value))
|
||||||
|
|
||||||
def _on_set_torrentfiles_location(self, key, value):
|
def _on_set_torrentfiles_location(self, key, value):
|
||||||
if self.config["copy_torrent_file"]:
|
if self.config["copy_torrent_file"]:
|
||||||
|
|
|
@ -137,7 +137,7 @@ class DelugeRPCProtocol(Protocol):
|
||||||
s += ", ".join([key + "=" + str(value) for key, value in call[3].items()])
|
s += ", ".join([key + "=" + str(value) for key, value in call[3].items()])
|
||||||
s += ")"
|
s += ")"
|
||||||
|
|
||||||
log.debug("RPCRequest: %s", s)
|
#log.debug("RPCRequest: %s", s)
|
||||||
reactor.callLater(0, self._dispatch, *call)
|
reactor.callLater(0, self._dispatch, *call)
|
||||||
|
|
||||||
def sendData(self, data):
|
def sendData(self, data):
|
||||||
|
@ -184,6 +184,20 @@ class DelugeRPCProtocol(Protocol):
|
||||||
:param kwargs: dict, the keyword-arguments to pass to `:param:method`
|
:param kwargs: dict, the keyword-arguments to pass to `:param:method`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
def sendError():
|
||||||
|
"""
|
||||||
|
Sends an error response with the contents of the exception that was raised.
|
||||||
|
"""
|
||||||
|
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
|
||||||
|
|
||||||
|
self.sendData((
|
||||||
|
RPC_ERROR,
|
||||||
|
request_id,
|
||||||
|
(exceptionType.__name__,
|
||||||
|
exceptionValue.message,
|
||||||
|
"".join(traceback.format_tb(exceptionTraceback)))
|
||||||
|
))
|
||||||
|
|
||||||
if method == "daemon.login":
|
if method == "daemon.login":
|
||||||
# This is a special case and used in the initial connection process
|
# This is a special case and used in the initial connection process
|
||||||
# We need to authenticate the user here
|
# We need to authenticate the user here
|
||||||
|
@ -191,8 +205,9 @@ class DelugeRPCProtocol(Protocol):
|
||||||
ret = component.get("AuthManager").authorize(*args, **kwargs)
|
ret = component.get("AuthManager").authorize(*args, **kwargs)
|
||||||
if ret:
|
if ret:
|
||||||
self.factory.authorized_sessions[self.transport.sessionno] = ret
|
self.factory.authorized_sessions[self.transport.sessionno] = ret
|
||||||
|
self.factory.session_protocols[self.transport.sessionno] = self
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# Send error packet here
|
sendError()
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
else:
|
else:
|
||||||
self.sendData((RPC_RESPONSE, request_id, (ret)))
|
self.sendData((RPC_RESPONSE, request_id, (ret)))
|
||||||
|
@ -200,6 +215,20 @@ class DelugeRPCProtocol(Protocol):
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
finally:
|
finally:
|
||||||
return
|
return
|
||||||
|
elif method == "daemon.set_event_interest":
|
||||||
|
# This special case is to allow clients to set which events they are
|
||||||
|
# interested in receiving.
|
||||||
|
# We are expecting a sequence from the client.
|
||||||
|
try:
|
||||||
|
if self.transport.sessionno not in self.factory.interested_events:
|
||||||
|
self.factory.interested_events[self.transport.sessionno] = []
|
||||||
|
self.factory.interested_events[self.transport.sessionno].extend(args[0])
|
||||||
|
except Exception, e:
|
||||||
|
sendError()
|
||||||
|
else:
|
||||||
|
self.sendData((RPC_RESPONSE, request_id, (True)))
|
||||||
|
finally:
|
||||||
|
return
|
||||||
|
|
||||||
if method in self.factory.methods:
|
if method in self.factory.methods:
|
||||||
try:
|
try:
|
||||||
|
@ -209,19 +238,9 @@ class DelugeRPCProtocol(Protocol):
|
||||||
# This session is not allowed to call this method
|
# This session is not allowed to call this method
|
||||||
log.debug("Session %s is trying to call a method it is not authorized to call!", self.transport.sessionno)
|
log.debug("Session %s is trying to call a method it is not authorized to call!", self.transport.sessionno)
|
||||||
raise NotAuthorizedError("Auth level too low: %s < %s" % (auth_level, method_auth_requirement))
|
raise NotAuthorizedError("Auth level too low: %s < %s" % (auth_level, method_auth_requirement))
|
||||||
|
|
||||||
ret = self.factory.methods[method](*args, **kwargs)
|
ret = self.factory.methods[method](*args, **kwargs)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# Send an error packet here
|
sendError()
|
||||||
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
|
|
||||||
|
|
||||||
self.sendData((
|
|
||||||
RPC_ERROR,
|
|
||||||
request_id,
|
|
||||||
(exceptionType.__name__,
|
|
||||||
exceptionValue.message,
|
|
||||||
"".join(traceback.format_tb(exceptionTraceback)))
|
|
||||||
))
|
|
||||||
# Don't bother printing out DelugeErrors, because they are just for the client
|
# Don't bother printing out DelugeErrors, because they are just for the client
|
||||||
if not isinstance(e, DelugeError):
|
if not isinstance(e, DelugeError):
|
||||||
log.exception("Exception calling RPC request: %s", e)
|
log.exception("Exception calling RPC request: %s", e)
|
||||||
|
@ -249,6 +268,10 @@ class RPCServer(component.Component):
|
||||||
self.factory.methods = {}
|
self.factory.methods = {}
|
||||||
# Holds the session_ids and auth levels
|
# Holds the session_ids and auth levels
|
||||||
self.factory.authorized_sessions = {}
|
self.factory.authorized_sessions = {}
|
||||||
|
# Holds the protocol objects with the session_id as key
|
||||||
|
self.factory.session_protocols = {}
|
||||||
|
# Holds the interested event list for the sessions
|
||||||
|
self.factory.interested_events = {}
|
||||||
|
|
||||||
if not listen:
|
if not listen:
|
||||||
return
|
return
|
||||||
|
@ -320,6 +343,22 @@ class RPCServer(component.Component):
|
||||||
"""
|
"""
|
||||||
return self.factory.methods.keys()
|
return self.factory.methods.keys()
|
||||||
|
|
||||||
|
def emit_event(self, event):
|
||||||
|
"""
|
||||||
|
Emits the event to interested clients.
|
||||||
|
|
||||||
|
:param event: DelugeEvent
|
||||||
|
"""
|
||||||
|
log.debug("intevents: %s", self.factory.interested_events)
|
||||||
|
# Find sessions interested in this event
|
||||||
|
for session_id, interest in self.factory.interested_events.iteritems():
|
||||||
|
if event.name in interest:
|
||||||
|
log.debug("Emit Event: %s %s", event.name, event.args)
|
||||||
|
# This session is interested so send a RPC_SIGNAL
|
||||||
|
self.factory.session_protocols[session_id].sendData(
|
||||||
|
(RPC_SIGNAL, event.name, event.args)
|
||||||
|
)
|
||||||
|
|
||||||
def __generate_ssl_keys(self):
|
def __generate_ssl_keys(self):
|
||||||
"""
|
"""
|
||||||
This method generates a new SSL key/cert.
|
This method generates a new SSL key/cert.
|
||||||
|
|
|
@ -99,7 +99,7 @@ class SignalManager(component.Component):
|
||||||
return
|
return
|
||||||
client = self.clients[uri]
|
client = self.clients[uri]
|
||||||
try:
|
try:
|
||||||
client.emit_signal(signal, *data)
|
client.emit_event_signal(signal, *data)
|
||||||
except (socket.error, Exception), e:
|
except (socket.error, Exception), e:
|
||||||
log.warning("Unable to emit signal to client %s: %s (%d)", client, e, count)
|
log.warning("Unable to emit signal to client %s: %s (%d)", client, e, count)
|
||||||
if count < 30:
|
if count < 30:
|
||||||
|
|
|
@ -40,8 +40,7 @@ import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
|
from deluge.event import *
|
||||||
import deluge.xmlrpclib
|
|
||||||
|
|
||||||
TORRENT_STATE = deluge.common.TORRENT_STATE
|
TORRENT_STATE = deluge.common.TORRENT_STATE
|
||||||
|
|
||||||
|
@ -677,7 +676,7 @@ class Torrent:
|
||||||
# show it as 'Paused'. We need to emit a torrent_paused signal because
|
# show it as 'Paused'. We need to emit a torrent_paused signal because
|
||||||
# the torrent_paused alert from libtorrent will not be generated.
|
# the torrent_paused alert from libtorrent will not be generated.
|
||||||
self.update_state()
|
self.update_state()
|
||||||
self.signals.emit("torrent_paused", self.torrent_id)
|
component.get("RPCServer").emit_event(TorrentStateChangedEvent(self.torrent_id, "Paused"))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.handle.pause()
|
self.handle.pause()
|
||||||
|
@ -706,7 +705,8 @@ class Torrent:
|
||||||
ratio = self.config["stop_seed_ratio"]
|
ratio = self.config["stop_seed_ratio"]
|
||||||
|
|
||||||
if self.get_ratio() >= ratio:
|
if self.get_ratio() >= ratio:
|
||||||
self.signals.emit("torrent_resume_at_stop_ratio")
|
#XXX: This should just be returned in the RPC Response, no event
|
||||||
|
#self.signals.emit_event("torrent_resume_at_stop_ratio")
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.options["auto_managed"]:
|
if self.options["auto_managed"]:
|
||||||
|
|
|
@ -42,6 +42,7 @@ except ImportError:
|
||||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||||
|
|
||||||
|
|
||||||
|
from deluge.event import *
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
|
@ -405,7 +406,7 @@ class TorrentManager(component.Component):
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
|
||||||
# Emit the torrent_added signal
|
# Emit the torrent_added signal
|
||||||
self.signals.emit("torrent_added", torrent.torrent_id)
|
component.get("RPCServer").emit_event(TorrentAddedEvent(torrent.torrent_id))
|
||||||
|
|
||||||
return torrent.torrent_id
|
return torrent.torrent_id
|
||||||
|
|
||||||
|
@ -452,7 +453,7 @@ class TorrentManager(component.Component):
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
|
||||||
# Emit the signal to the clients
|
# Emit the signal to the clients
|
||||||
self.signals.emit("torrent_removed", torrent_id)
|
component.get("RPCServer").emit_event(TorrentRemovedEvent(torrent_id))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -637,7 +638,7 @@ class TorrentManager(component.Component):
|
||||||
torrent.is_finished = True
|
torrent.is_finished = True
|
||||||
torrent.update_state()
|
torrent.update_state()
|
||||||
torrent.save_resume_data()
|
torrent.save_resume_data()
|
||||||
component.get("SignalManager").emit("torrent_finished", torrent_id)
|
component.get("RPCServer").emit_event(TorrentFinishedEvent(torrent_id))
|
||||||
|
|
||||||
def on_alert_torrent_paused(self, alert):
|
def on_alert_torrent_paused(self, alert):
|
||||||
log.debug("on_alert_torrent_paused")
|
log.debug("on_alert_torrent_paused")
|
||||||
|
@ -645,7 +646,7 @@ class TorrentManager(component.Component):
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
# Set the torrent state
|
# Set the torrent state
|
||||||
self.torrents[torrent_id].update_state()
|
self.torrents[torrent_id].update_state()
|
||||||
component.get("SignalManager").emit("torrent_paused", torrent_id)
|
component.get("RPCServer").emit_event(TorrentStateChangedEvent(torrent_id, "Paused"))
|
||||||
|
|
||||||
# Write the fastresume file
|
# Write the fastresume file
|
||||||
self.torrents[torrent_id].save_resume_data()
|
self.torrents[torrent_id].save_resume_data()
|
||||||
|
@ -662,6 +663,10 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
def on_alert_tracker_reply(self, alert):
|
def on_alert_tracker_reply(self, alert):
|
||||||
log.debug("on_alert_tracker_reply: %s", alert.message())
|
log.debug("on_alert_tracker_reply: %s", alert.message())
|
||||||
|
if not alert.handle.is_valid():
|
||||||
|
# Handle is no longer valid, probably a removed torrent
|
||||||
|
return
|
||||||
|
|
||||||
# Get the torrent_id
|
# Get the torrent_id
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
# Set the tracker status for the torrent
|
# Set the tracker status for the torrent
|
||||||
|
@ -725,15 +730,17 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
def on_alert_torrent_resumed(self, alert):
|
def on_alert_torrent_resumed(self, alert):
|
||||||
log.debug("on_alert_torrent_resumed")
|
log.debug("on_alert_torrent_resumed")
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent_id = str(alert.handle.info_hash())
|
||||||
|
torrent = self.torrents[torrent_id]
|
||||||
torrent.is_finished = torrent.handle.is_seed()
|
torrent.is_finished = torrent.handle.is_seed()
|
||||||
torrent.update_state()
|
torrent.update_state()
|
||||||
|
component.get("RPCServer").emit_event(TorrentResumedEvent(torrent_id))
|
||||||
|
|
||||||
def on_alert_state_changed(self, alert):
|
def on_alert_state_changed(self, alert):
|
||||||
log.debug("on_alert_state_changed")
|
log.debug("on_alert_state_changed")
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
self.torrents[torrent_id].update_state()
|
self.torrents[torrent_id].update_state()
|
||||||
component.get("SignalManager").emit("torrent_state_changed", torrent_id)
|
component.get("RPCServer").emit_event(TorrentStateChangedEvent(torrent_id, self.torrents[torrent_id].state))
|
||||||
|
|
||||||
def on_alert_save_resume_data(self, alert):
|
def on_alert_save_resume_data(self, alert):
|
||||||
log.debug("on_alert_save_resume_data")
|
log.debug("on_alert_save_resume_data")
|
||||||
|
@ -759,7 +766,7 @@ class TorrentManager(component.Component):
|
||||||
folder_rename = True
|
folder_rename = True
|
||||||
if len(wait_on_folder[2]) == 1:
|
if len(wait_on_folder[2]) == 1:
|
||||||
# This is the last alert we were waiting for, time to send signal
|
# This is the last alert we were waiting for, time to send signal
|
||||||
component.get("SignalManager").emit("torrent_folder_renamed", torrent_id, wait_on_folder[0], wait_on_folder[1])
|
component.get("RPCServer").emit_event(TorrentFolderRenamedEvent(torrent_id, wait_on_folder[0], wait_on_folder[1]))
|
||||||
del torrent.waiting_on_folder_rename[i]
|
del torrent.waiting_on_folder_rename[i]
|
||||||
break
|
break
|
||||||
# This isn't the last file to be renamed in this folder, so just
|
# This isn't the last file to be renamed in this folder, so just
|
||||||
|
@ -768,7 +775,7 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
if not folder_rename:
|
if not folder_rename:
|
||||||
# This is just a regular file rename so send the signal
|
# This is just a regular file rename so send the signal
|
||||||
component.get("SignalManager").emit("torrent_file_renamed", torrent_id, alert.index, alert.name)
|
component.get("RPCServer").emit_event(TorrentFileRenamedEvent(torrent_id, alert.index, alert.name))
|
||||||
|
|
||||||
def on_alert_metadata_received(self, alert):
|
def on_alert_metadata_received(self, alert):
|
||||||
log.debug("on_alert_metadata_received")
|
log.debug("on_alert_metadata_received")
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
#
|
||||||
|
# event.py
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
|
#
|
||||||
|
# Deluge is free software.
|
||||||
|
#
|
||||||
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
|
# GNU General Public License, as published by the Free Software
|
||||||
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# deluge is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with deluge. If not, write to:
|
||||||
|
# The Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Event module.
|
||||||
|
|
||||||
|
This module describes the types of events that can be generated by the daemon
|
||||||
|
and subsequently emitted to the clients.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
class DelugeEvent(object):
|
||||||
|
"""
|
||||||
|
The base class for all events.
|
||||||
|
|
||||||
|
:prop name: this is the name of the class which is in-turn the event name
|
||||||
|
:prop args: a list of the attribute values
|
||||||
|
|
||||||
|
"""
|
||||||
|
def _get_name(self):
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
|
def _get_args(self):
|
||||||
|
return self.__dict__.values()
|
||||||
|
|
||||||
|
name = property(fget=_get_name)
|
||||||
|
args = property(fget=_get_args)
|
||||||
|
|
||||||
|
class TorrentAddedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a new torrent is successfully added to the session.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id of the torrent that was added
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
|
||||||
|
class TorrentRemovedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a torrent has been removed from the session.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
|
||||||
|
class TorrentStateChangedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a torrent changes state.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id, state):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id
|
||||||
|
:param state: str, the new state
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
class TorrentQueueChangedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when the queue order has changed.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TorrentFolderRenamedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a folder within a torrent has been renamed.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id, old, new):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id
|
||||||
|
:param old: str, the old folder name
|
||||||
|
:param new: str, the new folder name
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
self.old = old
|
||||||
|
self.new = new
|
||||||
|
|
||||||
|
class TorrentFileRenamedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a file within a torrent has been renamed.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id, index, name):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id
|
||||||
|
:param index: int, the index of the file
|
||||||
|
:param name: str, the new filename
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
self.index = index
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
class TorrentFinishedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a torrent finishes downloading.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
|
||||||
|
class TorrentResumedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a torrent resumes from a paused state.
|
||||||
|
"""
|
||||||
|
def __init__(self, torrent_id):
|
||||||
|
"""
|
||||||
|
:param torrent_id: str, the torrent_id
|
||||||
|
"""
|
||||||
|
self.torrent_id = torrent_id
|
||||||
|
|
||||||
|
class NewVersionAvailableEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a more recent version of Deluge is available.
|
||||||
|
"""
|
||||||
|
def __init__(self, new_release):
|
||||||
|
"""
|
||||||
|
:param new_release: str, the new version that is available
|
||||||
|
"""
|
||||||
|
self.new_release = new_release
|
||||||
|
|
||||||
|
class SessionPausedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when the session has been paused.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SessionResumedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when the session has been resumed.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ConfigValueChangedEvent(DelugeEvent):
|
||||||
|
"""
|
||||||
|
Emitted when a config value changes in the Core.
|
||||||
|
"""
|
||||||
|
def __init__(self, key, value):
|
||||||
|
"""
|
||||||
|
:param key: str, the key that changed
|
||||||
|
:param value: the new value of the `:param:key`
|
||||||
|
"""
|
||||||
|
self.key = key
|
||||||
|
self.value = value
|
|
@ -28,6 +28,7 @@ import deluge.rencode as rencode
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
|
import deluge.component as component
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
|
|
||||||
if deluge.common.windows_check():
|
if deluge.common.windows_check():
|
||||||
|
@ -95,9 +96,9 @@ class DelugeRPCRequest(object):
|
||||||
return (self.request_id, self.method, self.args, self.kwargs)
|
return (self.request_id, self.method, self.args, self.kwargs)
|
||||||
|
|
||||||
class DelugeRPCProtocol(Protocol):
|
class DelugeRPCProtocol(Protocol):
|
||||||
__rpc_requests = {}
|
|
||||||
__buffer = None
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
|
self.__rpc_requests = {}
|
||||||
|
self.__buffer = None
|
||||||
# Set the protocol in the daemon so it can send data
|
# Set the protocol in the daemon so it can send data
|
||||||
self.factory.daemon.protocol = self
|
self.factory.daemon.protocol = self
|
||||||
# Get the address of the daemon that we've connected to
|
# Get the address of the daemon that we've connected to
|
||||||
|
@ -144,12 +145,12 @@ class DelugeRPCProtocol(Protocol):
|
||||||
message_type = request[0]
|
message_type = request[0]
|
||||||
|
|
||||||
if message_type == RPC_SIGNAL:
|
if message_type == RPC_SIGNAL:
|
||||||
signal = request[1]
|
event = request[1]
|
||||||
# A RPCSignal was received from the daemon so run any handlers
|
# A RPCSignal was received from the daemon so run any handlers
|
||||||
# associated with it.
|
# associated with it.
|
||||||
if signal in self.factory.daemon.signal_handlers:
|
if event in self.factory.event_handlers:
|
||||||
for handler in self.factory.daemon.signal_handlers[signal]:
|
for handler in self.factory.event_handlers[event]:
|
||||||
handler(*request[2])
|
reactor.callLater(0, handler, *request[2])
|
||||||
return
|
return
|
||||||
|
|
||||||
request_id = request[1]
|
request_id = request[1]
|
||||||
|
@ -181,15 +182,16 @@ class DelugeRPCProtocol(Protocol):
|
||||||
# response to this request. We use the extra information when printing
|
# response to this request. We use the extra information when printing
|
||||||
# out the error for debugging purposes.
|
# out the error for debugging purposes.
|
||||||
self.__rpc_requests[request.request_id] = request
|
self.__rpc_requests[request.request_id] = request
|
||||||
log.debug("Sending RPCRequest %s: %s", request.request_id, request)
|
#log.debug("Sending RPCRequest %s: %s", request.request_id, request)
|
||||||
# Send the request in a tuple because multiple requests can be sent at once
|
# Send the request in a tuple because multiple requests can be sent at once
|
||||||
self.transport.write(zlib.compress(rencode.dumps((request.format_message(),))))
|
self.transport.write(zlib.compress(rencode.dumps((request.format_message(),))))
|
||||||
|
|
||||||
class DelugeRPCClientFactory(ClientFactory):
|
class DelugeRPCClientFactory(ClientFactory):
|
||||||
protocol = DelugeRPCProtocol
|
protocol = DelugeRPCProtocol
|
||||||
|
|
||||||
def __init__(self, daemon):
|
def __init__(self, daemon, event_handlers):
|
||||||
self.daemon = daemon
|
self.daemon = daemon
|
||||||
|
self.event_handlers = event_handlers
|
||||||
|
|
||||||
def startedConnecting(self, connector):
|
def startedConnecting(self, connector):
|
||||||
log.info("Connecting to daemon at %s:%s..", connector.host, connector.port)
|
log.info("Connecting to daemon at %s:%s..", connector.host, connector.port)
|
||||||
|
@ -206,37 +208,16 @@ class DelugeRPCClientFactory(ClientFactory):
|
||||||
self.daemon.connected = False
|
self.daemon.connected = False
|
||||||
if self.daemon.disconnect_deferred:
|
if self.daemon.disconnect_deferred:
|
||||||
self.daemon.disconnect_deferred.callback(reason.value)
|
self.daemon.disconnect_deferred.callback(reason.value)
|
||||||
self.daemon.generate_event("disconnected")
|
|
||||||
|
if self.daemon.disconnect_callback:
|
||||||
|
self.daemon.disconnect_callback()
|
||||||
|
|
||||||
class DaemonProxy(object):
|
class DaemonProxy(object):
|
||||||
__event_handlers = {
|
pass
|
||||||
"connected": [],
|
|
||||||
"disconnected": []
|
|
||||||
}
|
|
||||||
|
|
||||||
def register_event_handler(self, event, handler):
|
|
||||||
"""
|
|
||||||
Registers a handler that will be called when an event happens.
|
|
||||||
|
|
||||||
:params event: str, the event to handle
|
|
||||||
:params handler: func, the handler function, f()
|
|
||||||
|
|
||||||
:raises KeyError: if event is not valid
|
|
||||||
"""
|
|
||||||
self.__event_handlers[event].append(handler)
|
|
||||||
|
|
||||||
def generate_event(self, event):
|
|
||||||
"""
|
|
||||||
Calls the event handlers for `:param:event`.
|
|
||||||
|
|
||||||
:param event: str, the event to generate
|
|
||||||
"""
|
|
||||||
for handler in self.__event_handlers[event]:
|
|
||||||
handler()
|
|
||||||
|
|
||||||
class DaemonSSLProxy(DaemonProxy):
|
class DaemonSSLProxy(DaemonProxy):
|
||||||
def __init__(self):
|
def __init__(self, event_handlers={}):
|
||||||
self.__factory = DelugeRPCClientFactory(self)
|
self.__factory = DelugeRPCClientFactory(self, event_handlers)
|
||||||
self.__request_counter = 0
|
self.__request_counter = 0
|
||||||
self.__deferred = {}
|
self.__deferred = {}
|
||||||
|
|
||||||
|
@ -251,6 +232,7 @@ class DaemonSSLProxy(DaemonProxy):
|
||||||
self.connected = False
|
self.connected = False
|
||||||
|
|
||||||
self.disconnect_deferred = None
|
self.disconnect_deferred = None
|
||||||
|
self.disconnect_callback = None
|
||||||
|
|
||||||
def connect(self, host, port, username, password):
|
def connect(self, host, port, username, password):
|
||||||
"""
|
"""
|
||||||
|
@ -268,11 +250,12 @@ class DaemonSSLProxy(DaemonProxy):
|
||||||
self.port = port
|
self.port = port
|
||||||
self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory())
|
self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory())
|
||||||
self.connect_deferred = defer.Deferred()
|
self.connect_deferred = defer.Deferred()
|
||||||
|
self.login_deferred = defer.Deferred()
|
||||||
|
|
||||||
|
# Upon connect we do a 'daemon.login' RPC
|
||||||
self.connect_deferred.addCallback(self.__on_connect, username, password)
|
self.connect_deferred.addCallback(self.__on_connect, username, password)
|
||||||
self.connect_deferred.addErrback(self.__on_connect_fail)
|
self.connect_deferred.addErrback(self.__on_connect_fail)
|
||||||
|
|
||||||
self.login_deferred = defer.Deferred()
|
|
||||||
# XXX: Add the login stuff..
|
|
||||||
return self.login_deferred
|
return self.login_deferred
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
|
@ -324,31 +307,34 @@ class DaemonSSLProxy(DaemonProxy):
|
||||||
"""
|
"""
|
||||||
return self.__deferred.pop(request_id)
|
return self.__deferred.pop(request_id)
|
||||||
|
|
||||||
def register_signal_handler(self, signal, handler):
|
def register_event_handler(self, event, handler):
|
||||||
"""
|
"""
|
||||||
Registers a handler function to be called when `:param:signal` is received
|
Registers a handler function to be called when `:param:event` is received
|
||||||
from the daemon.
|
from the daemon.
|
||||||
|
|
||||||
:param signal: str, the name of the signal to handle
|
:param event: str, the name of the event to handle
|
||||||
:param handler: function, the function to be called when `:param:signal`
|
:param handler: function, the function to be called when `:param:event`
|
||||||
is emitted from the daemon
|
is emitted from the daemon
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if signal not in self.signal_handlers:
|
if event not in self.factory.event_handlers:
|
||||||
self.signal_handlers[signal] = []
|
# This is a new event to handle, so we need to tell the daemon
|
||||||
|
# that we're interested in receiving this type of event
|
||||||
|
self.event_handlers[event] = []
|
||||||
|
self.call("daemon.set_event_interest", [event])
|
||||||
|
|
||||||
self.signal_handlers[signal].append(handler)
|
self.factory.event_handlers[event].append(handler)
|
||||||
|
|
||||||
def deregister_signal_handler(self, signal, handler):
|
def deregister_event_handler(self, event, handler):
|
||||||
"""
|
"""
|
||||||
Deregisters a signal handler.
|
Deregisters a event handler.
|
||||||
|
|
||||||
:param signal: str, the name of the signal
|
:param event: str, the name of the event
|
||||||
:param handler: function, the function registered
|
:param handler: function, the function registered
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if signal in self.signal_handlers and handler in self.signal_handlers[signal]:
|
if event in self.event_handlers and handler in self.event_handlers[event]:
|
||||||
self.signal_handlers[signal].remove(handler)
|
self.event_handlers[event].remove(handler)
|
||||||
|
|
||||||
def __rpcError(self, error_data):
|
def __rpcError(self, error_data):
|
||||||
"""
|
"""
|
||||||
|
@ -379,16 +365,26 @@ class DaemonSSLProxy(DaemonProxy):
|
||||||
self.__login_deferred.addErrback(self.__on_login_fail)
|
self.__login_deferred.addErrback(self.__on_login_fail)
|
||||||
|
|
||||||
def __on_connect_fail(self, reason):
|
def __on_connect_fail(self, reason):
|
||||||
|
log.debug("connect_fail: %s", reason)
|
||||||
self.login_deferred.callback(False)
|
self.login_deferred.callback(False)
|
||||||
|
|
||||||
def __on_login(self, result, username):
|
def __on_login(self, result, username):
|
||||||
self.username = username
|
self.username = username
|
||||||
|
# We need to tell the daemon what events we're interested in receiving
|
||||||
|
if self.__factory.event_handlers:
|
||||||
|
self.call("daemon.set_event_interest", self.__factory.event_handlers.keys())
|
||||||
self.login_deferred.callback(result)
|
self.login_deferred.callback(result)
|
||||||
self.generate_event("connected")
|
|
||||||
|
|
||||||
def __on_login_fail(self, result):
|
def __on_login_fail(self, result):
|
||||||
self.login_deferred.callback(False)
|
self.login_deferred.callback(False)
|
||||||
|
|
||||||
|
def set_disconnect_callback(self, cb):
|
||||||
|
"""
|
||||||
|
Set a function to be called when the connection to the daemon is lost
|
||||||
|
for any reason.
|
||||||
|
"""
|
||||||
|
self.disconnect_callback = cb
|
||||||
|
|
||||||
class DaemonClassicProxy(DaemonProxy):
|
class DaemonClassicProxy(DaemonProxy):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
import deluge.core.daemon
|
import deluge.core.daemon
|
||||||
|
@ -399,8 +395,6 @@ class DaemonClassicProxy(DaemonProxy):
|
||||||
self.port = 58846
|
self.port = 58846
|
||||||
self.user = "localclient"
|
self.user = "localclient"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
self.__daemon = None
|
self.__daemon = None
|
||||||
|
|
||||||
|
@ -451,12 +445,11 @@ class Client(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__event_handlers = {
|
__event_handlers = {
|
||||||
"connected": [],
|
|
||||||
"disconnected": []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._daemon_proxy = None
|
self._daemon_proxy = None
|
||||||
|
self.disconnect_callback = None
|
||||||
|
|
||||||
def connect(self, host="127.0.0.1", port=58846, username="", password=""):
|
def connect(self, host="127.0.0.1", port=58846, username="", password=""):
|
||||||
"""
|
"""
|
||||||
|
@ -470,9 +463,8 @@ class Client(object):
|
||||||
:returns: a Deferred object that will be called once the connection
|
:returns: a Deferred object that will be called once the connection
|
||||||
has been established or fails
|
has been established or fails
|
||||||
"""
|
"""
|
||||||
self._daemon_proxy = DaemonSSLProxy()
|
self._daemon_proxy = DaemonSSLProxy(self.__event_handlers)
|
||||||
self._daemon_proxy.register_event_handler("connected", self._on_connect)
|
self._daemon_proxy.set_disconnect_callback(self.__on_disconnect)
|
||||||
self._daemon_proxy.register_event_handler("disconnected", self._on_disconnect)
|
|
||||||
d = self._daemon_proxy.connect(host, port, username, password)
|
d = self._daemon_proxy.connect(host, port, username, password)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@ -544,24 +536,30 @@ class Client(object):
|
||||||
|
|
||||||
def register_event_handler(self, event, handler):
|
def register_event_handler(self, event, handler):
|
||||||
"""
|
"""
|
||||||
Registers a handler that will be called when an event happens.
|
Registers a handler that will be called when an event is received from the daemon.
|
||||||
|
|
||||||
:params event: str, the event to handle
|
:params event: str, the event to handle
|
||||||
:params handler: func, the handler function, f()
|
:params handler: func, the handler function, f(args)
|
||||||
|
|
||||||
:raises KeyError: if event is not valid
|
|
||||||
"""
|
"""
|
||||||
|
if event not in self.__event_handlers:
|
||||||
|
self.__event_handlers[event] = []
|
||||||
self.__event_handlers[event].append(handler)
|
self.__event_handlers[event].append(handler)
|
||||||
|
# We need to replicate this in the daemon proxy
|
||||||
|
if self._daemon_proxy:
|
||||||
|
self._daemon_proxy.register_event_handler(event, handler)
|
||||||
|
|
||||||
def __generate_event(self, event):
|
def deregister_event_handler(self, event, handler):
|
||||||
"""
|
"""
|
||||||
Calls the event handlers for `:param:event`.
|
Deregisters a event handler.
|
||||||
|
|
||||||
|
:param event: str, the name of the event
|
||||||
|
:param handler: function, the function registered
|
||||||
|
|
||||||
:param event: str, the event to generate
|
|
||||||
"""
|
"""
|
||||||
|
if event in self.__event_handlers and handler in self.__event_handlers[event]:
|
||||||
for handler in self.__event_handlers[event]:
|
self.__event_handlers[event].remove(handler)
|
||||||
handler()
|
if self._daemon_proxy:
|
||||||
|
self._daemon_proxy.register_event_handler(event, handler)
|
||||||
|
|
||||||
def force_call(self, block=False):
|
def force_call(self, block=False):
|
||||||
# no-op for now.. we'll see if we need this in the future
|
# no-op for now.. we'll see if we need this in the future
|
||||||
|
@ -570,11 +568,16 @@ class Client(object):
|
||||||
def __getattr__(self, method):
|
def __getattr__(self, method):
|
||||||
return DottedObject(self._daemon_proxy, method)
|
return DottedObject(self._daemon_proxy, method)
|
||||||
|
|
||||||
def _on_connect(self):
|
def set_disconnect_callback(self, cb):
|
||||||
self.__generate_event("connected")
|
"""
|
||||||
|
Set a function to be called whenever the client is disconnected from
|
||||||
|
the daemon for any reason.
|
||||||
|
"""
|
||||||
|
self.disconnect_callback = cb
|
||||||
|
|
||||||
def _on_disconnect(self):
|
def __on_disconnect(self):
|
||||||
self.__generate_event("disconnected")
|
if self.disconnect_callback:
|
||||||
|
self.disconnect_callback()
|
||||||
|
|
||||||
# This is the object clients will use
|
# This is the object clients will use
|
||||||
client = Client()
|
client = Client()
|
||||||
|
|
|
@ -363,6 +363,8 @@ class ConnectionManager(component.Component):
|
||||||
if self.gtkui_config["autoconnect"]:
|
if self.gtkui_config["autoconnect"]:
|
||||||
self.gtkui_config["autoconnect_host_id"] = host_id
|
self.gtkui_config["autoconnect_host_id"] = host_id
|
||||||
|
|
||||||
|
component.start()
|
||||||
|
|
||||||
def on_button_connect_clicked(self, widget=None):
|
def on_button_connect_clicked(self, widget=None):
|
||||||
model, row = self.hostlist.get_selection().get_selected()
|
model, row = self.hostlist.get_selection().get_selected()
|
||||||
status = model[row][HOSTLIST_COL_STATUS]
|
status = model[row][HOSTLIST_COL_STATUS]
|
||||||
|
|
|
@ -32,8 +32,7 @@ class CoreConfig(component.Component):
|
||||||
log.debug("CoreConfig init..")
|
log.debug("CoreConfig init..")
|
||||||
component.Component.__init__(self, "CoreConfig", ["Signals"])
|
component.Component.__init__(self, "CoreConfig", ["Signals"])
|
||||||
self.config = {}
|
self.config = {}
|
||||||
component.get("Signals").connect_to_signal("config_value_changed",
|
client.register_event_handler("ConfigValueChangedEvent", self.on_configvaluechanged_event)
|
||||||
self._on_config_value_changed)
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
client.core.get_config().addCallback(self._on_get_config)
|
client.core.get_config().addCallback(self._on_get_config)
|
||||||
|
@ -50,5 +49,5 @@ class CoreConfig(component.Component):
|
||||||
def _on_get_config(self, config):
|
def _on_get_config(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def _on_config_value_changed(self, key, value):
|
def on_configvaluechanged_event(self, key, value):
|
||||||
self.config[key] = value
|
self.config[key] = value
|
||||||
|
|
|
@ -190,10 +190,10 @@ class FilesTab(Tab):
|
||||||
"on_menuitem_expand_all_activate": self._on_menuitem_expand_all_activate
|
"on_menuitem_expand_all_activate": self._on_menuitem_expand_all_activate
|
||||||
})
|
})
|
||||||
|
|
||||||
# Connect to the 'torrent_file_renamed' signal
|
# Connect to various events from the daemon
|
||||||
component.get("Signals").connect_to_signal("torrent_file_renamed", self._on_torrent_file_renamed_signal)
|
client.register_event_handler("TorrentFileRenamedEvent", self._on_torrentfilerenamed_event)
|
||||||
component.get("Signals").connect_to_signal("torrent_folder_renamed", self._on_torrent_folder_renamed_signal)
|
client.register_event_handler("TorrentFolderRenamedEvent", self._on_torrentfolderrenamed_event)
|
||||||
component.get("Signals").connect_to_signal("torrent_removed", self._on_torrent_removed_signal)
|
client.register_event_handler("TorrentRemovedEvent", self._on_torrentremoved_event)
|
||||||
|
|
||||||
# Attempt to load state
|
# Attempt to load state
|
||||||
self.load_state()
|
self.load_state()
|
||||||
|
@ -544,7 +544,7 @@ class FilesTab(Tab):
|
||||||
def _on_filename_editing_canceled(self, renderer):
|
def _on_filename_editing_canceled(self, renderer):
|
||||||
self._editing_index = None
|
self._editing_index = None
|
||||||
|
|
||||||
def _on_torrent_file_renamed_signal(self, torrent_id, index, name):
|
def _on_torrentfilerenamed_event(self, torrent_id, index, name):
|
||||||
log.debug("index: %s name: %s", index, name)
|
log.debug("index: %s name: %s", index, name)
|
||||||
old_name = self.files_list[torrent_id][index]["path"]
|
old_name = self.files_list[torrent_id][index]["path"]
|
||||||
self.files_list[torrent_id][index]["path"] = name
|
self.files_list[torrent_id][index]["path"] = name
|
||||||
|
@ -690,7 +690,7 @@ class FilesTab(Tab):
|
||||||
self.treestore.remove(itr)
|
self.treestore.remove(itr)
|
||||||
itr = parent
|
itr = parent
|
||||||
|
|
||||||
def _on_torrent_folder_renamed_signal(self, torrent_id, old_folder, new_folder):
|
def _on_torrentfolderrenamed_event(self, torrent_id, old_folder, new_folder):
|
||||||
log.debug("on_torrent_folder_renamed_signal")
|
log.debug("on_torrent_folder_renamed_signal")
|
||||||
log.debug("old_folder: %s new_folder: %s", old_folder, new_folder)
|
log.debug("old_folder: %s new_folder: %s", old_folder, new_folder)
|
||||||
|
|
||||||
|
@ -736,7 +736,7 @@ class FilesTab(Tab):
|
||||||
# and if so, we delete it
|
# and if so, we delete it
|
||||||
self.remove_childless_folders(old_folder_iter_parent)
|
self.remove_childless_folders(old_folder_iter_parent)
|
||||||
|
|
||||||
def _on_torrent_removed_signal(self, torrent_id):
|
def _on_torrentremoved_event(self, torrent_id):
|
||||||
if torrent_id in self.files_list:
|
if torrent_id in self.files_list:
|
||||||
del self.files_list[torrent_id]
|
del self.files_list[torrent_id]
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ from preferences import Preferences
|
||||||
from systemtray import SystemTray
|
from systemtray import SystemTray
|
||||||
from statusbar import StatusBar
|
from statusbar import StatusBar
|
||||||
from connectionmanager import ConnectionManager
|
from connectionmanager import ConnectionManager
|
||||||
from signals import Signals
|
|
||||||
from pluginmanager import PluginManager
|
from pluginmanager import PluginManager
|
||||||
from ipcinterface import IPCInterface
|
from ipcinterface import IPCInterface
|
||||||
|
|
||||||
|
@ -128,7 +127,6 @@ class GtkUI:
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.error("Unable to initialize gettext/locale!")
|
log.error("Unable to initialize gettext/locale!")
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
|
|
||||||
# Setup signals
|
# Setup signals
|
||||||
try:
|
try:
|
||||||
import gnome.ui
|
import gnome.ui
|
||||||
|
@ -183,11 +181,7 @@ class GtkUI:
|
||||||
self.ipcinterface = IPCInterface(args)
|
self.ipcinterface = IPCInterface(args)
|
||||||
|
|
||||||
# We make sure that the UI components start once we get a core URI
|
# We make sure that the UI components start once we get a core URI
|
||||||
client.register_event_handler("connected", self._on_new_core)
|
client.set_disconnect_callback(self.__on_disconnect)
|
||||||
client.register_event_handler("disconnected", self._on_no_core)
|
|
||||||
|
|
||||||
# Start the signal receiver
|
|
||||||
self.signal_receiver = Signals()
|
|
||||||
|
|
||||||
# Initialize various components of the gtkui
|
# Initialize various components of the gtkui
|
||||||
self.mainwindow = MainWindow()
|
self.mainwindow = MainWindow()
|
||||||
|
@ -250,11 +244,11 @@ class GtkUI:
|
||||||
|
|
||||||
if self.config["classic_mode"]:
|
if self.config["classic_mode"]:
|
||||||
client.start_classic_mode()
|
client.start_classic_mode()
|
||||||
self._on_new_core()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _on_new_core(self):
|
def __on_disconnect(self):
|
||||||
component.start()
|
"""
|
||||||
|
Called when disconnected from the daemon. We basically just stop all
|
||||||
def _on_no_core(self):
|
the components here.
|
||||||
|
"""
|
||||||
component.stop()
|
component.stop()
|
||||||
|
|
|
@ -47,10 +47,11 @@ class IPCInterface(component.Component):
|
||||||
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
|
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
|
||||||
# We already have a running session, send a XMLRPC to the existing session
|
# We already have a running session, send a XMLRPC to the existing session
|
||||||
config = ConfigManager("gtkui.conf")
|
config = ConfigManager("gtkui.conf")
|
||||||
uri = "http://localhost:" + str(config["signal_port"])
|
# XXX: Need new IPC method
|
||||||
import deluge.xmlrpclib as xmlrpclib
|
# uri = "http://localhost:" + str(config["signal_port"])
|
||||||
rpc = xmlrpclib.ServerProxy(uri, allow_none=True)
|
# import deluge.xmlrpclib as xmlrpclib
|
||||||
rpc.emit_signal("args_from_external", args)
|
# rpc = xmlrpclib.ServerProxy(uri, allow_none=True)
|
||||||
|
# rpc.emit_signal("args_from_external", args)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
process_args(args)
|
process_args(args)
|
||||||
|
|
|
@ -80,6 +80,9 @@ class MainWindow(component.Component):
|
||||||
log.debug("Showing window")
|
log.debug("Showing window")
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
|
client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event)
|
||||||
|
client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
try:
|
try:
|
||||||
component.resume("TorrentView")
|
component.resume("TorrentView")
|
||||||
|
@ -215,3 +218,12 @@ class MainWindow(component.Component):
|
||||||
self.update()
|
self.update()
|
||||||
else:
|
else:
|
||||||
self.window.set_title("Deluge")
|
self.window.set_title("Deluge")
|
||||||
|
|
||||||
|
def on_newversionavailable_event(self, new_version):
|
||||||
|
if self.config["show_new_releases"]:
|
||||||
|
from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog
|
||||||
|
NewReleaseDialog().show(new_version)
|
||||||
|
|
||||||
|
def on_torrentfinished_event(self, torrent_id):
|
||||||
|
from deluge.ui.gtkui.notification import Notification
|
||||||
|
Notification().notify(torrent_id)
|
||||||
|
|
|
@ -164,6 +164,11 @@ class MenuBar(component.Component):
|
||||||
self.window.main_glade.get_widget("separatormenuitem").hide()
|
self.window.main_glade.get_widget("separatormenuitem").hide()
|
||||||
self.window.main_glade.get_widget("menuitem_connectionmanager").hide()
|
self.window.main_glade.get_widget("menuitem_connectionmanager").hide()
|
||||||
|
|
||||||
|
client.register_event_handler("TorrentStateChangedEvent", self.on_torrentstatechanged_event)
|
||||||
|
client.register_event_handler("TorrentResumedEvent", self.on_torrentresumed_event)
|
||||||
|
client.register_event_handler("SessionPausedEvent", self.on_sessionpaused_event)
|
||||||
|
client.register_event_handler("SessionResumedEvent", self.on_sessionresumed_event)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
for widget in self.change_sensitivity:
|
for widget in self.change_sensitivity:
|
||||||
self.window.main_glade.get_widget(widget).set_sensitive(True)
|
self.window.main_glade.get_widget(widget).set_sensitive(True)
|
||||||
|
@ -217,6 +222,18 @@ class MenuBar(component.Component):
|
||||||
return sep
|
return sep
|
||||||
|
|
||||||
### Callbacks ###
|
### Callbacks ###
|
||||||
|
def on_torrentstatechanged_event(self, torrent_id, state):
|
||||||
|
if state == "Paused":
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
|
def on_torrentresumed_event(self, torrent_id):
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
|
def on_sessionpaused_event(self):
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
|
def on_sessionresumed_event(self):
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
## File Menu ##
|
## File Menu ##
|
||||||
def on_menuitem_addtorrent_activate(self, data=None):
|
def on_menuitem_addtorrent_activate(self, data=None):
|
||||||
|
|
|
@ -32,7 +32,7 @@ from deluge.log import LOG as log
|
||||||
class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
component.Component):
|
component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "PluginManager", depend=["Signals"])
|
component.Component.__init__(self, "PluginManager")
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
deluge.pluginmanagerbase.PluginManagerBase.__init__(
|
deluge.pluginmanagerbase.PluginManagerBase.__init__(
|
||||||
self, "gtkui.conf", "deluge.plugin.gtkui")
|
self, "gtkui.conf", "deluge.plugin.gtkui")
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
#
|
|
||||||
# signals.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
|
||||||
#
|
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
|
|
||||||
import deluge.component as component
|
|
||||||
from deluge.ui.client import client
|
|
||||||
from deluge.ui.signalreceiver import SignalReceiver
|
|
||||||
from deluge.configmanager import ConfigManager
|
|
||||||
from deluge.log import LOG as log
|
|
||||||
|
|
||||||
class Signals(component.Component):
|
|
||||||
def __init__(self):
|
|
||||||
component.Component.__init__(self, "Signals")
|
|
||||||
# self.receiver = SignalReceiver()
|
|
||||||
self.config = ConfigManager("gtkui.conf")
|
|
||||||
#self.config["signal_port"] = self.receiver.get_port()
|
|
||||||
self.config.save()
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
return
|
|
||||||
self.receiver.set_remote(not client.is_localhost())
|
|
||||||
self.receiver.run()
|
|
||||||
|
|
||||||
self.receiver.connect_to_signal("torrent_added",
|
|
||||||
self.torrent_added_signal)
|
|
||||||
self.receiver.connect_to_signal("torrent_removed",
|
|
||||||
self.torrent_removed_signal)
|
|
||||||
self.receiver.connect_to_signal("torrent_paused", self.torrent_paused)
|
|
||||||
self.receiver.connect_to_signal("torrent_resumed",
|
|
||||||
self.torrent_resumed)
|
|
||||||
self.receiver.connect_to_signal("torrent_all_paused",
|
|
||||||
self.torrent_all_paused)
|
|
||||||
self.receiver.connect_to_signal("torrent_all_resumed",
|
|
||||||
self.torrent_all_resumed)
|
|
||||||
self.receiver.connect_to_signal("config_value_changed",
|
|
||||||
self.config_value_changed)
|
|
||||||
self.receiver.connect_to_signal("torrent_queue_changed",
|
|
||||||
self.torrent_queue_changed)
|
|
||||||
self.receiver.connect_to_signal("torrent_resume_at_stop_ratio",
|
|
||||||
self.torrent_resume_at_stop_ratio)
|
|
||||||
self.receiver.connect_to_signal("new_version_available",
|
|
||||||
self.new_version_available)
|
|
||||||
self.receiver.connect_to_signal("args_from_external",
|
|
||||||
self.args_from_external)
|
|
||||||
self.receiver.connect_to_signal("torrent_state_changed",
|
|
||||||
self.torrent_state_changed)
|
|
||||||
self.receiver.connect_to_signal("torrent_finished",
|
|
||||||
self.torrent_finished)
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
self.receiver.shutdown()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def connect_to_signal(self, signal, callback):
|
|
||||||
"""Connects a callback to a signal"""
|
|
||||||
#self.receiver.connect_to_signal(signal, callback)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def torrent_finished(self, torrent_id):
|
|
||||||
log.debug("torrent_finished signal received..")
|
|
||||||
log.debug("torrent id: %s", torrent_id)
|
|
||||||
from deluge.ui.gtkui.notification import Notification
|
|
||||||
Notification().notify(torrent_id)
|
|
||||||
|
|
||||||
def torrent_added_signal(self, torrent_id):
|
|
||||||
log.debug("torrent_added signal received..")
|
|
||||||
log.debug("torrent id: %s", torrent_id)
|
|
||||||
# Add the torrent to the treeview
|
|
||||||
component.get("TorrentView").add_row(torrent_id)
|
|
||||||
component.get("TorrentView").mark_dirty(torrent_id)
|
|
||||||
|
|
||||||
def torrent_removed_signal(self, torrent_id):
|
|
||||||
log.debug("torrent_remove signal received..")
|
|
||||||
log.debug("torrent id: %s", torrent_id)
|
|
||||||
# Remove the torrent from the treeview
|
|
||||||
component.get("TorrentView").remove_row(torrent_id)
|
|
||||||
|
|
||||||
def torrent_paused(self, torrent_id):
|
|
||||||
log.debug("torrent_paused signal received..")
|
|
||||||
component.get("TorrentView").mark_dirty(torrent_id)
|
|
||||||
component.get("TorrentView").update()
|
|
||||||
component.get("ToolBar").update_buttons()
|
|
||||||
component.get("MenuBar").update_menu()
|
|
||||||
|
|
||||||
def torrent_resumed(self, torrent_id):
|
|
||||||
log.debug("torrent_resumed signal received..")
|
|
||||||
component.get("TorrentView").mark_dirty(torrent_id)
|
|
||||||
component.get("TorrentView").update()
|
|
||||||
component.get("ToolBar").update_buttons()
|
|
||||||
component.get("MenuBar").update_menu()
|
|
||||||
|
|
||||||
def torrent_all_paused(self):
|
|
||||||
log.debug("torrent_all_paused signal received..")
|
|
||||||
component.get("TorrentView").mark_dirty()
|
|
||||||
component.get("TorrentView").update()
|
|
||||||
component.get("ToolBar").update_buttons()
|
|
||||||
component.get("MenuBar").update_menu()
|
|
||||||
|
|
||||||
def torrent_all_resumed(self):
|
|
||||||
log.debug("torrent_all_resumed signal received..")
|
|
||||||
component.get("TorrentView").mark_dirty()
|
|
||||||
component.get("TorrentView").update()
|
|
||||||
component.get("ToolBar").update_buttons()
|
|
||||||
component.get("MenuBar").update_menu()
|
|
||||||
|
|
||||||
def config_value_changed(self, key, value):
|
|
||||||
log.debug("config_value_changed signal received..")
|
|
||||||
component.get("StatusBar").config_value_changed(key, value)
|
|
||||||
|
|
||||||
def torrent_queue_changed(self):
|
|
||||||
log.debug("torrent_queue_changed signal received..")
|
|
||||||
component.get("TorrentView").mark_dirty()
|
|
||||||
component.get("TorrentView").update()
|
|
||||||
|
|
||||||
def torrent_resume_at_stop_ratio(self):
|
|
||||||
log.debug("torrent_resume_at_stop_ratio")
|
|
||||||
component.get("StatusBar").display_warning(
|
|
||||||
text=_("Torrent is past stop ratio."))
|
|
||||||
|
|
||||||
def new_version_available(self, value):
|
|
||||||
log.debug("new_version_available: %s", value)
|
|
||||||
if self.config["show_new_releases"]:
|
|
||||||
from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog
|
|
||||||
NewReleaseDialog().show(value)
|
|
||||||
|
|
||||||
def args_from_external(self, value):
|
|
||||||
log.debug("args_from_external: %s", value)
|
|
||||||
import ipcinterface
|
|
||||||
ipcinterface.process_args(value)
|
|
||||||
|
|
||||||
def torrent_state_changed(self, value):
|
|
||||||
log.debug("torrent_state_changed: %s", value)
|
|
|
@ -134,6 +134,8 @@ class StatusBar(component.Component):
|
||||||
# Hide if necessary
|
# Hide if necessary
|
||||||
self.visible(self.config["show_statusbar"])
|
self.visible(self.config["show_statusbar"])
|
||||||
|
|
||||||
|
client.register_event_handler("ConfigValueChangedEvent", self.on_configvaluechanged_event)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
# Add in images and labels
|
# Add in images and labels
|
||||||
self.remove_item(self.not_connected_item)
|
self.remove_item(self.not_connected_item)
|
||||||
|
@ -262,7 +264,7 @@ class StatusBar(component.Component):
|
||||||
# Only request health status while False
|
# Only request health status while False
|
||||||
client.core.get_health().addCallback(self._on_get_health)
|
client.core.get_health().addCallback(self._on_get_health)
|
||||||
|
|
||||||
def config_value_changed(self, key, value):
|
def on_configvaluechanged_event(self, key, value):
|
||||||
"""This is called when we received a config_value_changed signal from
|
"""This is called when we received a config_value_changed signal from
|
||||||
the core."""
|
the core."""
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ class SystemTray(component.Component):
|
||||||
self.tray_glade.get_widget("menuitem_quitdaemon").hide()
|
self.tray_glade.get_widget("menuitem_quitdaemon").hide()
|
||||||
self.tray_glade.get_widget("separatormenuitem4").hide()
|
self.tray_glade.get_widget("separatormenuitem4").hide()
|
||||||
|
|
||||||
component.get("Signals").connect_to_signal("config_value_changed", self.config_value_changed)
|
client.register_event_handler("ConfigValueChangedEvent", self.config_value_changed)
|
||||||
if not client.connected():
|
if not client.connected():
|
||||||
# Hide menu widgets because we're not connected to a host.
|
# Hide menu widgets because we're not connected to a host.
|
||||||
for widget in self.hide_widget_list:
|
for widget in self.hide_widget_list:
|
||||||
|
|
|
@ -29,6 +29,7 @@ import gtk, gtk.glade
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
|
from deluge.ui.client import client
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
from deluge.common import TORRENT_STATE
|
from deluge.common import TORRENT_STATE
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
|
@ -67,6 +68,10 @@ class ToolBar(component.Component):
|
||||||
|
|
||||||
# Hide if necessary
|
# Hide if necessary
|
||||||
self.visible(self.config["show_toolbar"])
|
self.visible(self.config["show_toolbar"])
|
||||||
|
client.register_event_handler("TorrentStateChangedEvent", self.on_torrentstatechanged_event)
|
||||||
|
client.register_event_handler("TorrentResumedEvent", self.on_torrentresumed_event)
|
||||||
|
client.register_event_handler("SessionPausedEvent", self.on_sessionpaused_event)
|
||||||
|
client.register_event_handler("SessionResumedEvent", self.on_sessionresumed_event)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
for widget in self.change_sensitivity:
|
for widget in self.change_sensitivity:
|
||||||
|
@ -128,6 +133,19 @@ class ToolBar(component.Component):
|
||||||
self.toolbar.remove(widget)
|
self.toolbar.remove(widget)
|
||||||
|
|
||||||
### Callbacks ###
|
### Callbacks ###
|
||||||
|
def on_torrentstatechanged_event(self, torrent_id, state):
|
||||||
|
if state == "Paused":
|
||||||
|
self.update_buttons()
|
||||||
|
|
||||||
|
def on_torrentresumed_event(self, torrent_id):
|
||||||
|
self.update_buttons()
|
||||||
|
|
||||||
|
def on_sessionpaused_event(self):
|
||||||
|
self.update_buttons()
|
||||||
|
|
||||||
|
def on_sessionresumed_event(self):
|
||||||
|
self.update_buttons()
|
||||||
|
|
||||||
def on_toolbutton_add_clicked(self, data):
|
def on_toolbutton_add_clicked(self, data):
|
||||||
log.debug("on_toolbutton_add_clicked")
|
log.debug("on_toolbutton_add_clicked")
|
||||||
# Use the menubar's callback
|
# Use the menubar's callback
|
||||||
|
|
|
@ -193,6 +193,13 @@ class TorrentView(listview.ListView, component.Component):
|
||||||
|
|
||||||
self.treeview.connect("drag-drop", self.on_drag_drop)
|
self.treeview.connect("drag-drop", self.on_drag_drop)
|
||||||
|
|
||||||
|
client.register_event_handler("TorrentStateChangedEvent", self.on_torrentstatechanged_event)
|
||||||
|
client.register_event_handler("TorrentAddedEvent", self.on_torrentadded_event)
|
||||||
|
client.register_event_handler("TorrentRemovedEvent", self.on_torrentremoved_event)
|
||||||
|
client.register_event_handler("SessionPausedEvent", self.on_sessionpaused_event)
|
||||||
|
client.register_event_handler("SessionResumedEvent", self.on_sessionresumed_event)
|
||||||
|
client.register_event_handler("TorrentQueueChangedEvent", self.on_torrentqueuechanged_event)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the torrentview"""
|
"""Start the torrentview"""
|
||||||
# We need to get the core session state to know which torrents are in
|
# We need to get the core session state to know which torrents are in
|
||||||
|
@ -424,3 +431,32 @@ class TorrentView(listview.ListView, component.Component):
|
||||||
|
|
||||||
def on_drag_drop(self, widget, drag_context, x, y, timestamp):
|
def on_drag_drop(self, widget, drag_context, x, y, timestamp):
|
||||||
widget.stop_emission("drag-drop")
|
widget.stop_emission("drag-drop")
|
||||||
|
|
||||||
|
def on_torrentadded_event(self, torrent_id):
|
||||||
|
self.add_row(torrent_id)
|
||||||
|
self.mark_dirty(torrent_id)
|
||||||
|
|
||||||
|
def on_torrentremoved_event(self, torrent_id):
|
||||||
|
self.remove_row(torrent_id)
|
||||||
|
|
||||||
|
def on_torrentstatechanged_event(self, torrent_id, state):
|
||||||
|
# Update the torrents state
|
||||||
|
for row in self.liststore:
|
||||||
|
if not torrent_id == row[self.columns["torrent_id"].column_indices[0]]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
row[self.get_column_index("Progress")[1]] = state
|
||||||
|
|
||||||
|
self.mark_dirty(torrent_id)
|
||||||
|
|
||||||
|
def on_sessionpaused_event(self):
|
||||||
|
self.mark_dirty()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def on_sessionresumed_event(self):
|
||||||
|
self.mark_dirty()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def on_torrentqueuechanged_event(self):
|
||||||
|
self.mark_dirty()
|
||||||
|
self.update()
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
#
|
|
||||||
# signalreceiver.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
|
||||||
#
|
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import socket
|
|
||||||
import random
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
|
|
||||||
from deluge.ui.client import client
|
|
||||||
import deluge.SimpleXMLRPCServer as SimpleXMLRPCServer
|
|
||||||
from SocketServer import ThreadingMixIn
|
|
||||||
import deluge.xmlrpclib as xmlrpclib
|
|
||||||
import threading
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from deluge.log import LOG as log
|
|
||||||
|
|
||||||
class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
log.debug("SignalReceiver init..")
|
|
||||||
# Set to true so that the receiver thread will exit
|
|
||||||
|
|
||||||
self.signals = {}
|
|
||||||
self.emitted_signals = []
|
|
||||||
|
|
||||||
self.remote = False
|
|
||||||
|
|
||||||
self.start_server()
|
|
||||||
|
|
||||||
def start_server(self, port=None):
|
|
||||||
# Setup the xmlrpc server
|
|
||||||
host = "127.0.0.1"
|
|
||||||
if self.remote:
|
|
||||||
host = ""
|
|
||||||
|
|
||||||
server_ready = False
|
|
||||||
while not server_ready:
|
|
||||||
if port:
|
|
||||||
_port = port
|
|
||||||
else:
|
|
||||||
_port = random.randint(40000, 65535)
|
|
||||||
try:
|
|
||||||
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(
|
|
||||||
self, (host, _port), logRequests=False, allow_none=True)
|
|
||||||
except socket.error, e:
|
|
||||||
log.debug("Trying again with another port: %s", e)
|
|
||||||
except:
|
|
||||||
log.error("Could not start SignalReceiver XMLRPC server: %s", e)
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
self.port = _port
|
|
||||||
server_ready = True
|
|
||||||
|
|
||||||
# Register the emit_signal function
|
|
||||||
self.register_function(self.emit_signal)
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
"""Shutdowns receiver thread"""
|
|
||||||
log.debug("Shutting down signalreceiver")
|
|
||||||
self._shutdown = True
|
|
||||||
# De-register with the daemon so it doesn't try to send us more signals
|
|
||||||
try:
|
|
||||||
client.deregister_client()
|
|
||||||
client.force_call()
|
|
||||||
except Exception, e:
|
|
||||||
log.debug("Unable to deregister client from server: %s", e)
|
|
||||||
|
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
|
||||||
log.debug("Joining listening thread..")
|
|
||||||
self.listening_thread.join(1.0)
|
|
||||||
return
|
|
||||||
|
|
||||||
def set_remote(self, remote):
|
|
||||||
self.remote = remote
|
|
||||||
self.start_server(self.port)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""This gets called when we start the thread"""
|
|
||||||
# Register the signal receiver with the core
|
|
||||||
self._shutdown = False
|
|
||||||
client.register_client(str(self.port))
|
|
||||||
|
|
||||||
self.listening_thread = threading.Thread(target=self.handle_thread)
|
|
||||||
|
|
||||||
gobject.timeout_add(50, self.handle_signals)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.listening_thread.start()
|
|
||||||
except Exception, e:
|
|
||||||
log.debug("Thread: %s", e)
|
|
||||||
|
|
||||||
def handle_thread(self):
|
|
||||||
try:
|
|
||||||
while not self._shutdown:
|
|
||||||
self.handle_request()
|
|
||||||
self._shutdown = False
|
|
||||||
except Exception, e:
|
|
||||||
log.debug("handle_thread: %s", e)
|
|
||||||
|
|
||||||
def get_port(self):
|
|
||||||
"""Get the port that the SignalReceiver is listening on"""
|
|
||||||
return self.port
|
|
||||||
|
|
||||||
def emit_signal(self, signal, *data):
|
|
||||||
"""Exported method used by the core to emit a signal to the client"""
|
|
||||||
self.emitted_signals.append((signal, data))
|
|
||||||
return
|
|
||||||
|
|
||||||
def handle_signals(self):
|
|
||||||
for signal, data in self.emitted_signals:
|
|
||||||
try:
|
|
||||||
for callback in self.signals[signal]:
|
|
||||||
gobject.idle_add(callback, *data)
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
log.warning("Unable to call callback for signal %s: %s", signal, e)
|
|
||||||
|
|
||||||
self.emitted_signals = []
|
|
||||||
return True
|
|
||||||
|
|
||||||
def connect_to_signal(self, signal, callback):
|
|
||||||
"""Connect to a signal"""
|
|
||||||
try:
|
|
||||||
if callback not in self.signals[signal]:
|
|
||||||
self.signals[signal].append(callback)
|
|
||||||
except KeyError:
|
|
||||||
self.signals[signal] = [callback]
|
|
Loading…
Reference in New Issue