diff --git a/src/base/bittorrent/addtorrentparams.h b/src/base/bittorrent/addtorrentparams.h index 59aa3a42b..70f2095d8 100644 --- a/src/base/bittorrent/addtorrentparams.h +++ b/src/base/bittorrent/addtorrentparams.h @@ -32,7 +32,8 @@ #include #include -#include "../tristatebool.h" +#include "base/tristatebool.h" +#include "torrenthandle.h" namespace BitTorrent { @@ -50,11 +51,12 @@ namespace BitTorrent TriStateBool addForced; TriStateBool addPaused; QVector filePriorities; // used if TorrentInfo is set - bool ignoreShareLimits = false; bool skipChecking = false; TriStateBool createSubfolder; TriStateBool useAutoTMM; int uploadLimit = -1; int downloadLimit = -1; + int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME; + qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO; }; } diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index e89ea1590..63c7f9bd6 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -77,6 +77,7 @@ #include "base/profile.h" #include "base/torrentfileguard.h" #include "base/torrentfilter.h" +#include "base/tristatebool.h" #include "base/unicodestrings.h" #include "base/utils/bytearray.h" #include "base/utils/fs.h" @@ -111,68 +112,6 @@ namespace return QString::fromUtf8(str.data(), static_cast(str.size())); } - bool readFile(const QString &path, QByteArray &buf) - { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qDebug("Cannot read file %s: %s", qUtf8Printable(path), qUtf8Printable(file.errorString())); - return false; - } - - buf = file.readAll(); - return true; - } - - bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, MagnetUri &magnetUri) - { - lt::error_code ec; - const lt::bdecode_node root = lt::bdecode(data, ec); - if (ec || (root.type() != lt::bdecode_node::dict_t)) return false; - - torrentParams = CreateTorrentParams(); - - torrentParams.restored = true; - torrentParams.skipChecking = false; - torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name")); - torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category")); - torrentParams.savePath = Profile::instance()->fromPortablePath( - Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath")))); - torrentParams.disableTempPath = root.dict_find_int_value("qBt-tempPathDisabled"); - torrentParams.sequential = root.dict_find_int_value("qBt-sequential"); - torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus"); - torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority"); - torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder"); - torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); - - const bool isAutoManaged = root.dict_find_int_value("auto_managed"); - const bool isPaused = root.dict_find_int_value("paused"); - torrentParams.paused = root.dict_find_int_value("qBt-paused", (isPaused && !isAutoManaged)); - torrentParams.forced = root.dict_find_int_value("qBt-forced", (!isPaused && !isAutoManaged)); - - const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit"); - if (ratioLimitString.empty()) - torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; - else - torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble(); - - const lt::bdecode_node tagsNode = root.dict_find("qBt-tags"); - if (tagsNode.type() == lt::bdecode_node::list_t) { - for (int i = 0; i < tagsNode.list_size(); ++i) { - const QString tag = fromLTString(tagsNode.list_string_value_at(i)); - if (Session::isValidTag(tag)) - torrentParams.tags << tag; - } - } - - const lt::bdecode_node addedTimeNode = root.dict_find("qBt-addedTime"); - if (addedTimeNode.type() == lt::bdecode_node::int_t) - torrentParams.addedTime = QDateTime::fromSecsSinceEpoch(addedTimeNode.int_value()); - - magnetUri = MagnetUri(fromLTString(root.dict_find_string_value("qBt-magnetUri"))); - - return true; - } - void torrentQueuePositionUp(const lt::torrent_handle &handle) { try { @@ -1658,12 +1597,11 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result) switch (result.status) { case Net::DownloadStatus::Success: emit downloadFromUrlFinished(result.url); - addTorrent_impl(CreateTorrentParams(m_downloadedTorrents.take(result.url)) - , MagnetUri(), TorrentInfo::load(result.data)); + addTorrent(TorrentInfo::load(result.data), m_downloadedTorrents.take(result.url)); break; case Net::DownloadStatus::RedirectedToMagnet: emit downloadFromUrlFinished(result.url); - addTorrent_impl(CreateTorrentParams(m_downloadedTorrents.take(result.url)), MagnetUri(result.magnet)); + addTorrent(MagnetUri {result.magnet}, m_downloadedTorrents.take(result.url)); break; default: emit downloadFromUrlFailed(result.url, result.errorString); @@ -1934,11 +1872,10 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams ¶ms) const MagnetUri magnetUri {source}; if (magnetUri.isValid()) - return addTorrent_impl(CreateTorrentParams(params), magnetUri); + return addTorrent(magnetUri, params); - TorrentFileGuard guard(source); - if (addTorrent_impl(CreateTorrentParams(params) - , MagnetUri(), TorrentInfo::loadFromFile(source))) { + TorrentFileGuard guard {source}; + if (addTorrent(TorrentInfo::loadFromFile(source), params)) { guard.markAsAddedToSession(); return true; } @@ -1946,184 +1883,203 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams ¶ms) return false; } +bool Session::addTorrent(const MagnetUri &magnetUri, const AddTorrentParams ¶ms) +{ + if (!magnetUri.isValid()) return false; + + const InfoHash hash = magnetUri.hash(); + + const auto it = m_loadedMetadata.constFind(hash); + if (it != m_loadedMetadata.constEnd()) { + // Adding preloaded torrent... + const TorrentInfo metadata = it->metadata; + if (metadata.isValid()) { + // Metadata is received and torrent_handle is being deleted + // so we can't reuse it. Just add torrent using its metadata. + return addTorrent(metadata, params); + } + + // Reuse existing torrent_handle + lt::torrent_handle handle = m_nativeSession->find_torrent(hash); + + // Preloaded torrent is in "Upload mode" so we need to disable it + // otherwise the torrent never be downloaded (until application restart) + handle.unset_flags(lt::torrent_flags::upload_mode); + + LoadTorrentParams createTorrentParams = initLoadTorrentParams(params); + createTorrentParams.ltAddTorrentParams = it->ltAddTorrentParams; + + if (createTorrentParams.paused) { + // Preloaded torrent isn't auto managed already + handle.pause(); + } + else if (!createTorrentParams.forced) { + handle.set_flags(lt::torrent_flags::auto_managed); + handle.pause(); + } + + m_loadedMetadata.remove(hash); + m_loadingTorrents.insert(hash, createTorrentParams); + + --m_extraLimit; + adjustLimits(); + + // use common last step of torrent loading + createTorrentHandle(handle); + return true; + } + + return addTorrent_impl(params, magnetUri); +} + bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms) { if (!torrentInfo.isValid()) return false; - return addTorrent_impl(CreateTorrentParams(params), MagnetUri(), torrentInfo); + return addTorrent_impl(params, MagnetUri(), torrentInfo); +} + +LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorrentParams) +{ + LoadTorrentParams loadTorrentParams; + + loadTorrentParams.name = addTorrentParams.name; + loadTorrentParams.tags = addTorrentParams.tags; + loadTorrentParams.disableTempPath = addTorrentParams.disableTempPath; + loadTorrentParams.sequential = addTorrentParams.sequential; + loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority; + loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping + loadTorrentParams.hasRootFolder = ((addTorrentParams.createSubfolder == TriStateBool::Undefined) + ? isKeepTorrentTopLevelFolder() + : (addTorrentParams.createSubfolder == TriStateBool::True)); + loadTorrentParams.forced = (addTorrentParams.addForced == TriStateBool::True); + loadTorrentParams.paused = ((addTorrentParams.addPaused == TriStateBool::Undefined) + ? isAddTorrentPaused() + : (addTorrentParams.addPaused == TriStateBool::True)); + loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit; + loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit; + + const bool useAutoTMM = ((addTorrentParams.useAutoTMM == TriStateBool::Undefined) + ? !isAutoTMMDisabledByDefault() + : (addTorrentParams.useAutoTMM == TriStateBool::True)); + if (useAutoTMM) + loadTorrentParams.savePath = ""; + else if (addTorrentParams.savePath.trimmed().isEmpty()) + loadTorrentParams.savePath = defaultSavePath(); + else + loadTorrentParams.savePath = normalizePath(addTorrentParams.savePath); + + const QString category = addTorrentParams.category; + if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category)) + loadTorrentParams.category = ""; + else + loadTorrentParams.category = addTorrentParams.category; + + return loadTorrentParams; } // Add a torrent to the BitTorrent session -bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri, - TorrentInfo torrentInfo, const QByteArray &fastresumeData) +bool Session::addTorrent_impl(const AddTorrentParams &addTorrentParams, const MagnetUri &magnetUri, TorrentInfo metadata) { - params.savePath = normalizeSavePath(params.savePath, ""); - - if (!params.category.isEmpty()) { - if (!m_categories.contains(params.category) && !addCategory(params.category)) - params.category = ""; - } - - const bool fromMagnetUri = magnetUri.isValid(); - lt::add_torrent_params p; - InfoHash hash; - - if (fromMagnetUri) { - hash = magnetUri.hash(); - - const auto it = m_loadedMetadata.constFind(hash); - if (it != m_loadedMetadata.constEnd()) { - // Adding preloaded torrent... - const TorrentInfo metadata = it.value(); - if (metadata.isValid()) { - // Metadata is received and torrent_handle is being deleted - // so we can't reuse it. Just add torrent using its metadata. - return addTorrent_impl(params - , MagnetUri {}, metadata, fastresumeData); - } - - // Reuse existing torrent_handle - lt::torrent_handle handle = m_nativeSession->find_torrent(hash); - - // Preloaded torrent is in "Upload mode" so we need to disable it - // otherwise the torrent never be downloaded (until application restart) - handle.unset_flags(lt::torrent_flags::upload_mode); - - if (params.paused) { - // Preloaded torrent isn't auto managed already - handle.pause(); - } - else if (!params.forced) { - handle.set_flags(lt::torrent_flags::auto_managed); - handle.pause(); - } - - - m_loadedMetadata.remove(hash); - m_addingTorrents.insert(hash, params); - - --m_extraLimit; - adjustLimits(); - - // use common 2nd step of torrent addition - createTorrentHandle(handle); - return true; - } - - p = magnetUri.addTorrentParams(); - if (isTempPathEnabled()) { - p.save_path = Utils::Fs::toNativePath(tempPath()).toStdString(); - } - else { - // If empty then Automatic mode, otherwise Manual mode - const QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath; - p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); - } - } - else { - if (!torrentInfo.isValid()) { - // We can have an invalid torrentInfo when there isn't a matching - // .torrent file to the .fastresume we loaded. Possibly from a - // failed upgrade. - return false; - } - - hash = torrentInfo.hash(); - } + const bool hasMetadata = metadata.isValid(); + const InfoHash hash = (hasMetadata ? metadata.hash() : magnetUri.hash()); // We should not add the torrent if it is already // processed or is pending to add to session - if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) + if (m_loadingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) return false; TorrentHandleImpl *const torrent = m_torrents.value(hash); if (torrent) { // a duplicate torrent is added - if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate())) + if (torrent->isPrivate() || (hasMetadata && metadata.isPrivate())) return false; // merge trackers and web seeds - torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers()); - torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds()); + torrent->addTrackers(hasMetadata ? metadata.trackers() : magnetUri.trackers()); + torrent->addUrlSeeds(hasMetadata ? metadata.urlSeeds() : magnetUri.urlSeeds()); return true; } - if (!fromMagnetUri) { - if (params.restored) { // load from existing fastresume - lt::error_code ec; - p = lt::read_resume_data(fastresumeData, ec); - p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString(); - } - else { // new torrent - if (!params.hasRootFolder) - torrentInfo.stripRootFolder(); + LoadTorrentParams loadTorrentParams = initLoadTorrentParams(addTorrentParams); + lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams; - // If empty then Automatic mode, otherwise Manual mode - QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath; - // Metadata - if (!params.hasSeedStatus) - findIncompleteFiles(torrentInfo, savePath); // if needed points savePath to incomplete folder too - p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); + // If empty then Automatic mode, otherwise Manual mode + QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath; + if (hasMetadata) { + if (!loadTorrentParams.hasRootFolder) + metadata.stripRootFolder(); - // if torrent name wasn't explicitly set we handle the case of - // initial renaming of torrent content and rename torrent accordingly - if (params.name.isEmpty()) { - QString contentName = torrentInfo.rootFolder(); - if (contentName.isEmpty() && (torrentInfo.filesCount() == 1)) - contentName = torrentInfo.fileName(0); + if (!loadTorrentParams.hasSeedStatus) + findIncompleteFiles(metadata, actualSavePath); // if needed points savePath to incomplete folder too - if (!contentName.isEmpty() && (contentName != torrentInfo.name())) - params.name = contentName; - } + // if torrent name wasn't explicitly set we handle the case of + // initial renaming of torrent content and rename torrent accordingly + if (loadTorrentParams.name.isEmpty()) { + QString contentName = metadata.rootFolder(); + if (contentName.isEmpty() && (metadata.filesCount() == 1)) + contentName = metadata.fileName(0); - Q_ASSERT(p.file_priorities.empty()); - std::transform(params.filePriorities.cbegin(), params.filePriorities.cend() - , std::back_inserter(p.file_priorities), [](const DownloadPriority priority) - { - return static_cast( - static_cast(priority)); - }); + if (!contentName.isEmpty() && (contentName != metadata.name())) + loadTorrentParams.name = contentName; } - p.ti = torrentInfo.nativeInfo(); + Q_ASSERT(p.file_priorities.empty()); + std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend() + , std::back_inserter(p.file_priorities), [](const DownloadPriority priority) + { + return static_cast( + static_cast(priority)); + }); + + p.ti = metadata.nativeInfo(); + } + else { + p = magnetUri.addTorrentParams(); + if (isTempPathEnabled()) + actualSavePath = tempPath(); } - if (fromMagnetUri && params.restored && params.addedTime.isValid()) - p.added_time = params.addedTime.toSecsSinceEpoch(); + p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString(); - if (fromMagnetUri || !params.restored) { - p.upload_limit = params.uploadLimit; - p.download_limit = params.downloadLimit; + p.upload_limit = addTorrentParams.uploadLimit; + p.download_limit = addTorrentParams.downloadLimit; - // Preallocation mode - p.storage_mode = isPreallocationEnabled() - ? lt::storage_mode_allocate : lt::storage_mode_sparse; + // Preallocation mode + p.storage_mode = isPreallocationEnabled() ? lt::storage_mode_allocate : lt::storage_mode_sparse; - // Seeding mode - // Skip checking and directly start seeding - if (params.skipChecking) - p.flags |= lt::torrent_flags::seed_mode; - else - p.flags &= ~lt::torrent_flags::seed_mode; - } + // Seeding mode + // Skip checking and directly start seeding + if (addTorrentParams.skipChecking) + p.flags |= lt::torrent_flags::seed_mode; + else + p.flags &= ~lt::torrent_flags::seed_mode; - // Common - p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked - if (params.paused || !params.forced) + if (loadTorrentParams.paused || !loadTorrentParams.forced) p.flags |= lt::torrent_flags::paused; else p.flags &= ~lt::torrent_flags::paused; - if (params.paused || params.forced) + if (loadTorrentParams.paused || loadTorrentParams.forced) p.flags &= ~lt::torrent_flags::auto_managed; else p.flags |= lt::torrent_flags::auto_managed; + return loadTorrent(loadTorrentParams); +} + +// Add a torrent to the BitTorrent session +bool Session::loadTorrent(LoadTorrentParams params) +{ + lt::add_torrent_params &p = params.ltAddTorrentParams; + + p.storage = customStorageConstructor; // Limits p.max_connections = maxConnectionsPerTorrent(); p.max_uploads = maxUploadsPerTorrent(); - p.storage = customStorageConstructor; + const bool hasMetadata = (p.ti && p.ti->is_valid()); + const InfoHash hash = (hasMetadata ? p.ti->info_hash() : p.info_hash); + m_loadingTorrents.insert(hash, params); - m_addingTorrents.insert(hash, params); // Adding torrent to BitTorrent session m_nativeSession->async_add_torrent(p); @@ -2171,7 +2127,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri) // We should not add torrent if it's already // processed or adding to session if (m_torrents.contains(hash)) return false; - if (m_addingTorrents.contains(hash)) return false; + if (m_loadingTorrents.contains(hash)) return false; if (m_loadedMetadata.contains(hash)) return false; qDebug("Adding torrent to preload metadata..."); @@ -2209,7 +2165,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri) if (ec) return false; // waiting for metadata... - m_loadedMetadata.insert(h.info_hash(), TorrentInfo()); + m_loadedMetadata.insert(h.info_hash(), {p, TorrentInfo {}}); ++m_extraLimit; adjustLimits(); @@ -2246,10 +2202,7 @@ void Session::generateResumeData(const bool final) if (!torrent->isValid()) continue; if (!final && !torrent->needSaveResumeData()) continue; - if (torrent->isPaused() - || torrent->hasError() - || torrent->hasMissingFiles()) - continue; + if (torrent->isPaused()) continue; torrent->saveResumeData(); } @@ -3519,7 +3472,7 @@ void Session::setMaxRatioAction(const MaxRatioAction act) bool Session::isKnownTorrent(const InfoHash &hash) const { return (m_torrents.contains(hash) - || m_addingTorrents.contains(hash) + || m_loadingTorrents.contains(hash) || m_loadedMetadata.contains(hash)); } @@ -3711,12 +3664,6 @@ void Session::handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, con #endif } -void Session::handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent) -{ - Q_UNUSED(torrent) - --m_numResumeData; -} - void Session::handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl) { emit trackerSuccess(torrent, trackerUrl); @@ -3935,36 +3882,106 @@ const CacheStatus &Session::cacheStatus() const return m_cacheStatus; } +bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams) +{ + torrentParams = {}; + + lt::error_code ec; + const lt::bdecode_node root = lt::bdecode(data, ec); + if (ec || (root.type() != lt::bdecode_node::dict_t)) return false; + + torrentParams.restored = true; + torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category")); + torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name")); + torrentParams.savePath = Profile::instance()->fromPortablePath( + Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath")))); + torrentParams.disableTempPath = root.dict_find_int_value("qBt-tempPathDisabled"); + torrentParams.sequential = root.dict_find_int_value("qBt-sequential"); + torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus"); + torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority"); + torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder"); + torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); + + const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit"); + if (ratioLimitString.empty()) + torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; + else + torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble(); + + const lt::bdecode_node tagsNode = root.dict_find("qBt-tags"); + if (tagsNode.type() == lt::bdecode_node::list_t) { + for (int i = 0; i < tagsNode.list_size(); ++i) { + const QString tag = fromLTString(tagsNode.list_string_value_at(i)); + if (Session::isValidTag(tag)) + torrentParams.tags << tag; + } + } + + // NOTE: Do we really need the following block in case of existing (restored) torrent? + torrentParams.savePath = normalizePath(torrentParams.savePath); + if (!torrentParams.category.isEmpty()) { + if (!m_categories.contains(torrentParams.category) && !addCategory(torrentParams.category)) + torrentParams.category = ""; + } + + lt::add_torrent_params &p = torrentParams.ltAddTorrentParams; + + p = lt::read_resume_data(root, ec); + p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString(); + if (metadata.isValid()) + p.ti = metadata.nativeInfo(); + + 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. + // === BEGIN DEPRECATED CODE === // + // Try to load from legacy data used in older versions for torrents w/o metadata + const lt::bdecode_node magnetURINode = root.dict_find("qBt-magnetUri"); + if (magnetURINode.type() == lt::bdecode_node::string_t) { + lt::parse_magnet_uri(magnetURINode.string_value(), p, ec); + + if (isTempPathEnabled()) { + p.save_path = Utils::Fs::toNativePath(tempPath()).toStdString(); + } + else { + // If empty then Automatic mode, otherwise Manual mode + const QString savePath = torrentParams.savePath.isEmpty() ? categorySavePath(torrentParams.category) : torrentParams.savePath; + p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); + } + + // Preallocation mode + p.storage_mode = (isPreallocationEnabled() ? lt::storage_mode_allocate : lt::storage_mode_sparse); + + const lt::bdecode_node addedTimeNode = root.dict_find("qBt-addedTime"); + if (addedTimeNode.type() == lt::bdecode_node::int_t) + p.added_time = addedTimeNode.int_value(); + } + // === END DEPRECATED CODE === // + else { + return false; + } + } + + return true; +} + // Will resume torrents in backup directory void Session::startUpTorrents() { - qDebug("Resuming torrents..."); - - const QDir resumeDataDir(m_resumeFolderPath); + const QDir resumeDataDir {m_resumeFolderPath}; QStringList fastresumes = resumeDataDir.entryList( QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted); - struct TorrentResumeData + const auto readFile = [](const QString &path, QByteArray &buf) -> bool { - QString hash; - MagnetUri magnetUri; - CreateTorrentParams addTorrentData; - QByteArray data; - }; + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING); + return false; + } - int resumedTorrentsCount = 0; - const auto startupTorrent = [this, &resumeDataDir, &resumedTorrentsCount](const TorrentResumeData ¶ms) - { - const QString filePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(params.hash)); - qDebug() << "Starting up torrent" << params.hash << "..."; - if (!addTorrent_impl(params.addTorrentData, params.magnetUri, TorrentInfo::loadFromFile(filePath), params.data)) - LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") - .arg(params.hash), Log::CRITICAL); - - // process add torrent messages before message queue overflow - if ((resumedTorrentsCount % 100) == 0) readAlerts(); - - ++resumedTorrentsCount; + buf = file.readAll(); + return true; }; qDebug("Starting up torrents..."); @@ -3989,6 +4006,7 @@ void Session::startUpTorrents() fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values(); } + int resumedTorrentsCount = 0; for (const QString &fastresumeName : asConst(fastresumes)) { const QRegularExpressionMatch rxMatch = rx.match(fastresumeName); if (!rxMatch.hasMatch()) continue; @@ -3996,11 +4014,23 @@ void Session::startUpTorrents() const QString hash = rxMatch.captured(1); const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName); QByteArray data; - CreateTorrentParams torrentParams; - MagnetUri magnetUri; - if (readFile(fastresumePath, data) - && loadTorrentResumeData(data, torrentParams, magnetUri)) { - startupTorrent({hash, magnetUri, torrentParams, data}); + LoadTorrentParams torrentParams; + const QString torrentFilePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(hash)); + TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath); + if (readFile(fastresumePath, data) && loadTorrentResumeData(data, metadata, torrentParams)) { + qDebug() << "Starting up torrent" << hash << "..."; + if (!loadTorrent(torrentParams)) + LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") + .arg(hash), Log::CRITICAL); + + // process add torrent messages before message queue overflow + if ((resumedTorrentsCount % 100) == 0) readAlerts(); + + ++resumedTorrentsCount; + } + else { + LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") + .arg(hash), Log::CRITICAL); } } } @@ -4184,22 +4214,21 @@ void Session::dispatchTorrentAlert(const lt::alert *a) void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) { - // Magnet added for preload its metadata - if (!m_addingTorrents.contains(nativeHandle.info_hash())) return; + Q_ASSERT(m_loadingTorrents.contains(nativeHandle.info_hash())); - const CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash()); + const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash()); - TorrentHandleImpl *const torrent = new TorrentHandleImpl(this, nativeHandle, params); + TorrentHandleImpl *const torrent = new TorrentHandleImpl {this, nativeHandle, params}; m_torrents.insert(torrent->hash(), torrent); - const bool fromMagnetUri = !torrent->hasMetadata(); + const bool hasMetadata = torrent->hasMetadata(); if (params.restored) { LogMsg(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name())); } else { // The following is useless for newly added magnet - if (!fromMagnetUri) { + if (hasMetadata) { // Backup torrent file const QDir resumeDataDir {m_resumeFolderPath}; const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())}; @@ -4231,10 +4260,10 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) m_seedingLimitTimer->start(); // Send torrent addition signal - emit torrentAdded(torrent); + emit torrentLoaded(torrent); // Send new torrent signal if (!params.restored) - emit torrentNew(torrent); + emit torrentAdded(torrent); // Torrent could have error just after adding to libtorrent if (torrent->hasError()) @@ -4242,8 +4271,8 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) #if (LIBTORRENT_VERSION_NUM < 10208) // Check if file(s) exist when using skip hash check - if (params.skipChecking && torrent->hasMetadata()) - nativeHandle.read_piece(lt::piece_index_t(0)); + if (nativeHandle.flags() & lt::torrent_flags::seed_mode) + nativeHandle.read_piece(lt::piece_index_t {0}); #endif } @@ -4252,10 +4281,10 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p) if (p->error) { qDebug("/!\\ Error: Failed to add torrent!"); QString msg = QString::fromStdString(p->message()); - LogMsg(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING); - emit addTorrentFailed(msg); + LogMsg(tr("Couldn't load torrent. Reason: %1").arg(msg), Log::WARNING); + emit loadTorrentFailed(msg); } - else { + else if (m_loadingTorrents.contains(p->handle.info_hash())) { createTorrentHandle(p->handle); } } @@ -4266,7 +4295,7 @@ void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p) const auto loadedMetadataIter = m_loadedMetadata.find(infoHash); if (loadedMetadataIter != m_loadedMetadata.end()) { - emit metadataLoaded(*loadedMetadataIter); + emit metadataLoaded(loadedMetadataIter->metadata); m_loadedMetadata.erase(loadedMetadataIter); } @@ -4323,7 +4352,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p) if (loadedMetadataIter != m_loadedMetadata.end()) { --m_extraLimit; adjustLimits(); - *loadedMetadataIter = TorrentInfo(p->handle.torrent_file()); + loadedMetadataIter->metadata = TorrentInfo {p->handle.torrent_file()}; m_nativeSession->remove_torrent(p->handle, lt::session::delete_files); } } diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 89bf98e4f..ee0bfce46 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -102,7 +103,7 @@ namespace BitTorrent class TorrentHandleImpl; class Tracker; class TrackerEntry; - struct CreateTorrentParams; + struct LoadTorrentParams; enum class MoveStorageMode; @@ -437,6 +438,7 @@ namespace BitTorrent bool isKnownTorrent(const InfoHash &hash) const; bool addTorrent(const QString &source, const AddTorrentParams ¶ms = AddTorrentParams()); + bool addTorrent(const MagnetUri &magnetUri, const AddTorrentParams ¶ms = AddTorrentParams()); bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = AddTorrentParams()); bool deleteTorrent(const InfoHash &hash, DeleteOption deleteOption = Torrent); bool loadMetadata(const MagnetUri &magnetUri); @@ -468,7 +470,6 @@ namespace BitTorrent void handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector &newUrlSeeds); void handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector &urlSeeds); void handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr &data); - void handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent); void handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl); void handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl); void handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl); @@ -476,7 +477,6 @@ namespace BitTorrent bool addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, MoveStorageMode mode); signals: - void addTorrentFailed(const QString &error); void allTorrentsFinished(); void categoryAdded(const QString &categoryName); void categoryRemoved(const QString &categoryName); @@ -484,6 +484,7 @@ namespace BitTorrent void downloadFromUrlFinished(const QString &url); void fullDiskError(BitTorrent::TorrentHandle *const torrent, const QString &msg); void IPFilterParsed(bool error, int ruleCount); + void loadTorrentFailed(const QString &error); void metadataLoaded(const BitTorrent::TorrentInfo &info); void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent); void speedLimitModeChanged(bool alternative); @@ -496,8 +497,8 @@ namespace BitTorrent void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory); void torrentFinished(BitTorrent::TorrentHandle *const torrent); void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent); + void torrentLoaded(BitTorrent::TorrentHandle *const torrent); void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent); - void torrentNew(BitTorrent::TorrentHandle *const torrent); void torrentPaused(BitTorrent::TorrentHandle *const torrent); void torrentResumed(BitTorrent::TorrentHandle *const torrent); void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent); @@ -576,9 +577,10 @@ namespace BitTorrent void applyOSMemoryPriority() const; #endif - bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri, - TorrentInfo torrentInfo = TorrentInfo(), - const QByteArray &fastresumeData = {}); + bool loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams); + bool loadTorrent(LoadTorrentParams params); + LoadTorrentParams initLoadTorrentParams(const AddTorrentParams &addTorrentParams); + bool addTorrent_impl(const AddTorrentParams &addTorrentParams, const MagnetUri &magnetUri, TorrentInfo torrentInfo = TorrentInfo()); bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const; void updateSeedingLimitTimer(); @@ -744,9 +746,15 @@ namespace BitTorrent QThread *m_ioThread = nullptr; ResumeDataSavingManager *m_resumeDataSavingManager = nullptr; - QHash m_loadedMetadata; + struct LoadedMetadataHandle + { + lt::add_torrent_params ltAddTorrentParams {}; + TorrentInfo metadata; + }; + QHash m_loadedMetadata; + QHash m_torrents; - QHash m_addingTorrents; + QHash m_loadingTorrents; QHash m_downloadedTorrents; QHash m_removingTorrents; QStringMap m_categories; diff --git a/src/base/bittorrent/torrenthandleimpl.cpp b/src/base/bittorrent/torrenthandleimpl.cpp index ff8e8a813..d1167e74e 100644 --- a/src/base/bittorrent/torrenthandleimpl.cpp +++ b/src/base/bittorrent/torrenthandleimpl.cpp @@ -57,7 +57,6 @@ #include "base/logger.h" #include "base/preferences.h" #include "base/profile.h" -#include "base/tristatebool.h" #include "base/utils/fs.h" #include "base/utils/string.h" #include "common.h" @@ -111,44 +110,10 @@ namespace } } -// CreateTorrentParams - -CreateTorrentParams::CreateTorrentParams(const AddTorrentParams ¶ms) - : name(params.name) - , category(params.category) - , tags(params.tags) - , savePath(params.savePath) - , uploadLimit(params.uploadLimit) - , downloadLimit(params.downloadLimit) - , disableTempPath(params.disableTempPath) - , sequential(params.sequential) - , firstLastPiecePriority(params.firstLastPiecePriority) - , hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping - , skipChecking(params.skipChecking) - , hasRootFolder(params.createSubfolder == TriStateBool::Undefined - ? Session::instance()->isKeepTorrentTopLevelFolder() - : params.createSubfolder == TriStateBool::True) - , forced(params.addForced == TriStateBool::True) - , paused(params.addPaused == TriStateBool::Undefined - ? Session::instance()->isAddTorrentPaused() - : params.addPaused == TriStateBool::True) - , filePriorities(params.filePriorities) - , ratioLimit(params.ignoreShareLimits ? TorrentHandleImpl::NO_RATIO_LIMIT : TorrentHandleImpl::USE_GLOBAL_RATIO) - , seedingTimeLimit(params.ignoreShareLimits ? TorrentHandleImpl::NO_SEEDING_TIME_LIMIT : TorrentHandleImpl::USE_GLOBAL_SEEDING_TIME) -{ - bool useAutoTMM = (params.useAutoTMM == TriStateBool::Undefined - ? !Session::instance()->isAutoTMMDisabledByDefault() - : params.useAutoTMM == TriStateBool::True); - if (useAutoTMM) - savePath = ""; - else if (savePath.trimmed().isEmpty()) - savePath = Session::instance()->defaultSavePath(); -} - // TorrentHandleImpl TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle, - const CreateTorrentParams ¶ms) + const LoadTorrentParams ¶ms) : QObject(session) , m_session(session) , m_nativeHandle(nativeHandle) @@ -162,6 +127,7 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle , m_tempPathDisabled(params.disableTempPath) , m_hasRootFolder(params.hasRootFolder) , m_useAutoTMM(params.savePath.isEmpty()) + , m_ltAddTorrentParams(params.ltAddTorrentParams) { if (m_useAutoTMM) m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category)); @@ -1505,18 +1471,36 @@ void TorrentHandleImpl::handleTorrentResumedAlert(const lt::torrent_resumed_aler void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) { - const bool useDummyResumeData = !p; - auto resumeDataPtr = std::make_shared(useDummyResumeData - ? lt::entry {} - : lt::write_resume_data(p->params)); - lt::entry &resumeData = *resumeDataPtr; + if (p && !m_hasMissingFiles) { + // Update recent resume data + m_ltAddTorrentParams = p->params; + } updateStatus(); + m_ltAddTorrentParams.added_time = addedTime().toSecsSinceEpoch(); + m_ltAddTorrentParams.save_path = Profile::instance()->toPortablePath( + QString::fromStdString(m_ltAddTorrentParams.save_path)).toStdString(); + if (!m_hasMissingFiles) { + m_ltAddTorrentParams.flags = m_nativeStatus.flags; + if (m_nativeStatus.flags & lt::torrent_flags::stop_when_ready) { + // We need to redefine these values when torrent starting/rechecking + // in "paused" state since native values can be logically wrong + // (torrent can be not paused and auto_managed when it is checking). + m_ltAddTorrentParams.flags |= lt::torrent_flags::paused; + m_ltAddTorrentParams.flags &= ~lt::torrent_flags::auto_managed; + m_ltAddTorrentParams.flags &= ~lt::torrent_flags::stop_when_ready; + } + } + + auto resumeDataPtr = std::make_shared(lt::write_resume_data(m_ltAddTorrentParams)); + lt::entry &resumeData = *resumeDataPtr; + + // TODO: The following code is deprecated. Remove after several releases in 4.3.x. + // === BEGIN DEPRECATED CODE === // + const bool useDummyResumeData = !p; if (useDummyResumeData) { resumeData["qBt-magnetUri"] = createMagnetURI().toStdString(); - resumeData["paused"] = isPaused(); - resumeData["auto_managed"] = isAutoManaged(); // Both firstLastPiecePriority and sequential need to be stored in the // resume data if there is no metadata, otherwise they won't be // restored if qBittorrent quits before the metadata are retrieved: @@ -1525,10 +1509,8 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale resumeData["qBt-addedTime"] = addedTime().toSecsSinceEpoch(); } - else { - const auto savePath = resumeData.find_key("save_path")->string(); - resumeData["save_path"] = Profile::instance()->toPortablePath(QString::fromStdString(savePath)).toStdString(); - } + // === END DEPRECATED CODE === // + resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance()->toPortablePath(m_savePath).toStdString(); resumeData["qBt-ratioLimit"] = static_cast(m_ratioLimit * 1000); resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit; @@ -1537,32 +1519,19 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale resumeData["qBt-name"] = m_name.toStdString(); resumeData["qBt-seedStatus"] = m_hasSeedStatus; resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled; - resumeData["qBt-queuePosition"] = (static_cast(nativeHandle().queue_position()) + 1); // qBt starts queue at 1 resumeData["qBt-hasRootFolder"] = m_hasRootFolder; - if (m_nativeStatus.flags & lt::torrent_flags::stop_when_ready) { - // We need to redefine these values when torrent starting/rechecking - // in "paused" state since native values can be logically wrong - // (torrent can be not paused and auto_managed when it is checking). - resumeData["paused"] = true; - resumeData["auto_managed"] = false; - } - m_session->handleTorrentResumeDataReady(this, resumeDataPtr); } void TorrentHandleImpl::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p) { - // if torrent has no metadata we should save dummy fastresume data - // containing Magnet URI and qBittorrent own resume data only - if (p->error.value() == lt::errors::no_metadata) { - handleSaveResumeDataAlert(nullptr); - } - else { - LogMsg(tr("Save resume data failed. Torrent: \"%1\", error: \"%2\"") - .arg(name(), QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL); - m_session->handleTorrentResumeDataFailed(this); - } + Q_UNUSED(p); + + // if torrent has no metadata libtorrent doesn't generate "fastresume" data + // so we should save dummy "fastresume" data containing the values used to + // load torrent and qBittorrent own resume data + handleSaveResumeDataAlert(nullptr); } void TorrentHandleImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p) diff --git a/src/base/bittorrent/torrenthandleimpl.h b/src/base/bittorrent/torrenthandleimpl.h index eff3d5984..640be682c 100644 --- a/src/base/bittorrent/torrenthandleimpl.h +++ b/src/base/bittorrent/torrenthandleimpl.h @@ -31,6 +31,7 @@ #include +#include #include #include #include @@ -53,35 +54,26 @@ namespace BitTorrent class Session; struct AddTorrentParams; - struct CreateTorrentParams + struct LoadTorrentParams { - CreateTorrentParams() = default; - explicit CreateTorrentParams(const AddTorrentParams ¶ms); + lt::add_torrent_params ltAddTorrentParams {}; - // for both new and restored torrents QString name; QString category; QSet tags; QString savePath; - int uploadLimit = -1; - int downloadLimit = -1; bool disableTempPath = false; bool sequential = false; bool firstLastPiecePriority = false; bool hasSeedStatus = false; - bool skipChecking = false; bool hasRootFolder = true; bool forced = false; bool paused = false; - bool restored = false; // is existing torrent job? - // for new torrents - QVector filePriorities; - QDateTime addedTime; - - // for restored torrents qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO; int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME; + + bool restored = false; // is existing torrent job? }; enum class MoveStorageMode @@ -97,7 +89,7 @@ namespace BitTorrent public: TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle, - const CreateTorrentParams ¶ms); + const LoadTorrentParams ¶ms); ~TorrentHandleImpl() override; bool isValid() const; @@ -329,5 +321,7 @@ namespace BitTorrent bool m_useAutoTMM; bool m_unchecked = false; + + lt::add_torrent_params m_ltAddTorrentParams; }; } diff --git a/src/gui/categoryfiltermodel.cpp b/src/gui/categoryfiltermodel.cpp index 45a93598a..25ad763c4 100644 --- a/src/gui/categoryfiltermodel.cpp +++ b/src/gui/categoryfiltermodel.cpp @@ -182,7 +182,7 @@ CategoryFilterModel::CategoryFilterModel(QObject *parent) connect(session, &Session::categoryRemoved, this, &CategoryFilterModel::categoryRemoved); connect(session, &Session::torrentCategoryChanged, this, &CategoryFilterModel::torrentCategoryChanged); connect(session, &Session::subcategoriesSupportChanged, this, &CategoryFilterModel::subcategoriesSupportChanged); - connect(session, &Session::torrentAdded, this, &CategoryFilterModel::torrentAdded); + connect(session, &Session::torrentLoaded, this, &CategoryFilterModel::torrentAdded); connect(session, &Session::torrentAboutToBeRemoved, this, &CategoryFilterModel::torrentAboutToBeRemoved); populate(); diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 5926c299a..d17804f4a 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -195,8 +195,8 @@ MainWindow::MainWindow(QWidget *parent) // Creating Bittorrent session connect(BitTorrent::Session::instance(), &BitTorrent::Session::fullDiskError, this, &MainWindow::fullDiskError); - connect(BitTorrent::Session::instance(), &BitTorrent::Session::addTorrentFailed, this, &MainWindow::addTorrentFailed); - connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentNew,this, &MainWindow::torrentNew); + connect(BitTorrent::Session::instance(), &BitTorrent::Session::loadTorrentFailed, this, &MainWindow::addTorrentFailed); + connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded,this, &MainWindow::torrentNew); connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &MainWindow::finishedTorrent); connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed, this, &MainWindow::handleDownloadFromUrlFailure); connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn); diff --git a/src/gui/tagfiltermodel.cpp b/src/gui/tagfiltermodel.cpp index c0d08e901..41985d18b 100644 --- a/src/gui/tagfiltermodel.cpp +++ b/src/gui/tagfiltermodel.cpp @@ -98,7 +98,7 @@ TagFilterModel::TagFilterModel(QObject *parent) connect(session, &Session::tagRemoved, this, &TagFilterModel::tagRemoved); connect(session, &Session::torrentTagAdded, this, &TagFilterModel::torrentTagAdded); connect(session, &Session::torrentTagRemoved, this, &TagFilterModel::torrentTagRemoved); - connect(session, &Session::torrentAdded, this, &TagFilterModel::torrentAdded); + connect(session, &Session::torrentLoaded, this, &TagFilterModel::torrentAdded); connect(session, &Session::torrentAboutToBeRemoved, this, &TagFilterModel::torrentAboutToBeRemoved); populate(); } diff --git a/src/gui/torrentcreatordialog.cpp b/src/gui/torrentcreatordialog.cpp index 0dba00e07..20e324fa8 100644 --- a/src/gui/torrentcreatordialog.cpp +++ b/src/gui/torrentcreatordialog.cpp @@ -209,7 +209,10 @@ void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QStr BitTorrent::AddTorrentParams params; params.savePath = branchPath; params.skipChecking = true; - params.ignoreShareLimits = m_ui->checkIgnoreShareLimits->isChecked(); + if (m_ui->checkIgnoreShareLimits->isChecked()) { + params.ratioLimit = BitTorrent::TorrentHandle::NO_RATIO_LIMIT; + params.seedingTimeLimit = BitTorrent::TorrentHandle::NO_SEEDING_TIME_LIMIT; + } params.useAutoTMM = TriStateBool::False; // otherwise if it is on by default, it will overwrite `savePath` to the default save path BitTorrent::Session::instance()->addTorrent(info, params); diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index ac2616260..a69f63fb8 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -120,7 +120,7 @@ BaseFilterWidget::BaseFilterWidget(QWidget *parent, TransferListWidget *transfer connect(this, &BaseFilterWidget::customContextMenuRequested, this, &BaseFilterWidget::showMenu); connect(this, &BaseFilterWidget::currentRowChanged, this, &BaseFilterWidget::applyFilter); - connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded + connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentLoaded , this, &BaseFilterWidget::handleNewTorrent); connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAboutToBeRemoved , this, &BaseFilterWidget::torrentAboutToBeDeleted); @@ -155,7 +155,7 @@ void BaseFilterWidget::toggleFilter(bool checked) StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *transferList) : BaseFilterWidget(parent, transferList) { - connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded + connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentLoaded , this, &StatusFilterWidget::updateTorrentNumbers); connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentsUpdated , this, &StatusFilterWidget::updateTorrentNumbers); diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 0b73b402a..41eb23f1f 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -137,7 +137,7 @@ TransferListModel::TransferListModel(QObject *parent) addTorrent(torrent); // Listen for torrent changes - connect(Session::instance(), &Session::torrentAdded, this, &TransferListModel::addTorrent); + connect(Session::instance(), &Session::torrentLoaded, this, &TransferListModel::addTorrent); connect(Session::instance(), &Session::torrentAboutToBeRemoved, this, &TransferListModel::handleTorrentAboutToBeRemoved); connect(Session::instance(), &Session::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated);