From d44c8639ec07caa54e4d6d9d7022dc74f4e7a46c Mon Sep 17 00:00:00 2001 From: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com> Date: Sun, 14 Jun 2026 01:41:30 -0700 Subject: [PATCH 1/2] Fix ETA for match-all share limits --- src/base/bittorrent/torrentimpl.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index c190c8d76329..c768e3fddebd 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -296,6 +296,30 @@ namespace { return ((value < 0) || (value == std::numeric_limits::max())) ? 0 : value; } + + qlonglong finishedTorrentShareLimitsEta(const ShareLimits &shareLimits, const qlonglong ratioEta + , const qlonglong seedingTimeEta, const qlonglong inactiveSeedingTimeEta) + { + const bool matchAll = (shareLimits.mode == ShareLimitsMode::MatchAll); + qlonglong result = (matchAll ? 0 : MAX_ETA); + bool hasLimit = false; + + const auto applyEta = [&](const bool enabled, const qlonglong eta) + { + if (!enabled) + return; + + hasLimit = true; + const qlonglong normalizedEta = std::max(eta, 0); + result = (matchAll ? std::max(result, normalizedEta) : std::min(result, normalizedEta)); + }; + + applyEta((shareLimits.ratioLimit >= 0), ratioEta); + applyEta((shareLimits.seedingTimeLimit >= 0), seedingTimeEta); + applyEta((shareLimits.inactiveSeedingTimeLimit >= 0), inactiveSeedingTimeEta); + + return (hasLimit ? result : MAX_ETA); + } } // TorrentImpl @@ -1422,7 +1446,7 @@ qlonglong TorrentImpl::eta() const inactiveSeedingTimeEta = std::max(inactiveSeedingTimeEta, 0); } - return std::min({ratioEta, seedingTimeEta, inactiveSeedingTimeEta}); + return finishedTorrentShareLimitsEta(shareLimits, ratioEta, seedingTimeEta, inactiveSeedingTimeEta); } if (!speedAverage.download) return MAX_ETA; From 8e8ab3a48bd260cc3f3a48f842ff4e5a42fcf330 Mon Sep 17 00:00:00 2001 From: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com> Date: Wed, 17 Jun 2026 00:48:44 -0700 Subject: [PATCH 2/2] Inline share limit ETA handling --- src/base/bittorrent/torrentimpl.cpp | 74 ++++++++++++----------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index c768e3fddebd..f0f186519b42 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -296,30 +296,6 @@ namespace { return ((value < 0) || (value == std::numeric_limits::max())) ? 0 : value; } - - qlonglong finishedTorrentShareLimitsEta(const ShareLimits &shareLimits, const qlonglong ratioEta - , const qlonglong seedingTimeEta, const qlonglong inactiveSeedingTimeEta) - { - const bool matchAll = (shareLimits.mode == ShareLimitsMode::MatchAll); - qlonglong result = (matchAll ? 0 : MAX_ETA); - bool hasLimit = false; - - const auto applyEta = [&](const bool enabled, const qlonglong eta) - { - if (!enabled) - return; - - hasLimit = true; - const qlonglong normalizedEta = std::max(eta, 0); - result = (matchAll ? std::max(result, normalizedEta) : std::min(result, normalizedEta)); - }; - - applyEta((shareLimits.ratioLimit >= 0), ratioEta); - applyEta((shareLimits.seedingTimeLimit >= 0), seedingTimeEta); - applyEta((shareLimits.inactiveSeedingTimeLimit >= 0), inactiveSeedingTimeEta); - - return (hasLimit ? result : MAX_ETA); - } } // TorrentImpl @@ -1408,48 +1384,60 @@ qlonglong TorrentImpl::totalUpload() const qlonglong TorrentImpl::eta() const { - if (isStopped()) return MAX_ETA; + if (isStopped()) + return MAX_ETA; const SpeedSampleAvg speedAverage = m_payloadRateMonitor.average(); if (isFinished()) { - const ShareLimits shareLimits = effectiveShareLimits(); - if ((shareLimits.ratioLimit < 0) && (shareLimits.seedingTimeLimit < 0) && (shareLimits.inactiveSeedingTimeLimit < 0)) - return MAX_ETA; + const qint64 ZERO_ETA = 0; - qlonglong ratioEta = MAX_ETA; + const ShareLimits shareLimits = effectiveShareLimits(); + QList etaList; - if ((speedAverage.upload > 0) && (shareLimits.ratioLimit >= 0)) + if (shareLimits.ratioLimit >= 0) { - qlonglong realDL = totalDownload(); + qint64 realDL = totalDownload(); if (realDL <= 0) realDL = wantedSize(); - ratioEta = ((realDL * shareLimits.ratioLimit) - totalUpload()) / speedAverage.upload; + const qreal uploadLimit = realDL * shareLimits.ratioLimit; + const qint64 uploaded = totalUpload(); + qint64 ratioEta = ZERO_ETA; + if (uploadLimit > uploaded) + { + ratioEta = (speedAverage.upload > 0) + ? (uploadLimit - uploaded) / speedAverage.upload + : MAX_ETA; + } + etaList.append(ratioEta); } - qlonglong seedingTimeEta = MAX_ETA; - if (shareLimits.seedingTimeLimit >= 0) { - seedingTimeEta = (shareLimits.seedingTimeLimit * 60) - finishedTime(); - if (seedingTimeEta < 0) - seedingTimeEta = 0; + const qint64 seedingTimeEta = std::max( + ((shareLimits.seedingTimeLimit * 60) - finishedTime()), ZERO_ETA); + etaList.append(seedingTimeEta); } - qlonglong inactiveSeedingTimeEta = MAX_ETA; - if (shareLimits.inactiveSeedingTimeLimit >= 0) { - inactiveSeedingTimeEta = (shareLimits.inactiveSeedingTimeLimit * 60) - timeSinceActivity(); - inactiveSeedingTimeEta = std::max(inactiveSeedingTimeEta, 0); + const qint64 inactiveSeedingTimeEta = std::max( + ((shareLimits.inactiveSeedingTimeLimit * 60) - timeSinceActivity()), ZERO_ETA); + etaList.append(inactiveSeedingTimeEta); } - return finishedTorrentShareLimitsEta(shareLimits, ratioEta, seedingTimeEta, inactiveSeedingTimeEta); + if (etaList.isEmpty()) + return MAX_ETA; + + return (shareLimits.mode == ShareLimitsMode::MatchAny) + ? std::ranges::min(etaList) + : std::ranges::max(etaList); } - if (!speedAverage.download) return MAX_ETA; + if (!speedAverage.download) + return MAX_ETA; return (wantedSize() - completedSize()) / speedAverage.download; }