[#1032] [Core] Force a torrent error if resume data is rejected

* Add two new methods, force_error_state and clear_forced_error_state.
 * Force error state upon rejected resume data.
 * Keep original resume data in forced_error state.
This commit is contained in:
Calum Lind 2015-10-06 12:58:20 +01:00
parent 34e92b9f12
commit 0ab7ebd017
2 changed files with 76 additions and 13 deletions

View File

@ -98,6 +98,14 @@ class TorrentOptions(dict):
self["file_priorities"] = []
self["mapped_files"] = {}
class TorrentError(object):
def __init__(self, error_message, was_paused=False, restart_to_resume=False):
self.error_message = error_message
self.was_paused = was_paused
self.restart_to_resume = restart_to_resume
class Torrent(object):
"""Torrent holds information about torrents added to the libtorrent session.
"""
@ -188,11 +196,14 @@ class Torrent(object):
# Various torrent options
self.handle.resolve_countries(True)
self.set_options(self.options)
# Details of torrent forced into error state (i.e. not by libtorrent).
self.forced_error = None
# Status message holds error info about the torrent
self.statusmsg = "OK"
self.set_options(self.options)
# The torrents state
self.update_state()
@ -391,7 +402,11 @@ class Torrent(object):
# First we check for an error from libtorrent, and set the state to that
# if any occurred.
if len(self.handle.status().error) > 0:
if self.forced_error:
self.state = "Error"
log.debug("Torrent Error state message: %s", self.forced_error.error_message)
self.set_status_message("Error: " + self.forced_error.error_message)
elif len(self.handle.status().error) > 0:
# This is an error'd torrent
self.state = "Error"
self.set_status_message(self.handle.status().error)
@ -427,6 +442,37 @@ class Torrent(object):
def set_status_message(self, message):
self.statusmsg = message
def force_error_state(self, message, restart_to_resume=True):
"""Forces the torrent into an error state.
For setting an error state not covered by libtorrent.
Args:
message (str): The error status message.
restart_to_resume (bool, optional): Prevent resuming clearing the error, only restarting
session can resume.
"""
status = self.handle.status()
self.handle.auto_managed(False)
self.forced_error = TorrentError(message, status.paused, restart_to_resume)
if not status.paused:
self.handle.pause()
self.update_state()
def clear_forced_error_state(self, update_state=True):
if not self.forced_error:
return
if self.forced_error.restart_to_resume:
log.error("Restart deluge to clear this torrent error")
if not self.forced_error.was_paused and self.options["auto_managed"]:
self.handle.auto_managed(True)
self.forced_error = None
self.set_status_message("OK")
if update_state:
self.update_state()
def get_eta(self):
"""Returns the ETA in seconds for this torrent"""
if self.status == None:
@ -782,6 +828,8 @@ class Torrent(object):
def pause(self):
"""Pause this torrent"""
if self.state == "Error":
return False
# Turn off auto-management so the torrent will not be unpaused by lt queueing
self.handle.auto_managed(False)
if self.handle.is_paused():
@ -806,6 +854,8 @@ class Torrent(object):
if self.handle.is_paused() and self.handle.is_auto_managed():
log.debug("Torrent is being auto-managed, cannot resume!")
return
elif self.forced_error and self.forced_error.was_paused:
log.debug("Skip resuming Error state torrent that was originally paused.")
else:
# Reset the status message just in case of resuming an Error'd torrent
self.set_status_message("OK")
@ -829,6 +879,9 @@ class Torrent(object):
return True
if self.forced_error and not self.forced_error.restart_to_resume:
self.clear_forced_error_state()
def connect_peer(self, ip, port):
"""adds manual peer"""
try:
@ -874,8 +927,12 @@ class Torrent(object):
def save_resume_data(self):
"""Signals libtorrent to build resume data for this torrent, it gets
returned in a libtorrent alert"""
self.handle.save_resume_data()
self.waiting_on_resume_data = True
# Don't generate fastresume data if torrent is in a Deluge Error state.
if self.forced_error:
log.debug("Skipped creating resume_data while in Error state")
else:
self.handle.save_resume_data()
self.waiting_on_resume_data = True
def on_metadata_received(self):
if self.options["prioritize_first_last_pieces"]:
@ -933,7 +990,11 @@ class Torrent(object):
def force_recheck(self):
"""Forces a recheck of the torrents pieces"""
self.forcing_recheck = True
self.forcing_recheck_paused = self.handle.is_paused()
if self.forced_error:
self.forcing_recheck_paused = self.forced_error.was_paused
self.clear_forced_error_state(update_state=False)
else:
self.forcing_recheck_paused = self.handle.is_paused()
# Store trackers for paused torrents to prevent unwanted announce before pausing again.
if self.forcing_recheck_paused:
self.set_trackers(None, reannounce=False)

View File

@ -205,6 +205,10 @@ class TorrentManager(component.Component):
self.alerts.register_handler("fastresume_rejected_alert",
self.on_alert_fastresume_rejected)
# Define timers
self.save_state_timer = LoopingCall(self.save_state)
self.save_resume_data_timer = LoopingCall(self.save_resume_data)
def start(self):
# Get the pluginmanager reference
self.plugins = component.get("CorePluginManager")
@ -212,14 +216,12 @@ class TorrentManager(component.Component):
# Run the old state upgrader before loading state
deluge.core.oldstateupgrader.OldStateUpgrader()
# Try to load the state from file
# Try to load the state from file.
self.load_state()
# Save the state every 5 minutes
self.save_state_timer = LoopingCall(self.save_state)
# Save the state and resume data every ~3 minutes.
self.save_state_timer.start(200, False)
self.save_resume_data_timer = LoopingCall(self.save_resume_data)
self.save_resume_data_timer.start(190)
self.save_resume_data_timer.start(190, False)
def stop(self):
# Stop timers
@ -682,6 +684,8 @@ class TorrentManager(component.Component):
for torrent in self.torrents.values():
if self.session.is_paused():
paused = torrent.handle.is_paused()
elif torrent.forced_error:
paused = torrent.forced_error.was_paused
elif torrent.state == "Paused":
paused = True
else:
@ -1147,9 +1151,7 @@ class TorrentManager(component.Component):
else:
error_msg = "Problem with resume data: %s" % alert_msg.split(":", 1)[1].strip()
torrent.set_status_message("Error: " + error_msg)
torrent.pause()
torrent.update_state()
torrent.force_error_state(error_msg, restart_to_resume=True)
def on_alert_file_renamed(self, alert):
log.debug("on_alert_file_renamed")