mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-12 15:57:57 +00:00
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.
This commit is contained in:
parent
ce7d8dee28
commit
67357e9964
@ -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;
|
||||
|
@ -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
|
||||
p.save_path = Profile::instance()->fromPortablePath(
|
||||
Path(fromLTString(p.save_path))).toString().toStdString();
|
||||
|
||||
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;
|
||||
|
||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
@ -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
|
||||
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
|
||||
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
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
@ -519,11 +531,22 @@ void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const
|
||||
QSqlQuery query {db};
|
||||
|
||||
try
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
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
|
||||
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;
|
||||
|
||||
|
@ -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
|
||||
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;
|
||||
|
@ -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
|
||||
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;
|
||||
|
@ -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()
|
||||
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()
|
||||
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
|
||||
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()
|
||||
{
|
||||
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)
|
||||
|
||||
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)
|
||||
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,7 +1780,19 @@ 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 ¶ms)
|
||||
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)
|
||||
#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;
|
||||
|
@ -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
|
||||
bool m_hasFirstLastPiecePriority = false;
|
||||
bool m_useAutoTMM;
|
||||
bool m_isStopped;
|
||||
StopCondition m_stopCondition;
|
||||
|
||||
bool m_unchecked = false;
|
||||
|
||||
|
@ -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()
|
||||
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();
|
||||
|
@ -203,37 +203,6 @@
|
||||
</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 "Download" page of the Options dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Do not delete .torrent file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="firstLastCheckBox">
|
||||
<property name="text">
|
||||
<string>Download first and last pieces first</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="skipCheckingCheckBox">
|
||||
<property name="text">
|
||||
<string>Skip hash check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<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">
|
||||
<property name="text">
|
||||
@ -241,6 +210,39 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="stopConditionLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="stopConditionLabel">
|
||||
<property name="text">
|
||||
<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">
|
||||
@ -254,6 +256,37 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="skipCheckingCheckBox">
|
||||
<property name="text">
|
||||
<string>Skip hash check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="sequentialCheckBox">
|
||||
<property name="text">
|
||||
<string>Download in sequential order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="firstLastCheckBox">
|
||||
<property name="text">
|
||||
<string>Download first and last pieces first</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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 "Download" page of the Options dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Do not delete .torrent file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -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()
|
||||
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
|
||||
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);
|
||||
|
@ -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>
|
||||
<tabstop>checkUseCustomTheme</tabstop>
|
||||
<tabstop>customThemeFilePath</tabstop>
|
||||
<tabstop>checkStartPaused</tabstop>
|
||||
<tabstop>stopConditionComboBox</tabstop>
|
||||
<tabstop>spinPort</tabstop>
|
||||
<tabstop>checkUPnP</tabstop>
|
||||
<tabstop>textWebUiUsername</tabstop>
|
||||
|
@ -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()
|
||||
addTorrentParams.sequential = seqDownload;
|
||||
addTorrentParams.firstLastPiecePriority = firstLastPiece;
|
||||
addTorrentParams.addPaused = addPaused;
|
||||
addTorrentParams.stopCondition = stopCondition;
|
||||
addTorrentParams.contentLayout = contentLayout;
|
||||
addTorrentParams.savePath = Path(savepath);
|
||||
addTorrentParams.downloadPath = Path(downloadPath);
|
||||
|
@ -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…
Reference in New Issue
Block a user