diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index f43688f57..a39fc748b 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -111,7 +111,7 @@ using namespace BitTorrent; namespace { bool readFile(const QString &path, QByteArray &buf); - bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri); + bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &prio, MagnetUri &magnetUri); void torrentQueuePositionUp(const libt::torrent_handle &handle); void torrentQueuePositionDown(const libt::torrent_handle &handle); @@ -2110,15 +2110,15 @@ bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams } // Add a torrent to the BitTorrent session -bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri, +bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri, TorrentInfo torrentInfo, const QByteArray &fastresumeData) { - addData.savePath = normalizeSavePath(addData.savePath, ""); + params.savePath = normalizeSavePath(params.savePath, ""); - if (!addData.category.isEmpty()) { - if (!m_categories.contains(addData.category) && !addCategory(addData.category)) { - qWarning() << "Couldn't create category" << addData.category; - addData.category = ""; + if (!params.category.isEmpty()) { + if (!m_categories.contains(params.category) && !addCategory(params.category)) { + qWarning() << "Couldn't create category" << params.category; + params.category = ""; } } @@ -2127,10 +2127,10 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri std::vector filePriorities; QString savePath; - if (addData.savePath.isEmpty()) // using Automatic mode - savePath = categorySavePath(addData.category); + if (params.savePath.isEmpty()) // using Automatic mode + savePath = categorySavePath(params.category); else // using Manual mode - savePath = addData.savePath; + savePath = params.savePath; bool fromMagnetUri = magnetUri.isValid(); if (fromMagnetUri) { @@ -2151,7 +2151,7 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri adjustLimits(); // use common 2nd step of torrent addition - m_addingTorrents.insert(hash, addData); + m_addingTorrents.insert(hash, params); createTorrentHandle(handle); return true; } @@ -2159,23 +2159,23 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri p = magnetUri.addTorrentParams(); } else if (torrentInfo.isValid()) { - if (!addData.resumed) { - if (!addData.hasRootFolder) + if (!params.restored) { + if (!params.hasRootFolder) torrentInfo.stripRootFolder(); // Metadata - if (!addData.hasSeedStatus) + if (!params.hasSeedStatus) findIncompleteFiles(torrentInfo, savePath); // if torrent name wasn't explicitly set we handle the case of // initial renaming of torrent content and rename torrent accordingly - if (addData.name.isEmpty()) { + if (params.name.isEmpty()) { QString contentName = torrentInfo.rootFolder(); if (contentName.isEmpty() && (torrentInfo.filesCount() == 1)) contentName = torrentInfo.fileName(0); if (!contentName.isEmpty() && (contentName != torrentInfo.name())) - addData.name = contentName; + params.name = contentName; } } @@ -2189,13 +2189,13 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri return false; } - if (addData.resumed && !fromMagnetUri) { + if (params.restored && !fromMagnetUri) { // Set torrent fast resume data p.resume_data = {fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()}; p.flags |= libt::add_torrent_params::flag_use_resume_save_path; } else { - foreach (int prio, addData.filePriorities) + foreach (int prio, params.filePriorities) filePriorities.push_back(prio); p.file_priorities = filePriorities; } @@ -2228,7 +2228,7 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri // Seeding mode // Skip checking and directly start seeding (new in libtorrent v0.15) - if (addData.skipChecking) + if (params.skipChecking) p.flags |= libt::add_torrent_params::flag_seed_mode; else p.flags &= ~libt::add_torrent_params::flag_seed_mode; @@ -2237,10 +2237,10 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri p.max_connections = maxConnectionsPerTorrent(); p.max_uploads = maxUploadsPerTorrent(); p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); - p.upload_limit = addData.uploadLimit; - p.download_limit = addData.downloadLimit; + p.upload_limit = params.uploadLimit; + p.download_limit = params.downloadLimit; - m_addingTorrents.insert(hash, addData); + m_addingTorrents.insert(hash, params); // Adding torrent to BitTorrent session m_nativeSession->async_add_torrent(p); return true; @@ -3849,7 +3849,7 @@ void Session::startUpTorrents() { QString hash; MagnetUri magnetUri; - AddTorrentData addTorrentData; + CreateTorrentParams addTorrentData; QByteArray data; } TorrentResumeData; @@ -3882,12 +3882,12 @@ void Session::startUpTorrents() QString hash = rxMatch.captured(1); QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName); QByteArray data; - AddTorrentData resumeData; + CreateTorrentParams torrentParams; MagnetUri magnetUri; int queuePosition; - if (readFile(fastresumePath, data) && loadTorrentResumeData(data, resumeData, queuePosition, magnetUri)) { + if (readFile(fastresumePath, data) && loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) { if (queuePosition <= nextQueuePosition) { - startupTorrent({ hash, magnetUri, resumeData, data }); + startupTorrent({ hash, magnetUri, torrentParams, data }); if (queuePosition == nextQueuePosition) { ++nextQueuePosition; @@ -3904,7 +3904,7 @@ void Session::startUpTorrents() if (q != queuePosition) { ++numOfRemappedFiles; } - queuedResumeData[q] = {hash, magnetUri, resumeData, data}; + queuedResumeData[q] = {hash, magnetUri, torrentParams, data}; } } } @@ -4108,25 +4108,19 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle) // Magnet added for preload its metadata if (!m_addingTorrents.contains(nativeHandle.info_hash())) return; - AddTorrentData data = m_addingTorrents.take(nativeHandle.info_hash()); + CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash()); - TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, data); + TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, params); m_torrents.insert(torrent->hash(), torrent); Logger *const logger = Logger::instance(); bool fromMagnetUri = !torrent->hasMetadata(); - if (data.resumed) { - if (fromMagnetUri && !data.addPaused) - torrent->resume(data.addForced); - - logger->addMessage(tr("'%1' resumed. (fast resume)", "'torrent name' was resumed. (fast resume)") - .arg(torrent->name())); + if (params.restored) { + logger->addMessage(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name())); } else { - qDebug("This is a NEW torrent (first time)..."); - // The following is useless for newly added magnet if (!fromMagnetUri) { // Backup torrent file @@ -4145,9 +4139,6 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle) if (isAddTrackersEnabled() && !torrent->isPrivate()) torrent->addTrackers(m_additionalTrackerList); - // Start torrent because it was added in paused state - if (!data.addPaused) - torrent->resume(); logger->addMessage(tr("'%1' added to download list.", "'torrent name' was added to download list.") .arg(torrent->name())); @@ -4163,7 +4154,7 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle) // Send torrent addition signal emit torrentAdded(torrent); // Send new torrent signal - if (!data.resumed) + if (!params.restored) emit torrentNew(torrent); } @@ -4513,11 +4504,11 @@ namespace return true; } - bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri) + bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &prio, MagnetUri &magnetUri) { - torrentData = AddTorrentData(); - torrentData.resumed = true; - torrentData.skipChecking = false; + torrentParams = CreateTorrentParams(); + torrentParams.restored = true; + torrentParams.skipChecking = false; libt::error_code ec; #if LIBTORRENT_VERSION_NUM < 10100 @@ -4530,36 +4521,36 @@ namespace if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false; #endif - torrentData.savePath = Profile::instance().fromPortablePath( + torrentParams.savePath = Profile::instance().fromPortablePath( Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath")))); std::string ratioLimitString = fast.dict_find_string_value("qBt-ratioLimit"); if (ratioLimitString.empty()) - torrentData.ratioLimit = fast.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; + torrentParams.ratioLimit = fast.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; else - torrentData.ratioLimit = QString::fromStdString(ratioLimitString).toDouble(); - torrentData.seedingTimeLimit = fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); + torrentParams.ratioLimit = QString::fromStdString(ratioLimitString).toDouble(); + torrentParams.seedingTimeLimit = fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); // ************************************************************************************** // Workaround to convert legacy label to category // TODO: Should be removed in future - torrentData.category = QString::fromStdString(fast.dict_find_string_value("qBt-label")); - if (torrentData.category.isEmpty()) + torrentParams.category = QString::fromStdString(fast.dict_find_string_value("qBt-label")); + if (torrentParams.category.isEmpty()) // ************************************************************************************** - torrentData.category = QString::fromStdString(fast.dict_find_string_value("qBt-category")); + torrentParams.category = QString::fromStdString(fast.dict_find_string_value("qBt-category")); // auto because the return type depends on the #if above. const auto tagsEntry = fast.dict_find_list("qBt-tags"); if (isList(tagsEntry)) - torrentData.tags = entryListToSet(tagsEntry); - torrentData.name = QString::fromStdString(fast.dict_find_string_value("qBt-name")); - torrentData.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus"); - torrentData.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled"); - torrentData.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder"); + torrentParams.tags = entryListToSet(tagsEntry); + torrentParams.name = QString::fromStdString(fast.dict_find_string_value("qBt-name")); + torrentParams.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus"); + torrentParams.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled"); + torrentParams.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder"); magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri"))); - torrentData.addPaused = fast.dict_find_int_value("qBt-paused"); - torrentData.addForced = fast.dict_find_int_value("qBt-forced"); - torrentData.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority"); - torrentData.sequential = fast.dict_find_int_value("qBt-sequential"); + torrentParams.paused = fast.dict_find_int_value("qBt-paused"); + torrentParams.forced = fast.dict_find_int_value("qBt-forced"); + torrentParams.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority"); + torrentParams.sequential = fast.dict_find_int_value("qBt-sequential"); prio = fast.dict_find_int_value("qBt-queuePosition"); diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 4153840a6..1d41eec03 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -138,7 +138,7 @@ namespace BitTorrent class Tracker; class MagnetUri; class TrackerEntry; - struct AddTorrentData; + struct CreateTorrentParams; struct TorrentStatusReport { @@ -598,7 +598,7 @@ namespace BitTorrent void enableIPFilter(); void disableIPFilter(); - bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri, + bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri, TorrentInfo torrentInfo = TorrentInfo(), const QByteArray &fastresumeData = QByteArray()); bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const; @@ -758,7 +758,7 @@ namespace BitTorrent QHash m_loadedMetadata; QHash m_torrents; - QHash m_addingTorrents; + QHash m_addingTorrents; QHash m_downloadedTorrents; QHash m_removingTorrents; TorrentStatusReport m_torrentStatusReport; diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index ea91a067e..cb6efb875 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -85,16 +85,16 @@ namespace // AddTorrentData -AddTorrentData::AddTorrentData() - : resumed(false) +CreateTorrentParams::CreateTorrentParams() + : restored(false) , disableTempPath(false) , sequential(false) , firstLastPiecePriority(false) , hasSeedStatus(false) , skipChecking(false) , hasRootFolder(true) - , addForced(false) - , addPaused(false) + , forced(false) + , paused(false) , uploadLimit(-1) , downloadLimit(-1) , ratioLimit(TorrentHandle::USE_GLOBAL_RATIO) @@ -102,8 +102,8 @@ AddTorrentData::AddTorrentData() { } -AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms) - : resumed(false) +CreateTorrentParams::CreateTorrentParams(const AddTorrentParams ¶ms) + : restored(false) , name(params.name) , category(params.category) , tags(params.tags) @@ -116,8 +116,8 @@ AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms) , hasRootFolder(params.createSubfolder == TriStateBool::Undefined ? Session::instance()->isCreateTorrentSubfolder() : params.createSubfolder == TriStateBool::True) - , addForced(params.addForced == TriStateBool::True) - , addPaused(params.addPaused == TriStateBool::Undefined + , forced(params.addForced == TriStateBool::True) + , paused(params.addPaused == TriStateBool::Undefined ? Session::instance()->isAddTorrentPaused() : params.addPaused == TriStateBool::True) , uploadLimit(params.uploadLimit) @@ -172,23 +172,23 @@ namespace } TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle, - const AddTorrentData &data) + const CreateTorrentParams ¶ms) : QObject(session) , m_session(session) , m_nativeHandle(nativeHandle) , m_state(TorrentState::Unknown) , m_renameCount(0) - , m_useAutoTMM(data.savePath.isEmpty()) - , m_name(data.name) - , m_savePath(Utils::Fs::toNativePath(data.savePath)) - , m_category(data.category) - , m_tags(data.tags) - , m_hasSeedStatus(data.hasSeedStatus) - , m_ratioLimit(data.ratioLimit) - , m_seedingTimeLimit(data.seedingTimeLimit) - , m_tempPathDisabled(data.disableTempPath) + , m_useAutoTMM(params.savePath.isEmpty()) + , m_name(params.name) + , m_savePath(Utils::Fs::toNativePath(params.savePath)) + , m_category(params.category) + , m_tags(params.tags) + , m_hasSeedStatus(params.hasSeedStatus) + , m_ratioLimit(params.ratioLimit) + , m_seedingTimeLimit(params.seedingTimeLimit) + , m_tempPathDisabled(params.disableTempPath) , m_hasMissingFiles(false) - , m_hasRootFolder(data.hasRootFolder) + , m_hasRootFolder(params.hasRootFolder) , m_needsToSetFirstLastPiecePriority(false) , m_pauseAfterRecheck(false) { @@ -206,15 +206,29 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle // download sequentially or have first/last piece priority enabled when // its resume data was saved. These two settings are restored later. But // if we set them to false now, both will erroneously not be restored. - if (!data.resumed || data.sequential) - setSequentialDownload(data.sequential); - if (!data.resumed || data.firstLastPiecePriority) - setFirstLastPiecePriority(data.firstLastPiecePriority); + if (!params.restored || params.sequential) + setSequentialDownload(params.sequential); + if (!params.restored || params.firstLastPiecePriority) + setFirstLastPiecePriority(params.firstLastPiecePriority); - if (!data.resumed && hasMetadata()) { + if (!params.restored && hasMetadata()) { if (filesCount() == 1) m_hasRootFolder = false; } + + // "started" means "all initialization has completed and torrent has started regular processing". + // When torrent added/restored in "paused" state it become "started" immediately after construction. + // When it is added/restored in "resumed" state, it become "started" after it is really resumed + // (i.e. after receiving "torrent resumed" alert). + m_started = (params.restored && hasMetadata() ? isPaused() : params.paused); + + if (!m_started) { + if (!params.restored || !hasMetadata()) { + // Resume torrent because it was added in "resumed" state + // but it's actually paused during initialization + resume(params.forced); + } + } } TorrentHandle::~TorrentHandle() {} @@ -1589,7 +1603,11 @@ void TorrentHandle::handleTorrentPausedAlert(const libtorrent::torrent_paused_al void TorrentHandle::handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p) { Q_UNUSED(p); - m_session->handleTorrentResumed(this); + + if (m_started) + m_session->handleTorrentResumed(this); + else + m_started = true; } void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p) diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index b225d5140..d5ad32bf7 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -88,10 +88,10 @@ namespace BitTorrent class TrackerEntry; struct AddTorrentParams; - struct AddTorrentData + struct CreateTorrentParams { - bool resumed; - // for both new and resumed torrents + bool restored; // is existing torrent job? + // for both new and restored torrents QString name; QString category; QSet tags; @@ -102,18 +102,18 @@ namespace BitTorrent bool hasSeedStatus; bool skipChecking; bool hasRootFolder; - bool addForced; - bool addPaused; + bool forced; + bool paused; int uploadLimit; int downloadLimit; // for new torrents QVector filePriorities; - // for resumed torrents + // for restored torrents qreal ratioLimit; int seedingTimeLimit; - AddTorrentData(); - AddTorrentData(const AddTorrentParams ¶ms); + CreateTorrentParams(); + CreateTorrentParams(const AddTorrentParams ¶ms); }; struct TrackerInfo @@ -170,7 +170,7 @@ namespace BitTorrent static const int MAX_SEEDING_TIME; TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle, - const AddTorrentData &data); + const CreateTorrentParams ¶ms); ~TorrentHandle(); bool isValid() const; @@ -462,6 +462,8 @@ namespace BitTorrent bool m_pauseAfterRecheck; QHash m_trackerInfos; + + bool m_started = false; }; }