Use an asynchronous batch torrent status method for updating the

torrentview list.
Other various minor stuff.
This commit is contained in:
Andrew Resch 2007-12-28 05:29:54 +00:00
parent e2e3073f23
commit c8508c8d15
8 changed files with 126 additions and 59 deletions

4
TODO
View File

@ -19,4 +19,6 @@
* Add command line option to change config dir.. --config
* Add method for plugins to add labels
* Add context menus for labels.. ie. setting options for all torrents in label
* Create asynchronous batch torrent status method
* Implement caching in core
* Use the batch torrent status info as a cache for other torrent status requests
* Don't save fastresume files on exit for finished or paused torrents

View File

@ -35,7 +35,7 @@ import gettext
import locale
import pkg_resources
import sys
import pickle
import cPickle as pickle
import shutil
import os
@ -54,7 +54,7 @@ from deluge.core.pluginmanager import PluginManager
from deluge.core.alertmanager import AlertManager
from deluge.core.signalmanager import SignalManager
from deluge.log import LOG as log
DEFAULT_PREFS = {
"config_location": deluge.common.get_config_dir(),
"daemon_port": 58846,
@ -332,10 +332,8 @@ class Core(
log.debug("Resuming torrent %s", torrent_id)
if self.torrents.resume(torrent_id):
self.torrent_resumed(torrent_id)
def export_get_torrent_status(self, torrent_id, keys):
# Convert the array of strings to a python list of strings
keys = deluge.common.pythonize(keys)
# Build the status dictionary
try:
status = self.torrents[torrent_id].get_status(keys)
@ -347,8 +345,33 @@ class Core(
leftover_fields = list(set(keys) - set(status.keys()))
if len(leftover_fields) > 0:
status.update(self.plugins.get_status(torrent_id, leftover_fields))
return xmlrpclib.Binary(pickle.dumps(status))
return pickle.dumps(status)
def export_get_torrents_status(self, torrent_ids, keys):
"""Returns dictionary of statuses for torrent_ids"""
# This is an async command, so we want to return right away
gobject.idle_add(self._get_torrents_status, torrent_ids, keys)
def _get_torrents_status(self, torrent_ids, keys):
status_dict = {}.fromkeys(torrent_ids)
# Get the torrent status for each torrent_id
for torrent_id in torrent_ids:
try:
status = self.torrents[torrent_id].get_status(keys)
except KeyError:
return None
# Get the leftover fields and ask the plugin manager to fill them
leftover_fields = list(set(keys) - set(status.keys()))
if len(leftover_fields) > 0:
status.update(
self.plugins.get_status(torrent_id, leftover_fields))
status_dict[torrent_id] = status
# Emit the torrent_status signal to the clients
self.torrent_status(pickle.dumps(status_dict))
return False
def export_get_session_state(self):
"""Returns a list of torrent_ids in the session."""
# Get the torrent list from the TorrentManager
@ -449,6 +472,10 @@ class Core(
"""Emitted when all torrents have been resumed"""
log.debug("torrent_all_resumed signal emitted")
self.signals.emit("torrent_all_resumed", torrent_id)
def torrent_status(self, status):
"""Emitted when the torrent statuses are ready to be sent"""
self.signals.emit("torrent_status", status)
# Config set functions
def _on_set_torrentfiles_location(self, key, value):

View File

@ -32,6 +32,7 @@
# statement from all source files in the program, then also delete it here.
import xmlrpclib
import socket
import gobject
@ -67,6 +68,6 @@ class SignalManager(component.Component):
def _emit(self, client, signal, data):
try:
client.emit_signal(signal, data)
except:
log.warning("Unable to emit signal to client %s", client)
except (socket.error, Exception), e:
log.warning("Unable to emit signal to client %s: %s", client, e)

View File

@ -326,8 +326,19 @@ def get_torrent_status(torrent_id, keys):
if status == None:
return {}
return pickle.loads(status.data)
return pickle.loads(status)
def get_torrents_status(torrent_ids, keys):
"""Builds a dictionary of torrent_ids status. Expects 2 lists. This is
asynchronous so the return value will be sent as the signal
'torrent_status'"""
try:
get_core().get_torrents_status(torrent_ids, keys)
except (AttributeError, socket.error):
set_core_uri(None)
return None
@cache
def get_session_state():
try:

View File

@ -56,7 +56,9 @@ class Signals(component.Component):
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.torrent_all_resumed)
self.receiver.connect_to_signal("torrent_status",
self.torrent_status)
def stop(self):
self.receiver.shutdown()
@ -94,3 +96,6 @@ class Signals(component.Component):
log.debug("torrent_all_resumed signal received..")
component.get("TorrentView").update()
component.get("ToolBar").update_buttons()
def torrent_status(self, status):
component.get("TorrentView").on_torrent_status_signal(status)

View File

@ -38,6 +38,7 @@ pygtk.require('2.0')
import gtk, gtk.glade
import gettext
import gobject
import cPickle as pickle
import deluge.common
import deluge.component as component
@ -119,6 +120,8 @@ class TorrentView(listview.ListView, component.Component):
# Try to load the state file if available
self.load_state("torrentview.state")
self.status_signal_received = True
# Register the columns menu with the listview so it gets updated
# accordingly.
self.register_checklist_menu(
@ -230,27 +233,18 @@ class TorrentView(listview.ListView, component.Component):
model.set_value(row, filter_column, True)
else:
model.set_value(row, filter_column, False)
self.liststore.foreach(foreachrow, self.filter)
if self.liststore != None:
self.liststore.foreach(self.update_row, columns)
def update_row(self, model=None, path=None, row=None, columns=None):
"""Updates the column values for 'row'. If columns is None it will
update all visible columns."""
# Check to see if this row is visible and return if not
if not model.get_value(row, self.columns["filter"].column_indices[0]):
# We will only send another status request if we have received the
# previous. This is to prevent things from going out of sync.
if not self.status_signal_received:
return
# Get the torrent_id from the liststore
torrent_id = model.get_value(row,
self.columns["torrent_id"].column_indices[0])
# Store the 'status_fields' we need to send to core
status_keys = []
# Store the actual columns we will be updating
columns_to_update = []
self.columns_to_update = []
if columns is None:
# We need to iterate through all columns
@ -265,10 +259,10 @@ class TorrentView(listview.ListView, component.Component):
and self.columns[column].status_field is not None:
for field in self.columns[column].status_field:
status_keys.append(field)
columns_to_update.append(column)
self.columns_to_update.append(column)
# Remove duplicate keys
columns_to_update = list(set(columns_to_update))
self.columns_to_update = list(set(self.columns_to_update))
# If there is nothing in status_keys then we must not continue
if status_keys is []:
@ -276,32 +270,63 @@ class TorrentView(listview.ListView, component.Component):
# Remove duplicates from status_key list
status_keys = list(set(status_keys))
status = client.get_torrent_status(torrent_id,
status_keys)
# Set values for each column in the row
for column in columns_to_update:
column_index = self.get_column_index(column)
if type(column_index) is not list:
# We only have a single list store column we need to update
try:
model.set_value(row,
column_index,
status[self.columns[column].status_field[0]])
except (TypeError, KeyError), e:
log.warning("Unable to update column %s: %s",
column, e)
else:
# We have more than 1 liststore column to update
for index in column_index:
# Only update the column if the status field exists
try:
model.set_value(row,
index,
status[self.columns[column].status_field[
column_index.index(index)]])
except:
pass
# Create list of torrent_ids in need of status updates
torrent_ids = []
row = self.liststore.get_iter_first()
while row != None:
# Only add this torrent_id if it's not filtered
if self.liststore.get_value(
row, self.columns["filter"].column_indices[0]) == True:
torrent_ids.append(self.liststore.get_value(
row, self.columns["torrent_id"].column_indices[0]))
row = self.liststore.iter_next(row)
if torrent_ids == []:
return
# Request the statuses for all these torrent_ids, this is async so we
# will deal with the return in a signal callback.
self.status_signal_received = False
client.get_torrents_status(torrent_ids, status_keys)
def on_torrent_status_signal(self, status):
"""Callback function for get_torrents_status(). 'status' should be a
dictionary of {torrent_id: {key, value}}."""
status = pickle.loads(status)
row = self.liststore.get_iter_first()
while row != None:
torrent_id = self.liststore.get_value(
row, self.columns["torrent_id"].column_indices[0])
if torrent_id in status.keys():
# Set values for each column in the row
for column in self.columns_to_update:
column_index = self.get_column_index(column)
if type(column_index) is not list:
# We only have a single list store column we need to
# update
try:
self.liststore.set_value(row,
column_index,
status[torrent_id][
self.columns[column].status_field[0]])
except (TypeError, KeyError), e:
log.warning("Unable to update column %s: %s",
column, e)
else:
# We have more than 1 liststore column to update
for index in column_index:
# Only update the column if the status field exists
try:
self.liststore.set_value(row,
index,
status[torrent_id][
self.columns[column].status_field[
column_index.index(index)]])
except:
pass
row = self.liststore.iter_next(row)
self.status_signal_received = True
def add_row(self, torrent_id):
"""Adds a new torrent row to the treeview"""
@ -312,8 +337,6 @@ class TorrentView(listview.ListView, component.Component):
row,
self.columns["torrent_id"].column_indices[0],
torrent_id)
# Update the new row so
self.update_row(model=self.liststore, row=row)
def remove_row(self, torrent_id):
"""Removes a row with torrent_id"""

View File

@ -79,7 +79,6 @@ class SignalReceiver(
self.register_function(self.emit_signal)
# Register the signal receiver with the core
# FIXME: send actual URI not localhost
core = client.get_core()
core.register_client(str(port))
@ -115,7 +114,6 @@ class SignalReceiver(
def emit_signal(self, signal, data):
"""Exported method used by the core to emit a signal to the client"""
log.debug("Received signal %s with data %s from core..", signal, data)
try:
if data != None:
for callback in self.signals[signal]:

View File

@ -56,7 +56,7 @@ _extra_compile_args = [
"-O2"
]
removals = ["-g", "-p", "-Wstrict-prototypes"]
removals = ["-Wstrict-prototypes"]
if python_version == '2.5':
cv_opt = sysconfig.get_config_vars()["CFLAGS"]