diff options
Diffstat (limited to 'deluge/core/torrent.py')
-rw-r--r-- | deluge/core/torrent.py | 148 |
1 files changed, 97 insertions, 51 deletions
diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index ede5200f0..57ec26f37 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -14,11 +13,12 @@ Attributes: """ -from __future__ import division, unicode_literals - import logging import os import socket +import time +from typing import Optional +from urllib.parse import urlparse from twisted.internet.defer import Deferred, DeferredList @@ -34,18 +34,6 @@ from deluge.event import ( TorrentTrackerStatusEvent, ) -try: - from urllib.parse import urlparse -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - -try: - from future_builtins import zip -except ImportError: - # Ignore on Py3. - pass - log = logging.getLogger(__name__) LT_TORRENT_STATE_MAP = { @@ -94,7 +82,7 @@ def convert_lt_files(files): """Indexes and decodes files from libtorrent get_files(). Args: - files (list): The libtorrent torrent files. + files (file_storage): The libtorrent torrent files. Returns: list of dict: The files. @@ -109,18 +97,18 @@ def convert_lt_files(files): } """ filelist = [] - for index, _file in enumerate(files): + for index in range(files.num_files()): try: - file_path = _file.path.decode('utf8') + file_path = files.file_path(index).decode('utf8') except AttributeError: - file_path = _file.path + file_path = files.file_path(index) filelist.append( { 'index': index, 'path': file_path.replace('\\', '/'), - 'size': _file.size, - 'offset': _file.offset, + 'size': files.file_size(index), + 'offset': files.file_offset(index), } ) @@ -161,7 +149,7 @@ class TorrentOptions(dict): """ def __init__(self): - super(TorrentOptions, self).__init__() + super().__init__() config = ConfigManager('core.conf').config options_conf_map = { 'add_paused': 'add_paused', @@ -191,14 +179,14 @@ class TorrentOptions(dict): self['seed_mode'] = False -class TorrentError(object): +class TorrentError: 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: """Torrent holds information about torrents added to the libtorrent session. Args: @@ -248,9 +236,10 @@ class Torrent(object): self.handle = handle self.magnet = magnet - self.status = self.handle.status() + self._status: Optional['lt.torrent_status'] = None + self._status_last_update: float = 0.0 - self.torrent_info = self.handle.get_torrent_info() + self.torrent_info = self.handle.torrent_file() self.has_metadata = self.status.has_metadata self.options = TorrentOptions() @@ -281,7 +270,6 @@ class Torrent(object): self.prev_status = {} self.waiting_on_folder_rename = [] - self.update_status(self.handle.status()) self._create_status_funcs() self.set_options(self.options) self.update_state() @@ -289,6 +277,18 @@ class Torrent(object): if log.isEnabledFor(logging.DEBUG): log.debug('Torrent object created.') + def _set_handle_flags(self, flag: lt.torrent_flags, set_flag: bool): + """set or unset a flag to the lt handle + + Args: + flag (lt.torrent_flags): the flag to set/unset + set_flag (bool): True for setting the flag, False for unsetting it + """ + if set_flag: + self.handle.set_flags(flag) + else: + self.handle.unset_flags(flag) + def on_metadata_received(self): """Process the metadata received alert for this torrent""" self.has_metadata = True @@ -373,7 +373,7 @@ class Torrent(object): """Sets maximum download speed for this torrent. Args: - m_up_speed (float): Maximum download speed in KiB/s. + m_down_speed (float): Maximum download speed in KiB/s. """ self.options['max_download_speed'] = m_down_speed if m_down_speed < 0: @@ -405,7 +405,7 @@ class Torrent(object): return # A list of priorities for each piece in the torrent - priorities = self.handle.piece_priorities() + priorities = self.handle.get_piece_priorities() def get_file_piece(idx, byte_offset): return self.torrent_info.map_file(idx, byte_offset, 0).piece @@ -438,7 +438,10 @@ class Torrent(object): sequential (bool): Enable sequential downloading. """ self.options['sequential_download'] = sequential - self.handle.set_sequential_download(sequential) + self._set_handle_flags( + flag=lt.torrent_flags.sequential_download, + set_flag=sequential, + ) def set_auto_managed(self, auto_managed): """Set auto managed mode, i.e. will be started or queued automatically. @@ -448,7 +451,10 @@ class Torrent(object): """ self.options['auto_managed'] = auto_managed if not (self.status.paused and not self.status.auto_managed): - self.handle.auto_managed(auto_managed) + self._set_handle_flags( + flag=lt.torrent_flags.auto_managed, + set_flag=auto_managed, + ) self.update_state() def set_super_seeding(self, super_seeding): @@ -458,7 +464,10 @@ class Torrent(object): super_seeding (bool): Enable super seeding. """ self.options['super_seeding'] = super_seeding - self.handle.super_seeding(super_seeding) + self._set_handle_flags( + flag=lt.torrent_flags.super_seeding, + set_flag=super_seeding, + ) def set_stop_ratio(self, stop_ratio): """The seeding ratio to stop (or remove) the torrent at. @@ -519,7 +528,7 @@ class Torrent(object): self.handle.prioritize_files(file_priorities) else: log.debug('Unable to set new file priorities.') - file_priorities = self.handle.file_priorities() + file_priorities = self.handle.get_file_priorities() if 0 in self.options['file_priorities']: # Previously marked a file 'skip' so check for any 0's now >0. @@ -569,7 +578,7 @@ class Torrent(object): trackers (list of dicts): A list of trackers. """ if trackers is None: - self.trackers = [tracker for tracker in self.handle.trackers()] + self.trackers = list(self.handle.trackers()) self.tracker_host = None return @@ -634,7 +643,7 @@ class Torrent(object): def update_state(self): """Updates the state, based on libtorrent's torrent state""" - status = self.handle.status() + status = self.get_lt_status() session_paused = component.get('Core').session.is_paused() old_state = self.state self.set_status_message() @@ -646,7 +655,10 @@ class Torrent(object): elif status_error: self.state = 'Error' # auto-manage status will be reverted upon resuming. - self.handle.auto_managed(False) + self._set_handle_flags( + flag=lt.torrent_flags.auto_managed, + set_flag=False, + ) self.set_status_message(decode_bytes(status_error)) elif status.moving_storage: self.state = 'Moving' @@ -699,8 +711,11 @@ class Torrent(object): restart_to_resume (bool, optional): Prevent resuming clearing the error, only restarting session can resume. """ - status = self.handle.status() - self.handle.auto_managed(False) + status = self.get_lt_status() + self._set_handle_flags( + flag=lt.torrent_flags.auto_managed, + set_flag=False, + ) self.forced_error = TorrentError(message, status.paused, restart_to_resume) if not status.paused: self.handle.pause() @@ -714,7 +729,10 @@ class Torrent(object): 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._set_handle_flags( + flag=lt.torrent_flags.auto_managed, + set_flag=True, + ) self.forced_error = None self.set_status_message('OK') if update_state: @@ -838,7 +856,7 @@ class Torrent(object): 'client': client, 'country': country, 'down_speed': peer.payload_down_speed, - 'ip': '%s:%s' % (peer.ip[0], peer.ip[1]), + 'ip': f'{peer.ip[0]}:{peer.ip[1]}', 'progress': peer.progress, 'seed': peer.flags & peer.seed, 'up_speed': peer.payload_up_speed, @@ -857,7 +875,7 @@ class Torrent(object): def get_file_priorities(self): """Return the file priorities""" - if not self.handle.has_metadata(): + if not self.handle.status().has_metadata: return [] if not self.options['file_priorities']: @@ -910,7 +928,7 @@ class Torrent(object): # Check if hostname is an IP address and just return it if that's the case try: socket.inet_aton(host) - except socket.error: + except OSError: pass else: # This is an IP address because an exception wasn't raised @@ -946,10 +964,10 @@ class Torrent(object): if self.has_metadata: # Use the top-level folder as torrent name. - filename = decode_bytes(self.torrent_info.file_at(0).path) + filename = decode_bytes(self.torrent_info.files().file_path(0)) name = filename.replace('\\', '/', 1).split('/', 1)[0] else: - name = decode_bytes(self.handle.name()) + name = decode_bytes(self.handle.status().name) if not name: name = self.torrent_id @@ -1008,7 +1026,7 @@ class Torrent(object): dict: a dictionary of the status keys and their values """ if update: - self.update_status(self.handle.status()) + self.get_lt_status() if all_keys: keys = list(self.status_funcs) @@ -1038,13 +1056,35 @@ class Torrent(object): return status_dict - def update_status(self, status): + def get_lt_status(self) -> 'lt.torrent_status': + """Get the torrent status fresh, not from cache. + + This should be used when a guaranteed fresh status is needed rather than + `torrent.handle.status()` because it will update the cache as well. + """ + self.status = self.handle.status() + return self.status + + @property + def status(self) -> 'lt.torrent_status': + """Cached copy of the libtorrent status for this torrent. + + If it has not been updated within the last five seconds, it will be + automatically refreshed. + """ + if self._status_last_update < (time.time() - 5): + self.status = self.handle.status() + return self._status + + @status.setter + def status(self, status: 'lt.torrent_status') -> None: """Updates the cached status. Args: - status (libtorrent.torrent_status): a libtorrent torrent status + status: a libtorrent torrent status """ - self.status = status + self._status = status + self._status_last_update = time.time() def _create_status_funcs(self): """Creates the functions for getting torrent status""" @@ -1166,7 +1206,10 @@ class Torrent(object): """ # Turn off auto-management so the torrent will not be unpaused by lt queueing - self.handle.auto_managed(False) + self._set_handle_flags( + flag=lt.torrent_flags.auto_managed, + set_flag=False, + ) if self.state == 'Error': log.debug('Unable to pause torrent while in Error state') elif self.status.paused: @@ -1201,7 +1244,10 @@ class Torrent(object): else: # Check if torrent was originally being auto-managed. if self.options['auto_managed']: - self.handle.auto_managed(True) + self._set_handle_flags( + flag=lt.torrent_flags.auto_managed, + set_flag=True, + ) try: self.handle.resume() except RuntimeError as ex: @@ -1305,7 +1351,7 @@ class Torrent(object): try: with open(filepath, 'wb') as save_file: save_file.write(filedump) - except IOError as ex: + except OSError as ex: log.error('Unable to save torrent file to: %s', ex) filepath = os.path.join(get_config_dir(), 'state', self.torrent_id + '.torrent') |