diff --git a/DEPENDS.md b/DEPENDS.md index 197556dd0..41a7ec09f 100644 --- a/DEPENDS.md +++ b/DEPENDS.md @@ -41,7 +41,7 @@ All modules will require the [common](#common) section dependencies. ## Core (deluged daemon) -- [libtorrent] _>= 1.1.1_ +- [libtorrent] _>= 1.2.0_ - [GeoIP] or [pygeoip] - Optional: IP address country lookup. (_Debian: `python-geoip`_) ## GTK UI diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index bae962770..55e825727 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -80,7 +80,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. @@ -95,18 +95,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), } ) @@ -236,7 +236,7 @@ class Torrent: self.magnet = magnet self.status = self.handle.status() - 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() @@ -275,6 +275,18 @@ class Torrent: 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 @@ -359,7 +371,7 @@ class Torrent: """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: @@ -391,7 +403,7 @@ class Torrent: 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 @@ -424,7 +436,10 @@ class Torrent: 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. @@ -434,7 +449,10 @@ class Torrent: """ 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): @@ -444,7 +462,10 @@ class Torrent: 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. @@ -505,7 +526,7 @@ class Torrent: 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. @@ -632,7 +653,10 @@ class Torrent: 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' @@ -686,7 +710,10 @@ class Torrent: session can resume. """ status = self.handle.status() - self.handle.auto_managed(False) + 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() @@ -700,7 +727,10 @@ class Torrent: 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: @@ -843,7 +873,7 @@ class Torrent: 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']: @@ -932,10 +962,10 @@ class Torrent: 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 @@ -1152,7 +1182,10 @@ class Torrent: """ # 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: @@ -1187,7 +1220,10 @@ class Torrent: 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: diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index ef1cabe6b..753bc894c 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -360,10 +360,9 @@ class TorrentManager(component.Component): if torrent_id in self.prefetching_metadata: return self.prefetching_metadata[torrent_id].defer - add_torrent_params = {} - add_torrent_params['save_path'] = gettempdir() - add_torrent_params['url'] = magnet.strip().encode('utf8') - add_torrent_params['flags'] = ( + add_torrent_params = lt.parse_magnet_uri(magnet) + add_torrent_params.save_path = gettempdir() + add_torrent_params.flags = ( ( LT_DEFAULT_ADD_TORRENT_FLAGS | lt.add_torrent_params_flags_t.flag_duplicate_is_error @@ -401,7 +400,10 @@ class TorrentManager(component.Component): metadata = b'' if isinstance(torrent_info, lt.torrent_info): log.debug('prefetch metadata received') - metadata = torrent_info.metadata() + if VersionSplit(LT_VERSION) < VersionSplit('2.0.0.0'): + metadata = torrent_info.metadata() + else: + metadata = torrent_info.info_section() return torrent_id, b64encode(metadata) @@ -436,14 +438,10 @@ class TorrentManager(component.Component): elif magnet: magnet_info = get_magnet_info(magnet) if magnet_info: - add_torrent_params['url'] = magnet.strip().encode('utf8') add_torrent_params['name'] = magnet_info['name'] torrent_id = magnet_info['info_hash'] # Workaround lt 1.2 bug for magnet resume data with no metadata - if resume_data and VersionSplit(LT_VERSION) >= VersionSplit('1.2.10.0'): - add_torrent_params['info_hash'] = bytes( - bytearray.fromhex(torrent_id) - ) + add_torrent_params['info_hash'] = bytes(bytearray.fromhex(torrent_id)) else: raise AddTorrentError( 'Unable to add magnet, invalid magnet info: %s' % magnet @@ -1399,22 +1397,18 @@ class TorrentManager(component.Component): log.debug( 'Tracker Error Alert: %s [%s]', decode_bytes(alert.message()), error_message ) - if VersionSplit(LT_VERSION) >= VersionSplit('1.2.0.0'): - # libtorrent 1.2 added endpoint struct to each tracker. to prevent false updates - # we will need to verify that at least one endpoint to the errored tracker is working - for tracker in torrent.handle.trackers(): - if tracker['url'] == alert.url: - if any( - endpoint['last_error']['value'] == 0 - for endpoint in tracker['endpoints'] - ): - torrent.set_tracker_status('Announce OK') - else: - torrent.set_tracker_status('Error: ' + error_message) - break - else: - # preserve old functionality for libtorrent < 1.2 - torrent.set_tracker_status('Error: ' + error_message) + # libtorrent 1.2 added endpoint struct to each tracker. to prevent false updates + # we will need to verify that at least one endpoint to the errored tracker is working + for tracker in torrent.handle.trackers(): + if tracker['url'] == alert.url: + if any( + endpoint['last_error']['value'] == 0 + for endpoint in tracker['endpoints'] + ): + torrent.set_tracker_status('Announce OK') + else: + torrent.set_tracker_status('Error: ' + error_message) + break def on_alert_storage_moved(self, alert): """Alert handler for libtorrent storage_moved_alert""" @@ -1488,7 +1482,9 @@ class TorrentManager(component.Component): return if torrent_id in self.torrents: # libtorrent add_torrent expects bencoded resume_data. - self.resume_data[torrent_id] = lt.bencode(alert.resume_data) + self.resume_data[torrent_id] = lt.bencode( + lt.write_resume_data(alert.params) + ) if torrent_id in self.waiting_on_resume_data: self.waiting_on_resume_data[torrent_id].callback(None) diff --git a/deluge/tests/test_torrent.py b/deluge/tests/test_torrent.py index 811fc3f5b..6c07531dd 100644 --- a/deluge/tests/test_torrent.py +++ b/deluge/tests/test_torrent.py @@ -109,7 +109,7 @@ class TestTorrent(BaseTestCase): # updates and will return old value. Also need to remove a priority # value as one file is much smaller than piece size so doesn't show. await prios_set # Delay to wait for alert from lt - piece_prio = handle.piece_priorities() + piece_prio = handle.get_piece_priorities() result = all(p in piece_prio for p in [3, 2, 0, 5, 6, 7]) assert result @@ -151,9 +151,9 @@ class TestTorrent(BaseTestCase): handle = self.session.add_torrent(atp) self.torrent = Torrent(handle, {}) - priorities_original = handle.piece_priorities() + priorities_original = handle.get_piece_priorities() self.torrent.set_prioritize_first_last_pieces(True) - priorities = handle.piece_priorities() + priorities = handle.get_piece_priorities() # The length of the list of new priorites is the same as the original assert len(priorities_original) == len(priorities) @@ -175,7 +175,7 @@ class TestTorrent(BaseTestCase): self.torrent.set_prioritize_first_last_pieces(True) # Reset pirorities self.torrent.set_prioritize_first_last_pieces(False) - priorities = handle.piece_priorities() + priorities = handle.get_piece_priorities() # Test the priority of the prioritized pieces for i in priorities: diff --git a/deluge/ui/console/cmdline/commands/status.py b/deluge/ui/console/cmdline/commands/status.py index 2ca1cda2f..05c9796ce 100644 --- a/deluge/ui/console/cmdline/commands/status.py +++ b/deluge/ui/console/cmdline/commands/status.py @@ -62,7 +62,12 @@ class Command(BaseCommand): deferreds = [] ds = client.core.get_session_status( - ['num_peers', 'payload_upload_rate', 'payload_download_rate', 'dht_nodes'] + [ + 'peer.num_peers_connected', + 'payload_upload_rate', + 'payload_download_rate', + 'dht.dht_nodes', + ] ) ds.addCallback(on_session_status) deferreds.append(ds) @@ -92,7 +97,7 @@ class Command(BaseCommand): '{!info!}Total download: %s' % fspeed(self.status['payload_download_rate']) ) - self.console.write('{!info!}DHT Nodes: %i' % self.status['dht_nodes']) + self.console.write('{!info!}DHT Nodes: %i' % self.status['dht.dht_nodes']) if isinstance(self.torrents, int): if self.torrents == -2: diff --git a/deluge/ui/console/modes/torrentdetail.py b/deluge/ui/console/modes/torrentdetail.py index eab9dfef1..bac9b9472 100644 --- a/deluge/ui/console/modes/torrentdetail.py +++ b/deluge/ui/console/modes/torrentdetail.py @@ -541,13 +541,13 @@ class TorrentDetail(BaseMode, PopupsHandler): # Seed/peer info s = '{{!info!}}{}:{{!green!}} {} {{!input!}}({})'.format( torrent_data_fields['seeds']['name'], - status['num_seeds'], + status['peer.num_peers_connected'], status['total_seeds'], ) row = self.add_string(row, s) s = '{{!info!}}{}:{{!red!}} {} {{!input!}}({})'.format( torrent_data_fields['peers']['name'], - status['num_peers'], + status['peer.num_peers_connected'], status['total_peers'], ) row = self.add_string(row, s) diff --git a/deluge/ui/console/widgets/statusbars.py b/deluge/ui/console/widgets/statusbars.py index 57128eabf..1b9173707 100644 --- a/deluge/ui/console/widgets/statusbars.py +++ b/deluge/ui/console/widgets/statusbars.py @@ -36,19 +36,23 @@ class StatusBars(component.Component): def on_get_session_status(status): self.upload = deluge.common.fsize(status['payload_upload_rate']) self.download = deluge.common.fsize(status['payload_download_rate']) - self.connections = status['num_peers'] + self.connections = status['peer.num_peers_connected'] if 'dht_nodes' in status: - self.dht = status['dht_nodes'] + self.dht = status['dht.dht_nodes'] self.update_statusbars() def on_get_external_ip(external_ip): self.external_ip = external_ip - keys = ['num_peers', 'payload_upload_rate', 'payload_download_rate'] + keys = [ + 'peer.num_peers_connected', + 'payload_upload_rate', + 'payload_download_rate', + ] if self.config['dht']: - keys.append('dht_nodes') + keys.append('dht.dht_nodes') client.core.get_session_status(keys).addCallback(on_get_session_status) client.core.get_external_ip().addCallback(on_get_external_ip) diff --git a/deluge/ui/gtk3/statusbar.py b/deluge/ui/gtk3/statusbar.py index 3001a49ee..0a2e80095 100644 --- a/deluge/ui/gtk3/statusbar.py +++ b/deluge/ui/gtk3/statusbar.py @@ -317,18 +317,22 @@ class StatusBar(component.Component): def send_status_request(self): # Sends an async request for data from the core keys = [ - 'num_peers', + 'peer.num_peers_connected', 'upload_rate', 'download_rate', 'payload_upload_rate', 'payload_download_rate', + 'net.sent_bytes', + 'net.recv_bytes', + 'net.sent_payload_bytes', + 'net.recv_payload_bytes', ] if self.dht_status: - keys.append('dht_nodes') + keys.append('dht.dht_nodes') if not self.health: - keys.append('has_incoming_connections') + keys.append('net.has_incoming_connections') client.core.get_session_status(keys).addCallback(self._on_get_session_status) client.core.get_free_space().addCallback(self._on_get_free_space) @@ -367,18 +371,18 @@ class StatusBar(component.Component): self.upload_protocol_rate = ( status['upload_rate'] - status['payload_upload_rate'] ) // 1024 - self.num_connections = status['num_peers'] + self.num_connections = status['peer.num_peers_connected'] self.update_download_label() self.update_upload_label() self.update_traffic_label() self.update_connections_label() - if 'dht_nodes' in status: - self.dht_nodes = status['dht_nodes'] + if 'dht.dht_nodes' in status: + self.dht_nodes = status['dht.dht_nodes'] self.update_dht_label() - if 'has_incoming_connections' in status: - self.health = status['has_incoming_connections'] + if 'net.has_incoming_connections' in status: + self.health = status['net.has_incoming_connections'] if self.health: self.remove_item(self.health_item) diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py index aa15d5ac6..e16c440ed 100644 --- a/deluge/ui/web/json_api.py +++ b/deluge/ui/web/json_api.py @@ -515,7 +515,7 @@ class WebApi(JSONComponent): return d def got_stats(stats): - ui_info['stats']['num_connections'] = stats['num_peers'] + ui_info['stats']['num_connections'] = stats['peer.num_peers_connected'] ui_info['stats']['upload_rate'] = stats['payload_upload_rate'] ui_info['stats']['download_rate'] = stats['payload_download_rate'] ui_info['stats']['download_protocol_rate'] = ( @@ -524,9 +524,9 @@ class WebApi(JSONComponent): ui_info['stats']['upload_protocol_rate'] = ( stats['upload_rate'] - stats['payload_upload_rate'] ) - ui_info['stats']['dht_nodes'] = stats['dht_nodes'] + ui_info['stats']['dht_nodes'] = stats['dht.dht_nodes'] ui_info['stats']['has_incoming_connections'] = stats[ - 'has_incoming_connections' + 'net.has_incoming_connections' ] def got_filters(filters): @@ -552,13 +552,13 @@ class WebApi(JSONComponent): d3 = client.core.get_session_status( [ - 'num_peers', + 'peer.num_peers_connected', 'payload_download_rate', 'payload_upload_rate', 'download_rate', 'upload_rate', - 'dht_nodes', - 'has_incoming_connections', + 'dht.dht_nodes', + 'net.has_incoming_connections', ] ) d3.addCallback(got_stats)