From 564d845d8729febb4e12eb35a1e616bd4c1d8ab9 Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Thu, 12 Dec 2019 16:12:06 +0200 Subject: [PATCH] Use the incomplete folder where appropriate Restored torrents already have the correct path saved in their fastresume. Don't override it. Magnets and new torrents have to be set explicitly to the incomplete folder if it is enabled. Closes #11552 --- src/base/bittorrent/session.cpp | 225 +++++++++++++++++++++++--------- 1 file changed, 166 insertions(+), 59 deletions(-) diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index d4c394a0a..797dc8584 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -1969,6 +1969,7 @@ bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri, TorrentInfo torrentInfo, const QByteArray &fastresumeData) { +#if (LIBTORRENT_VERSION_NUM < 10200) params.savePath = normalizeSavePath(params.savePath, ""); if (!params.category.isEmpty()) { @@ -2000,12 +2001,9 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne lt::torrent_handle handle = m_nativeSession->find_torrent(hash); // We need to pause it first to create TorrentHandle within the same // underlying state as in other cases. -#if (LIBTORRENT_VERSION_NUM < 10200) handle.auto_managed(false); -#else - handle.unset_flags(lt::torrent_flags::auto_managed); -#endif handle.pause(); + // createTorrentHandle() for this is called in the torrent_paused_alert handler m_loadedMetadata.remove(hash); m_addingTorrents.insert(hash, params); @@ -2013,6 +2011,14 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne } 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()) { @@ -2041,13 +2047,8 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne return true; } - // Record if .fastresume is complete, that is whether it contains - // the required fields to resume a torrent. - bool hasCompleteFastresume = false; - if (!fromMagnetUri) { if (params.restored) { // load from existing fastresume -#if (LIBTORRENT_VERSION_NUM < 10200) // Make sure the torrent will be initially checked and then paused // to perform some service jobs on it. We will start it if needed. // (Workaround to easily support libtorrent-1.1 @@ -2058,27 +2059,17 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne p.resume_data = std::vector {patchedFastresumeData.constData() , (patchedFastresumeData.constData() + patchedFastresumeData.size())}; p.flags |= lt::add_torrent_params::flag_use_resume_save_path; - - // Still setup the default parameters and let libtorrent handle - // the parameter merging - hasCompleteFastresume = false; -#else - lt::error_code ec; - p = lt::read_resume_data(fastresumeData, ec); - - // libtorrent will always apply `file_priorities` to torrents, - // if the field is present then the fastresume is considered to - // be correctly generated and should be complete. - hasCompleteFastresume = !p.file_priorities.empty(); -#endif } else { // new torrent if (!params.hasRootFolder) torrentInfo.stripRootFolder(); + // 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); + findIncompleteFiles(torrentInfo, savePath); // if needed points savePath to incomplete folder too + p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); // if torrent name wasn't explicitly set we handle the case of // initial renaming of torrent content and rename torrent accordingly @@ -2095,12 +2086,7 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne std::transform(params.filePriorities.cbegin(), params.filePriorities.cend() , std::back_inserter(p.file_priorities), [](const DownloadPriority priority) { -#if (LIBTORRENT_VERSION_NUM < 10200) return static_cast(priority); -#else - return static_cast( - static_cast(priority)); -#endif }); } @@ -2108,63 +2094,184 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne } // Common -#if (LIBTORRENT_VERSION_NUM < 10200) p.flags &= ~lt::add_torrent_params::flag_duplicate_is_error; // Already checked -#else - p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked -#endif // Make sure the torrent will be initially checked and then paused // to perform some service jobs on it. We will start it if needed. -#if (LIBTORRENT_VERSION_NUM < 10200) p.flags |= lt::add_torrent_params::flag_paused; p.flags |= lt::add_torrent_params::flag_auto_managed; p.flags |= lt::add_torrent_params::flag_stop_when_ready; + // Limits + p.max_connections = maxConnectionsPerTorrent(); + p.max_uploads = maxUploadsPerTorrent(); + p.upload_limit = params.uploadLimit; + p.download_limit = params.downloadLimit; + // 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::add_torrent_params::flag_seed_mode; + else + p.flags &= ~lt::add_torrent_params::flag_seed_mode; + + m_addingTorrents.insert(hash, params); + // Adding torrent to BitTorrent session + m_nativeSession->async_add_torrent(p); + + return true; #else - p.flags |= lt::torrent_flags::paused; - p.flags |= lt::torrent_flags::auto_managed; - p.flags |= lt::torrent_flags::stop_when_ready; -#endif + params.savePath = normalizeSavePath(params.savePath, ""); - if (!hasCompleteFastresume) { - // Limits - p.max_connections = maxConnectionsPerTorrent(); - p.max_uploads = maxUploadsPerTorrent(); - p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); + 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); + // We need to pause it first to create TorrentHandle within the same + // underlying state as in other cases. + handle.unset_flags(lt::torrent_flags::auto_managed); + handle.pause(); + // createTorrentHandle() for this is called in the torrent_paused_alert handler + + m_loadedMetadata.remove(hash); + m_addingTorrents.insert(hash, params); + 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(); + } + + // 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)) + return false; + + TorrentHandle *const torrent = m_torrents.value(hash); + if (torrent) { // a duplicate torrent is added + if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate())) + return false; + + // merge trackers and web seeds + torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers()); + torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds()); + return true; + } + + if (!fromMagnetUri) { + if (params.restored) { // load from existing fastresume + lt::error_code ec; + p = lt::read_resume_data(fastresumeData, ec); + } + else { // new torrent + if (!params.hasRootFolder) + torrentInfo.stripRootFolder(); + + // 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 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 (!contentName.isEmpty() && (contentName != torrentInfo.name())) + params.name = contentName; + } + + 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)); + }); + } + + p.ti = torrentInfo.nativeInfo(); + } + + if (fromMagnetUri && params.restored && params.addedTime.isValid()) + p.added_time = params.addedTime.toSecsSinceEpoch(); + + if (fromMagnetUri || !params.restored) { p.upload_limit = params.uploadLimit; p.download_limit = params.downloadLimit; -#if (LIBTORRENT_VERSION_NUM >= 10200) - if (params.addedTime.isValid()) - p.added_time = params.addedTime.toSecsSinceEpoch(); -#endif - // 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) { -#if (LIBTORRENT_VERSION_NUM < 10200) - p.flags |= lt::add_torrent_params::flag_seed_mode; -#else + if (params.skipChecking) p.flags |= lt::torrent_flags::seed_mode; -#endif - } - else { -#if (LIBTORRENT_VERSION_NUM < 10200) - p.flags &= ~lt::add_torrent_params::flag_seed_mode; -#else + else p.flags &= ~lt::torrent_flags::seed_mode; -#endif - } } + // Common + p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked + // Make sure the torrent will be initially checked and then paused + // to perform some service jobs on it. We will start it if needed. + p.flags |= lt::torrent_flags::paused; + p.flags |= lt::torrent_flags::auto_managed; + p.flags |= lt::torrent_flags::stop_when_ready; + // Limits + p.max_connections = maxConnectionsPerTorrent(); + p.max_uploads = maxUploadsPerTorrent(); + m_addingTorrents.insert(hash, params); // Adding torrent to BitTorrent session m_nativeSession->async_add_torrent(p); return true; +#endif } bool Session::findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const