diff --git a/src/tribler/core/components/libtorrent/conftest.py b/src/tribler/core/components/libtorrent/conftest.py index 86a7f5e6e8b..900b4debf11 100644 --- a/src/tribler/core/components/libtorrent/conftest.py +++ b/src/tribler/core/components/libtorrent/conftest.py @@ -69,7 +69,10 @@ def mock_lt_status(): lt_status.download_rate = 43 lt_status.total_upload = 100 lt_status.total_download = 200 - lt_status.all_time_upload = 100 + lt_status.total_payload_upload = 30 + lt_status.total_payload_download = 100 + lt_status.all_time_upload = 200 + lt_status.all_time_download = 1000 lt_status.total_done = 200 lt_status.list_peers = 10 lt_status.download_payload_rate = 10 diff --git a/src/tribler/core/components/libtorrent/download_manager/download.py b/src/tribler/core/components/libtorrent/download_manager/download.py index 58a6e8ff0f6..e62e50133c7 100644 --- a/src/tribler/core/components/libtorrent/download_manager/download.py +++ b/src/tribler/core/components/libtorrent/download_manager/download.py @@ -34,7 +34,7 @@ from tribler.core.utilities.notifier import Notifier from tribler.core.utilities.osutils import fix_filebasename from tribler.core.utilities.path_util import Path -from tribler.core.utilities.simpledefs import DOWNLOAD, DownloadStatus +from tribler.core.utilities.simpledefs import DownloadStatus from tribler.core.utilities.unicode import ensure_unicode, hexlify from tribler.core.utilities.utilities import bdecode_compat, safe_repr @@ -447,7 +447,7 @@ def on_torrent_finished_alert(self, alert: lt.torrent_finished_alert): self._logger.info(f'On torrent finished alert: {safe_repr(alert)}') self.update_lt_status(self.handle.status()) self.checkpoint() - downloaded = self.get_state().get_total_transferred(DOWNLOAD) + downloaded = self.get_state().total_download if downloaded > 0 and self.stream is not None and self.notifier is not None: name = self.tdef.get_name_as_unicode() infohash = self.tdef.get_infohash().hex() @@ -465,7 +465,7 @@ def _stop_if_finished(self): seeding_ratio = self.download_defaults.seeding_ratio seeding_time = self.download_defaults.seeding_time if (mode == 'never' or - (mode == 'ratio' and state.get_seeding_ratio() >= seeding_ratio) or + (mode == 'ratio' and state.get_all_time_ratio() >= seeding_ratio) or (mode == 'time' and state.get_seeding_time() >= seeding_time)): self.stop() @@ -510,7 +510,7 @@ def force_recheck(self): self.handle.resume() self.handle.force_recheck() - def get_state(self): + def get_state(self) -> DownloadState: """ Returns a snapshot of the current state of the download @return DownloadState """ diff --git a/src/tribler/core/components/libtorrent/download_manager/download_state.py b/src/tribler/core/components/libtorrent/download_manager/download_state.py index 3793189c0aa..ce4a4ece66d 100644 --- a/src/tribler/core/components/libtorrent/download_manager/download_state.py +++ b/src/tribler/core/components/libtorrent/download_manager/download_state.py @@ -4,7 +4,9 @@ Author(s): Arno Bakker """ import logging +from typing import Optional +from tribler.core.components.libtorrent.utils.libtorrent_helper import libtorrent from tribler.core.utilities.simpledefs import ( DownloadStatus, UPLOAD, ) @@ -31,7 +33,7 @@ class DownloadState: cf. libtorrent torrent_status """ - def __init__(self, download, lt_status, error): + def __init__(self, download, lt_status: Optional[libtorrent.torrent_status], error): """ Internal constructor. @param download The download this state belongs too. @@ -97,21 +99,66 @@ def get_current_payload_speed(self, direct): return self.lt_status.upload_payload_rate return self.lt_status.download_payload_rate - def get_total_transferred(self, direct): + @property + def all_time_upload(self) -> int: + """ Returns accumulated upload payload byte counter. + It is saved in and restored from resume data to keep totals across sessions. """ - Returns the total amount of up or downloaded bytes. - @return The amount in bytes. + if not self.lt_status: + return 0 + return self.lt_status.all_time_upload + + @property + def all_time_download(self) -> int: + """ Returns accumulated download payload byte counter. + It is saved in and restored from resume data to keep totals across sessions. + """ + if not self.lt_status: + return 0 + return self.lt_status.all_time_download + + @property + def total_upload(self) -> int: + """ Returns the number of bytes uploaded to all peers, accumulated, this session only + """ + if not self.lt_status: + return 0 + return self.lt_status.total_upload + + @property + def total_download(self) -> int: + """ Returns the number of bytes downloaded to all peers, accumulated, this session only """ if not self.lt_status: return 0 - elif direct == UPLOAD: - return self.lt_status.total_upload return self.lt_status.total_download - def get_seeding_ratio(self): - if self.lt_status and self.lt_status.total_done > 0: - return self.lt_status.all_time_upload / float(self.lt_status.total_done) - return 0 + @property + def total_payload_upload(self) -> int: + """ + Returns the amount of bytes send this session, but only the actual payload data. + @return The amount in bytes. + """ + if not self.lt_status: + return 0 + return self.lt_status.total_payload_upload + + @property + def total_payload_download(self) -> int: + """ + Returns the amount of bytes received this session, but only the actual payload data. + @return The amount in bytes. + """ + if not self.lt_status: + return 0 + return self.lt_status.total_payload_download + + def get_all_time_ratio(self) -> float: + """ Returns the accumulated seeding ratio of the download across multiple sessions. + """ + if not self.lt_status or not self.all_time_download: + return 0 + return self.all_time_upload / self.all_time_download def get_seeding_time(self): return self.lt_status.finished_time if self.lt_status else 0 diff --git a/src/tribler/core/components/libtorrent/restapi/downloads_endpoint.py b/src/tribler/core/components/libtorrent/restapi/downloads_endpoint.py index d78f13a854d..c145de71363 100644 --- a/src/tribler/core/components/libtorrent/restapi/downloads_endpoint.py +++ b/src/tribler/core/components/libtorrent/restapi/downloads_endpoint.py @@ -6,7 +6,7 @@ from aiohttp import web from aiohttp_apispec import docs, json_schema from ipv8.REST.schema import schema -from ipv8.messaging.anonymization.tunnel import CIRCUIT_ID_PORT, PEER_FLAG_EXIT_BT +from ipv8.messaging.anonymization.tunnel import PEER_FLAG_EXIT_BT from marshmallow.fields import Boolean, Float, Integer, List, String from tribler.core.components.libtorrent.download_manager.download import Download, IllegalFileIndex @@ -237,9 +237,9 @@ def get_files_info_json_paged(download: Download, view_start: Path, view_size: i 'eta': Integer, 'num_peers': Integer, 'num_seeds': Integer, - 'total_up': Integer, - 'total_down': Integer, - 'ratio': Float, + 'all_time_upload': Integer, + 'all_time_download': Integer, + 'all_time_ratio': Float, 'files': String, 'trackers': String, 'hops': Integer, @@ -313,7 +313,6 @@ async def get_downloads(self, request): download_status = get_extended_status( self.tunnel_community, download) if self.tunnel_community else download.get_state().get_status() - download_json = { "name": download_name, "progress": state.get_progress(), @@ -328,9 +327,9 @@ async def get_downloads(self, request): "num_seeds": num_seeds, "num_connected_peers": num_connected_peers, "num_connected_seeds": num_connected_seeds, - "total_up": state.get_total_transferred(UPLOAD), - "total_down": state.get_total_transferred(DOWNLOAD), - "ratio": state.get_seeding_ratio(), + "all_time_upload": state.all_time_upload, + "all_time_download": state.all_time_download, + "all_time_ratio": state.get_all_time_ratio(), "trackers": tracker_info, "hops": download.config.get_hops(), "anon_download": download.get_anon_mode(), @@ -363,7 +362,6 @@ async def get_downloads(self, request): if 'extended_version' in peer_info: peer_info['extended_version'] = _safe_extended_peer_info(peer_info['extended_version']) - download_json["peers"] = peer_list # Add piece information if requested @@ -688,7 +686,6 @@ async def collapse_tree_directory(self, request): return RESTResponse({'path': path}) - @docs( tags=["Libtorrent"], summary="Expand a tree directory.", diff --git a/src/tribler/core/components/libtorrent/tests/test_download.py b/src/tribler/core/components/libtorrent/tests/test_download.py index 6ba9dee6dde..a58915fe12b 100644 --- a/src/tribler/core/components/libtorrent/tests/test_download.py +++ b/src/tribler/core/components/libtorrent/tests/test_download.py @@ -804,7 +804,7 @@ def test_on_torrent_finished_alert(test_download: Download): test_download.handle = Mock(is_valid=Mock(return_value=True)) test_download.notifier = MagicMock() test_download.stream = Mock() - test_download.get_state = Mock(return_value=Mock(get_total_transferred=Mock(return_value=1))) + test_download.get_state = Mock(return_value=Mock(total_download=1)) test_download.on_torrent_finished_alert(Mock()) diff --git a/src/tribler/core/components/libtorrent/tests/test_download_state.py b/src/tribler/core/components/libtorrent/tests/test_download_state.py index 1e841dc40a1..a96d9329592 100644 --- a/src/tribler/core/components/libtorrent/tests/test_download_state.py +++ b/src/tribler/core/components/libtorrent/tests/test_download_state.py @@ -34,7 +34,12 @@ def test_getters_setters_1(mock_download): assert download_state.get_progress() == 0 assert download_state.get_error() is None assert download_state.get_current_speed(UPLOAD) == 0 - assert download_state.get_total_transferred(UPLOAD) == 0 + assert download_state.total_upload == 0 + assert download_state.total_download == 0 + assert download_state.total_payload_download == 0 + assert download_state.total_payload_upload == 0 + assert download_state.all_time_upload == 0 + assert download_state.all_time_download == 0 assert download_state.get_num_seeds_peers() == (0, 0) assert download_state.get_peerlist() == [] @@ -48,9 +53,16 @@ def test_getters_setters_2(mock_download, mock_lt_status): assert download_state.get_status() == DownloadStatus.DOWNLOADING assert download_state.get_current_speed(UPLOAD) == 123 assert download_state.get_current_speed(DOWNLOAD) == 43 - assert download_state.get_total_transferred(UPLOAD) == 100 - assert download_state.get_total_transferred(DOWNLOAD) == 200 - assert download_state.get_seeding_ratio() == 0.5 + assert download_state.total_upload == 100 + assert download_state.total_download == 200 + + assert download_state.total_payload_upload == 30 + assert download_state.total_payload_download == 100 + + assert download_state.all_time_upload == 200 + assert download_state.all_time_download == 1000 + assert download_state.get_all_time_ratio() == 0.2 + assert download_state.get_eta() == 0.25 assert download_state.get_num_seeds_peers() == (5, 5) assert download_state.get_pieces_complete() == [] diff --git a/src/tribler/gui/debug_window.py b/src/tribler/gui/debug_window.py index e15b9292802..e7e58e7cd7a 100644 --- a/src/tribler/gui/debug_window.py +++ b/src/tribler/gui/debug_window.py @@ -329,7 +329,7 @@ def on_ipv8_general_stats(self, data): return self.window().ipv8_general_tree_widget.clear() for key, value in data["ipv8_statistics"].items(): - if key in ('total_up', 'total_down'): + if key in ('total_upload', 'total_download'): value = f"{value / (1024.0 * 1024.0):.2f} MB" elif key == 'session_uptime': value = f"{str(datetime.timedelta(seconds=int(value)))}" diff --git a/src/tribler/gui/qt_resources/mainwindow.ui b/src/tribler/gui/qt_resources/mainwindow.ui index a8d826d20da..8e8e47e261a 100644 --- a/src/tribler/gui/qt_resources/mainwindow.ui +++ b/src/tribler/gui/qt_resources/mainwindow.ui @@ -3486,7 +3486,14 @@ margin-right: 10px; - + + + + + + + + font-weight: bold; @@ -3496,20 +3503,13 @@ margin-right: 10px; - + - - - - - - - diff --git a/src/tribler/gui/widgets/downloadsdetailstabwidget.py b/src/tribler/gui/widgets/downloadsdetailstabwidget.py index 8b6b9cc46bc..ad79de3026a 100644 --- a/src/tribler/gui/widgets/downloadsdetailstabwidget.py +++ b/src/tribler/gui/widgets/downloadsdetailstabwidget.py @@ -8,7 +8,6 @@ from tribler.core.utilities.simpledefs import DownloadStatus from tribler.gui.defs import STATUS_STRING -from tribler.gui.network.request_manager import request_manager from tribler.gui.utilities import compose_magnetlink, connect, copy_to_clipboard, format_size, format_speed, tr from tribler.gui.widgets.torrentfiletreewidget import PreformattedTorrentFileTreeWidget @@ -172,11 +171,13 @@ def update_pages(self, new_download=False): ) self.window().download_detail_infohash_label.setText(self.current_download['infohash']) self.window().download_detail_destination_label.setText(self.current_download["destination"]) - up = format_size(self.current_download['total_up']) - down = format_size(self.current_download['total_down']) + all_time_upload = format_size(self.current_download['all_time_upload']) + all_time_download = format_size(self.current_download['all_time_download']) + all_time_ratio = self.current_download['all_time_ratio'] self.window().download_detail_ratio_label.setText( - f"{self.current_download['ratio']:.3f}, up: {up}, down: {down}" + f"{all_time_ratio:.3f}, upload: {all_time_upload}, download: {all_time_download}" ) + self.window().download_detail_availability_label.setText(f"{self.current_download['availability']:.2f}") if new_download: diff --git a/src/tribler/gui/widgets/downloadspage.py b/src/tribler/gui/widgets/downloadspage.py index 80d2ec0c535..4c5bc0fb900 100644 --- a/src/tribler/gui/widgets/downloadspage.py +++ b/src/tribler/gui/widgets/downloadspage.py @@ -66,8 +66,8 @@ def __init__(self): self.dialog = None self.loading_message_widget: Optional[LoadingDownloadWidgetItem] = None self.loading_list_item: Optional[LoadingListItem] = None - self.total_download = 0 - self.total_upload = 0 + self.total_download_speed = 0 + self.total_upload_speed = 0 # Used to keep track of the last processed request with a purpose of ignoring old requests self.last_processed_request_id = 0 @@ -203,8 +203,8 @@ def on_received_downloads(self, result): self.downloads = result - self.total_download = 0 - self.total_upload = 0 + self.total_download_speed = 0 + self.total_upload_speed = 0 download_infohashes = set() @@ -223,8 +223,8 @@ def on_received_downloads(self, result): item.update_with_download(download) - self.total_download += download["speed_down"] - self.total_upload += download["speed_up"] + self.total_download_speed += download["speed_down"] + self.total_upload_speed += download["speed_up"] download_infohashes.add(download["infohash"]) @@ -246,7 +246,7 @@ def on_received_downloads(self, result): del self.download_widgets[infohash] self.window().tray_set_tooltip( - f"Down: {format_speed(self.total_download)}, Up: {format_speed(self.total_upload)}" + f"Down: {format_speed(self.total_download_speed)}, Up: {format_speed(self.total_upload_speed)}" ) self.update_download_visibility() self.refresh_top_panel() diff --git a/src/tribler/gui/widgets/downloadwidgetitem.py b/src/tribler/gui/widgets/downloadwidgetitem.py index eb44ee12fec..ca40235ff00 100644 --- a/src/tribler/gui/widgets/downloadwidgetitem.py +++ b/src/tribler/gui/widgets/downloadwidgetitem.py @@ -96,7 +96,7 @@ def update_item(self): self.setText(5, f"{self.download_info['num_connected_peers']} ({self.download_info['num_peers']})") self.setText(6, format_speed(self.download_info["speed_down"])) self.setText(7, format_speed(self.download_info["speed_up"])) - self.setText(8, f"{float(self.download_info['ratio']):.3f}") + self.setText(8, f"{float(self.download_info['all_time_ratio']):.3f}") self.setText(9, "yes" if self.download_info["anon_download"] else "no") self.setText(10, str(self.download_info["hops"]) if self.download_info["anon_download"] else "-") self.setText(12, datetime.fromtimestamp(int(self.download_info["time_added"])).strftime('%Y-%m-%d %H:%M'))