mirror of
https://git.deluge-torrent.org/deluge
synced 2025-04-20 11:35:49 +00:00
Fix #475 catch unicode decoding errors
This commit is contained in:
parent
87a8085ef7
commit
7dff5178e6
2 changed files with 91 additions and 82 deletions
|
@ -1,3 +1,7 @@
|
|||
Deluge 1.0.6 (In Development)
|
||||
Core:
|
||||
* Fix #475 catch unicode decoding errors
|
||||
|
||||
Deluge 1.0.5 (09 November 2008)
|
||||
GtkUI:
|
||||
* Increase the per-torrent stop share ratio max to 99999.0
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
# torrentmanager.py
|
||||
#
|
||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||
#
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
#
|
||||
# deluge is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
|
@ -60,7 +60,7 @@ class TorrentState:
|
|||
total_uploaded=0,
|
||||
trackers=None,
|
||||
compact=False,
|
||||
paused=False,
|
||||
paused=False,
|
||||
save_path=None,
|
||||
max_connections=-1,
|
||||
max_upload_slots=-1,
|
||||
|
@ -105,7 +105,7 @@ class TorrentManager(component.Component):
|
|||
"""TorrentManager contains a list of torrents in the current libtorrent
|
||||
session. This object is also responsible for saving the state of the
|
||||
session for use on restart."""
|
||||
|
||||
|
||||
def __init__(self, session, alerts):
|
||||
component.Component.__init__(self, "TorrentManager", interval=5000, depend=["PluginManager"])
|
||||
log.debug("TorrentManager init..")
|
||||
|
@ -118,12 +118,12 @@ class TorrentManager(component.Component):
|
|||
|
||||
# Create the torrents dict { torrent_id: Torrent }
|
||||
self.torrents = {}
|
||||
|
||||
|
||||
# This is a list of torrent_id when we shutdown the torrentmanager.
|
||||
# We use this list to determine if all active torrents have been paused
|
||||
# and that their resume data has been written.
|
||||
self.shutdown_torrent_pause_list = []
|
||||
|
||||
|
||||
# Register set functions
|
||||
self.config.register_set_function("max_connections_per_torrent",
|
||||
self.on_set_max_connections_per_torrent)
|
||||
|
@ -133,9 +133,9 @@ class TorrentManager(component.Component):
|
|||
self.on_set_max_upload_speed_per_torrent)
|
||||
self.config.register_set_function("max_download_speed_per_torrent",
|
||||
self.on_set_max_download_speed_per_torrent)
|
||||
|
||||
|
||||
# Register alert functions
|
||||
self.alerts.register_handler("torrent_finished_alert",
|
||||
self.alerts.register_handler("torrent_finished_alert",
|
||||
self.on_alert_torrent_finished)
|
||||
self.alerts.register_handler("torrent_paused_alert",
|
||||
self.on_alert_torrent_paused)
|
||||
|
@ -152,15 +152,15 @@ class TorrentManager(component.Component):
|
|||
self.on_alert_tracker_error)
|
||||
self.alerts.register_handler("storage_moved_alert",
|
||||
self.on_alert_storage_moved)
|
||||
self.alerts.register_handler("torrent_resumed_alert",
|
||||
self.alerts.register_handler("torrent_resumed_alert",
|
||||
self.on_alert_torrent_resumed)
|
||||
self.alerts.register_handler("state_changed_alert",
|
||||
self.on_alert_state_changed)
|
||||
|
||||
|
||||
def start(self):
|
||||
# Get the pluginmanager reference
|
||||
self.plugins = component.get("PluginManager")
|
||||
|
||||
|
||||
self.signals = component.get("SignalManager")
|
||||
|
||||
# Run the old state upgrader before loading state
|
||||
|
@ -172,11 +172,11 @@ class TorrentManager(component.Component):
|
|||
# Save the state every 5 minutes
|
||||
self.save_state_timer = gobject.timeout_add(300000, self.save_state)
|
||||
self.save_resume_data_timer = gobject.timeout_add(290000, self.save_resume_data)
|
||||
|
||||
|
||||
def stop(self):
|
||||
# Save state on shutdown
|
||||
self.save_state()
|
||||
|
||||
|
||||
component.pause("AlertManager")
|
||||
for key in self.torrents.keys():
|
||||
if not self.torrents[key].handle.is_paused():
|
||||
|
@ -184,11 +184,11 @@ class TorrentManager(component.Component):
|
|||
self.torrents[key].handle.auto_managed(False)
|
||||
self.torrents[key].handle.pause()
|
||||
self.shutdown_torrent_pause_list.append(key)
|
||||
while self.shutdown_torrent_pause_list:
|
||||
while self.shutdown_torrent_pause_list:
|
||||
time.sleep(0.1)
|
||||
# Wait for all alerts
|
||||
self.alerts.handle_alerts(True)
|
||||
|
||||
|
||||
def update(self):
|
||||
for torrent_id, torrent in self.torrents.items():
|
||||
if self.config["stop_seed_at_ratio"] or torrent.stop_at_ratio:
|
||||
|
@ -205,11 +205,11 @@ class TorrentManager(component.Component):
|
|||
def __getitem__(self, torrent_id):
|
||||
"""Return the Torrent with torrent_id"""
|
||||
return self.torrents[torrent_id]
|
||||
|
||||
|
||||
def get_torrent_list(self):
|
||||
"""Returns a list of torrent_ids"""
|
||||
return self.torrents.keys()
|
||||
|
||||
|
||||
def get_torrent_info_from_file(self, filepath):
|
||||
"""Returns a torrent_info for the file specified or None"""
|
||||
torrent_info = None
|
||||
|
@ -221,16 +221,16 @@ class TorrentManager(component.Component):
|
|||
_file.close()
|
||||
except (IOError, RuntimeError), e:
|
||||
log.warning("Unable to open %s: %s", filepath, e)
|
||||
|
||||
|
||||
return torrent_info
|
||||
|
||||
|
||||
def get_resume_data_from_file(self, torrent_id):
|
||||
"""Returns an entry with the resume data or None"""
|
||||
fastresume = ""
|
||||
try:
|
||||
_file = open(
|
||||
os.path.join(
|
||||
self.config["state_location"],
|
||||
self.config["state_location"],
|
||||
torrent_id + ".fastresume"),
|
||||
"rb")
|
||||
fastresume = _file.read()
|
||||
|
@ -239,24 +239,24 @@ class TorrentManager(component.Component):
|
|||
log.debug("Unable to load .fastresume: %s", e)
|
||||
|
||||
return str(fastresume)
|
||||
|
||||
|
||||
def add(self, torrent_info=None, state=None, options=None, save_state=True,
|
||||
filedump=None, filename=None):
|
||||
"""Add a torrent to the manager and returns it's torrent_id"""
|
||||
|
||||
|
||||
if torrent_info is None and state is None and filedump is None:
|
||||
log.debug("You must specify a valid torrent_info or a torrent state object!")
|
||||
return
|
||||
|
||||
log.debug("torrentmanager.add")
|
||||
add_torrent_params = {}
|
||||
|
||||
|
||||
if filedump is not None:
|
||||
try:
|
||||
torrent_info = lt.torrent_info(lt.bdecode(filedump))
|
||||
except Exception, e:
|
||||
log.error("Unable to decode torrent file!: %s", e)
|
||||
|
||||
|
||||
if torrent_info is None:
|
||||
# We have no torrent_info so we need to add the torrent with information
|
||||
# from the state object.
|
||||
|
@ -273,14 +273,14 @@ class TorrentManager(component.Component):
|
|||
options["download_location"] = state.save_path
|
||||
options["auto_managed"] = state.auto_managed
|
||||
options["add_paused"] = state.paused
|
||||
|
||||
|
||||
add_torrent_params["ti"] =\
|
||||
self.get_torrent_info_from_file(
|
||||
os.path.join(self.config["state_location"], state.torrent_id + ".torrent"))
|
||||
if not add_torrent_params["ti"]:
|
||||
log.error("Unable to add torrent!")
|
||||
return
|
||||
|
||||
|
||||
add_torrent_params["resume_data"] = self.get_resume_data_from_file(state.torrent_id)
|
||||
else:
|
||||
# We have a torrent_info object so we're not loading from state.
|
||||
|
@ -304,44 +304,50 @@ class TorrentManager(component.Component):
|
|||
else:
|
||||
for key in options_keys:
|
||||
if not options.has_key(key):
|
||||
options[key] = self.config[key]
|
||||
options[key] = self.config[key]
|
||||
|
||||
add_torrent_params["ti"] = torrent_info
|
||||
add_torrent_params["resume_data"] = ""
|
||||
|
||||
|
||||
#log.info("Adding torrent: %s", filename)
|
||||
log.debug("options: %s", options)
|
||||
|
||||
|
||||
# Set the right storage_mode
|
||||
if options["compact_allocation"]:
|
||||
storage_mode = lt.storage_mode_t(2)
|
||||
else:
|
||||
storage_mode = lt.storage_mode_t(1)
|
||||
|
||||
|
||||
try:
|
||||
# Try to encode this as utf8 if needed
|
||||
options["download_location"] = options["download_location"].encode("utf8")
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
|
||||
# Fill in the rest of the add_torrent_params dictionary
|
||||
add_torrent_params["save_path"] = options["download_location"].encode("utf8")
|
||||
add_torrent_params["save_path"] = options["download_location"]
|
||||
|
||||
add_torrent_params["storage_mode"] = storage_mode
|
||||
add_torrent_params["paused"] = True
|
||||
add_torrent_params["auto_managed"] = False
|
||||
add_torrent_params["duplicate_is_error"] = True
|
||||
|
||||
|
||||
# We need to pause the AlertManager momentarily to prevent alerts
|
||||
# for this torrent being generated before a Torrent object is created.
|
||||
component.pause("AlertManager")
|
||||
|
||||
|
||||
handle = None
|
||||
try:
|
||||
handle = self.session.add_torrent(add_torrent_params)
|
||||
except RuntimeError, e:
|
||||
log.warning("Error adding torrent: %s", e)
|
||||
|
||||
|
||||
if not handle or not handle.is_valid():
|
||||
log.debug("torrent handle is invalid!")
|
||||
# The torrent was not added to the session
|
||||
component.resume("AlertManager")
|
||||
return
|
||||
|
||||
|
||||
log.debug("handle id: %s", str(handle.info_hash()))
|
||||
# Create a Torrent object
|
||||
torrent = Torrent(handle, options, state, filename)
|
||||
|
@ -349,7 +355,7 @@ class TorrentManager(component.Component):
|
|||
self.torrents[torrent.torrent_id] = torrent
|
||||
if self.config["queue_new_to_top"]:
|
||||
handle.queue_position_top()
|
||||
|
||||
|
||||
component.resume("AlertManager")
|
||||
|
||||
# Resume the torrent if needed
|
||||
|
@ -360,7 +366,7 @@ class TorrentManager(component.Component):
|
|||
# Write the .torrent file to the state directory
|
||||
if filedump:
|
||||
try:
|
||||
save_file = open(os.path.join(self.config["state_location"],
|
||||
save_file = open(os.path.join(self.config["state_location"],
|
||||
torrent.torrent_id + ".torrent"),
|
||||
"wb")
|
||||
save_file.write(filedump)
|
||||
|
@ -383,10 +389,10 @@ class TorrentManager(component.Component):
|
|||
if save_state:
|
||||
# Save the session state
|
||||
self.save_state()
|
||||
|
||||
|
||||
# Emit the torrent_added signal
|
||||
self.signals.emit("torrent_added", torrent.torrent_id)
|
||||
|
||||
|
||||
return torrent.torrent_id
|
||||
|
||||
def load_torrent(self, torrent_id):
|
||||
|
@ -397,16 +403,16 @@ class TorrentManager(component.Component):
|
|||
log.debug("Attempting to open %s for add.", torrent_id)
|
||||
_file = open(
|
||||
os.path.join(
|
||||
self.config["state_location"], torrent_id + ".torrent"),
|
||||
self.config["state_location"], torrent_id + ".torrent"),
|
||||
"rb")
|
||||
filedump = lt.bdecode(_file.read())
|
||||
_file.close()
|
||||
except (IOError, RuntimeError), e:
|
||||
log.warning("Unable to open %s: %s", torrent_id, e)
|
||||
return False
|
||||
|
||||
|
||||
return filedump
|
||||
|
||||
|
||||
def remove(self, torrent_id, remove_torrent=False, remove_data=False):
|
||||
"""Remove a torrent from the manager"""
|
||||
try:
|
||||
|
@ -415,17 +421,17 @@ class TorrentManager(component.Component):
|
|||
# Remove data if set
|
||||
if remove_data:
|
||||
option = 1
|
||||
self.session.remove_torrent(self.torrents[torrent_id].handle,
|
||||
self.session.remove_torrent(self.torrents[torrent_id].handle,
|
||||
option)
|
||||
except (RuntimeError, KeyError), e:
|
||||
log.warning("Error removing torrent: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
# Remove the .torrent file if requested
|
||||
if remove_torrent:
|
||||
try:
|
||||
torrent_file = os.path.join(
|
||||
self.config["torrentfiles_location"],
|
||||
self.config["torrentfiles_location"],
|
||||
self.torrents[torrent_id].filename)
|
||||
os.remove(torrent_file)
|
||||
except Exception, e:
|
||||
|
@ -433,24 +439,24 @@ class TorrentManager(component.Component):
|
|||
|
||||
# Remove the .fastresume if it exists
|
||||
self.torrents[torrent_id].delete_fastresume()
|
||||
|
||||
|
||||
# Remove the .torrent file in the state
|
||||
self.torrents[torrent_id].delete_torrentfile()
|
||||
|
||||
|
||||
# Remove the torrent from deluge's session
|
||||
try:
|
||||
del self.torrents[torrent_id]
|
||||
except KeyError, ValueError:
|
||||
return False
|
||||
|
||||
|
||||
# Save the session state
|
||||
self.save_state()
|
||||
|
||||
|
||||
# Emit the signal to the clients
|
||||
self.signals.emit("torrent_removed", torrent_id)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def pause_all(self):
|
||||
"""Pauses all torrents.. Returns a list of torrents paused."""
|
||||
torrent_was_paused = False
|
||||
|
@ -460,7 +466,7 @@ class TorrentManager(component.Component):
|
|||
torrent_was_paused = True
|
||||
except:
|
||||
log.warning("Unable to pause torrent %s", key)
|
||||
|
||||
|
||||
return torrent_was_paused
|
||||
|
||||
def resume_all(self):
|
||||
|
@ -473,7 +479,7 @@ class TorrentManager(component.Component):
|
|||
log.warning("Unable to resume torrent %s", key)
|
||||
|
||||
return torrent_was_resumed
|
||||
|
||||
|
||||
def load_state(self):
|
||||
"""Load the state of the TorrentManager from the torrents.state file"""
|
||||
state = TorrentManagerState()
|
||||
|
@ -486,7 +492,7 @@ class TorrentManager(component.Component):
|
|||
state_file.close()
|
||||
except (EOFError, IOError, Exception), e:
|
||||
log.warning("Unable to load state file: %s", e)
|
||||
|
||||
|
||||
# Try to use an old state
|
||||
try:
|
||||
if dir(state.torrents[0]) != dir(TorrentState()):
|
||||
|
@ -495,7 +501,7 @@ class TorrentManager(component.Component):
|
|||
setattr(s, attr, getattr(TorrentState(), attr, None))
|
||||
except Exception, e:
|
||||
log.warning("Unable to update state file to a compatible version: %s", e)
|
||||
|
||||
|
||||
# Reorder the state.torrents list to add torrents in the correct queue
|
||||
# order.
|
||||
ordered_state = []
|
||||
|
@ -506,14 +512,14 @@ class TorrentManager(component.Component):
|
|||
break
|
||||
if torrent_state not in ordered_state:
|
||||
ordered_state.append(torrent_state)
|
||||
|
||||
|
||||
for torrent_state in ordered_state:
|
||||
try:
|
||||
self.add(state=torrent_state, save_state=False)
|
||||
except AttributeError, e:
|
||||
log.error("Torrent state file is either corrupt or incompatible!")
|
||||
break
|
||||
|
||||
|
||||
# Run the post_session_load plugin hooks
|
||||
self.plugins.run_post_session_load()
|
||||
|
||||
|
@ -525,14 +531,14 @@ class TorrentManager(component.Component):
|
|||
paused = False
|
||||
if torrent.state == "Paused":
|
||||
paused = True
|
||||
|
||||
|
||||
torrent_state = TorrentState(
|
||||
torrent.torrent_id,
|
||||
torrent.filename,
|
||||
torrent.get_status(["total_uploaded"])["total_uploaded"],
|
||||
torrent.get_status(["total_uploaded"])["total_uploaded"],
|
||||
torrent.trackers,
|
||||
torrent.compact,
|
||||
paused,
|
||||
torrent.compact,
|
||||
paused,
|
||||
torrent.save_path,
|
||||
torrent.max_connections,
|
||||
torrent.max_upload_slots,
|
||||
|
@ -548,18 +554,18 @@ class TorrentManager(component.Component):
|
|||
torrent.remove_at_ratio
|
||||
)
|
||||
state.torrents.append(torrent_state)
|
||||
|
||||
|
||||
# Pickle the TorrentManagerState object
|
||||
try:
|
||||
log.debug("Saving torrent state file.")
|
||||
state_file = open(
|
||||
os.path.join(self.config["state_location"], "torrents.state"),
|
||||
os.path.join(self.config["state_location"], "torrents.state"),
|
||||
"wb")
|
||||
cPickle.dump(state, state_file)
|
||||
state_file.close()
|
||||
except IOError:
|
||||
log.warning("Unable to save state file.")
|
||||
|
||||
|
||||
# We return True so that the timer thread will continue
|
||||
return True
|
||||
|
||||
|
@ -567,7 +573,7 @@ class TorrentManager(component.Component):
|
|||
"""Saves resume data for all the torrents"""
|
||||
for torrent in self.torrents.values():
|
||||
torrent.write_fastresume()
|
||||
|
||||
|
||||
def queue_top(self, torrent_id):
|
||||
"""Queue torrent to top"""
|
||||
if self.torrents[torrent_id].get_queue_position() == 0:
|
||||
|
@ -588,7 +594,7 @@ class TorrentManager(component.Component):
|
|||
"""Queue torrent down one position"""
|
||||
if self.torrents[torrent_id].get_queue_position() == (len(self.torrents) - 1):
|
||||
return False
|
||||
|
||||
|
||||
self.torrents[torrent_id].handle.queue_position_down()
|
||||
return True
|
||||
|
||||
|
@ -599,13 +605,13 @@ class TorrentManager(component.Component):
|
|||
|
||||
self.torrents[torrent_id].handle.queue_position_bottom()
|
||||
return True
|
||||
|
||||
|
||||
def on_set_max_connections_per_torrent(self, key, value):
|
||||
"""Sets the per-torrent connection limit"""
|
||||
log.debug("max_connections_per_torrent set to %s..", value)
|
||||
for key in self.torrents.keys():
|
||||
self.torrents[key].set_max_connections(value)
|
||||
|
||||
|
||||
def on_set_max_upload_slots_per_torrent(self, key, value):
|
||||
"""Sets the per-torrent upload slot limit"""
|
||||
log.debug("max_upload_slots_per_torrent set to %s..", value)
|
||||
|
@ -616,7 +622,7 @@ class TorrentManager(component.Component):
|
|||
log.debug("max_upload_speed_per_torrent set to %s..", value)
|
||||
for key in self.torrents.keys():
|
||||
self.torrents[key].set_max_upload_speed(value)
|
||||
|
||||
|
||||
def on_set_max_download_speed_per_torrent(self, key, value):
|
||||
log.debug("max_download_speed_per_torrent set to %s..", value)
|
||||
for key in self.torrents.keys():
|
||||
|
@ -638,7 +644,7 @@ class TorrentManager(component.Component):
|
|||
torrent.update_state()
|
||||
torrent.write_fastresume()
|
||||
component.get("SignalManager").emit("torrent_finished", torrent_id)
|
||||
|
||||
|
||||
def on_alert_torrent_paused(self, alert):
|
||||
log.debug("on_alert_torrent_paused")
|
||||
# Get the torrent_id
|
||||
|
@ -646,20 +652,20 @@ class TorrentManager(component.Component):
|
|||
# Set the torrent state
|
||||
self.torrents[torrent_id].update_state()
|
||||
component.get("SignalManager").emit("torrent_paused", torrent_id)
|
||||
|
||||
|
||||
# Write the fastresume file
|
||||
self.torrents[torrent_id].write_fastresume()
|
||||
|
||||
|
||||
if torrent_id in self.shutdown_torrent_pause_list:
|
||||
self.shutdown_torrent_pause_list.remove(torrent_id)
|
||||
|
||||
|
||||
def on_alert_torrent_checked(self, alert):
|
||||
log.debug("on_alert_torrent_checked")
|
||||
# Get the torrent_id
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
# Set the torrent state
|
||||
self.torrents[torrent_id].update_state()
|
||||
|
||||
|
||||
def on_alert_tracker_reply(self, alert):
|
||||
log.debug("on_alert_tracker_reply")
|
||||
# Get the torrent_id
|
||||
|
@ -670,7 +676,7 @@ class TorrentManager(component.Component):
|
|||
self.torrents[torrent_id].set_tracker_status(_("Announce OK"))
|
||||
except KeyError:
|
||||
log.debug("torrent_id doesn't exist.")
|
||||
|
||||
|
||||
# Check to see if we got any peer information from the tracker
|
||||
if alert.handle.status().num_complete == -1 or \
|
||||
alert.handle.status().num_incomplete == -1:
|
||||
|
@ -685,7 +691,7 @@ class TorrentManager(component.Component):
|
|||
except RuntimeError:
|
||||
log.debug("Invalid torrent handle.")
|
||||
return
|
||||
|
||||
|
||||
# Set the tracker status for the torrent
|
||||
try:
|
||||
self.torrents[torrent_id].set_tracker_status(_("Announce Sent"))
|
||||
|
@ -703,7 +709,7 @@ class TorrentManager(component.Component):
|
|||
self.torrents[torrent_id].set_tracker_status(tracker_status)
|
||||
except KeyError:
|
||||
log.debug("torrent_id doesn't exist.")
|
||||
|
||||
|
||||
def on_alert_tracker_warning(self, alert):
|
||||
log.debug("on_alert_tracker_warning")
|
||||
# Get the torrent_id
|
||||
|
@ -723,7 +729,7 @@ class TorrentManager(component.Component):
|
|||
torrent.set_tracker_status(tracker_status)
|
||||
except KeyError:
|
||||
log.debug("torrent_id doesn't exist.")
|
||||
|
||||
|
||||
def on_alert_storage_moved(self, alert):
|
||||
log.debug("on_alert_storage_moved")
|
||||
log.debug("save_path: %s", alert.handle.save_path())
|
||||
|
@ -740,10 +746,9 @@ class TorrentManager(component.Component):
|
|||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
torrent.is_finished = torrent.handle.is_seed()
|
||||
torrent.update_state()
|
||||
|
||||
|
||||
def on_alert_state_changed(self, alert):
|
||||
log.debug("on_alert_state_changed")
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
self.torrents[torrent_id].update_state()
|
||||
component.get("SignalManager").emit("torrent_state_changed", torrent_id)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue