Browse Source

Allow to set torrent stop condition

PR #17814.

Closes #17792.
Closes #929.

(Actually it should close all issues about lack of ability to stop torrent after metadata downloaded or after files are initially checked.)

Also makes explicit the temporary start of the torrent in the case when recheck of the stopped torrent is performed.
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
67357e9964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/base/bittorrent/addtorrentparams.h
  2. 22
      src/base/bittorrent/bencoderesumedatastorage.cpp
  3. 42
      src/base/bittorrent/dbresumedatastorage.cpp
  4. 2
      src/base/bittorrent/dbresumedatastorage.h
  5. 1
      src/base/bittorrent/loadtorrentparams.h
  6. 21
      src/base/bittorrent/nativetorrentextension.cpp
  7. 1
      src/base/bittorrent/nativetorrentextension.h
  8. 2
      src/base/bittorrent/session.h
  9. 12
      src/base/bittorrent/sessionimpl.cpp
  10. 3
      src/base/bittorrent/sessionimpl.h
  11. 13
      src/base/bittorrent/torrent.h
  12. 67
      src/base/bittorrent/torrentimpl.cpp
  13. 4
      src/base/bittorrent/torrentimpl.h
  14. 19
      src/gui/addnewtorrentdialog.cpp
  15. 79
      src/gui/addnewtorrentdialog.ui
  16. 20
      src/gui/optionsdialog.cpp
  17. 47
      src/gui/optionsdialog.ui
  18. 6
      src/webui/api/torrentscontroller.cpp
  19. 2
      src/webui/webapplication.h

1
src/base/bittorrent/addtorrentparams.h

@ -55,6 +55,7 @@ namespace BitTorrent @@ -55,6 +55,7 @@ namespace BitTorrent
bool firstLastPiecePriority = false;
bool addForced = false;
std::optional<bool> addPaused;
std::optional<Torrent::StopCondition> stopCondition;
PathList filePaths; // used if TorrentInfo is set
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
bool skipChecking = false;

22
src/base/bittorrent/bencoderesumedatastorage.cpp

@ -245,6 +245,9 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre @@ -245,6 +245,9 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
// fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default);
// === END REPLACEMENT CODE === //
torrentParams.stopCondition = Utils::String::toEnum(
fromLTString(resumeDataRoot.dict_find_string_value("qBt-stopCondition")), Torrent::StopCondition::None);
const lt::string_view ratioLimitString = resumeDataRoot.dict_find_string_value("qBt-ratioLimit");
if (ratioLimitString.empty())
torrentParams.ratioLimit = resumeDataRoot.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
@ -284,20 +287,14 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre @@ -284,20 +287,14 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
p.save_path = Profile::instance()->fromPortablePath(
Path(fromLTString(p.save_path))).toString().toStdString();
torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed)
? TorrentOperatingMode::AutoManaged : TorrentOperatingMode::Forced;
if (p.flags & lt::torrent_flags::stop_when_ready)
{
// If torrent has "stop_when_ready" flag set then it is actually "stopped"
torrentParams.stopped = true;
torrentParams.operatingMode = TorrentOperatingMode::AutoManaged;
// ...but temporarily "resumed" to perform some service jobs (e.g. checking)
p.flags &= ~lt::torrent_flags::paused;
p.flags |= lt::torrent_flags::auto_managed;
}
else
{
torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed)
? TorrentOperatingMode::AutoManaged : TorrentOperatingMode::Forced;
p.flags &= ~lt::torrent_flags::stop_when_ready;
torrentParams.stopCondition = Torrent::StopCondition::FilesChecked;
}
const bool hasMetadata = (p.ti && p.ti->is_valid());
@ -393,6 +390,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co @@ -393,6 +390,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
data["qBt-seedStatus"] = resumeData.hasSeedStatus;
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
data["qBt-stopCondition"] = Utils::String::fromEnum(resumeData.stopCondition).toStdString();
if (!resumeData.useAutoTMM)
{

42
src/base/bittorrent/dbresumedatastorage.cpp

@ -61,7 +61,7 @@ namespace @@ -61,7 +61,7 @@ namespace
{
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_qs;
const int DB_VERSION = 2;
const int DB_VERSION = 3;
const QString DB_TABLE_META = u"meta"_qs;
const QString DB_TABLE_TORRENTS = u"torrents"_qs;
@ -94,6 +94,7 @@ namespace @@ -94,6 +94,7 @@ namespace
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn("has_seed_status");
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
const Column DB_COLUMN_STOPPED = makeColumn("stopped");
const Column DB_COLUMN_STOP_CONDITION = makeColumn("stop_condition");
const Column DB_COLUMN_RESUMEDATA = makeColumn("libtorrent_resume_data");
const Column DB_COLUMN_METADATA = makeColumn("metadata");
const Column DB_COLUMN_VALUE = makeColumn("value");
@ -213,6 +214,8 @@ namespace BitTorrent @@ -213,6 +214,8 @@ namespace BitTorrent
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
resumeData.stopCondition = Utils::String::toEnum(
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
resumeData.savePath = Profile::instance()->fromPortablePath(
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
@ -241,6 +244,12 @@ namespace BitTorrent @@ -241,6 +244,12 @@ namespace BitTorrent
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
.toString().toStdString();
if (p.flags & lt::torrent_flags::stop_when_ready)
{
p.flags &= ~lt::torrent_flags::stop_when_ready;
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
}
return resumeData;
}
}
@ -263,9 +272,9 @@ BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject @@ -263,9 +272,9 @@ BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject
}
else
{
const int dbVersion = currentDBVersion();
if ((dbVersion == 1) || !db.record(DB_TABLE_TORRENTS).contains(DB_COLUMN_DOWNLOAD_PATH.name))
updateDBFromVersion1();
const int dbVersion = (!db.record(DB_TABLE_TORRENTS).contains(DB_COLUMN_DOWNLOAD_PATH.name) ? 1 : currentDBVersion());
if (dbVersion != DB_VERSION)
updateDB(dbVersion);
}
m_asyncWorker = new Worker(dbPath, u"ResumeDataStorageWorker"_qs, m_dbLock);
@ -507,8 +516,11 @@ void BitTorrent::DBResumeDataStorage::createDB() const @@ -507,8 +516,11 @@ void BitTorrent::DBResumeDataStorage::createDB() const
}
}
void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const
void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
{
Q_ASSERT(fromVersion > 0);
Q_ASSERT(fromVersion != DB_VERSION);
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
const QWriteLocker locker {&m_dbLock};
@ -520,10 +532,21 @@ void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const @@ -520,10 +532,21 @@ void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const
try
{
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
if (!query.exec(alterTableTorrentsQuery))
throw RuntimeError(query.lastError().text());
if (fromVersion == 1)
{
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
if (!query.exec(alterTableTorrentsQuery))
throw RuntimeError(query.lastError().text());
}
if (fromVersion <= 2)
{
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"));
if (!query.exec(alterTableTorrentsQuery))
throw RuntimeError(query.lastError().text());
}
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
if (!query.prepare(updateMetaVersionQuery))
@ -662,6 +685,7 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L @@ -662,6 +685,7 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
query.bindValue(DB_COLUMN_HAS_SEED_STATUS.placeholder, resumeData.hasSeedStatus);
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(resumeData.operatingMode));
query.bindValue(DB_COLUMN_STOPPED.placeholder, resumeData.stopped);
query.bindValue(DB_COLUMN_STOP_CONDITION.placeholder, Utils::String::fromEnum(resumeData.stopCondition));
if (!resumeData.useAutoTMM)
{

2
src/base/bittorrent/dbresumedatastorage.h

@ -57,7 +57,7 @@ namespace BitTorrent @@ -57,7 +57,7 @@ namespace BitTorrent
void doLoadAll() const override;
int currentDBVersion() const;
void createDB() const;
void updateDBFromVersion1() const;
void updateDB(int fromVersion) const;
QThread *m_ioThread = nullptr;

1
src/base/bittorrent/loadtorrentparams.h

@ -54,6 +54,7 @@ namespace BitTorrent @@ -54,6 +54,7 @@ namespace BitTorrent
bool firstLastPiecePriority = false;
bool hasSeedStatus = false;
bool stopped = false;
Torrent::StopCondition stopCondition;
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;

21
src/base/bittorrent/nativetorrentextension.cpp

@ -30,14 +30,6 @@ @@ -30,14 +30,6 @@
#include <libtorrent/torrent_status.hpp>
namespace
{
bool isAutoManaged(const lt::torrent_status &torrentStatus)
{
return static_cast<bool>(torrentStatus.flags & lt::torrent_flags::auto_managed);
}
}
NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrentHandle, ExtensionData *data)
: m_torrentHandle {torrentHandle}
, m_data {data}
@ -65,19 +57,10 @@ NativeTorrentExtension::~NativeTorrentExtension() @@ -65,19 +57,10 @@ NativeTorrentExtension::~NativeTorrentExtension()
delete m_data;
}
bool NativeTorrentExtension::on_pause()
{
if (!isAutoManaged(m_torrentHandle.status({})))
m_torrentHandle.unset_flags(lt::torrent_flags::stop_when_ready);
// return `false` to allow standard handler
// and other extensions to be also invoked.
return false;
}
void NativeTorrentExtension::on_state(const lt::torrent_status::state_t state)
{
if (m_state == lt::torrent_status::downloading_metadata)
if ((m_state == lt::torrent_status::downloading_metadata)
|| (m_state == lt::torrent_status::checking_files))
{
m_torrentHandle.unset_flags(lt::torrent_flags::auto_managed);
m_torrentHandle.pause();

1
src/base/bittorrent/nativetorrentextension.h

@ -40,7 +40,6 @@ public: @@ -40,7 +40,6 @@ public:
~NativeTorrentExtension();
private:
bool on_pause() override;
void on_state(lt::torrent_status::state_t state) override;
lt::torrent_handle m_torrentHandle;

2
src/base/bittorrent/session.h

@ -208,6 +208,8 @@ namespace BitTorrent @@ -208,6 +208,8 @@ namespace BitTorrent
virtual void setPeXEnabled(bool enabled) = 0;
virtual bool isAddTorrentPaused() const = 0;
virtual void setAddTorrentPaused(bool value) = 0;
virtual Torrent::StopCondition torrentStopCondition() const = 0;
virtual void setTorrentStopCondition(Torrent::StopCondition stopCondition) = 0;
virtual TorrentContentLayout torrentContentLayout() const = 0;
virtual void setTorrentContentLayout(TorrentContentLayout value) = 0;
virtual bool isTrackerEnabled() const = 0;

12
src/base/bittorrent/sessionimpl.cpp

@ -438,6 +438,7 @@ SessionImpl::SessionImpl(QObject *parent) @@ -438,6 +438,7 @@ SessionImpl::SessionImpl(QObject *parent)
, m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_qs), -1, [](qreal r) { return r < 0 ? -1. : r;})
, m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_qs), -1, lowerLimited(-1))
, m_isAddTorrentPaused(BITTORRENT_SESSION_KEY(u"AddTorrentPaused"_qs), false)
, m_torrentStopCondition(BITTORRENT_SESSION_KEY(u"TorrentStopCondition"_qs), Torrent::StopCondition::None)
, m_torrentContentLayout(BITTORRENT_SESSION_KEY(u"TorrentContentLayout"_qs), TorrentContentLayout::Original)
, m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY(u"AddExtensionToIncompleteFiles"_qs), false)
, m_refreshInterval(BITTORRENT_SESSION_KEY(u"RefreshInterval"_qs), 1500)
@ -950,6 +951,16 @@ void SessionImpl::setAddTorrentPaused(const bool value) @@ -950,6 +951,16 @@ void SessionImpl::setAddTorrentPaused(const bool value)
m_isAddTorrentPaused = value;
}
Torrent::StopCondition SessionImpl::torrentStopCondition() const
{
return m_torrentStopCondition;
}
void SessionImpl::setTorrentStopCondition(const Torrent::StopCondition stopCondition)
{
m_torrentStopCondition = stopCondition;
}
bool SessionImpl::isTrackerEnabled() const
{
return m_isTrackerEnabled;
@ -2497,6 +2508,7 @@ LoadTorrentParams SessionImpl::initLoadTorrentParams(const AddTorrentParams &add @@ -2497,6 +2508,7 @@ LoadTorrentParams SessionImpl::initLoadTorrentParams(const AddTorrentParams &add
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
loadTorrentParams.operatingMode = (addTorrentParams.addForced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged);
loadTorrentParams.stopped = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
loadTorrentParams.stopCondition = addTorrentParams.stopCondition.value_or(torrentStopCondition());
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;

3
src/base/bittorrent/sessionimpl.h

@ -188,6 +188,8 @@ namespace BitTorrent @@ -188,6 +188,8 @@ namespace BitTorrent
void setPeXEnabled(bool enabled) override;
bool isAddTorrentPaused() const override;
void setAddTorrentPaused(bool value) override;
Torrent::StopCondition torrentStopCondition() const override;
void setTorrentStopCondition(Torrent::StopCondition stopCondition) override;
TorrentContentLayout torrentContentLayout() const override;
void setTorrentContentLayout(TorrentContentLayout value) override;
bool isTrackerEnabled() const override;
@ -620,6 +622,7 @@ namespace BitTorrent @@ -620,6 +622,7 @@ namespace BitTorrent
CachedSettingValue<qreal> m_globalMaxRatio;
CachedSettingValue<int> m_globalMaxSeedingMinutes;
CachedSettingValue<bool> m_isAddTorrentPaused;
CachedSettingValue<Torrent::StopCondition> m_torrentStopCondition;
CachedSettingValue<TorrentContentLayout> m_torrentContentLayout;
CachedSettingValue<bool> m_isAppendExtensionEnabled;
CachedSettingValue<int> m_refreshInterval;

13
src/base/bittorrent/torrent.h

@ -108,7 +108,17 @@ namespace BitTorrent @@ -108,7 +108,17 @@ namespace BitTorrent
class Torrent : public AbstractFileStorage
{
Q_GADGET
public:
enum class StopCondition
{
None = 0,
MetadataReceived = 1,
FilesChecked = 2
};
Q_ENUM(StopCondition)
static const qreal USE_GLOBAL_RATIO;
static const qreal NO_RATIO_LIMIT;
@ -300,6 +310,9 @@ namespace BitTorrent @@ -300,6 +310,9 @@ namespace BitTorrent
virtual void clearPeers() = 0;
virtual bool setMetadata(const TorrentInfo &torrentInfo) = 0;
virtual StopCondition stopCondition() const = 0;
virtual void setStopCondition(StopCondition stopCondition) = 0;
virtual QString createMagnetURI() const = 0;
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;

67
src/base/bittorrent/torrentimpl.cpp

@ -295,6 +295,8 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession @@ -295,6 +295,8 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
}
}
setStopCondition(params.stopCondition);
const auto *extensionData = static_cast<ExtensionData *>(m_ltAddTorrentParams.userdata);
m_trackerEntries.reserve(static_cast<decltype(m_trackerEntries)::size_type>(extensionData->trackers.size()));
for (const lt::announce_entry &announceEntry : extensionData->trackers)
@ -993,9 +995,7 @@ void TorrentImpl::updateState() @@ -993,9 +995,7 @@ void TorrentImpl::updateState()
else
m_state = isForced() ? TorrentState::ForcedDownloadingMetadata : TorrentState::DownloadingMetadata;
}
else if ((m_nativeStatus.state == lt::torrent_status::checking_files)
&& (!isPaused() || (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
|| !(m_nativeStatus.flags & lt::torrent_flags::paused)))
else if ((m_nativeStatus.state == lt::torrent_status::checking_files) && !isPaused())
{
// If the torrent is not just in the "checking" state, but is being actually checked
m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading;
@ -1423,8 +1423,8 @@ void TorrentImpl::forceRecheck() @@ -1423,8 +1423,8 @@ void TorrentImpl::forceRecheck()
if (isPaused())
{
// When "force recheck" is applied on paused torrent, we temporarily resume it
// (really we just allow libtorrent to resume it by enabling auto management for it).
m_nativeHandle.set_flags(lt::torrent_flags::stop_when_ready | lt::torrent_flags::auto_managed);
resume();
setStopCondition(StopCondition::FilesChecked);
}
}
@ -1581,6 +1581,17 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi @@ -1581,6 +1581,17 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
p.save_path = savePath.toString().toStdString();
p.ti = metadata;
if (stopCondition() == StopCondition::MetadataReceived)
{
m_stopCondition = StopCondition::None;
m_isStopped = true;
p.flags |= lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
m_session->handleTorrentPaused(this);
}
reload();
// If first/last piece priority was specified when adding this torrent,
@ -1639,6 +1650,7 @@ void TorrentImpl::pause() @@ -1639,6 +1650,7 @@ void TorrentImpl::pause()
{
if (!m_isStopped)
{
m_stopCondition = StopCondition::None;
m_isStopped = true;
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentPaused(this);
@ -1674,10 +1686,6 @@ void TorrentImpl::resume(const TorrentOperatingMode mode) @@ -1674,10 +1686,6 @@ void TorrentImpl::resume(const TorrentOperatingMode mode)
if (m_isStopped)
{
// Torrent may have been temporarily resumed to perform checking files
// so we have to ensure it will not pause after checking is done.
m_nativeHandle.unset_flags(lt::torrent_flags::stop_when_ready);
m_isStopped = false;
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentResumed(this);
@ -1756,13 +1764,13 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p) @@ -1756,13 +1764,13 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
return;
}
if (stopCondition() == StopCondition::FilesChecked)
pause();
m_statusUpdatedTriggers.enqueue([this]()
{
qDebug("\"%s\" have just finished checking.", qUtf8Printable(name()));
if (m_nativeStatus.need_save_resume)
m_session->handleTorrentNeedSaveResumeData(this);
if (!m_hasMissingFiles)
{
if ((progress() < 1.0) && (wantedSize() > 0))
@ -1772,8 +1780,20 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p) @@ -1772,8 +1780,20 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
adjustStorageLocation();
manageIncompleteFiles();
if (!isPaused())
{
// torrent is internally paused using NativeTorrentExtension after files checked
// so we need to resume it if there is no corresponding "stop condition" set
setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged);
if (m_operatingMode == TorrentOperatingMode::Forced)
m_nativeHandle.resume();
}
}
if (m_nativeStatus.need_save_resume)
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentChecked(this);
});
}
@ -1909,6 +1929,7 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params) @@ -1909,6 +1929,7 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params)
resumeData.firstLastPiecePriority = m_hasFirstLastPiecePriority;
resumeData.hasSeedStatus = m_hasSeedStatus;
resumeData.stopped = m_isStopped;
resumeData.stopCondition = m_stopCondition;
resumeData.operatingMode = m_operatingMode;
resumeData.ltAddTorrentParams = m_ltAddTorrentParams;
resumeData.useAutoTMM = m_useAutoTMM;
@ -2166,6 +2187,28 @@ bool TorrentImpl::setMetadata(const TorrentInfo &torrentInfo) @@ -2166,6 +2187,28 @@ bool TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
#endif
}
Torrent::StopCondition TorrentImpl::stopCondition() const
{
return m_stopCondition;
}
void TorrentImpl::setStopCondition(const StopCondition stopCondition)
{
if (stopCondition == m_stopCondition)
return;
if (isPaused())
return;
if ((stopCondition == StopCondition::MetadataReceived) && hasMetadata())
return;
if ((stopCondition == StopCondition::FilesChecked) && hasMetadata() && !isChecking())
return;
m_stopCondition = stopCondition;
}
bool TorrentImpl::isMoveInProgress() const
{
return m_storageIsMoving;

4
src/base/bittorrent/torrentimpl.h

@ -227,6 +227,9 @@ namespace BitTorrent @@ -227,6 +227,9 @@ namespace BitTorrent
void clearPeers() override;
bool setMetadata(const TorrentInfo &torrentInfo) override;
StopCondition stopCondition() const override;
void setStopCondition(StopCondition stopCondition) override;
QString createMagnetURI() const override;
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
@ -332,6 +335,7 @@ namespace BitTorrent @@ -332,6 +335,7 @@ namespace BitTorrent
bool m_hasFirstLastPiecePriority = false;
bool m_useAutoTMM;
bool m_isStopped;
StopCondition m_stopCondition;
bool m_unchecked = false;

19
src/gui/addnewtorrentdialog.cpp

@ -218,6 +218,24 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP @@ -218,6 +218,24 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
m_ui->downloadPath->setMaxVisibleItems(20);
m_ui->startTorrentCheckBox->setChecked(!m_torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
m_ui->stopConditionComboBox->setToolTip(
u"<html><body><p><b>" + tr("None") + u"</b> - " + tr("No stop condition is set.") + u"</p><p><b>" +
tr("Metadata received") + u"</b> - " + tr("Torrent will stop after metadata is received.") +
u" <em>" + tr("Torrents that have metadata initially aren't affected.") + u"</em></p><p><b>" +
tr("Files checked") + u"</b> - " + tr("Torrent will stop after files are initially checked.") +
u" <em>" + tr("This will also download metadata if it wasn't there initially.") + u"</em></p></body></html>");
m_ui->stopConditionComboBox->setItemData(0, QVariant::fromValue(BitTorrent::Torrent::StopCondition::None));
m_ui->stopConditionComboBox->setItemData(1, QVariant::fromValue(BitTorrent::Torrent::StopCondition::MetadataReceived));
m_ui->stopConditionComboBox->setItemData(2, QVariant::fromValue(BitTorrent::Torrent::StopCondition::FilesChecked));
m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(
QVariant::fromValue(m_torrentParams.stopCondition.value_or(session->torrentStopCondition()))));
m_ui->stopConditionLabel->setEnabled(m_ui->startTorrentCheckBox->isChecked());
m_ui->stopConditionComboBox->setEnabled(m_ui->startTorrentCheckBox->isChecked());
connect(m_ui->startTorrentCheckBox, &QCheckBox::toggled, this, [this](const bool checked)
{
m_ui->stopConditionLabel->setEnabled(checked);
m_ui->stopConditionComboBox->setEnabled(checked);
});
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does its job at this point
m_ui->comboTTM->setCurrentIndex(session->isAutoTMMDisabledByDefault() ? 0 : 1);
@ -872,6 +890,7 @@ void AddNewTorrentDialog::accept() @@ -872,6 +890,7 @@ void AddNewTorrentDialog::accept()
m_torrentParams.filePriorities = m_contentModel->model()->getFilePriorities();
m_torrentParams.addPaused = !m_ui->startTorrentCheckBox->isChecked();
m_torrentParams.stopCondition = m_ui->stopConditionComboBox->currentData().value<BitTorrent::Torrent::StopCondition>();
m_torrentParams.contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
m_torrentParams.sequential = m_ui->sequentialCheckBox->isChecked();

79
src/gui/addnewtorrentdialog.ui

@ -203,22 +203,58 @@ @@ -203,22 +203,58 @@
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QCheckBox" name="doNotDeleteTorrentCheckBox">
<property name="toolTip">
<string>When checked, the .torrent file will not be deleted regardless of the settings at the &quot;Download&quot; page of the Options dialog</string>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="startTorrentCheckBox">
<property name="text">
<string>Do not delete .torrent file</string>
<string>Start torrent</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="firstLastCheckBox">
<item row="0" column="1">
<layout class="QHBoxLayout" name="stopConditionLayout">
<item>
<widget class="QLabel" name="stopConditionLabel">
<property name="text">
<string>Download first and last pieces first</string>
<string>Stop condition:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="stopConditionComboBox">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Metadata received</string>
</property>
</item>
<item>
<property name="text">
<string>Files checked</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="skipCheckingCheckBox">
@ -227,32 +263,29 @@ @@ -227,32 +263,29 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="2" column="0">
<widget class="QCheckBox" name="sequentialCheckBox">
<property name="text">
<string>Download in sequential order</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="startTorrentCheckBox">
<item row="2" column="1">
<widget class="QCheckBox" name="firstLastCheckBox">
<property name="text">
<string>Start torrent</string>
<string>Download first and last pieces first</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="3" column="0">
<widget class="QCheckBox" name="doNotDeleteTorrentCheckBox">
<property name="toolTip">
<string>When checked, the .torrent file will not be deleted regardless of the settings at the &quot;Download&quot; page of the Options dialog</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
<property name="text">
<string>Do not delete .torrent file</string>
</property>
</spacer>
</widget>
</item>
</layout>
</item>

20
src/gui/optionsdialog.cpp

@ -525,6 +525,19 @@ void OptionsDialog::loadDownloadsTabOptions() @@ -525,6 +525,19 @@ void OptionsDialog::loadDownloadsTabOptions()
m_ui->contentLayoutComboBox->setCurrentIndex(static_cast<int>(session->torrentContentLayout()));
m_ui->checkStartPaused->setChecked(session->isAddTorrentPaused());
m_ui->stopConditionComboBox->setToolTip(
u"<html><body><p><b>" + tr("None") + u"</b> - " + tr("No stop condition is set.") + u"</p><p><b>" +
tr("Metadata received") + u"</b> - " + tr("Torrent will stop after metadata is received.") +
u" <em>" + tr("Torrents that have metadata initially aren't affected.") + u"</em></p><p><b>" +
tr("Files checked") + u"</b> - " + tr("Torrent will stop after files are initially checked.") +
u" <em>" + tr("This will also download metadata if it wasn't there initially.") + u"</em></p></body></html>");
m_ui->stopConditionComboBox->setItemData(0, QVariant::fromValue(BitTorrent::Torrent::StopCondition::None));
m_ui->stopConditionComboBox->setItemData(1, QVariant::fromValue(BitTorrent::Torrent::StopCondition::MetadataReceived));
m_ui->stopConditionComboBox->setItemData(2, QVariant::fromValue(BitTorrent::Torrent::StopCondition::FilesChecked));
m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(QVariant::fromValue(session->torrentStopCondition())));
m_ui->stopConditionLabel->setEnabled(!m_ui->checkStartPaused->isChecked());
m_ui->stopConditionComboBox->setEnabled(!m_ui->checkStartPaused->isChecked());
const TorrentFileGuard::AutoDeleteMode autoDeleteMode = TorrentFileGuard::autoDeleteMode();
m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
@ -633,6 +646,12 @@ void OptionsDialog::loadDownloadsTabOptions() @@ -633,6 +646,12 @@ void OptionsDialog::loadDownloadsTabOptions()
connect(m_ui->contentLayoutComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->checkStartPaused, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkStartPaused, &QAbstractButton::toggled, this, [this](const bool checked)
{
m_ui->stopConditionLabel->setEnabled(!checked);
m_ui->stopConditionComboBox->setEnabled(!checked);
});
connect(m_ui->stopConditionComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->deleteCancelledTorrentBox, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
@ -692,6 +711,7 @@ void OptionsDialog::saveDownloadsTabOptions() const @@ -692,6 +711,7 @@ void OptionsDialog::saveDownloadsTabOptions() const
session->setTorrentContentLayout(static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
session->setAddTorrentPaused(addTorrentsInPause());
session->setTorrentStopCondition(m_ui->stopConditionComboBox->currentData().value<BitTorrent::Torrent::StopCondition>());
TorrentFileGuard::setAutoDeleteMode(!m_ui->deleteTorrentBox->isChecked() ? TorrentFileGuard::Never
: !m_ui->deleteCancelledTorrentBox->isChecked() ? TorrentFileGuard::IfAdded
: TorrentFileGuard::Always);

47
src/gui/optionsdialog.ui

@ -839,6 +839,52 @@ @@ -839,6 +839,52 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="stopConditionLayout">
<item>
<widget class="QLabel" name="stopConditionLabel">
<property name="text">
<string>Torrent stop condition:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="stopConditionComboBox">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Metadata received</string>
</property>
</item>
<item>
<property name="text">
<string>Files checked</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="stopConditionSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="deleteTorrentBox">
<property name="toolTip">
@ -3555,6 +3601,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string> @@ -3555,6 +3601,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
<tabstop>checkUseCustomTheme</tabstop>
<tabstop>customThemeFilePath</tabstop>
<tabstop>checkStartPaused</tabstop>
<tabstop>stopConditionComboBox</tabstop>
<tabstop>spinPort</tabstop>
<tabstop>checkUPnP</tabstop>
<tabstop>textWebUiUsername</tabstop>

6
src/webui/api/torrentscontroller.cpp

@ -665,6 +665,11 @@ void TorrentsController::addAction() @@ -665,6 +665,11 @@ void TorrentsController::addAction()
const int seedingTimeLimit = parseInt(params()[u"seedingTimeLimit"_qs]).value_or(BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
const std::optional<bool> autoTMM = parseBool(params()[u"autoTMM"_qs]);
const QString stopConditionParam = params()[u"stopCondition"_qs];
const std::optional<BitTorrent::Torrent::StopCondition> stopCondition = (!stopConditionParam.isEmpty()
? Utils::String::toEnum(stopConditionParam, BitTorrent::Torrent::StopCondition::None)
: std::optional<BitTorrent::Torrent::StopCondition> {});
const QString contentLayoutParam = params()[u"contentLayout"_qs];
const std::optional<BitTorrent::TorrentContentLayout> contentLayout = (!contentLayoutParam.isEmpty()
? Utils::String::toEnum(contentLayoutParam, BitTorrent::TorrentContentLayout::Original)
@ -693,6 +698,7 @@ void TorrentsController::addAction() @@ -693,6 +698,7 @@ void TorrentsController::addAction()
addTorrentParams.sequential = seqDownload;
addTorrentParams.firstLastPiecePriority = firstLastPiece;
addTorrentParams.addPaused = addPaused;
addTorrentParams.stopCondition = stopCondition;
addTorrentParams.contentLayout = contentLayout;
addTorrentParams.savePath = Path(savepath);
addTorrentParams.downloadPath = Path(downloadPath);

2
src/webui/webapplication.h

@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
#include "base/utils/version.h"
#include "api/isessionmanager.h"
inline const Utils::Version<3, 2> API_VERSION {2, 8, 14};
inline const Utils::Version<3, 2> API_VERSION {2, 8, 15};
class APIController;
class AuthController;

Loading…
Cancel
Save