mirror of
https://git.deluge-torrent.org/deluge
synced 2025-08-09 01:48:40 +00:00
[#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:
parent
34e92b9f12
commit
0ab7ebd017
2 changed files with 76 additions and 13 deletions
|
@ -98,6 +98,14 @@ class TorrentOptions(dict):
|
||||||
self["file_priorities"] = []
|
self["file_priorities"] = []
|
||||||
self["mapped_files"] = {}
|
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):
|
class Torrent(object):
|
||||||
"""Torrent holds information about torrents added to the libtorrent session.
|
"""Torrent holds information about torrents added to the libtorrent session.
|
||||||
"""
|
"""
|
||||||
|
@ -188,11 +196,14 @@ class Torrent(object):
|
||||||
# Various torrent options
|
# Various torrent options
|
||||||
self.handle.resolve_countries(True)
|
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
|
# Status message holds error info about the torrent
|
||||||
self.statusmsg = "OK"
|
self.statusmsg = "OK"
|
||||||
|
|
||||||
|
self.set_options(self.options)
|
||||||
|
|
||||||
# The torrents state
|
# The torrents state
|
||||||
self.update_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
|
# First we check for an error from libtorrent, and set the state to that
|
||||||
# if any occurred.
|
# 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
|
# This is an error'd torrent
|
||||||
self.state = "Error"
|
self.state = "Error"
|
||||||
self.set_status_message(self.handle.status().error)
|
self.set_status_message(self.handle.status().error)
|
||||||
|
@ -427,6 +442,37 @@ class Torrent(object):
|
||||||
def set_status_message(self, message):
|
def set_status_message(self, message):
|
||||||
self.statusmsg = 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):
|
def get_eta(self):
|
||||||
"""Returns the ETA in seconds for this torrent"""
|
"""Returns the ETA in seconds for this torrent"""
|
||||||
if self.status == None:
|
if self.status == None:
|
||||||
|
@ -782,6 +828,8 @@ class Torrent(object):
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
"""Pause this torrent"""
|
"""Pause this torrent"""
|
||||||
|
if self.state == "Error":
|
||||||
|
return False
|
||||||
# Turn off auto-management so the torrent will not be unpaused by lt queueing
|
# Turn off auto-management so the torrent will not be unpaused by lt queueing
|
||||||
self.handle.auto_managed(False)
|
self.handle.auto_managed(False)
|
||||||
if self.handle.is_paused():
|
if self.handle.is_paused():
|
||||||
|
@ -806,6 +854,8 @@ class Torrent(object):
|
||||||
if self.handle.is_paused() and self.handle.is_auto_managed():
|
if self.handle.is_paused() and self.handle.is_auto_managed():
|
||||||
log.debug("Torrent is being auto-managed, cannot resume!")
|
log.debug("Torrent is being auto-managed, cannot resume!")
|
||||||
return
|
return
|
||||||
|
elif self.forced_error and self.forced_error.was_paused:
|
||||||
|
log.debug("Skip resuming Error state torrent that was originally paused.")
|
||||||
else:
|
else:
|
||||||
# Reset the status message just in case of resuming an Error'd torrent
|
# Reset the status message just in case of resuming an Error'd torrent
|
||||||
self.set_status_message("OK")
|
self.set_status_message("OK")
|
||||||
|
@ -829,6 +879,9 @@ class Torrent(object):
|
||||||
|
|
||||||
return True
|
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):
|
def connect_peer(self, ip, port):
|
||||||
"""adds manual peer"""
|
"""adds manual peer"""
|
||||||
try:
|
try:
|
||||||
|
@ -874,8 +927,12 @@ class Torrent(object):
|
||||||
def save_resume_data(self):
|
def save_resume_data(self):
|
||||||
"""Signals libtorrent to build resume data for this torrent, it gets
|
"""Signals libtorrent to build resume data for this torrent, it gets
|
||||||
returned in a libtorrent alert"""
|
returned in a libtorrent alert"""
|
||||||
self.handle.save_resume_data()
|
# Don't generate fastresume data if torrent is in a Deluge Error state.
|
||||||
self.waiting_on_resume_data = True
|
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):
|
def on_metadata_received(self):
|
||||||
if self.options["prioritize_first_last_pieces"]:
|
if self.options["prioritize_first_last_pieces"]:
|
||||||
|
@ -933,7 +990,11 @@ class Torrent(object):
|
||||||
def force_recheck(self):
|
def force_recheck(self):
|
||||||
"""Forces a recheck of the torrents pieces"""
|
"""Forces a recheck of the torrents pieces"""
|
||||||
self.forcing_recheck = True
|
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.
|
# Store trackers for paused torrents to prevent unwanted announce before pausing again.
|
||||||
if self.forcing_recheck_paused:
|
if self.forcing_recheck_paused:
|
||||||
self.set_trackers(None, reannounce=False)
|
self.set_trackers(None, reannounce=False)
|
||||||
|
|
|
@ -205,6 +205,10 @@ class TorrentManager(component.Component):
|
||||||
self.alerts.register_handler("fastresume_rejected_alert",
|
self.alerts.register_handler("fastresume_rejected_alert",
|
||||||
self.on_alert_fastresume_rejected)
|
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):
|
def start(self):
|
||||||
# Get the pluginmanager reference
|
# Get the pluginmanager reference
|
||||||
self.plugins = component.get("CorePluginManager")
|
self.plugins = component.get("CorePluginManager")
|
||||||
|
@ -212,14 +216,12 @@ class TorrentManager(component.Component):
|
||||||
# Run the old state upgrader before loading state
|
# Run the old state upgrader before loading state
|
||||||
deluge.core.oldstateupgrader.OldStateUpgrader()
|
deluge.core.oldstateupgrader.OldStateUpgrader()
|
||||||
|
|
||||||
# Try to load the state from file
|
# Try to load the state from file.
|
||||||
self.load_state()
|
self.load_state()
|
||||||
|
|
||||||
# Save the state every 5 minutes
|
# Save the state and resume data every ~3 minutes.
|
||||||
self.save_state_timer = LoopingCall(self.save_state)
|
|
||||||
self.save_state_timer.start(200, False)
|
self.save_state_timer.start(200, False)
|
||||||
self.save_resume_data_timer = LoopingCall(self.save_resume_data)
|
self.save_resume_data_timer.start(190, False)
|
||||||
self.save_resume_data_timer.start(190)
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
# Stop timers
|
# Stop timers
|
||||||
|
@ -682,6 +684,8 @@ class TorrentManager(component.Component):
|
||||||
for torrent in self.torrents.values():
|
for torrent in self.torrents.values():
|
||||||
if self.session.is_paused():
|
if self.session.is_paused():
|
||||||
paused = torrent.handle.is_paused()
|
paused = torrent.handle.is_paused()
|
||||||
|
elif torrent.forced_error:
|
||||||
|
paused = torrent.forced_error.was_paused
|
||||||
elif torrent.state == "Paused":
|
elif torrent.state == "Paused":
|
||||||
paused = True
|
paused = True
|
||||||
else:
|
else:
|
||||||
|
@ -1147,9 +1151,7 @@ class TorrentManager(component.Component):
|
||||||
else:
|
else:
|
||||||
error_msg = "Problem with resume data: %s" % alert_msg.split(":", 1)[1].strip()
|
error_msg = "Problem with resume data: %s" % alert_msg.split(":", 1)[1].strip()
|
||||||
|
|
||||||
torrent.set_status_message("Error: " + error_msg)
|
torrent.force_error_state(error_msg, restart_to_resume=True)
|
||||||
torrent.pause()
|
|
||||||
torrent.update_state()
|
|
||||||
|
|
||||||
def on_alert_file_renamed(self, alert):
|
def on_alert_file_renamed(self, alert):
|
||||||
log.debug("on_alert_file_renamed")
|
log.debug("on_alert_file_renamed")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue