Cleanup torrentmanger code
* Remove legacy/old state file code * Passes flake8 cleanly * Most pylint messages dealt with * Code now uses >=Python 2.6 'with' and 'as' statements
This commit is contained in:
parent
77cb794e4d
commit
e3f3b6d751
|
@ -1,143 +0,0 @@
|
||||||
#
|
|
||||||
# oldstateupgrader.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2008 Andrew Resch <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.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# You must obey the GNU General Public License in all respects for all of
|
|
||||||
# the code used other than OpenSSL. If you modify file(s) with this
|
|
||||||
# exception, you may extend this exception to your version of the file(s),
|
|
||||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
|
||||||
# this exception statement from your version. If you delete this exception
|
|
||||||
# statement from all source files in the program, then also delete it here.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pickle
|
|
||||||
import cPickle
|
|
||||||
import shutil
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from deluge._libtorrent import lt
|
|
||||||
|
|
||||||
from deluge.configmanager import ConfigManager, get_config_dir
|
|
||||||
import deluge.core.torrentmanager
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
#start : http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286203
|
|
||||||
def makeFakeClass(module, name):
|
|
||||||
class FakeThing(object):
|
|
||||||
pass
|
|
||||||
FakeThing.__name__ = name
|
|
||||||
FakeThing.__module__ = '(fake)' + module
|
|
||||||
return FakeThing
|
|
||||||
|
|
||||||
class PickleUpgrader(pickle.Unpickler):
|
|
||||||
def find_class(self, module, cname):
|
|
||||||
# Pickle tries to load a couple things like copy_reg and
|
|
||||||
# __builtin__.object even though a pickle file doesn't
|
|
||||||
# explicitly reference them (afaict): allow them to be loaded
|
|
||||||
# normally.
|
|
||||||
if module in ('copy_reg', '__builtin__'):
|
|
||||||
thing = pickle.Unpickler.find_class(self, module, cname)
|
|
||||||
return thing
|
|
||||||
return makeFakeClass(module, cname)
|
|
||||||
# end: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286203
|
|
||||||
|
|
||||||
class OldStateUpgrader:
|
|
||||||
def __init__(self):
|
|
||||||
self.config = ConfigManager("core.conf")
|
|
||||||
self.state05_location = os.path.join(get_config_dir(), "persistent.state")
|
|
||||||
self.state10_location = os.path.join(get_config_dir(), "state", "torrents.state")
|
|
||||||
if os.path.exists(self.state05_location) and not os.path.exists(self.state10_location):
|
|
||||||
# If the 0.5 state file exists and the 1.0 doesn't, then let's upgrade it
|
|
||||||
self.upgrade05()
|
|
||||||
|
|
||||||
def upgrade05(self):
|
|
||||||
try:
|
|
||||||
state = PickleUpgrader(open(self.state05_location, "rb")).load()
|
|
||||||
except Exception, e:
|
|
||||||
log.debug("Unable to open 0.5 state file: %s", e)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check to see if we can upgrade this file
|
|
||||||
if type(state).__name__ == 'list':
|
|
||||||
log.warning("0.5 state file is too old to upgrade")
|
|
||||||
return
|
|
||||||
|
|
||||||
new_state = deluge.core.torrentmanager.TorrentManagerState()
|
|
||||||
for ti, uid in state.torrents.items():
|
|
||||||
torrent_path = os.path.join(get_config_dir(), "torrentfiles", ti.filename)
|
|
||||||
try:
|
|
||||||
torrent_info = None
|
|
||||||
log.debug("Attempting to create torrent_info from %s", torrent_path)
|
|
||||||
_file = open(torrent_path, "rb")
|
|
||||||
torrent_info = lt.torrent_info(lt.bdecode(_file.read()))
|
|
||||||
_file.close()
|
|
||||||
except (IOError, RuntimeError), e:
|
|
||||||
log.warning("Unable to open %s: %s", torrent_path, e)
|
|
||||||
|
|
||||||
# Copy the torrent file to the new location
|
|
||||||
import shutil
|
|
||||||
shutil.copyfile(torrent_path, os.path.join(get_config_dir(), "state", str(torrent_info.info_hash()) + ".torrent"))
|
|
||||||
|
|
||||||
# Set the file prioritiy property if not already there
|
|
||||||
if not hasattr(ti, "priorities"):
|
|
||||||
ti.priorities = [1] * torrent_info.num_files()
|
|
||||||
|
|
||||||
# Create the new TorrentState object
|
|
||||||
new_torrent = deluge.core.torrentmanager.TorrentState(
|
|
||||||
torrent_id=str(torrent_info.info_hash()),
|
|
||||||
filename=ti.filename,
|
|
||||||
save_path=ti.save_dir,
|
|
||||||
compact=ti.compact,
|
|
||||||
paused=ti.user_paused,
|
|
||||||
total_uploaded=ti.uploaded_memory,
|
|
||||||
max_upload_speed=ti.upload_rate_limit,
|
|
||||||
max_download_speed=ti.download_rate_limit,
|
|
||||||
file_priorities=ti.priorities,
|
|
||||||
queue=state.queue.index(ti)
|
|
||||||
)
|
|
||||||
# Append the object to the state list
|
|
||||||
new_state.torrents.append(new_torrent)
|
|
||||||
|
|
||||||
# Now we need to write out the new state file
|
|
||||||
try:
|
|
||||||
log.debug("Saving torrent state file.")
|
|
||||||
state_file = open(
|
|
||||||
os.path.join(get_config_dir(), "state", "torrents.state"), "wb")
|
|
||||||
cPickle.dump(new_state, state_file)
|
|
||||||
state_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.warning("Unable to save state file: %s", e)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Rename the persistent.state file
|
|
||||||
try:
|
|
||||||
os.rename(self.state05_location, self.state05_location + ".old")
|
|
||||||
except Exception, e:
|
|
||||||
log.debug("Unable to rename old persistent.state file! %s", e)
|
|
|
@ -49,18 +49,19 @@ from twisted.internet import reactor
|
||||||
|
|
||||||
from deluge._libtorrent import lt
|
from deluge._libtorrent import lt
|
||||||
|
|
||||||
from deluge.event import *
|
from deluge.event import (TorrentAddedEvent, PreTorrentRemovedEvent, TorrentRemovedEvent,
|
||||||
from deluge.error import *
|
SessionStartedEvent, TorrentFinishedEvent, TorrentStateChangedEvent,
|
||||||
|
TorrentResumedEvent, TorrentFileRenamedEvent, TorrentFileCompletedEvent)
|
||||||
|
from deluge.error import InvalidTorrentError
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.configmanager import ConfigManager, get_config_dir
|
from deluge.configmanager import ConfigManager, get_config_dir
|
||||||
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
|
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
|
||||||
from deluge.core.torrent import Torrent
|
from deluge.core.torrent import Torrent, TorrentOptions, sanitize_filepath
|
||||||
from deluge.core.torrent import TorrentOptions
|
|
||||||
import deluge.core.oldstateupgrader
|
|
||||||
from deluge.common import utf8_encoded, decode_string
|
from deluge.common import utf8_encoded, decode_string
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TorrentState:
|
class TorrentState:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
torrent_id=None,
|
torrent_id=None,
|
||||||
|
@ -89,8 +90,7 @@ class TorrentState:
|
||||||
time_added=-1,
|
time_added=-1,
|
||||||
last_seen_complete=0,
|
last_seen_complete=0,
|
||||||
owner=None,
|
owner=None,
|
||||||
shared=False
|
shared=False):
|
||||||
):
|
|
||||||
self.torrent_id = torrent_id
|
self.torrent_id = torrent_id
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.total_uploaded = total_uploaded
|
self.total_uploaded = total_uploaded
|
||||||
|
@ -121,10 +121,12 @@ class TorrentState:
|
||||||
self.move_completed_path = move_completed_path
|
self.move_completed_path = move_completed_path
|
||||||
self.shared = shared
|
self.shared = shared
|
||||||
|
|
||||||
|
|
||||||
class TorrentManagerState:
|
class TorrentManagerState:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.torrents = []
|
self.torrents = []
|
||||||
|
|
||||||
|
|
||||||
class TorrentManager(component.Component):
|
class TorrentManager(component.Component):
|
||||||
"""
|
"""
|
||||||
TorrentManager contains a list of torrents in the current libtorrent
|
TorrentManager contains a list of torrents in the current libtorrent
|
||||||
|
@ -135,7 +137,7 @@ class TorrentManager(component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "TorrentManager", interval=5,
|
component.Component.__init__(self, "TorrentManager", interval=5,
|
||||||
depend=["CorePluginManager", "AlertManager"])
|
depend=["CorePluginManager", "AlertManager"])
|
||||||
log.debug("TorrentManager init..")
|
log.debug("TorrentManager init...")
|
||||||
# Set the libtorrent session
|
# Set the libtorrent session
|
||||||
self.session = component.get("Core").session
|
self.session = component.get("Core").session
|
||||||
# Set the alertmanager
|
# Set the alertmanager
|
||||||
|
@ -175,48 +177,35 @@ class TorrentManager(component.Component):
|
||||||
self.on_set_max_download_speed_per_torrent)
|
self.on_set_max_download_speed_per_torrent)
|
||||||
|
|
||||||
# Register alert functions
|
# Register alert functions
|
||||||
self.alerts.register_handler("torrent_finished_alert",
|
self.alerts.register_handler("torrent_finished_alert", self.on_alert_torrent_finished)
|
||||||
self.on_alert_torrent_finished)
|
self.alerts.register_handler("torrent_paused_alert", self.on_alert_torrent_paused)
|
||||||
self.alerts.register_handler("torrent_paused_alert",
|
self.alerts.register_handler("torrent_checked_alert", self.on_alert_torrent_checked)
|
||||||
self.on_alert_torrent_paused)
|
self.alerts.register_handler("tracker_reply_alert", self.on_alert_tracker_reply)
|
||||||
self.alerts.register_handler("torrent_checked_alert",
|
self.alerts.register_handler("tracker_announce_alert", self.on_alert_tracker_announce)
|
||||||
self.on_alert_torrent_checked)
|
self.alerts.register_handler("tracker_warning_alert", self.on_alert_tracker_warning)
|
||||||
self.alerts.register_handler("tracker_reply_alert",
|
self.alerts.register_handler("tracker_error_alert", self.on_alert_tracker_error)
|
||||||
self.on_alert_tracker_reply)
|
self.alerts.register_handler("storage_moved_alert", self.on_alert_storage_moved)
|
||||||
self.alerts.register_handler("tracker_announce_alert",
|
self.alerts.register_handler("torrent_resumed_alert", self.on_alert_torrent_resumed)
|
||||||
self.on_alert_tracker_announce)
|
self.alerts.register_handler("state_changed_alert", self.on_alert_state_changed)
|
||||||
self.alerts.register_handler("tracker_warning_alert",
|
self.alerts.register_handler("save_resume_data_alert", self.on_alert_save_resume_data)
|
||||||
self.on_alert_tracker_warning)
|
|
||||||
self.alerts.register_handler("tracker_error_alert",
|
|
||||||
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.on_alert_torrent_resumed)
|
|
||||||
self.alerts.register_handler("state_changed_alert",
|
|
||||||
self.on_alert_state_changed)
|
|
||||||
self.alerts.register_handler("save_resume_data_alert",
|
|
||||||
self.on_alert_save_resume_data)
|
|
||||||
self.alerts.register_handler("save_resume_data_failed_alert",
|
self.alerts.register_handler("save_resume_data_failed_alert",
|
||||||
self.on_alert_save_resume_data_failed)
|
self.on_alert_save_resume_data_failed)
|
||||||
self.alerts.register_handler("file_renamed_alert",
|
self.alerts.register_handler("file_renamed_alert", self.on_alert_file_renamed)
|
||||||
self.on_alert_file_renamed)
|
self.alerts.register_handler("metadata_received_alert", self.on_alert_metadata_received)
|
||||||
self.alerts.register_handler("metadata_received_alert",
|
self.alerts.register_handler("file_error_alert", self.on_alert_file_error)
|
||||||
self.on_alert_metadata_received)
|
self.alerts.register_handler("file_completed_alert", self.on_alert_file_completed)
|
||||||
self.alerts.register_handler("file_error_alert",
|
self.alerts.register_handler("state_update_alert", self.on_alert_state_update)
|
||||||
self.on_alert_file_error)
|
|
||||||
self.alerts.register_handler("file_completed_alert",
|
# Define timers
|
||||||
self.on_alert_file_completed)
|
self.save_state_timer = LoopingCall(self.save_state)
|
||||||
self.alerts.register_handler("state_update_alert",
|
self.save_resume_data_timer = LoopingCall(self.save_resume_data)
|
||||||
self.on_alert_state_update)
|
self.save_all_resume_data_timer = LoopingCall(self.save_resume_data, self.torrents.keys())
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
# Get the pluginmanager reference
|
|
||||||
self.plugins = component.get("CorePluginManager")
|
|
||||||
|
|
||||||
# Check for old temp file to verify safe shutdown
|
# Check for old temp file to verify safe shutdown
|
||||||
if os.path.isfile(self.temp_file):
|
if os.path.isfile(self.temp_file):
|
||||||
def archive_file(filename):
|
def archive_file(filename):
|
||||||
|
"""Archives the file in 'archive' sub-directory with timestamp appended"""
|
||||||
import datetime
|
import datetime
|
||||||
filepath = os.path.join(self.state_dir, filename)
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
filepath_bak = filepath + ".bak"
|
filepath_bak = filepath + ".bak"
|
||||||
|
@ -241,19 +230,13 @@ class TorrentManager(component.Component):
|
||||||
with file(self.temp_file, 'a'):
|
with file(self.temp_file, 'a'):
|
||||||
os.utime(self.temp_file, None)
|
os.utime(self.temp_file, None)
|
||||||
|
|
||||||
# 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()
|
self.load_state()
|
||||||
|
|
||||||
# Save the state periodically
|
# Save the state periodically
|
||||||
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, False)
|
||||||
# Force update for all resume data a bit less frequently
|
# Force update for all resume data a bit less frequently
|
||||||
self.save_all_resume_data_timer = LoopingCall(self.save_resume_data, self.torrents.keys())
|
|
||||||
self.save_all_resume_data_timer.start(900, False)
|
self.save_all_resume_data_timer.start(900, False)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
@ -276,6 +259,7 @@ class TorrentManager(component.Component):
|
||||||
self.torrents[key].prev_status_cleanup_loop.stop()
|
self.torrents[key].prev_status_cleanup_loop.stop()
|
||||||
|
|
||||||
def remove_temp_file(result):
|
def remove_temp_file(result):
|
||||||
|
"""Remove the temp_file to signify successfully saved state"""
|
||||||
if result and os.path.isfile(self.temp_file):
|
if result and os.path.isfile(self.temp_file):
|
||||||
os.remove(self.temp_file)
|
os.remove(self.temp_file)
|
||||||
|
|
||||||
|
@ -287,10 +271,9 @@ class TorrentManager(component.Component):
|
||||||
for torrent_id, torrent in self.torrents.items():
|
for torrent_id, torrent in self.torrents.items():
|
||||||
if torrent.options["stop_at_ratio"] and torrent.state not in (
|
if torrent.options["stop_at_ratio"] and torrent.state not in (
|
||||||
"Checking", "Allocating", "Paused", "Queued"):
|
"Checking", "Allocating", "Paused", "Queued"):
|
||||||
# If the global setting is set, but the per-torrent isn't..
|
# If the global setting is set, but the per-torrent isn't...
|
||||||
# Just skip to the next torrent.
|
# Just skip to the next torrent.
|
||||||
# This is so that a user can turn-off the stop at ratio option
|
# This is so that a user can turn-off the stop at ratio option on a per-torrent basis
|
||||||
# on a per-torrent basis
|
|
||||||
if not torrent.options["stop_at_ratio"]:
|
if not torrent.options["stop_at_ratio"]:
|
||||||
continue
|
continue
|
||||||
if torrent.get_ratio() >= torrent.options["stop_ratio"] and torrent.is_finished:
|
if torrent.get_ratio() >= torrent.options["stop_ratio"] and torrent.is_finished:
|
||||||
|
@ -313,46 +296,23 @@ class TorrentManager(component.Component):
|
||||||
current_user = component.get("RPCServer").get_session_user()
|
current_user = component.get("RPCServer").get_session_user()
|
||||||
for torrent_id in torrent_ids[:]:
|
for torrent_id in torrent_ids[:]:
|
||||||
torrent_status = self[torrent_id].get_status(["owner", "shared"])
|
torrent_status = self[torrent_id].get_status(["owner", "shared"])
|
||||||
if torrent_status["owner"] != current_user and torrent_status["shared"] == False:
|
if torrent_status["owner"] != current_user and not torrent_status["shared"]:
|
||||||
torrent_ids.pop(torrent_ids.index(torrent_id))
|
torrent_ids.pop(torrent_ids.index(torrent_id))
|
||||||
return torrent_ids
|
return torrent_ids
|
||||||
|
|
||||||
def get_torrent_info_from_file(self, filepath):
|
def get_torrent_info_from_file(self, filepath):
|
||||||
"""Returns a torrent_info for the file specified or None"""
|
"""Returns a torrent_info for the file specified or None"""
|
||||||
torrent_info = None
|
|
||||||
# Get the torrent data from the torrent file
|
# Get the torrent data from the torrent file
|
||||||
try:
|
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("Attempting to create torrent_info from %s", filepath)
|
log.debug("Attempting to extract torrent_info from %s", filepath)
|
||||||
_file = open(filepath, "rb")
|
try:
|
||||||
|
with open(filepath, "rb") as _file:
|
||||||
torrent_info = lt.torrent_info(lt.bdecode(_file.read()))
|
torrent_info = lt.torrent_info(lt.bdecode(_file.read()))
|
||||||
_file.close()
|
except (IOError, RuntimeError) as ex:
|
||||||
except (IOError, RuntimeError), e:
|
log.warning("Unable to open torrent file %s: %s", filepath, ex)
|
||||||
log.warning("Unable to open %s: %s", filepath, e)
|
else:
|
||||||
|
|
||||||
return torrent_info
|
return torrent_info
|
||||||
|
|
||||||
def legacy_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.state_dir, torrent_id + ".fastresume"), "rb")
|
|
||||||
fastresume = _file.read()
|
|
||||||
_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.debug("Unable to load .fastresume: %s", e)
|
|
||||||
|
|
||||||
return str(fastresume)
|
|
||||||
|
|
||||||
def legacy_delete_resume_data(self, torrent_id):
|
|
||||||
"""Deletes the .fastresume file"""
|
|
||||||
path = os.path.join(self.state_dir, torrent_id + ".fastresume")
|
|
||||||
log.debug("Deleting fastresume file: %s", path)
|
|
||||||
try:
|
|
||||||
os.remove(path)
|
|
||||||
except Exception, e:
|
|
||||||
log.warning("Unable to delete the fastresume file: %s", e)
|
|
||||||
|
|
||||||
def add(self, torrent_info=None, state=None, options=None, save_state=True,
|
def add(self, torrent_info=None, state=None, options=None, save_state=True,
|
||||||
filedump=None, filename=None, magnet=None, resume_data=None, owner=None):
|
filedump=None, filename=None, magnet=None, resume_data=None, owner=None):
|
||||||
"""Add a torrent to the manager and returns it's torrent_id"""
|
"""Add a torrent to the manager and returns it's torrent_id"""
|
||||||
|
@ -365,9 +325,9 @@ class TorrentManager(component.Component):
|
||||||
if filedump is not None:
|
if filedump is not None:
|
||||||
try:
|
try:
|
||||||
torrent_info = lt.torrent_info(lt.bdecode(filedump))
|
torrent_info = lt.torrent_info(lt.bdecode(filedump))
|
||||||
except Exception, e:
|
except RuntimeError as ex:
|
||||||
log.error("Unable to decode torrent file!: %s", e)
|
log.error("Unable to decode torrent file!: %s", ex)
|
||||||
# XXX: Probably should raise an exception here..
|
# XXX: Probably should raise an exception here...
|
||||||
return
|
return
|
||||||
|
|
||||||
if torrent_info is None and state:
|
if torrent_info is None and state:
|
||||||
|
@ -395,23 +355,16 @@ class TorrentManager(component.Component):
|
||||||
options["shared"] = state.shared
|
options["shared"] = state.shared
|
||||||
owner = state.owner
|
owner = state.owner
|
||||||
|
|
||||||
ti = self.get_torrent_info_from_file(
|
torrent_info = self.get_torrent_info_from_file(
|
||||||
os.path.join(get_config_dir(),
|
os.path.join(self.state_dir, state.torrent_id + ".torrent"))
|
||||||
"state", state.torrent_id + ".torrent"))
|
if torrent_info:
|
||||||
if ti:
|
add_torrent_params["ti"] = torrent_info
|
||||||
add_torrent_params["ti"] = ti
|
|
||||||
elif state.magnet:
|
elif state.magnet:
|
||||||
magnet = state.magnet
|
magnet = state.magnet
|
||||||
else:
|
else:
|
||||||
log.error("Unable to add torrent!")
|
log.error("Unable to add torrent!")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Handle legacy case with storing resume data in individual files
|
|
||||||
# for each torrent
|
|
||||||
if resume_data is None:
|
|
||||||
resume_data = self.legacy_get_resume_data_from_file(state.torrent_id)
|
|
||||||
self.legacy_delete_resume_data(state.torrent_id)
|
|
||||||
|
|
||||||
if resume_data:
|
if resume_data:
|
||||||
add_torrent_params["resume_data"] = resume_data
|
add_torrent_params["resume_data"] = resume_data
|
||||||
else:
|
else:
|
||||||
|
@ -445,12 +398,12 @@ class TorrentManager(component.Component):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if options is None and load defaults
|
# Check if options is None and load defaults
|
||||||
if options == None:
|
if options is None:
|
||||||
options = TorrentOptions()
|
options = TorrentOptions()
|
||||||
else:
|
else:
|
||||||
o = TorrentOptions()
|
_options = TorrentOptions()
|
||||||
o.update(options)
|
_options.update(options)
|
||||||
options = o
|
options = _options
|
||||||
|
|
||||||
# Check for renamed files and if so, rename them in the torrent_info
|
# Check for renamed files and if so, rename them in the torrent_info
|
||||||
# before adding to the session.
|
# before adding to the session.
|
||||||
|
@ -460,7 +413,7 @@ class TorrentManager(component.Component):
|
||||||
fname = unicode(fname, "utf-8")
|
fname = unicode(fname, "utf-8")
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
fname = deluge.core.torrent.sanitize_filepath(fname)
|
fname = sanitize_filepath(fname)
|
||||||
log.debug("renaming file index %s to %s", index, fname)
|
log.debug("renaming file index %s to %s", index, fname)
|
||||||
try:
|
try:
|
||||||
torrent_info.rename_file(index, fname)
|
torrent_info.rename_file(index, fname)
|
||||||
|
@ -501,8 +454,8 @@ class TorrentManager(component.Component):
|
||||||
handle = lt.add_magnet_uri(self.session, utf8_encoded(magnet), add_torrent_params)
|
handle = lt.add_magnet_uri(self.session, utf8_encoded(magnet), add_torrent_params)
|
||||||
else:
|
else:
|
||||||
handle = self.session.add_torrent(add_torrent_params)
|
handle = self.session.add_torrent(add_torrent_params)
|
||||||
except RuntimeError, e:
|
except RuntimeError as ex:
|
||||||
log.warning("Error adding torrent: %s", e)
|
log.warning("Error adding torrent: %s", ex)
|
||||||
|
|
||||||
if not handle or not handle.is_valid():
|
if not handle or not handle.is_valid():
|
||||||
log.debug("torrent handle is invalid!")
|
log.debug("torrent handle is invalid!")
|
||||||
|
@ -534,23 +487,19 @@ class TorrentManager(component.Component):
|
||||||
# Write the .torrent file to the state directory
|
# Write the .torrent file to the state directory
|
||||||
if filedump:
|
if filedump:
|
||||||
try:
|
try:
|
||||||
save_file = open(os.path.join(self.state_dir, torrent.torrent_id + ".torrent"), "wb")
|
with open(os.path.join(self.state_dir, torrent.torrent_id + ".torrent"), "wb") as save_file:
|
||||||
save_file.write(filedump)
|
save_file.write(filedump)
|
||||||
save_file.close()
|
except IOError as ex:
|
||||||
except IOError, e:
|
log.warning("Unable to save torrent file: %s", ex)
|
||||||
log.warning("Unable to save torrent file: %s", e)
|
|
||||||
|
|
||||||
# If the user has requested a copy of the torrent be saved elsewhere
|
# If the user has requested a copy of the torrent be saved elsewhere
|
||||||
# we need to do that.
|
# we need to do that.
|
||||||
if self.config["copy_torrent_file"] and filename is not None:
|
if self.config["copy_torrent_file"] and filename is not None:
|
||||||
try:
|
try:
|
||||||
save_file = open(
|
with open(os.path.join(self.config["torrentfiles_location"], filename), "wb") as save_file:
|
||||||
os.path.join(self.config["torrentfiles_location"], filename),
|
|
||||||
"wb")
|
|
||||||
save_file.write(filedump)
|
save_file.write(filedump)
|
||||||
save_file.close()
|
except IOError as ex:
|
||||||
except IOError, e:
|
log.warning("Unable to save torrent file: %s", ex)
|
||||||
log.warning("Unable to save torrent file: %s", e)
|
|
||||||
|
|
||||||
if save_state:
|
if save_state:
|
||||||
# Save the session state
|
# Save the session state
|
||||||
|
@ -564,26 +513,21 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
if log.isEnabledFor(logging.INFO):
|
if log.isEnabledFor(logging.INFO):
|
||||||
name_and_owner = torrent.get_status(["name", "owner"])
|
name_and_owner = torrent.get_status(["name", "owner"])
|
||||||
log.info("Torrent %s from user \"%s\" %s" % (
|
log.info("Torrent %s from user \"%s\" %s",
|
||||||
name_and_owner["name"],
|
name_and_owner["name"],
|
||||||
name_and_owner["owner"],
|
name_and_owner["owner"],
|
||||||
from_state and "loaded" or "added")
|
from_state and "loaded" or "added")
|
||||||
)
|
|
||||||
return torrent.torrent_id
|
return torrent.torrent_id
|
||||||
|
|
||||||
def load_torrent(self, torrent_id):
|
def load_torrent(self, torrent_id):
|
||||||
"""Load a torrent file from state and return it's torrent info"""
|
"""Load a torrent file from state and return it's torrent info"""
|
||||||
filedump = None
|
|
||||||
# Get the torrent data from the torrent file
|
|
||||||
try:
|
try:
|
||||||
log.debug("Attempting to open %s for add.", torrent_id)
|
log.debug("Attempting to open %s for add.", torrent_id)
|
||||||
_file = open(os.path.join(self.state_dir, torrent_id + ".torrent"), "rb")
|
with open(os.path.join(self.state_dir, torrent_id + ".torrent"), "rb") as _file:
|
||||||
filedump = lt.bdecode(_file.read())
|
filedump = lt.bdecode(_file.read())
|
||||||
_file.close()
|
except (IOError, RuntimeError) as ex:
|
||||||
except (IOError, RuntimeError), e:
|
log.warning("Unable to open torrent file %s: %s", torrent_id, ex)
|
||||||
log.warning("Unable to open %s: %s", torrent_id, e)
|
else:
|
||||||
return False
|
|
||||||
|
|
||||||
return filedump
|
return filedump
|
||||||
|
|
||||||
def remove(self, torrent_id, remove_data=False):
|
def remove(self, torrent_id, remove_data=False):
|
||||||
|
@ -610,10 +554,9 @@ class TorrentManager(component.Component):
|
||||||
component.get("EventManager").emit(PreTorrentRemovedEvent(torrent_id))
|
component.get("EventManager").emit(PreTorrentRemovedEvent(torrent_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.session.remove_torrent(self.torrents[torrent_id].handle,
|
self.session.remove_torrent(self.torrents[torrent_id].handle, 1 if remove_data else 0)
|
||||||
1 if remove_data else 0)
|
except (RuntimeError, KeyError) as ex:
|
||||||
except (RuntimeError, KeyError), e:
|
log.warning("Error removing torrent: %s", ex)
|
||||||
log.warning("Error removing torrent: %s", e)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Remove fastresume data if it is exists
|
# Remove fastresume data if it is exists
|
||||||
|
@ -624,18 +567,13 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
# Remove the torrent file from the user specified directory
|
# Remove the torrent file from the user specified directory
|
||||||
filename = self.torrents[torrent_id].filename
|
filename = self.torrents[torrent_id].filename
|
||||||
if self.config["copy_torrent_file"] \
|
if self.config["copy_torrent_file"] and self.config["del_copy_torrent_file"] and filename:
|
||||||
and self.config["del_copy_torrent_file"] \
|
users_torrent_file = os.path.join(self.config["torrentfiles_location"], filename)
|
||||||
and filename:
|
log.info("Delete user's torrent file: %s", users_torrent_file)
|
||||||
try:
|
try:
|
||||||
users_torrent_file = os.path.join(
|
|
||||||
self.config["torrentfiles_location"],
|
|
||||||
filename)
|
|
||||||
log.info("Delete user's torrent file: %s",
|
|
||||||
users_torrent_file)
|
|
||||||
os.remove(users_torrent_file)
|
os.remove(users_torrent_file)
|
||||||
except Exception, e:
|
except OSError as ex:
|
||||||
log.warning("Unable to remove copy torrent file: %s", e)
|
log.warning("Unable to remove copy torrent file: %s", ex)
|
||||||
|
|
||||||
# Stop the looping call
|
# Stop the looping call
|
||||||
self.torrents[torrent_id].prev_status_cleanup_loop.stop()
|
self.torrents[torrent_id].prev_status_cleanup_loop.stop()
|
||||||
|
@ -673,7 +611,7 @@ class TorrentManager(component.Component):
|
||||||
try:
|
try:
|
||||||
with open(_filepath, "rb") as _file:
|
with open(_filepath, "rb") as _file:
|
||||||
state = cPickle.load(_file)
|
state = cPickle.load(_file)
|
||||||
except (IOError, EOFError, cPickle.UnpicklingError), ex:
|
except (IOError, EOFError, cPickle.UnpicklingError) as ex:
|
||||||
log.warning("Unable to load %s: %s", _filepath, ex)
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
state = None
|
state = None
|
||||||
else:
|
else:
|
||||||
|
@ -683,17 +621,6 @@ class TorrentManager(component.Component):
|
||||||
if state is None:
|
if state is None:
|
||||||
state = TorrentManagerState()
|
state = TorrentManagerState()
|
||||||
|
|
||||||
# Try to use an old state
|
|
||||||
try:
|
|
||||||
if len(state.torrents) > 0:
|
|
||||||
state_tmp = TorrentState()
|
|
||||||
if dir(state.torrents[0]) != dir(state_tmp):
|
|
||||||
for attr in (set(dir(state_tmp)) - set(dir(state.torrents[0]))):
|
|
||||||
for s in state.torrents:
|
|
||||||
setattr(s, attr, getattr(state_tmp, attr, None))
|
|
||||||
except Exception, e:
|
|
||||||
log.exception("Unable to update state file to a compatible version: %s", e)
|
|
||||||
|
|
||||||
# Reorder the state.torrents list to add torrents in the correct queue
|
# Reorder the state.torrents list to add torrents in the correct queue
|
||||||
# order.
|
# order.
|
||||||
state.torrents.sort(key=operator.attrgetter("queue"), reverse=self.config["queue_new_to_top"])
|
state.torrents.sort(key=operator.attrgetter("queue"), reverse=self.config["queue_new_to_top"])
|
||||||
|
@ -707,8 +634,8 @@ class TorrentManager(component.Component):
|
||||||
try:
|
try:
|
||||||
self.add(state=torrent_state, save_state=False,
|
self.add(state=torrent_state, save_state=False,
|
||||||
resume_data=resume_data.get(torrent_state.torrent_id))
|
resume_data=resume_data.get(torrent_state.torrent_id))
|
||||||
except AttributeError, e:
|
except AttributeError as ex:
|
||||||
log.error("Torrent state file is either corrupt or incompatible! %s", e)
|
log.error("Torrent state file is either corrupt or incompatible! %s", ex)
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
break
|
break
|
||||||
|
@ -729,8 +656,7 @@ class TorrentManager(component.Component):
|
||||||
torrent_status = torrent.get_status([
|
torrent_status = torrent.get_status([
|
||||||
"total_uploaded",
|
"total_uploaded",
|
||||||
"last_seen_complete"
|
"last_seen_complete"
|
||||||
], update=True
|
], update=True)
|
||||||
)
|
|
||||||
|
|
||||||
torrent_state = TorrentState(
|
torrent_state = TorrentState(
|
||||||
torrent.torrent_id,
|
torrent.torrent_id,
|
||||||
|
@ -802,7 +728,7 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
deferreds = []
|
deferreds = []
|
||||||
|
|
||||||
def on_torrent_resume_save(result, torrent_id):
|
def on_torrent_resume_save(dummy_result, torrent_id):
|
||||||
self.waiting_on_resume_data.pop(torrent_id, None)
|
self.waiting_on_resume_data.pop(torrent_id, None)
|
||||||
|
|
||||||
for torrent_id in torrent_ids:
|
for torrent_id in torrent_ids:
|
||||||
|
@ -816,11 +742,16 @@ class TorrentManager(component.Component):
|
||||||
def on_all_resume_data_finished(result):
|
def on_all_resume_data_finished(result):
|
||||||
if result:
|
if result:
|
||||||
if self.save_resume_data_file():
|
if self.save_resume_data_file():
|
||||||
|
# Return True for the remove_temp_file() callback in stop()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
|
return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
|
||||||
|
|
||||||
def load_resume_data_file(self):
|
def load_resume_data_file(self):
|
||||||
|
"""Loads the resume data from file for all the torrents
|
||||||
|
:returns: resume_data
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
filename = "torrents.fastresume"
|
filename = "torrents.fastresume"
|
||||||
filepath = os.path.join(self.state_dir, filename)
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
filepath_bak = filepath + ".bak"
|
filepath_bak = filepath + ".bak"
|
||||||
|
@ -831,7 +762,7 @@ class TorrentManager(component.Component):
|
||||||
try:
|
try:
|
||||||
with open(_filepath, "rb") as _file:
|
with open(_filepath, "rb") as _file:
|
||||||
resume_data = lt.bdecode(_file.read())
|
resume_data = lt.bdecode(_file.read())
|
||||||
except (IOError, EOFError, RuntimeError), ex:
|
except (IOError, EOFError, RuntimeError) as ex:
|
||||||
log.warning("Unable to load %s: %s", _filepath, ex)
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
resume_data = None
|
resume_data = None
|
||||||
else:
|
else:
|
||||||
|
@ -911,28 +842,29 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
def on_set_max_connections_per_torrent(self, key, value):
|
def on_set_max_connections_per_torrent(self, key, value):
|
||||||
"""Sets the per-torrent connection limit"""
|
"""Sets the per-torrent connection limit"""
|
||||||
log.debug("max_connections_per_torrent set to %s..", value)
|
log.debug("max_connections_per_torrent set to %s...", value)
|
||||||
for key in self.torrents.keys():
|
for key in self.torrents.keys():
|
||||||
self.torrents[key].set_max_connections(value)
|
self.torrents[key].set_max_connections(value)
|
||||||
|
|
||||||
def on_set_max_upload_slots_per_torrent(self, key, value):
|
def on_set_max_upload_slots_per_torrent(self, key, value):
|
||||||
"""Sets the per-torrent upload slot limit"""
|
"""Sets the per-torrent upload slot limit"""
|
||||||
log.debug("max_upload_slots_per_torrent set to %s..", value)
|
log.debug("max_upload_slots_per_torrent set to %s...", value)
|
||||||
for key in self.torrents.keys():
|
for key in self.torrents.keys():
|
||||||
self.torrents[key].set_max_upload_slots(value)
|
self.torrents[key].set_max_upload_slots(value)
|
||||||
|
|
||||||
def on_set_max_upload_speed_per_torrent(self, key, value):
|
def on_set_max_upload_speed_per_torrent(self, key, value):
|
||||||
log.debug("max_upload_speed_per_torrent set to %s..", value)
|
log.debug("max_upload_speed_per_torrent set to %s...", value)
|
||||||
for key in self.torrents.keys():
|
for key in self.torrents.keys():
|
||||||
self.torrents[key].set_max_upload_speed(value)
|
self.torrents[key].set_max_upload_speed(value)
|
||||||
|
|
||||||
def on_set_max_download_speed_per_torrent(self, key, value):
|
def on_set_max_download_speed_per_torrent(self, key, value):
|
||||||
log.debug("max_download_speed_per_torrent set to %s..", value)
|
log.debug("max_download_speed_per_torrent set to %s...", value)
|
||||||
for key in self.torrents.keys():
|
for key in self.torrents.keys():
|
||||||
self.torrents[key].set_max_download_speed(value)
|
self.torrents[key].set_max_download_speed(value)
|
||||||
|
|
||||||
## Alert handlers ##
|
## Alert handlers ##
|
||||||
def on_alert_torrent_finished(self, alert):
|
def on_alert_torrent_finished(self, alert):
|
||||||
|
"""Alert handler for libtorrent torrent_finished_alert"""
|
||||||
log.debug("on_alert_torrent_finished")
|
log.debug("on_alert_torrent_finished")
|
||||||
try:
|
try:
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
|
@ -944,6 +876,7 @@ class TorrentManager(component.Component):
|
||||||
# If total_download is 0, do not move, it's likely the torrent wasn't downloaded, but just added.
|
# If total_download is 0, do not move, it's likely the torrent wasn't downloaded, but just added.
|
||||||
total_download = torrent.get_status(["total_payload_download"])["total_payload_download"]
|
total_download = torrent.get_status(["total_payload_download"])["total_payload_download"]
|
||||||
|
|
||||||
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s",
|
log.debug("Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s",
|
||||||
torrent.is_finished, total_download, torrent.options["move_completed"],
|
torrent.is_finished, total_download, torrent.options["move_completed"],
|
||||||
torrent.options["move_completed_path"])
|
torrent.options["move_completed_path"])
|
||||||
|
@ -956,6 +889,7 @@ class TorrentManager(component.Component):
|
||||||
torrent.update_state()
|
torrent.update_state()
|
||||||
if not torrent.is_finished and total_download:
|
if not torrent.is_finished and total_download:
|
||||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||||
|
|
||||||
torrent.is_finished = True
|
torrent.is_finished = True
|
||||||
|
|
||||||
# Torrent is no longer part of the queue
|
# Torrent is no longer part of the queue
|
||||||
|
@ -963,6 +897,7 @@ class TorrentManager(component.Component):
|
||||||
self.queued_torrents.remove(torrent_id)
|
self.queued_torrents.remove(torrent_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Sometimes libtorrent fires a TorrentFinishedEvent twice
|
# Sometimes libtorrent fires a TorrentFinishedEvent twice
|
||||||
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("%s isn't in queued torrents set?", torrent_id)
|
log.debug("%s isn't in queued torrents set?", torrent_id)
|
||||||
|
|
||||||
# Only save resume data if it was actually downloaded something. Helps
|
# Only save resume data if it was actually downloaded something. Helps
|
||||||
|
@ -974,12 +909,13 @@ class TorrentManager(component.Component):
|
||||||
self.save_resume_data((torrent_id, ))
|
self.save_resume_data((torrent_id, ))
|
||||||
|
|
||||||
def on_alert_torrent_paused(self, alert):
|
def on_alert_torrent_paused(self, alert):
|
||||||
|
"""Alert handler for libtorrent torrent_paused_alert"""
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("on_alert_torrent_paused")
|
log.debug("on_alert_torrent_paused")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
except:
|
torrent = self.torrents[torrent_id]
|
||||||
|
except (RuntimeError, KeyError):
|
||||||
return
|
return
|
||||||
# Set the torrent state
|
# Set the torrent state
|
||||||
old_state = torrent.state
|
old_state = torrent.state
|
||||||
|
@ -992,11 +928,12 @@ class TorrentManager(component.Component):
|
||||||
self.save_resume_data((torrent_id,))
|
self.save_resume_data((torrent_id,))
|
||||||
|
|
||||||
def on_alert_torrent_checked(self, alert):
|
def on_alert_torrent_checked(self, alert):
|
||||||
|
"""Alert handler for libtorrent torrent_checked_alert"""
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("on_alert_torrent_checked")
|
log.debug("on_alert_torrent_checked")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check to see if we're forcing a recheck and set it back to paused
|
# Check to see if we're forcing a recheck and set it back to paused
|
||||||
|
@ -1010,11 +947,12 @@ class TorrentManager(component.Component):
|
||||||
torrent.update_state()
|
torrent.update_state()
|
||||||
|
|
||||||
def on_alert_tracker_reply(self, alert):
|
def on_alert_tracker_reply(self, alert):
|
||||||
|
"""Alert handler for libtorrent tracker_reply_alert"""
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("on_alert_tracker_reply: %s", decode_string(alert.message()))
|
log.debug("on_alert_tracker_reply: %s", decode_string(alert.message()))
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set the tracker status for the torrent
|
# Set the tracker status for the torrent
|
||||||
|
@ -1027,50 +965,55 @@ class TorrentManager(component.Component):
|
||||||
torrent.scrape_tracker()
|
torrent.scrape_tracker()
|
||||||
|
|
||||||
def on_alert_tracker_announce(self, alert):
|
def on_alert_tracker_announce(self, alert):
|
||||||
|
"""Alert handler for libtorrent tracker_announce_alert"""
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("on_alert_tracker_announce")
|
log.debug("on_alert_tracker_announce")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set the tracker status for the torrent
|
# Set the tracker status for the torrent
|
||||||
torrent.set_tracker_status(_("Announce Sent"))
|
torrent.set_tracker_status(_("Announce Sent"))
|
||||||
|
|
||||||
def on_alert_tracker_warning(self, alert):
|
def on_alert_tracker_warning(self, alert):
|
||||||
|
"""Alert handler for libtorrent tracker_warning_alert"""
|
||||||
log.debug("on_alert_tracker_warning")
|
log.debug("on_alert_tracker_warning")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
tracker_status = '%s: %s' % (_("Warning"), decode_string(alert.message()))
|
tracker_status = '%s: %s' % (_("Warning"), decode_string(alert.message()))
|
||||||
# Set the tracker status for the torrent
|
# Set the tracker status for the torrent
|
||||||
torrent.set_tracker_status(tracker_status)
|
torrent.set_tracker_status(tracker_status)
|
||||||
|
|
||||||
def on_alert_tracker_error(self, alert):
|
def on_alert_tracker_error(self, alert):
|
||||||
|
"""Alert handler for libtorrent tracker_error_alert"""
|
||||||
log.debug("on_alert_tracker_error")
|
log.debug("on_alert_tracker_error")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
tracker_status = "%s: %s" % (_("Error"), decode_string(alert.msg))
|
tracker_status = "%s: %s" % (_("Error"), decode_string(alert.msg))
|
||||||
torrent.set_tracker_status(tracker_status)
|
torrent.set_tracker_status(tracker_status)
|
||||||
|
|
||||||
def on_alert_storage_moved(self, alert):
|
def on_alert_storage_moved(self, alert):
|
||||||
|
"""Alert handler for libtorrent storage_moved_alert"""
|
||||||
log.debug("on_alert_storage_moved")
|
log.debug("on_alert_storage_moved")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
torrent.set_save_path(os.path.normpath(alert.handle.save_path()))
|
torrent.set_save_path(os.path.normpath(alert.handle.save_path()))
|
||||||
torrent.set_move_completed(False)
|
torrent.set_move_completed(False)
|
||||||
|
|
||||||
def on_alert_torrent_resumed(self, alert):
|
def on_alert_torrent_resumed(self, alert):
|
||||||
|
"""Alert handler for libtorrent torrent_resumed_alert"""
|
||||||
log.debug("on_alert_torrent_resumed")
|
log.debug("on_alert_torrent_resumed")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
except:
|
torrent = self.torrents[torrent_id]
|
||||||
|
except (RuntimeError, KeyError):
|
||||||
return
|
return
|
||||||
old_state = torrent.state
|
old_state = torrent.state
|
||||||
torrent.update_state()
|
torrent.update_state()
|
||||||
|
@ -1080,12 +1023,15 @@ class TorrentManager(component.Component):
|
||||||
component.get("EventManager").emit(TorrentResumedEvent(torrent_id))
|
component.get("EventManager").emit(TorrentResumedEvent(torrent_id))
|
||||||
|
|
||||||
def on_alert_state_changed(self, alert):
|
def on_alert_state_changed(self, alert):
|
||||||
|
"""Alert handler for libtorrent state_changed_alert
|
||||||
|
Emits a TorrentStateChangedEvent if state has changed
|
||||||
|
"""
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("on_alert_state_changed")
|
log.debug("on_alert_state_changed")
|
||||||
try:
|
try:
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
torrent = self.torrents[torrent_id]
|
torrent = self.torrents[torrent_id]
|
||||||
except:
|
except (RuntimeError, KeyError):
|
||||||
return
|
return
|
||||||
|
|
||||||
old_state = torrent.state
|
old_state = torrent.state
|
||||||
|
@ -1101,9 +1047,13 @@ class TorrentManager(component.Component):
|
||||||
component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state))
|
component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state))
|
||||||
|
|
||||||
def on_alert_save_resume_data(self, alert):
|
def on_alert_save_resume_data(self, alert):
|
||||||
|
"""Alert handler for libtorrent save_resume_data_alert"""
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
log.debug("on_alert_save_resume_data")
|
log.debug("on_alert_save_resume_data")
|
||||||
|
try:
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
|
except RuntimeError:
|
||||||
|
return
|
||||||
|
|
||||||
if torrent_id in self.torrents:
|
if torrent_id in self.torrents:
|
||||||
# Libtorrent in add_torrent() expects resume_data to be bencoded
|
# Libtorrent in add_torrent() expects resume_data to be bencoded
|
||||||
|
@ -1113,19 +1063,26 @@ class TorrentManager(component.Component):
|
||||||
self.waiting_on_resume_data[torrent_id].callback(None)
|
self.waiting_on_resume_data[torrent_id].callback(None)
|
||||||
|
|
||||||
def on_alert_save_resume_data_failed(self, alert):
|
def on_alert_save_resume_data_failed(self, alert):
|
||||||
|
"""Alert handler for libtorrent save_resume_data_failed_alert"""
|
||||||
log.debug("on_alert_save_resume_data_failed: %s", decode_string(alert.message()))
|
log.debug("on_alert_save_resume_data_failed: %s", decode_string(alert.message()))
|
||||||
|
try:
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
|
except RuntimeError:
|
||||||
|
return
|
||||||
|
|
||||||
if torrent_id in self.waiting_on_resume_data:
|
if torrent_id in self.waiting_on_resume_data:
|
||||||
self.waiting_on_resume_data[torrent_id].errback(Exception(decode_string(alert.message())))
|
self.waiting_on_resume_data[torrent_id].errback(Exception(decode_string(alert.message())))
|
||||||
|
|
||||||
def on_alert_file_renamed(self, alert):
|
def on_alert_file_renamed(self, alert):
|
||||||
|
"""Alert handler for libtorrent file_renamed_alert
|
||||||
|
Emits a TorrentFileCompletedEvent for renamed files
|
||||||
|
"""
|
||||||
log.debug("on_alert_file_renamed")
|
log.debug("on_alert_file_renamed")
|
||||||
log.debug("index: %s name: %s", alert.index, decode_string(alert.name))
|
log.debug("index: %s name: %s", alert.index, decode_string(alert.name))
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
except:
|
torrent = self.torrents[torrent_id]
|
||||||
|
except (RuntimeError, KeyError):
|
||||||
return
|
return
|
||||||
|
|
||||||
# We need to see if this file index is in a waiting_on_folder dict
|
# We need to see if this file index is in a waiting_on_folder dict
|
||||||
|
@ -1139,30 +1096,53 @@ class TorrentManager(component.Component):
|
||||||
self.save_resume_data((torrent_id,))
|
self.save_resume_data((torrent_id,))
|
||||||
|
|
||||||
def on_alert_metadata_received(self, alert):
|
def on_alert_metadata_received(self, alert):
|
||||||
|
"""Alert handler for libtorrent metadata_received_alert"""
|
||||||
log.debug("on_alert_metadata_received")
|
log.debug("on_alert_metadata_received")
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
torrent.on_metadata_received()
|
torrent.on_metadata_received()
|
||||||
|
|
||||||
def on_alert_file_error(self, alert):
|
def on_alert_file_error(self, alert):
|
||||||
|
"""Alert handler for libtorrent file_error_alert"""
|
||||||
log.debug("on_alert_file_error: %s", decode_string(alert.message()))
|
log.debug("on_alert_file_error: %s", decode_string(alert.message()))
|
||||||
try:
|
try:
|
||||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
torrent.update_state()
|
torrent.update_state()
|
||||||
|
|
||||||
def on_alert_file_completed(self, alert):
|
def on_alert_file_completed(self, alert):
|
||||||
|
"""Alert handler for libtorrent file_completed_alert
|
||||||
|
Emits a TorrentFileCompletedEvent when an individual file completes downloading
|
||||||
|
"""
|
||||||
log.debug("file_completed_alert: %s", decode_string(alert.message()))
|
log.debug("file_completed_alert: %s", decode_string(alert.message()))
|
||||||
try:
|
try:
|
||||||
torrent_id = str(alert.handle.info_hash())
|
torrent_id = str(alert.handle.info_hash())
|
||||||
except:
|
except RuntimeError:
|
||||||
return
|
return
|
||||||
component.get("EventManager").emit(
|
component.get("EventManager").emit(
|
||||||
TorrentFileCompletedEvent(torrent_id, alert.index))
|
TorrentFileCompletedEvent(torrent_id, alert.index))
|
||||||
|
|
||||||
|
def on_alert_state_update(self, alert):
|
||||||
|
"""Alert handler for libtorrent state_update_alert
|
||||||
|
Result of a session.post_torrent_updates() call and contains the torrent status
|
||||||
|
of all torrents that changed since last time this was posted.
|
||||||
|
"""
|
||||||
|
log.debug("on_status_notification: %s", alert.message())
|
||||||
|
self.last_state_update_alert_ts = time.time()
|
||||||
|
|
||||||
|
for t_status in alert.status:
|
||||||
|
try:
|
||||||
|
torrent_id = str(t_status.info_hash)
|
||||||
|
except RuntimeError:
|
||||||
|
continue
|
||||||
|
if torrent_id in self.torrents:
|
||||||
|
self.torrents[torrent_id].update_status(t_status)
|
||||||
|
|
||||||
|
self.handle_torrents_status_callback(self.torrents_status_requests.pop())
|
||||||
|
|
||||||
def separate_keys(self, keys, torrent_ids):
|
def separate_keys(self, keys, torrent_ids):
|
||||||
"""Separates the input keys into keys for the Torrent class
|
"""Separates the input keys into keys for the Torrent class
|
||||||
and keys for plugins.
|
and keys for plugins.
|
||||||
|
@ -1176,17 +1156,6 @@ class TorrentManager(component.Component):
|
||||||
return torrent_keys, leftover_keys
|
return torrent_keys, leftover_keys
|
||||||
return [], []
|
return [], []
|
||||||
|
|
||||||
def on_alert_state_update(self, alert):
|
|
||||||
log.debug("on_status_notification: %s", alert.message())
|
|
||||||
self.last_state_update_alert_ts = time.time()
|
|
||||||
|
|
||||||
for s in alert.status:
|
|
||||||
torrent_id = str(s.info_hash)
|
|
||||||
if torrent_id in self.torrents:
|
|
||||||
self.torrents[torrent_id].update_status(s)
|
|
||||||
|
|
||||||
self.handle_torrents_status_callback(self.torrents_status_requests.pop())
|
|
||||||
|
|
||||||
def handle_torrents_status_callback(self, status_request):
|
def handle_torrents_status_callback(self, status_request):
|
||||||
"""
|
"""
|
||||||
Builds the status dictionary with the values from the Torrent.
|
Builds the status dictionary with the values from the Torrent.
|
||||||
|
|
Loading…
Reference in New Issue