Use an asynchronous batch torrent status method for updating the
torrentview list. Other various minor stuff.
This commit is contained in:
parent
e2e3073f23
commit
c8508c8d15
4
TODO
4
TODO
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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]:
|
||||
|
|
Loading…
Reference in New Issue