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 command line option to change config dir.. --config
* Add method for plugins to add labels * Add method for plugins to add labels
* Add context menus for labels.. ie. setting options for all torrents in label * 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 locale
import pkg_resources import pkg_resources
import sys import sys
import pickle import cPickle as pickle
import shutil import shutil
import os import os
@ -54,7 +54,7 @@ from deluge.core.pluginmanager import PluginManager
from deluge.core.alertmanager import AlertManager from deluge.core.alertmanager import AlertManager
from deluge.core.signalmanager import SignalManager from deluge.core.signalmanager import SignalManager
from deluge.log import LOG as log from deluge.log import LOG as log
DEFAULT_PREFS = { DEFAULT_PREFS = {
"config_location": deluge.common.get_config_dir(), "config_location": deluge.common.get_config_dir(),
"daemon_port": 58846, "daemon_port": 58846,
@ -332,10 +332,8 @@ class Core(
log.debug("Resuming torrent %s", torrent_id) log.debug("Resuming torrent %s", torrent_id)
if self.torrents.resume(torrent_id): if self.torrents.resume(torrent_id):
self.torrent_resumed(torrent_id) self.torrent_resumed(torrent_id)
def export_get_torrent_status(self, torrent_id, keys): 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 # Build the status dictionary
try: try:
status = self.torrents[torrent_id].get_status(keys) status = self.torrents[torrent_id].get_status(keys)
@ -347,8 +345,33 @@ class Core(
leftover_fields = list(set(keys) - set(status.keys())) leftover_fields = list(set(keys) - set(status.keys()))
if len(leftover_fields) > 0: if len(leftover_fields) > 0:
status.update(self.plugins.get_status(torrent_id, leftover_fields)) 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): def export_get_session_state(self):
"""Returns a list of torrent_ids in the session.""" """Returns a list of torrent_ids in the session."""
# Get the torrent list from the TorrentManager # Get the torrent list from the TorrentManager
@ -449,6 +472,10 @@ class Core(
"""Emitted when all torrents have been resumed""" """Emitted when all torrents have been resumed"""
log.debug("torrent_all_resumed signal emitted") log.debug("torrent_all_resumed signal emitted")
self.signals.emit("torrent_all_resumed", torrent_id) 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 # Config set functions
def _on_set_torrentfiles_location(self, key, value): 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. # statement from all source files in the program, then also delete it here.
import xmlrpclib import xmlrpclib
import socket
import gobject import gobject
@ -67,6 +68,6 @@ class SignalManager(component.Component):
def _emit(self, client, signal, data): def _emit(self, client, signal, data):
try: try:
client.emit_signal(signal, data) client.emit_signal(signal, data)
except: except (socket.error, Exception), e:
log.warning("Unable to emit signal to client %s", client) 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: if status == None:
return {} 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 @cache
def get_session_state(): def get_session_state():
try: try:

View File

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

View File

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

View File

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