Apply share limits when torrent downloading is finished

PR #20917.
Closes #20874.
This commit is contained in:
Vladimir Golovnev 2024-06-12 09:03:07 +03:00 committed by sledgehammer999
parent 683492648f
commit 05e3130baa
No known key found for this signature in database
GPG key ID: 6E4A2D025B7CC9A2
2 changed files with 87 additions and 73 deletions

View file

@ -550,7 +550,14 @@ SessionImpl::SessionImpl(QObject *parent)
, this, [this]() { m_recentErroredTorrents.clear(); }); , this, [this]() { m_recentErroredTorrents.clear(); });
m_seedingLimitTimer->setInterval(10s); m_seedingLimitTimer->setInterval(10s);
connect(m_seedingLimitTimer, &QTimer::timeout, this, &SessionImpl::processShareLimits); connect(m_seedingLimitTimer, &QTimer::timeout, this, [this]
{
// We shouldn't iterate over `m_torrents` in the loop below
// since `deleteTorrent()` modifies it indirectly
const QHash<TorrentID, TorrentImpl *> torrents {m_torrents};
for (TorrentImpl *torrent : torrents)
processTorrentShareLimits(torrent);
});
initializeNativeSession(); initializeNativeSession();
configureComponents(); configureComponents();
@ -2236,72 +2243,66 @@ void SessionImpl::populateAdditionalTrackers()
m_additionalTrackerEntries = parseTrackerEntries(additionalTrackers()); m_additionalTrackerEntries = parseTrackerEntries(additionalTrackers());
} }
void SessionImpl::processShareLimits() void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
{ {
if (!torrent->isFinished() || torrent->isForced())
return;
const auto effectiveLimit = []<typename T>(const T limit, const T useGlobalLimit, const T globalLimit) -> T const auto effectiveLimit = []<typename T>(const T limit, const T useGlobalLimit, const T globalLimit) -> T
{ {
return (limit == useGlobalLimit) ? globalLimit : limit; return (limit == useGlobalLimit) ? globalLimit : limit;
}; };
// We shouldn't iterate over `m_torrents` in the loop below const qreal ratioLimit = effectiveLimit(torrent->ratioLimit(), Torrent::USE_GLOBAL_RATIO, globalMaxRatio());
// since `deleteTorrent()` modifies it indirectly const int seedingTimeLimit = effectiveLimit(torrent->seedingTimeLimit(), Torrent::USE_GLOBAL_SEEDING_TIME, globalMaxSeedingMinutes());
const QHash<TorrentID, TorrentImpl *> torrents {m_torrents}; const int inactiveSeedingTimeLimit = effectiveLimit(torrent->inactiveSeedingTimeLimit(), Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME, globalMaxInactiveSeedingMinutes());
for (const auto &[torrentID, torrent] : torrents.asKeyValueRange())
bool reached = false;
QString description;
if (const qreal ratio = torrent->realRatio();
(ratioLimit >= 0) && (ratio <= Torrent::MAX_RATIO) && (ratio >= ratioLimit))
{ {
if (!torrent->isFinished() || torrent->isForced()) reached = true;
continue; description = tr("Torrent reached the share ratio limit.");
}
else if (const qlonglong seedingTimeInMinutes = torrent->finishedTime() / 60;
(seedingTimeLimit >= 0) && (seedingTimeInMinutes <= Torrent::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit))
{
reached = true;
description = tr("Torrent reached the seeding time limit.");
}
else if (const qlonglong inactiveSeedingTimeInMinutes = torrent->timeSinceActivity() / 60;
(inactiveSeedingTimeLimit >= 0) && (inactiveSeedingTimeInMinutes <= Torrent::MAX_INACTIVE_SEEDING_TIME) && (inactiveSeedingTimeInMinutes >= inactiveSeedingTimeLimit))
{
reached = true;
description = tr("Torrent reached the inactive seeding time limit.");
}
const qreal ratioLimit = effectiveLimit(torrent->ratioLimit(), Torrent::USE_GLOBAL_RATIO, globalMaxRatio()); if (reached)
const int seedingTimeLimit = effectiveLimit(torrent->seedingTimeLimit(), Torrent::USE_GLOBAL_SEEDING_TIME, globalMaxSeedingMinutes()); {
const int inactiveSeedingTimeLimit = effectiveLimit(torrent->inactiveSeedingTimeLimit(), Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME, globalMaxInactiveSeedingMinutes()); const QString torrentName = tr("Torrent: \"%1\".").arg(torrent->name());
const ShareLimitAction shareLimitAction = (torrent->shareLimitAction() == ShareLimitAction::Default) ? m_shareLimitAction : torrent->shareLimitAction();
bool reached = false; if (shareLimitAction == ShareLimitAction::Remove)
QString description;
if (const qreal ratio = torrent->realRatio();
(ratioLimit >= 0) && (ratio <= Torrent::MAX_RATIO) && (ratio >= ratioLimit))
{ {
reached = true; LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent."), torrentName));
description = tr("Torrent reached the share ratio limit."); deleteTorrent(torrent->id());
} }
else if (const qlonglong seedingTimeInMinutes = torrent->finishedTime() / 60; else if (shareLimitAction == ShareLimitAction::RemoveWithContent)
(seedingTimeLimit >= 0) && (seedingTimeInMinutes <= Torrent::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit))
{ {
reached = true; LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent and deleting its content."), torrentName));
description = tr("Torrent reached the seeding time limit."); deleteTorrent(torrent->id(), DeleteTorrentAndFiles);
} }
else if (const qlonglong inactiveSeedingTimeInMinutes = torrent->timeSinceActivity() / 60; else if ((shareLimitAction == ShareLimitAction::Stop) && !torrent->isStopped())
(inactiveSeedingTimeLimit >= 0) && (inactiveSeedingTimeInMinutes <= Torrent::MAX_INACTIVE_SEEDING_TIME) && (inactiveSeedingTimeInMinutes >= inactiveSeedingTimeLimit))
{ {
reached = true; torrent->stop();
description = tr("Torrent reached the inactive seeding time limit."); LogMsg(u"%1 %2 %3"_s.arg(description, tr("Torrent stopped."), torrentName));
} }
else if ((shareLimitAction == ShareLimitAction::EnableSuperSeeding) && !torrent->isStopped() && !torrent->superSeeding())
if (reached)
{ {
const QString torrentName = tr("Torrent: \"%1\".").arg(torrent->name()); torrent->setSuperSeeding(true);
const ShareLimitAction shareLimitAction = (torrent->shareLimitAction() == ShareLimitAction::Default) ? m_shareLimitAction : torrent->shareLimitAction(); LogMsg(u"%1 %2 %3"_s.arg(description, tr("Super seeding enabled."), torrentName));
if (shareLimitAction == ShareLimitAction::Remove)
{
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent."), torrentName));
deleteTorrent(torrentID);
}
else if (shareLimitAction == ShareLimitAction::RemoveWithContent)
{
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent and deleting its content."), torrentName));
deleteTorrent(torrentID, DeleteTorrentAndFiles);
}
else if ((shareLimitAction == ShareLimitAction::Stop) && !torrent->isStopped())
{
torrent->stop();
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Torrent stopped."), torrentName));
}
else if ((shareLimitAction == ShareLimitAction::EnableSuperSeeding) && !torrent->isStopped() && !torrent->superSeeding())
{
torrent->setSuperSeeding(true);
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Super seeding enabled."), torrentName));
}
} }
} }
} }
@ -4906,7 +4907,7 @@ void SessionImpl::updateSeedingLimitTimer()
if ((globalMaxRatio() == Torrent::NO_RATIO_LIMIT) && !hasPerTorrentRatioLimit() if ((globalMaxRatio() == Torrent::NO_RATIO_LIMIT) && !hasPerTorrentRatioLimit()
&& (globalMaxSeedingMinutes() == Torrent::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit() && (globalMaxSeedingMinutes() == Torrent::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit()
&& (globalMaxInactiveSeedingMinutes() == Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT) && !hasPerTorrentInactiveSeedingTimeLimit()) && (globalMaxInactiveSeedingMinutes() == Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT) && !hasPerTorrentInactiveSeedingTimeLimit())
{ {
if (m_seedingLimitTimer->isActive()) if (m_seedingLimitTimer->isActive())
m_seedingLimitTimer->stop(); m_seedingLimitTimer->stop();
} }
@ -5018,18 +5019,7 @@ void SessionImpl::handleTorrentChecked(TorrentImpl *const torrent)
void SessionImpl::handleTorrentFinished(TorrentImpl *const torrent) void SessionImpl::handleTorrentFinished(TorrentImpl *const torrent)
{ {
LogMsg(tr("Torrent download finished. Torrent: \"%1\"").arg(torrent->name())); m_pendingFinishedTorrents.append(torrent);
emit torrentFinished(torrent);
if (const Path exportPath = finishedTorrentExportDirectory(); !exportPath.isEmpty())
exportTorrentFile(torrent, exportPath);
const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
{
return !(torrent->isFinished() || torrent->isStopped() || torrent->isErrored());
});
if (!hasUnfinishedTorrents)
emit allTorrentsFinished();
} }
void SessionImpl::handleTorrentResumeDataReady(TorrentImpl *const torrent, const LoadTorrentParams &data) void SessionImpl::handleTorrentResumeDataReady(TorrentImpl *const torrent, const LoadTorrentParams &data)
@ -6095,6 +6085,29 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
if (!updatedTorrents.isEmpty()) if (!updatedTorrents.isEmpty())
emit torrentsUpdated(updatedTorrents); emit torrentsUpdated(updatedTorrents);
if (!m_pendingFinishedTorrents.isEmpty())
{
for (TorrentImpl *torrent : m_pendingFinishedTorrents)
{
LogMsg(tr("Torrent download finished. Torrent: \"%1\"").arg(torrent->name()));
emit torrentFinished(torrent);
if (const Path exportPath = finishedTorrentExportDirectory(); !exportPath.isEmpty())
exportTorrentFile(torrent, exportPath);
processTorrentShareLimits(torrent);
}
m_pendingFinishedTorrents.clear();
const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
{
return !(torrent->isFinished() || torrent->isStopped() || torrent->isErrored());
});
if (!hasUnfinishedTorrents)
emit allTorrentsFinished();
}
if (m_needSaveTorrentsQueue) if (m_needSaveTorrentsQueue)
saveTorrentsQueue(); saveTorrentsQueue();

View file

@ -487,7 +487,6 @@ namespace BitTorrent
void configureDeferred(); void configureDeferred();
void readAlerts(); void readAlerts();
void enqueueRefresh(); void enqueueRefresh();
void processShareLimits();
void generateResumeData(); void generateResumeData();
void handleIPFilterParsed(int ruleCount); void handleIPFilterParsed(int ruleCount);
void handleIPFilterError(); void handleIPFilterError();
@ -536,6 +535,7 @@ namespace BitTorrent
void enableIPFilter(); void enableIPFilter();
void disableIPFilter(); void disableIPFilter();
void processTrackerStatuses(); void processTrackerStatuses();
void processTorrentShareLimits(TorrentImpl *torrent);
void populateExcludedFileNamesRegExpList(); void populateExcludedFileNamesRegExpList();
void prepareStartup(); void prepareStartup();
void handleLoadedResumeData(ResumeSessionContext *context); void handleLoadedResumeData(ResumeSessionContext *context);
@ -599,14 +599,6 @@ namespace BitTorrent
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers); void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
// BitTorrent
lt::session *m_nativeSession = nullptr;
NativeSessionExtension *m_nativeSessionExtension = nullptr;
bool m_deferredConfigureScheduled = false;
bool m_IPFilteringConfigured = false;
mutable bool m_listenInterfaceConfigured = false;
CachedSettingValue<QString> m_DHTBootstrapNodes; CachedSettingValue<QString> m_DHTBootstrapNodes;
CachedSettingValue<bool> m_isDHTEnabled; CachedSettingValue<bool> m_isDHTEnabled;
CachedSettingValue<bool> m_isLSDEnabled; CachedSettingValue<bool> m_isLSDEnabled;
@ -733,6 +725,13 @@ namespace BitTorrent
CachedSettingValue<int> m_I2POutboundLength; CachedSettingValue<int> m_I2POutboundLength;
SettingValue<bool> m_startPaused; SettingValue<bool> m_startPaused;
lt::session *m_nativeSession = nullptr;
NativeSessionExtension *m_nativeSessionExtension = nullptr;
bool m_deferredConfigureScheduled = false;
bool m_IPFilteringConfigured = false;
mutable bool m_listenInterfaceConfigured = false;
bool m_isRestored = false; bool m_isRestored = false;
bool m_isPaused = isStartPaused(); bool m_isPaused = isStartPaused();
@ -809,6 +808,8 @@ namespace BitTorrent
QTimer *m_wakeupCheckTimer = nullptr; QTimer *m_wakeupCheckTimer = nullptr;
QDateTime m_wakeupCheckTimestamp; QDateTime m_wakeupCheckTimestamp;
QList<TorrentImpl *> m_pendingFinishedTorrents;
friend void Session::initInstance(); friend void Session::initInstance();
friend void Session::freeInstance(); friend void Session::freeInstance();
friend Session *Session::instance(); friend Session *Session::instance();