mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-10-22 10:46:04 +03:00
Apply share limits when torrent downloading is finished
PR #20917. Closes #20874.
This commit is contained in:
parent
d89f289f82
commit
65d143d4c4
2 changed files with 87 additions and 73 deletions
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue