Add new component SessionProxy
Use SessionProxy in place of core.get_torrent_status calls
This commit is contained in:
parent
0b44023f92
commit
da9af84dc1
|
@ -81,7 +81,8 @@ class DetailsTab(Tab):
|
|||
status_keys = ["name", "total_size", "num_files",
|
||||
"tracker", "save_path", "message", "hash", "comment"]
|
||||
|
||||
client.core.get_torrent_status(selected, status_keys).addCallback(self._on_get_torrent_status)
|
||||
session = component.get("SessionProxy")
|
||||
session.get_torrent_status(selected, status_keys).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def _on_get_torrent_status(self, status):
|
||||
# Check to see if we got valid data from the core
|
||||
|
|
|
@ -94,8 +94,8 @@ class EditTrackersDialog:
|
|||
return
|
||||
|
||||
# Get the trackers for this torrent
|
||||
|
||||
client.core.get_torrent_status(self.torrent_id, ["trackers"]).addCallback(self._on_get_torrent_status)
|
||||
session = component.get("SessionProxy")
|
||||
session.get_torrent_status(self.torrent_id, ["trackers"]).addCallback(self._on_get_torrent_status)
|
||||
client.force_call()
|
||||
|
||||
def _on_get_torrent_status(self, status):
|
||||
|
|
|
@ -315,7 +315,7 @@ class FilesTab(Tab):
|
|||
log.debug("Getting file list from core..")
|
||||
status_keys += ["files"]
|
||||
|
||||
client.core.get_torrent_status(self.torrent_id, status_keys).addCallback(self._on_get_torrent_status)
|
||||
component.get("SessionProxy").get_torrent_status(self.torrent_id, status_keys).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def clear(self):
|
||||
self.treestore.clear()
|
||||
|
@ -323,7 +323,7 @@ class FilesTab(Tab):
|
|||
|
||||
def _on_row_activated(self, tree, path, view_column):
|
||||
if client.is_localhost:
|
||||
client.core.get_torrent_status(self.torrent_id, ["save_path", "files"]).addCallback(self._on_open_file)
|
||||
component.get("SessionProxy").get_torrent_status(self.torrent_id, ["save_path", "files"]).addCallback(self._on_open_file)
|
||||
|
||||
def get_file_path(self, row, path=""):
|
||||
if not row:
|
||||
|
|
|
@ -80,6 +80,7 @@ from ipcinterface import IPCInterface
|
|||
from deluge.ui.tracker_icons import TrackerIcons
|
||||
from queuedtorrents import QueuedTorrents
|
||||
from addtorrentdialog import AddTorrentDialog
|
||||
from deluge.ui.sessionproxy import SessionProxy
|
||||
import dialogs
|
||||
import common
|
||||
|
||||
|
@ -207,6 +208,7 @@ class GtkUI(object):
|
|||
client.set_disconnect_callback(self.__on_disconnect)
|
||||
|
||||
self.trackericons = TrackerIcons()
|
||||
self.sessionproxy = SessionProxy()
|
||||
# Initialize various components of the gtkui
|
||||
self.mainwindow = MainWindow()
|
||||
self.menubar = MenuBar()
|
||||
|
@ -229,7 +231,7 @@ class GtkUI(object):
|
|||
from twisted.internet.task import LoopingCall
|
||||
rpc_stats = LoopingCall(self.print_rpc_stats)
|
||||
rpc_stats.start(10)
|
||||
|
||||
|
||||
reactor.callWhenRunning(self._on_reactor_start)
|
||||
# Start the gtk main loop
|
||||
gtk.gdk.threads_enter()
|
||||
|
@ -266,7 +268,7 @@ class GtkUI(object):
|
|||
sent = client.get_bytes_sent()
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
|
||||
log.debug("sent: %s recv: %s", deluge.common.fsize(sent), deluge.common.fsize(recv))
|
||||
t = time.time()
|
||||
delta_time = t - self.daemon_bps[0]
|
||||
|
@ -277,7 +279,7 @@ class GtkUI(object):
|
|||
recv_rate = deluge.common.fspeed(float(delta_recv) / float(delta_time))
|
||||
log.debug("sent rate: %s recv rate: %s", sent_rate, recv_rate)
|
||||
self.daemon_bps = (t, sent, recv)
|
||||
|
||||
|
||||
def _on_reactor_start(self):
|
||||
log.debug("_on_reactor_start")
|
||||
self.mainwindow.first_show()
|
||||
|
|
|
@ -314,7 +314,7 @@ class MenuBar(component.Component):
|
|||
def _on_torrent_status(status):
|
||||
deluge.common.open_file(status["save_path"])
|
||||
for torrent_id in component.get("TorrentView").get_selected_torrents():
|
||||
client.core.get_torrent_status(torrent_id, ["save_path"]).addCallback(_on_torrent_status)
|
||||
component.get("SessionProxy").get_torrent_status(torrent_id, ["save_path"]).addCallback(_on_torrent_status)
|
||||
|
||||
def on_menuitem_move_activate(self, data=None):
|
||||
log.debug("on_menuitem_move_activate")
|
||||
|
@ -337,8 +337,7 @@ class MenuBar(component.Component):
|
|||
component.get("TorrentView").get_selected_torrents(), result)
|
||||
chooser.destroy()
|
||||
else:
|
||||
client.core.get_torrent_status(component.get("TorrentView").get_selected_torrent(), ["save_path"]).addCallback(self.show_move_storage_dialog)
|
||||
client.force_call(False)
|
||||
component.get("SessionProxy").get_torrent_status(component.get("TorrentView").get_selected_torrent(), ["save_path"]).addCallback(self.show_move_storage_dialog)
|
||||
|
||||
def show_move_storage_dialog(self, status):
|
||||
log.debug("show_move_storage_dialog")
|
||||
|
@ -461,12 +460,11 @@ class MenuBar(component.Component):
|
|||
"separatormenuitem",
|
||||
"menuitem_connectionmanager"
|
||||
]
|
||||
|
||||
|
||||
if value:
|
||||
attr = "hide"
|
||||
else:
|
||||
attr = "show"
|
||||
|
||||
|
||||
for item in items:
|
||||
getattr(self.window.main_glade.get_widget(item), attr)()
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class Notification:
|
|||
self.get_torrent_status(torrent_id)
|
||||
|
||||
def get_torrent_status(self, torrent_id):
|
||||
client.core.get_torrent_status(torrent_id, ["name", "num_files", "total_payload_download"]).addCallback(self._on_get_torrent_status)
|
||||
component.get("SessionProxy").get_torrent_status(torrent_id, ["name", "num_files", "total_payload_download"]).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def _on_get_torrent_status(self, status):
|
||||
if status is None:
|
||||
|
|
|
@ -98,7 +98,7 @@ class OptionsTab(Tab):
|
|||
if torrent_id != self.prev_torrent_id:
|
||||
self.prev_status = None
|
||||
|
||||
client.core.get_torrent_status(torrent_id,
|
||||
component.get("SessionProxy").get_torrent_status(torrent_id,
|
||||
["max_download_speed",
|
||||
"max_upload_speed",
|
||||
"max_connections",
|
||||
|
|
|
@ -257,7 +257,7 @@ class PeersTab(Tab):
|
|||
self.peers = {}
|
||||
self.torrent_id = torrent_id
|
||||
|
||||
client.core.get_torrent_status(torrent_id, ["peers"]).addCallback(self._on_get_torrent_status)
|
||||
component.get("SessionProxy").get_torrent_status(torrent_id, ["peers"]).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def get_flag_pixbuf(self, country):
|
||||
if country == " ":
|
||||
|
|
|
@ -116,7 +116,7 @@ class StatusTab(Tab):
|
|||
"max_upload_speed", "max_download_speed", "active_time",
|
||||
"seeding_time", "seed_rank", "is_auto_managed", "time_added"]
|
||||
|
||||
client.core.get_torrent_status(
|
||||
component.get("SessionProxy").get_torrent_status(
|
||||
selected, status_keys).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def _on_get_torrent_status(self, status):
|
||||
|
|
|
@ -164,7 +164,7 @@ def seed_peer_column_sort(model, iter1, iter2, data):
|
|||
class TorrentView(listview.ListView, component.Component):
|
||||
"""TorrentView handles the listing of torrents."""
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "TorrentView", interval=2)
|
||||
component.Component.__init__(self, "TorrentView", interval=2, depend=["SessionProxy"])
|
||||
self.window = component.get("MainWindow")
|
||||
# Call the ListView constructor
|
||||
listview.ListView.__init__(self,
|
||||
|
@ -256,10 +256,9 @@ class TorrentView(listview.ListView, component.Component):
|
|||
"""Start the torrentview"""
|
||||
# We need to get the core session state to know which torrents are in
|
||||
# the session so we can add them to our list.
|
||||
client.core.get_session_state().addCallback(self._on_session_state)
|
||||
component.get("SessionProxy").get_torrents_status({}, []).addCallback(self._on_session_state)
|
||||
|
||||
def _on_session_state(self, state):
|
||||
log.debug("on_session_state: %s", state)
|
||||
self.treeview.freeze_child_notify()
|
||||
model = self.treeview.get_model()
|
||||
for torrent_id in state:
|
||||
|
@ -268,7 +267,10 @@ class TorrentView(listview.ListView, component.Component):
|
|||
self.treeview.set_model(model)
|
||||
self.treeview.thaw_child_notify()
|
||||
self.got_state = True
|
||||
self.update()
|
||||
# Update the view right away with our status
|
||||
self.status = state
|
||||
self.set_columns_to_update()
|
||||
self.update_view()
|
||||
|
||||
def stop(self):
|
||||
"""Stops the torrentview"""
|
||||
|
@ -294,10 +296,8 @@ class TorrentView(listview.ListView, component.Component):
|
|||
self.filter = dict(filter_dict) #copied version of filter_dict.
|
||||
self.update()
|
||||
|
||||
def send_status_request(self, columns=None):
|
||||
# Store the 'status_fields' we need to send to core
|
||||
def set_columns_to_update(self, columns=None):
|
||||
status_keys = []
|
||||
# Store the actual columns we will be updating
|
||||
self.columns_to_update = []
|
||||
|
||||
if columns is None:
|
||||
|
@ -317,6 +317,11 @@ class TorrentView(listview.ListView, component.Component):
|
|||
|
||||
# Remove duplicate keys
|
||||
self.columns_to_update = list(set(self.columns_to_update))
|
||||
return status_keys
|
||||
|
||||
def send_status_request(self, columns=None):
|
||||
# Store the 'status_fields' we need to send to core
|
||||
status_keys = self.set_columns_to_update(columns)
|
||||
|
||||
# If there is nothing in status_keys then we must not continue
|
||||
if status_keys is []:
|
||||
|
@ -327,8 +332,8 @@ class TorrentView(listview.ListView, component.Component):
|
|||
|
||||
# Request the statuses for all these torrent_ids, this is async so we
|
||||
# will deal with the return in a signal callback.
|
||||
client.core.get_torrents_status(
|
||||
self.filter, status_keys, True).addCallback(self._on_get_torrents_status)
|
||||
component.get("SessionProxy").get_torrents_status(
|
||||
self.filter, status_keys).addCallback(self._on_get_torrents_status)
|
||||
|
||||
def update(self):
|
||||
if self.got_state:
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
#
|
||||
# sessionproxy.py
|
||||
#
|
||||
# Copyright (C) 2010 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.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
#
|
||||
|
||||
from twisted.internet.defer import maybeDeferred, succeed
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from deluge.log import LOG as log
|
||||
import time
|
||||
|
||||
class SessionProxy(component.Component):
|
||||
"""
|
||||
The SessionProxy component is used to cache session information client-side
|
||||
to reduce the number of RPCs needed to provide a rich user interface.
|
||||
|
||||
On start-up it will query the Core for a full status of all the torrents in
|
||||
the session. After that point, it will query the Core for only changes in
|
||||
the status of the torrents and will try to satisfy client requests from the
|
||||
cache.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
log.debug("SessionProxy init..")
|
||||
component.Component.__init__(self, "SessionProxy", interval=5)
|
||||
|
||||
# Set the cache time in seconds
|
||||
# This is how long data will be valid before refetching from the core
|
||||
self.cache_time = 1.5
|
||||
|
||||
# Hold the torrents' status.. {torrent_id: [time, {status_dict}], ...}
|
||||
self.torrents = {}
|
||||
|
||||
# Hold the time awhen the last state filter was used and the torrent_ids returned
|
||||
# [(filter_dict, time, (torrent_id, ...)), ...]
|
||||
self.state_filter_queries = []
|
||||
|
||||
client.register_event_handler("TorrentStateChangedEvent", self.on_torrent_state_changed)
|
||||
client.register_event_handler("TorrentRemovedEvent", self.on_torrent_removed)
|
||||
|
||||
def start(self):
|
||||
def on_torrent_status(status):
|
||||
# Save the time we got the torrent status
|
||||
t = time.time()
|
||||
for key, value in status.items():
|
||||
self.torrents[key] = [t, value]
|
||||
|
||||
return client.core.get_torrents_status({}, [], True).addCallback(on_torrent_status)
|
||||
|
||||
def stop(self):
|
||||
self.torrents = {}
|
||||
self.state_filter_queries = {}
|
||||
|
||||
def update(self):
|
||||
# Clean-up any stale filter queries
|
||||
sfq = self.state_filter_queries
|
||||
t = time.time()
|
||||
self.state_filter_queries = [x for x in sfq if (t - x[1]) < self.cache_time]
|
||||
|
||||
def create_status_dict(self, torrent_ids, keys):
|
||||
"""
|
||||
Creates a status dict from the cache.
|
||||
|
||||
:param torrent_ids: the torrent_ids
|
||||
:type torrent_ids: list of strings
|
||||
:param keys: the status keys
|
||||
:type keys: list of strings
|
||||
|
||||
:returns: a dict with the status information for the *torrent_ids*
|
||||
:rtype: dict
|
||||
|
||||
"""
|
||||
sd = {}
|
||||
for torrent_id in torrent_ids:
|
||||
sd[torrent_id] = dict([(x, y) for x, y in self.torrents[torrent_id][1].iteritems() if x in keys])
|
||||
return sd
|
||||
|
||||
def get_torrent_status(self, torrent_id, keys):
|
||||
"""
|
||||
Get a status dict for one torrent.
|
||||
|
||||
:param torrent_id: the torrent_id
|
||||
:type torrent_id: string
|
||||
:param keys: the status keys
|
||||
:type keys: list of strings
|
||||
|
||||
:returns: a dict of status information
|
||||
:rtype: dict
|
||||
|
||||
"""
|
||||
if torrent_id in self.torrents:
|
||||
if time.time() - self.torrents[torrent_id][0] < self.cache_time:
|
||||
return succeed(self.create_status_dict([torrent_id], keys)[torrent_id])
|
||||
else:
|
||||
d = client.core.get_torrent_status(torrent_id, keys, True)
|
||||
def on_status(result, torrent_id):
|
||||
self.torrents[torrent_id][0] = time.time()
|
||||
self.torrents[torrent_id][1].update(result)
|
||||
return self.torrents[torrent_id][1]
|
||||
return d.addCallback(on_status, torrent_id)
|
||||
else:
|
||||
d = client.core.get_torrent_status(torrent_id, keys, True)
|
||||
def on_status(result):
|
||||
if result:
|
||||
self.torrents[torrent_id] = (time.time(), result)
|
||||
return result
|
||||
return d.addCallback(on_status)
|
||||
|
||||
def get_torrents_status(self, filter_dict, keys):
|
||||
"""
|
||||
Get a dict of torrent statuses.
|
||||
|
||||
The filter can take 2 keys, *state* and *id*. The state filter can be
|
||||
one of the torrent states or the special one *Active*. The *id* key is
|
||||
simply a list of torrent_ids.
|
||||
|
||||
:param filter_dict: the filter used for this query
|
||||
:type filter_dict: dict
|
||||
:param keys: the status keys
|
||||
:type keys: list of strings
|
||||
|
||||
:returns: a dict of torrent_ids and their status dicts
|
||||
:rtype: dict
|
||||
|
||||
"""
|
||||
# Helper functions and callbacks ---------------------------------------
|
||||
def on_status(result, torrent_ids, keys):
|
||||
# Update the internal torrent status dict with the update values
|
||||
t = time.time()
|
||||
for key, value in result.items():
|
||||
self.torrents[key][0] = t
|
||||
self.torrents[key][1].update(value)
|
||||
|
||||
# Create the status dict
|
||||
if not torrent_ids:
|
||||
torrent_ids = self.torrents.keys()
|
||||
|
||||
return self.create_status_dict(torrent_ids, keys)
|
||||
|
||||
def find_torrents_to_fetch(torrent_ids):
|
||||
to_fetch = []
|
||||
t = time.time()
|
||||
for key in torrent_ids:
|
||||
torrent = self.torrents[key]
|
||||
if t - torrent[0] > self.cache_time:
|
||||
to_fetch.append(key)
|
||||
|
||||
return to_fetch
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
if not filter_dict:
|
||||
# This means we want all the torrents status
|
||||
# We get a list of any torrent_ids with expired status dicts
|
||||
to_fetch = find_torrents_to_fetch(self.torrents.keys())
|
||||
if to_fetch:
|
||||
d = client.core.get_torrents_status({"id": to_fetch}, keys, True)
|
||||
return d.addCallback(on_status, [], keys)
|
||||
|
||||
# Don't need to fetch anything
|
||||
return maybeDeferred(self.create_status_dict, self.torrents.keys(), keys)
|
||||
|
||||
if "state" in filter_dict:
|
||||
# We check if a similar query has already been done within our cache time
|
||||
# and return results for those torrent_ids
|
||||
for sfq in self.state_filter_queries:
|
||||
if sfq[0] == filter_dict and (time.time() - sfq[1]) < self.cache_time:
|
||||
# We found a winner!
|
||||
if "id" in filter_dict:
|
||||
filter_fict["id"].extend(sfq[2])
|
||||
else:
|
||||
filter_dict["id"] = sfq[2]
|
||||
del filter_dict["state"]
|
||||
break
|
||||
|
||||
if "state" in filter_dict:
|
||||
# If we got there, then there is no suitable cached filter query, so
|
||||
# we'll just query the core
|
||||
d = client.core.get_torrents_status(filer_dict, keys. True)
|
||||
def on_filter_status(result, keys):
|
||||
return on_status(result, result.keys(), keys)
|
||||
return d.addCallback(on_filter_status, keys)
|
||||
|
||||
# At this point we should have a filter with just "id" in it
|
||||
to_fetch = find_torrents_to_fetch(filter_dict["id"])
|
||||
if to_fetch:
|
||||
d = client.core.get_torrents_status({"id": to_fetch}, keys, True)
|
||||
return d.addCallback(on_status, filter_dict["id"], keys)
|
||||
else:
|
||||
# Don't need to fetch anything, so just return data from the cache
|
||||
return maybeDeferred(self.create_status_dict, filter_dict["id"], keys)
|
||||
|
||||
def on_torrent_state_changed(self, torrent_id, state):
|
||||
self.torrents[torrent_id][1]["state"] = state
|
||||
|
||||
def on_torrent_removed(self, torrent_id):
|
||||
del self.torrents[torrent_id]
|
Loading…
Reference in New Issue