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 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -334,8 +334,6 @@ class Core(
|
||||||
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,7 +345,32 @@ 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."""
|
||||||
|
@ -450,6 +473,10 @@ class Core(
|
||||||
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):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -326,7 +326,18 @@ 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():
|
||||||
|
|
|
@ -57,6 +57,8 @@ class Signals(component.Component):
|
||||||
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)
|
||||||
|
|
|
@ -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(
|
||||||
|
@ -232,25 +235,16 @@ class TorrentView(listview.ListView, component.Component):
|
||||||
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):
|
# We will only send another status request if we have received the
|
||||||
"""Updates the column values for 'row'. If columns is None it will
|
# previous. This is to prevent things from going out of sync.
|
||||||
update all visible columns."""
|
if not self.status_signal_received:
|
||||||
|
|
||||||
# Check to see if this row is visible and return if not
|
|
||||||
if not model.get_value(row, self.columns["filter"].column_indices[0]):
|
|
||||||
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,18 +270,46 @@ 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 = []
|
||||||
|
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
|
# Set values for each column in the row
|
||||||
for column in columns_to_update:
|
for column in self.columns_to_update:
|
||||||
column_index = self.get_column_index(column)
|
column_index = self.get_column_index(column)
|
||||||
if type(column_index) is not list:
|
if type(column_index) is not list:
|
||||||
# We only have a single list store column we need to update
|
# We only have a single list store column we need to
|
||||||
|
# update
|
||||||
try:
|
try:
|
||||||
model.set_value(row,
|
self.liststore.set_value(row,
|
||||||
column_index,
|
column_index,
|
||||||
status[self.columns[column].status_field[0]])
|
status[torrent_id][
|
||||||
|
self.columns[column].status_field[0]])
|
||||||
except (TypeError, KeyError), e:
|
except (TypeError, KeyError), e:
|
||||||
log.warning("Unable to update column %s: %s",
|
log.warning("Unable to update column %s: %s",
|
||||||
column, e)
|
column, e)
|
||||||
|
@ -296,12 +318,15 @@ class TorrentView(listview.ListView, component.Component):
|
||||||
for index in column_index:
|
for index in column_index:
|
||||||
# Only update the column if the status field exists
|
# Only update the column if the status field exists
|
||||||
try:
|
try:
|
||||||
model.set_value(row,
|
self.liststore.set_value(row,
|
||||||
index,
|
index,
|
||||||
status[self.columns[column].status_field[
|
status[torrent_id][
|
||||||
|
self.columns[column].status_field[
|
||||||
column_index.index(index)]])
|
column_index.index(index)]])
|
||||||
except:
|
except:
|
||||||
pass
|
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"""
|
||||||
|
|
|
@ -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]:
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -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"]
|
||||||
|
|
Loading…
Reference in New Issue