[#2250] [Core] [GTKUI] Added method remove_torrents to core

Removing multiple torrents with remove_torrents will be faster
than calling remove_torrent for each torrent, as the state file
will be written only once, after all the torrents are deleted.
This commit is contained in:
bendikro 2014-12-01 16:00:26 +01:00 committed by Calum Lind
parent 2aaae7c6a1
commit 08363f28dd
5 changed files with 151 additions and 38 deletions

View File

@ -326,22 +326,48 @@ class Core(component.Component):
@export
def remove_torrent(self, torrent_id, remove_data):
"""
Removes a torrent from the session.
"""Removes a single torrent from the session.
:param torrent_id: the torrent_id of the torrent to remove
:type torrent_id: string
:param remove_data: if True, remove the data associated with this torrent
:type remove_data: boolean
:returns: True if removed successfully
:rtype: bool
Args:
torrent_id (str): The torrent ID to remove.
remove_data (bool): If True, also remove the downloaded data.
:raises InvalidTorrentError: if the torrent_id does not exist in the session
Returns:
bool: True if removed successfully.
Raises:
InvalidTorrentError: If the torrent ID does not exist in the session.
"""
log.debug("Removing torrent %s from the core.", torrent_id)
return self.torrentmanager.remove(torrent_id, remove_data)
@export
def remove_torrents(self, torrent_ids, remove_data):
"""Remove multiple torrents from the session.
Args:
torrent_ids (list): The torrent IDs to remove.
remove_data (bool, optional): If True, also remove the downloaded data.
Returns:
list: a list containing all the errors, empty list if no errors occured
"""
log.info("Removing %d torrents from core.", len(torrent_ids))
def do_remove_torrents():
errors = []
for torrent_id in torrent_ids:
try:
self.torrentmanager.remove(torrent_id, remove_data=remove_data, save_state=False)
except InvalidTorrentError, ite:
errors.append((torrent_id, ite))
# Save the session state
self.torrentmanager.save_state()
return errors
return task.deferLater(reactor, 0, do_remove_torrents)
@export
def get_session_status(self, keys):
"""

View File

@ -523,12 +523,13 @@ class TorrentManager(component.Component):
from_state and "loaded" or "added")
return torrent.torrent_id
def remove(self, torrent_id, remove_data=False):
"""Remove specified torrent from the session.
def remove(self, torrent_id, remove_data=False, save_state=True):
"""Remove a torrent from the session.
Args:
torrent_id (str): The torrent to remove.
torrent_id (str): The torrent ID to remove.
remove_data (bool, optional): If True, remove the downloaded data, defaults to False.
save_state (bool, optional): If True, save the session state after removal, defaults to True.
Returns:
bool: True if removed successfully, False if not.
@ -538,16 +539,16 @@ class TorrentManager(component.Component):
"""
try:
torrent_name = self.torrents[torrent_id].get_status(["name"])["name"]
torrent = self.torrents[torrent_id]
except KeyError:
raise InvalidTorrentError("torrent_id not in session")
raise InvalidTorrentError("torrent_id '%s' not in session." % torrent_id)
# Emit the signal to the clients
component.get("EventManager").emit(PreTorrentRemovedEvent(torrent_id))
try:
self.session.remove_torrent(self.torrents[torrent_id].handle, 1 if remove_data else 0)
except (RuntimeError, KeyError) as ex:
self.session.remove_torrent(torrent.handle, 1 if remove_data else 0)
except RuntimeError as ex:
log.warning("Error removing torrent: %s", ex)
return False
@ -555,10 +556,11 @@ class TorrentManager(component.Component):
self.resume_data.pop(torrent_id, None)
# Remove the .torrent file in the state
self.torrents[torrent_id].delete_torrentfile()
torrent.delete_torrentfile()
# Remove the torrent file from the user specified directory
filename = self.torrents[torrent_id].filename
torrent_name = torrent.get_status(["name"])["name"]
filename = torrent.filename
if self.config["copy_torrent_file"] and self.config["del_copy_torrent_file"] and filename:
users_torrent_file = os.path.join(self.config["torrentfiles_location"], filename)
log.info("Delete user's torrent file: %s", users_torrent_file)
@ -568,25 +570,22 @@ class TorrentManager(component.Component):
log.warning("Unable to remove copy torrent file: %s", ex)
# Remove from set if it wasn't finished
if not self.torrents[torrent_id].is_finished:
if not torrent.is_finished:
try:
self.queued_torrents.remove(torrent_id)
except KeyError:
log.debug("%s isn't in queued torrents set?", torrent_id)
raise InvalidTorrentError("%s isn't in queued torrents set?" % torrent_id)
# Remove the torrent from deluge's session
try:
del self.torrents[torrent_id]
except (KeyError, ValueError):
return False
del self.torrents[torrent_id]
# Save the session state
self.save_state()
if save_state:
self.save_state()
# Emit the signal to the clients
component.get("EventManager").emit(TorrentRemovedEvent(torrent_id))
log.info("Torrent %s removed by user: %s", torrent_name,
component.get("RPCServer").get_session_user())
log.info("Torrent %s removed by user: %s", torrent_name, component.get("RPCServer").get_session_user())
return True
def load_state(self):

View File

@ -1,3 +1,4 @@
import base64
import os
from hashlib import sha1 as sha
@ -11,9 +12,10 @@ from twisted.web.server import Site
from twisted.web.static import File
import deluge.component as component
import deluge.error
import deluge.core.torrent
from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer
from deluge.error import InvalidTorrentError
from deluge.ui.web.common import compress
from . import common
@ -104,7 +106,6 @@ class CoreTestCase(BaseTestCase):
def test_add_torrent_file(self):
options = {}
filename = os.path.join(os.path.dirname(__file__), "test.torrent")
import base64
torrent_id = self.core.add_torrent_file(filename, base64.encodestring(open(filename).read()), options)
# Get the info hash from the test.torrent
@ -168,16 +169,53 @@ class CoreTestCase(BaseTestCase):
def test_remove_torrent(self):
options = {}
filename = os.path.join(os.path.dirname(__file__), "test.torrent")
import base64
torrent_id = self.core.add_torrent_file(filename, base64.encodestring(open(filename).read()), options)
self.assertRaises(deluge.error.InvalidTorrentError, self.core.remove_torrent, "torrentidthatdoesntexist", True)
ret = self.core.remove_torrent(torrent_id, True)
self.assertTrue(ret)
removed = self.core.remove_torrent(torrent_id, True)
self.assertTrue(removed)
self.assertEquals(len(self.core.get_session_state()), 0)
def test_remove_torrent_invalid(self):
d = self.core.remove_torrents(["torrentidthatdoesntexist"], True)
def test_true(val):
self.assertTrue(val[0][0] == "torrentidthatdoesntexist")
self.assertTrue(type(val[0][1]) == InvalidTorrentError)
d.addCallback(test_true)
return d
def test_remove_torrents(self):
options = {}
filename = os.path.join(os.path.dirname(__file__), "test.torrent")
torrent_id = self.core.add_torrent_file(filename, base64.encodestring(open(filename).read()), options)
filename2 = os.path.join(os.path.dirname(__file__), "unicode_filenames.torrent")
torrent_id2 = self.core.add_torrent_file(filename2, base64.encodestring(open(filename2).read()), options)
d = self.core.remove_torrents([torrent_id, torrent_id2], True)
def test_ret(val):
self.assertTrue(val == [])
d.addCallback(test_ret)
def test_session_state(val):
self.assertEquals(len(self.core.get_session_state()), 0)
d.addCallback(test_session_state)
return d
def test_remove_torrents_invalid(self):
options = {}
filename = os.path.join(os.path.dirname(__file__), "test.torrent")
torrent_id = self.core.add_torrent_file(filename, base64.encodestring(open(filename).read()), options)
d = self.core.remove_torrents(["invalidid1", "invalidid2", torrent_id], False)
def test_ret(val):
self.assertTrue(len(val) == 2)
self.assertTrue(val[0][0] == "invalidid1")
self.assertTrue(type(val[0][1]) == InvalidTorrentError)
self.assertTrue(val[1][0] == "invalidid2")
self.assertTrue(type(val[1][1]) == InvalidTorrentError)
d.addCallback(test_ret)
return d
def test_get_session_status(self):
status = self.core.get_session_status(["upload_rate", "download_rate"])
self.assertEquals(type(status), dict)

View File

@ -0,0 +1,44 @@
import base64
import os
import warnings
from twisted.trial import unittest
import common
from deluge import component
from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer
from deluge.error import InvalidTorrentError
warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.resetwarnings()
class TorrentmanagerTestCase(unittest.TestCase):
def setUp(self): # NOQA
common.set_tmp_config_dir()
self.rpcserver = RPCServer(listen=False)
self.core = Core()
self.torrentManager = self.core.torrentmanager
return component.start()
def tearDown(self): # NOQA
def on_shutdown(result):
component._ComponentRegistry.components = {}
del self.rpcserver
del self.core
del self.torrentManager
return component.shutdown().addCallback(on_shutdown)
def test_remove_torrent(self):
filename = os.path.join(os.path.dirname(__file__), "test.torrent")
torrent_id = self.core.add_torrent_file(filename, base64.encodestring(open(filename).read()), {})
self.assertTrue(self.torrentManager.remove(torrent_id, False))
def test_remove_torrent_false(self):
"""Test when remove_torrent returns False"""
raise unittest.SkipTest("")
def test_remove_invalid_torrent(self):
self.assertRaises(InvalidTorrentError, self.torrentManager.remove, "torrentidthatdoesntexist")

View File

@ -67,8 +67,14 @@ class RemoveTorrentDialog(object):
# Unselect all to avoid issues with the selection changed event
component.get("TorrentView").treeview.get_selection().unselect_all()
for torrent_id in self.__torrent_ids:
client.core.remove_torrent(torrent_id, remove_data)
def on_removed_finished(errors):
if errors:
log.info("Error(s) occured when trying to delete torrent(s).")
for t_id, e_msg in errors:
log.warn("Error removing torrent %s : %s" % (t_id, e_msg))
d = client.core.remove_torrents(self.__torrent_ids, remove_data)
d.addCallback(on_removed_finished)
def run(self):
"""