diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 1eb0af257..7d00eef32 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -2253,15 +2253,13 @@ void Session::exportTorrentFile(const TorrentHandle *torrent, TorrentExportFolde } } -void Session::generateResumeData(const bool final) +void Session::generateResumeData() { for (TorrentHandleImpl *const torrent : asConst(m_torrents)) { if (!torrent->isValid()) continue; - if (!final && !torrent->needSaveResumeData()) continue; - if (torrent->isPaused()) continue; - - torrent->saveResumeData(); + if (torrent->needSaveResumeData()) + torrent->saveResumeData(); } } @@ -2273,10 +2271,10 @@ void Session::saveResumeData() if (isQueueingSystemEnabled()) saveTorrentsQueue(); - generateResumeData(true); + generateResumeData(); while (m_numResumeData > 0) { - const std::vector alerts = getPendingAlerts(lt::seconds(30)); + const std::vector alerts = getPendingAlerts(lt::seconds {30}); if (alerts.empty()) { LogMsg(tr("Error: Aborted saving resume data for %1 outstanding torrents.").arg(QString::number(m_numResumeData)) , Log::CRITICAL); @@ -3736,8 +3734,7 @@ void Session::handleTorrentMetadataReceived(TorrentHandleImpl *const torrent) void Session::handleTorrentPaused(TorrentHandleImpl *const torrent) { - if (!torrent->hasError() && !torrent->hasMissingFiles()) - torrent->saveResumeData(); + torrent->saveResumeData(); emit torrentPaused(torrent); } @@ -4069,6 +4066,19 @@ bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &m if (metadata.isValid()) p.ti = metadata.nativeInfo(); + torrentParams.paused = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed); + if (!torrentParams.paused) { + // If torrent has "stop_when_ready" flag set then it is actually "stopped" + // but temporarily "resumed" to perform some service jobs (e.g. checking) + torrentParams.paused = !!(p.flags & lt::torrent_flags::stop_when_ready); + } + else { + // Fix inconsistent state when "paused" torrent has "stop_when_ready" flag set + p.flags &= ~lt::torrent_flags::stop_when_ready; + } + + torrentParams.forced = !(p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed); + const bool hasMetadata = (p.ti && p.ti->is_valid()); if (!hasMetadata && !root.dict_find("info-hash")) { // TODO: The following code is deprecated. Remove after several releases in 4.3.x. diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 78fb2a731..ffabfc2de 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -533,7 +533,7 @@ namespace BitTorrent void readAlerts(); void enqueueRefresh(); void processShareLimits(); - void generateResumeData(bool final = false); + void generateResumeData(); void handleIPFilterParsed(int ruleCount); void handleIPFilterError(); void handleDownloadFinished(const Net::DownloadResult &result); diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index 0df2a9c7d..05671f938 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -49,6 +49,16 @@ namespace BitTorrent const qreal TorrentHandle::MAX_RATIO = 9999.; const int TorrentHandle::MAX_SEEDING_TIME = 525600; + bool TorrentHandle::isResumed() const + { + return !isPaused(); + } + + qlonglong TorrentHandle::remainingSize() const + { + return wantedSize() - completedSize(); + } + void TorrentHandle::toggleSequentialDownload() { setSequentialDownload(!isSequentialDownload()); diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index b457f26b3..d086a15f8 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -49,6 +49,12 @@ namespace BitTorrent class TrackerEntry; struct PeerAddress; + enum class TorrentOperatingMode + { + AutoManaged = 0, + Forced = 1 + }; + enum class TorrentState { Unknown = -1, @@ -110,7 +116,6 @@ namespace BitTorrent virtual qlonglong totalSize() const = 0; virtual qlonglong wantedSize() const = 0; virtual qlonglong completedSize() const = 0; - virtual qlonglong incompletedSize() const = 0; virtual qlonglong pieceLength() const = 0; virtual qlonglong wastedSize() const = 0; virtual QString currentTracker() const = 0; @@ -195,7 +200,6 @@ namespace BitTorrent virtual TorrentInfo info() const = 0; virtual bool isSeed() const = 0; virtual bool isPaused() const = 0; - virtual bool isResumed() const = 0; virtual bool isQueued() const = 0; virtual bool isForced() const = 0; virtual bool isChecking() const = 0; @@ -267,7 +271,7 @@ namespace BitTorrent virtual void setSequentialDownload(bool enable) = 0; virtual void setFirstLastPiecePriority(bool enabled) = 0; virtual void pause() = 0; - virtual void resume(bool forced = false) = 0; + virtual void resume(TorrentOperatingMode mode = TorrentOperatingMode::AutoManaged) = 0; virtual void move(QString path) = 0; virtual void forceReannounce(int index = -1) = 0; virtual void forceDHTAnnounce() = 0; @@ -289,6 +293,9 @@ namespace BitTorrent virtual QString createMagnetURI() const = 0; + bool isResumed() const; + qlonglong remainingSize() const; + void toggleSequentialDownload(); void toggleFirstLastPiecePriority(); }; diff --git a/src/base/bittorrent/torrenthandleimpl.cpp b/src/base/bittorrent/torrenthandleimpl.cpp index 751f71155..bd7da213f 100644 --- a/src/base/bittorrent/torrenthandleimpl.cpp +++ b/src/base/bittorrent/torrenthandleimpl.cpp @@ -112,10 +112,12 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle , m_tags(params.tags) , m_ratioLimit(params.ratioLimit) , m_seedingTimeLimit(params.seedingTimeLimit) + , m_operatingMode(params.forced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged) , m_hasSeedStatus(params.hasSeedStatus) , m_hasRootFolder(params.hasRootFolder) , m_hasFirstLastPiecePriority(params.firstLastPiecePriority) , m_useAutoTMM(params.savePath.isEmpty()) + , m_isStopped(params.paused) , m_ltAddTorrentParams(params.ltAddTorrentParams) { if (m_useAutoTMM) @@ -220,11 +222,6 @@ qlonglong TorrentHandleImpl::completedSize() const return m_nativeStatus.total_wanted_done; } -qlonglong TorrentHandleImpl::incompletedSize() const -{ - return (m_nativeStatus.total_wanted - m_nativeStatus.total_wanted_done); -} - qlonglong TorrentHandleImpl::pieceLength() const { return m_torrentInfo.pieceLength(); @@ -298,11 +295,6 @@ QString TorrentHandleImpl::actualStorageLocation() const return QString::fromStdString(m_nativeStatus.save_path); } -bool TorrentHandleImpl::isAutoManaged() const -{ - return static_cast(m_nativeStatus.flags & lt::torrent_flags::auto_managed); -} - void TorrentHandleImpl::setAutoManaged(const bool enable) { if (enable) @@ -466,7 +458,9 @@ bool TorrentHandleImpl::connectPeer(const PeerAddress &peerAddress) bool TorrentHandleImpl::needSaveResumeData() const { - return m_nativeHandle.need_save_resume_data(); + if (m_isStopped && !(m_nativeStatus.flags & lt::torrent_flags::auto_managed)) + return false; + return m_nativeStatus.need_save_resume; } void TorrentHandleImpl::saveResumeData() @@ -492,19 +486,18 @@ int TorrentHandleImpl::piecesHave() const qreal TorrentHandleImpl::progress() const { - if (!isChecking()) { - if (!m_nativeStatus.total_wanted) - return 0.; + if (isChecking()) + return m_nativeStatus.progress; - if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted) - return 1.; + if (m_nativeStatus.total_wanted == 0) + return 0.; - const qreal progress = static_cast(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted; - Q_ASSERT((progress >= 0.f) && (progress <= 1.f)); - return progress; - } + if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted) + return 1.; - return m_nativeStatus.progress; + const qreal progress = static_cast(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted; + Q_ASSERT((progress >= 0.f) && (progress <= 1.f)); + return progress; } QString TorrentHandleImpl::category() const @@ -629,19 +622,13 @@ TorrentInfo TorrentHandleImpl::info() const bool TorrentHandleImpl::isPaused() const { - return ((m_nativeStatus.flags & lt::torrent_flags::paused) - && !isAutoManaged()); -} - -bool TorrentHandleImpl::isResumed() const -{ - return !isPaused(); + return m_isStopped; } bool TorrentHandleImpl::isQueued() const { - return ((m_nativeStatus.flags & lt::torrent_flags::paused) - && isAutoManaged()); + // Torrent is Queued if it isn't in Paused state but paused internally + return ((m_nativeStatus.flags & lt::torrent_flags::paused) && !isPaused()); } bool TorrentHandleImpl::isChecking() const @@ -706,21 +693,13 @@ bool TorrentHandleImpl::isErrored() const bool TorrentHandleImpl::isSeed() const { - // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 - //bool result; - //result = m_nativeHandle.is_seed()); - //return result; - // May suffer from approximation problems - //return (progress() == 1.); - // This looks safe return ((m_nativeStatus.state == lt::torrent_status::finished) || (m_nativeStatus.state == lt::torrent_status::seeding)); } bool TorrentHandleImpl::isForced() const { - return (!(m_nativeStatus.flags & lt::torrent_flags::paused) - && !isAutoManaged()); + return (!isPaused() && (m_operatingMode == TorrentOperatingMode::Forced)); } bool TorrentHandleImpl::isSequentialDownload() const @@ -743,62 +722,49 @@ void TorrentHandleImpl::updateState() if (m_nativeStatus.state == lt::torrent_status::checking_resume_data) { m_state = TorrentState::CheckingResumeData; } - else if (isPaused()) { - if (isMoveInProgress()) { - m_state = TorrentState::Moving; - } - else if (hasMissingFiles()) { - m_state = TorrentState::MissingFiles; - } - else if (hasError()) { - m_state = TorrentState::Error; - } - else { - m_state = isSeed() ? TorrentState::PausedUploading : TorrentState::PausedDownloading; - } + else if (m_nativeStatus.state == lt::torrent_status::allocating) { + m_state = TorrentState::Allocating; + } + else if (isMoveInProgress()) { + m_state = TorrentState::Moving; + } + else if (hasMissingFiles()) { + m_state = TorrentState::MissingFiles; + } + else if (hasError()) { + m_state = TorrentState::Error; + } + 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))) { + // If the torrent is not just in the "checking" state, but is being actually checked + m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading; + } + else if (isSeed()) { + if (isPaused()) + m_state = TorrentState::PausedUploading; + else if (m_session->isQueueingSystemEnabled() && isQueued()) + m_state = TorrentState::QueuedUploading; + else if (isForced()) + m_state = TorrentState::ForcedUploading; + else if (m_nativeStatus.upload_payload_rate > 0) + m_state = TorrentState::Uploading; + else + m_state = TorrentState::StalledUploading; } else { - if (m_nativeStatus.state == lt::torrent_status::checking_files) { - m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading; - } - else if (m_nativeStatus.state == lt::torrent_status::allocating) { - m_state = TorrentState::Allocating; - } - else if (isMoveInProgress()) { - m_state = TorrentState::Moving; - } - else if (hasMissingFiles()) { - m_state = TorrentState::MissingFiles; - } - else if (hasError()) { - m_state = TorrentState::Error; - } - else if (m_session->isQueueingSystemEnabled() && isQueued() && !isChecking()) { - m_state = isSeed() ? TorrentState::QueuedUploading : TorrentState::QueuedDownloading; - } - else { - switch (m_nativeStatus.state) { - case lt::torrent_status::finished: - case lt::torrent_status::seeding: - if (isForced()) - m_state = TorrentState::ForcedUploading; - else - m_state = m_nativeStatus.upload_payload_rate > 0 ? TorrentState::Uploading : TorrentState::StalledUploading; - break; - case lt::torrent_status::downloading_metadata: - m_state = TorrentState::DownloadingMetadata; - break; - case lt::torrent_status::downloading: - if (isForced()) - m_state = TorrentState::ForcedDownloading; - else - m_state = m_nativeStatus.download_payload_rate > 0 ? TorrentState::Downloading : TorrentState::StalledDownloading; - break; - default: - qWarning("Unrecognized torrent status, should not happen!!! status was %d", m_nativeStatus.state); - m_state = TorrentState::Unknown; - } - } + if (isPaused()) + m_state = TorrentState::PausedDownloading; + else if (m_nativeStatus.state == lt::torrent_status::downloading_metadata) + m_state = TorrentState::DownloadingMetadata; + else if (m_session->isQueueingSystemEnabled() && isQueued()) + m_state = TorrentState::QueuedDownloading; + else if (isForced()) + m_state = TorrentState::ForcedDownloading; + else if (m_nativeStatus.download_payload_rate > 0) + m_state = TorrentState::Downloading; + else + m_state = TorrentState::StalledDownloading; } } @@ -1206,6 +1172,7 @@ void TorrentHandleImpl::forceRecheck() if (!hasMetadata()) return; m_nativeHandle.force_recheck(); + m_hasMissingFiles = false; m_unchecked = false; if (isPaused()) { @@ -1278,24 +1245,18 @@ void TorrentHandleImpl::applyFirstLastPiecePriority(const bool enabled, const QV void TorrentHandleImpl::pause() { - if (isPaused()) return; - setAutoManaged(false); m_nativeHandle.pause(); - // Libtorrent doesn't emit a torrent_paused_alert when the - // torrent is queued (no I/O) - // We test on the cached m_nativeStatus - if (isQueued()) - m_session->handleTorrentPaused(this); -} + m_speedMonitor.reset(); -void TorrentHandleImpl::resume(bool forced) -{ - resume_impl(forced); + if (!m_isStopped) { + m_isStopped = true; + m_session->handleTorrentPaused(this); + } } -void TorrentHandleImpl::resume_impl(bool forced) +void TorrentHandleImpl::resume(const TorrentOperatingMode mode) { if (hasError()) m_nativeHandle.clear_error(); @@ -1305,9 +1266,22 @@ void TorrentHandleImpl::resume_impl(bool forced) m_nativeHandle.force_recheck(); } - setAutoManaged(!forced); - if (forced) + 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); + } + + setAutoManaged(mode == TorrentOperatingMode::AutoManaged); + if (mode == TorrentOperatingMode::Forced) m_nativeHandle.resume(); + + m_operatingMode = mode; + + if (m_isStopped) { + m_isStopped = false; + m_session->handleTorrentResumed(this); + } } void TorrentHandleImpl::moveStorage(const QString &newPath, const MoveStorageMode mode) @@ -1442,18 +1416,11 @@ void TorrentHandleImpl::handleTorrentFinishedAlert(const lt::torrent_finished_al void TorrentHandleImpl::handleTorrentPausedAlert(const lt::torrent_paused_alert *p) { Q_UNUSED(p); - - updateStatus(); - m_speedMonitor.reset(); - - m_session->handleTorrentPaused(this); } void TorrentHandleImpl::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p) { Q_UNUSED(p); - - m_session->handleTorrentResumed(this); } void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) @@ -1463,7 +1430,17 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale m_ltAddTorrentParams = p->params; } - updateStatus(); + if (!m_isStopped) { + // Torrent can be actually "running" but temporarily "paused" to perform some + // service jobs behind the scenes so we need to restore it as "running" + if (m_operatingMode == TorrentOperatingMode::AutoManaged) { + m_ltAddTorrentParams.flags |= lt::torrent_flags::auto_managed; + } + else { + m_ltAddTorrentParams.flags &= ~lt::torrent_flags::paused; + m_ltAddTorrentParams.flags &= ~lt::torrent_flags::auto_managed; + } + } m_ltAddTorrentParams.added_time = addedTime().toSecsSinceEpoch(); m_ltAddTorrentParams.save_path = Profile::instance()->toPortablePath( @@ -1476,6 +1453,8 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale // === BEGIN DEPRECATED CODE === // const bool useDummyResumeData = !p; if (useDummyResumeData) { + updateStatus(); + resumeData["qBt-magnetUri"] = createMagnetURI().toStdString(); // sequentialDownload needs to be stored in the // resume data if there is no metadata, otherwise they won't be @@ -1516,7 +1495,7 @@ void TorrentHandleImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejec if (p->error.value() == lt::errors::mismatching_file_size) { // Mismatching file size (files were probably moved) m_hasMissingFiles = true; - LogMsg(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL); + LogMsg(tr("File sizes mismatch for torrent '%1'. Cannot proceed further.").arg(name()), Log::CRITICAL); } else { LogMsg(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...") @@ -1619,13 +1598,6 @@ void TorrentHandleImpl::handleMetadataReceivedAlert(const lt::metadata_received_ m_hasRootFolder = false; m_session->handleTorrentMetadataReceived(this); - if (isPaused()) { - // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert - // and the torrent can be paused when metadata is received - m_speedMonitor.reset(); - m_session->handleTorrentPaused(this); - } - // If first/last piece priority was specified when adding this torrent, // we should apply it now that we have metadata: if (m_hasFirstLastPiecePriority) diff --git a/src/base/bittorrent/torrenthandleimpl.h b/src/base/bittorrent/torrenthandleimpl.h index 8a2e23ae3..d0cfb7d31 100644 --- a/src/base/bittorrent/torrenthandleimpl.h +++ b/src/base/bittorrent/torrenthandleimpl.h @@ -101,7 +101,6 @@ namespace BitTorrent qlonglong totalSize() const override; qlonglong wantedSize() const override; qlonglong completedSize() const override; - qlonglong incompletedSize() const override; qlonglong pieceLength() const override; qlonglong wastedSize() const override; QString currentTracker() const override; @@ -143,7 +142,6 @@ namespace BitTorrent TorrentInfo info() const override; bool isSeed() const override; bool isPaused() const override; - bool isResumed() const override; bool isQueued() const override; bool isForced() const override; bool isChecking() const override; @@ -209,7 +207,7 @@ namespace BitTorrent void setSequentialDownload(bool enable) override; void setFirstLastPiecePriority(bool enabled) override; void pause() override; - void resume(bool forced = false) override; + void resume(TorrentOperatingMode mode = TorrentOperatingMode::AutoManaged) override; void move(QString path) override; void forceReannounce(int index = -1) override; void forceDHTAnnounce() override; @@ -270,9 +268,8 @@ namespace BitTorrent void handleTrackerReplyAlert(const lt::tracker_reply_alert *p); void handleTrackerWarningAlert(const lt::tracker_warning_alert *p); - void resume_impl(bool forced); bool isMoveInProgress() const; - bool isAutoManaged() const; + void setAutoManaged(bool enable); void adjustActualSavePath(); @@ -310,12 +307,14 @@ namespace BitTorrent QSet m_tags; qreal m_ratioLimit; int m_seedingTimeLimit; + TorrentOperatingMode m_operatingMode; bool m_hasSeedStatus; bool m_fastresumeDataRejected = false; bool m_hasMissingFiles = false; bool m_hasRootFolder; bool m_hasFirstLastPiecePriority = false; bool m_useAutoTMM; + bool m_isStopped; bool m_unchecked = false; diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 17e115e25..62f2d91ea 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -374,7 +374,7 @@ QString TransferListModel::displayValue(const BitTorrent::TorrentHandle *torrent case TR_AMOUNT_UPLOADED_SESSION: return unitString(torrent->totalPayloadUpload()); case TR_AMOUNT_LEFT: - return unitString(torrent->incompletedSize()); + return unitString(torrent->remainingSize()); case TR_TIME_ELAPSED: return timeElapsedString(torrent->activeTime(), torrent->seedingTime()); case TR_SAVE_PATH: @@ -442,7 +442,7 @@ QVariant TransferListModel::internalValue(const BitTorrent::TorrentHandle *torre case TR_AMOUNT_UPLOADED_SESSION: return torrent->totalPayloadUpload(); case TR_AMOUNT_LEFT: - return torrent->incompletedSize(); + return torrent->remainingSize(); case TR_TIME_ELAPSED: return !alt ? torrent->activeTime() : torrent->seedingTime(); case TR_SAVE_PATH: diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 9aa5a5fae..cb1e40521 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -357,7 +357,7 @@ void TransferListWidget::startSelectedTorrents() void TransferListWidget::forceStartSelectedTorrents() { for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents())) - torrent->resume(true); + torrent->resume(BitTorrent::TorrentOperatingMode::Forced); } void TransferListWidget::startVisibleTorrents() @@ -965,14 +965,24 @@ void TransferListWidget::displayListMenu(const QPoint &) allSameSuperSeeding = false; } } + if (!torrent->isForced()) needsForce = true; else needsStart = true; + if (torrent->isPaused()) needsStart = true; else needsPause = true; + + if (torrent->isErrored() || torrent->hasMissingFiles()) { + // If torrent is in "errored" or "missing files" state + // it cannot keep further processing until you restart it. + needsStart = true; + needsForce = true; + } + if (torrent->hasMetadata()) needsPreview = true; diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index 50df565e6..8d52f40f8 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -119,7 +119,7 @@ QVariantMap serialize(const BitTorrent::TorrentHandle &torrent) {KEY_TORRENT_AMOUNT_UPLOADED, torrent.totalUpload()}, {KEY_TORRENT_AMOUNT_DOWNLOADED_SESSION, torrent.totalPayloadDownload()}, {KEY_TORRENT_AMOUNT_UPLOADED_SESSION, torrent.totalPayloadUpload()}, - {KEY_TORRENT_AMOUNT_LEFT, torrent.incompletedSize()}, + {KEY_TORRENT_AMOUNT_LEFT, torrent.remainingSize()}, {KEY_TORRENT_AMOUNT_COMPLETED, torrent.completedSize()}, {KEY_TORRENT_MAX_RATIO, torrent.maxRatio()}, {KEY_TORRENT_MAX_SEEDING_TIME, torrent.maxSeedingTime()}, diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index d969428b1..5082fd73f 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -888,7 +888,10 @@ void TorrentsController::setForceStartAction() const bool value {parseBool(params()["value"], false)}; const QStringList hashes {params()["hashes"].split('|')}; - applyToTorrents(hashes, [value](BitTorrent::TorrentHandle *const torrent) { torrent->resume(value); }); + applyToTorrents(hashes, [value](BitTorrent::TorrentHandle *const torrent) + { + torrent->resume(value ? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged); + }); } void TorrentsController::deleteAction()