mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-10 23:07:59 +00:00
Search for existing files in separate thread
This commit is contained in:
parent
9497300a4a
commit
a93b675cb8
@ -8,6 +8,7 @@ add_library(qbt_base STATIC
|
||||
bittorrent/common.h
|
||||
bittorrent/customstorage.h
|
||||
bittorrent/downloadpriority.h
|
||||
bittorrent/filesearcher.h
|
||||
bittorrent/filterparserthread.h
|
||||
bittorrent/infohash.h
|
||||
bittorrent/ltqhash.h
|
||||
@ -90,6 +91,7 @@ add_library(qbt_base STATIC
|
||||
bittorrent/bandwidthscheduler.cpp
|
||||
bittorrent/customstorage.cpp
|
||||
bittorrent/downloadpriority.cpp
|
||||
bittorrent/filesearcher.cpp
|
||||
bittorrent/filterparserthread.cpp
|
||||
bittorrent/infohash.cpp
|
||||
bittorrent/magneturi.cpp
|
||||
|
@ -7,6 +7,7 @@ HEADERS += \
|
||||
$$PWD/bittorrent/common.h \
|
||||
$$PWD/bittorrent/customstorage.h \
|
||||
$$PWD/bittorrent/downloadpriority.h \
|
||||
$$PWD/bittorrent/filesearcher.h \
|
||||
$$PWD/bittorrent/filterparserthread.h \
|
||||
$$PWD/bittorrent/infohash.h \
|
||||
$$PWD/bittorrent/ltqhash.h \
|
||||
@ -90,6 +91,7 @@ SOURCES += \
|
||||
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/customstorage.cpp \
|
||||
$$PWD/bittorrent/downloadpriority.cpp \
|
||||
$$PWD/bittorrent/filesearcher.cpp \
|
||||
$$PWD/bittorrent/filterparserthread.cpp \
|
||||
$$PWD/bittorrent/infohash.cpp \
|
||||
$$PWD/bittorrent/magneturi.cpp \
|
||||
|
69
src/base/bittorrent/filesearcher.cpp
Normal file
69
src/base/bittorrent/filesearcher.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "filesearcher.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include "base/bittorrent/common.h"
|
||||
#include "base/bittorrent/infohash.h"
|
||||
|
||||
void FileSearcher::search(const BitTorrent::InfoHash &id, const QStringList &originalFileNames
|
||||
, const QString &completeSavePath, const QString &incompleteSavePath)
|
||||
{
|
||||
const auto findInDir = [](const QString &dirPath, QStringList &fileNames) -> bool
|
||||
{
|
||||
const QDir dir {dirPath};
|
||||
bool found = false;
|
||||
for (QString &fileName : fileNames)
|
||||
{
|
||||
if (dir.exists(fileName))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (dir.exists(fileName + QB_EXT))
|
||||
{
|
||||
found = true;
|
||||
fileName += QB_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
QString savePath = completeSavePath;
|
||||
QStringList adjustedFileNames = originalFileNames;
|
||||
const bool found = findInDir(savePath, adjustedFileNames);
|
||||
if (!found && !incompleteSavePath.isEmpty())
|
||||
{
|
||||
savePath = incompleteSavePath;
|
||||
findInDir(savePath, adjustedFileNames);
|
||||
}
|
||||
|
||||
emit searchFinished(id, savePath, adjustedFileNames);
|
||||
}
|
52
src/base/bittorrent/filesearcher.h
Normal file
52
src/base/bittorrent/filesearcher.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class InfoHash;
|
||||
}
|
||||
|
||||
class FileSearcher final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(FileSearcher)
|
||||
|
||||
public:
|
||||
FileSearcher() = default;
|
||||
|
||||
public slots:
|
||||
void search(const BitTorrent::InfoHash &id, const QStringList &originalFileNames
|
||||
, const QString &completeSavePath, const QString &incompleteSavePath);
|
||||
|
||||
signals:
|
||||
void searchFinished(const BitTorrent::InfoHash &id, const QString &savePath, const QStringList &fileNames);
|
||||
};
|
@ -33,6 +33,8 @@
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
const int InfoHashTypeId = qRegisterMetaType<InfoHash>();
|
||||
|
||||
InfoHash::InfoHash()
|
||||
: m_valid(false)
|
||||
{
|
||||
|
@ -26,11 +26,11 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_INFOHASH_H
|
||||
#define BITTORRENT_INFOHASH_H
|
||||
#pragma once
|
||||
|
||||
#include <libtorrent/sha1_hash.hpp>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
|
||||
namespace BitTorrent
|
||||
@ -64,4 +64,4 @@ namespace BitTorrent
|
||||
uint qHash(const InfoHash &key, uint seed);
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_INFOHASH_H
|
||||
Q_DECLARE_METATYPE(BitTorrent::InfoHash)
|
||||
|
@ -88,6 +88,7 @@
|
||||
#include "bandwidthscheduler.h"
|
||||
#include "common.h"
|
||||
#include "customstorage.h"
|
||||
#include "filesearcher.h"
|
||||
#include "filterparserthread.h"
|
||||
#include "ltunderlyingtype.h"
|
||||
#include "magneturi.h"
|
||||
@ -509,6 +510,12 @@ Session::Session(QObject *parent)
|
||||
m_resumeDataSavingManager = new ResumeDataSavingManager {m_resumeFolderPath};
|
||||
m_resumeDataSavingManager->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
|
||||
|
||||
m_fileSearcher = new FileSearcher;
|
||||
m_fileSearcher->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_fileSearcher, &QObject::deleteLater);
|
||||
connect(m_fileSearcher, &FileSearcher::searchFinished, this, &Session::fileSearchFinished);
|
||||
|
||||
m_ioThread->start();
|
||||
|
||||
// Regular saving of fastresume data
|
||||
@ -1765,6 +1772,24 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
|
||||
}
|
||||
}
|
||||
|
||||
void Session::fileSearchFinished(const InfoHash &id, const QString &savePath, const QStringList &fileNames)
|
||||
{
|
||||
const auto loadingTorrentsIter = m_loadingTorrents.find(id);
|
||||
if (loadingTorrentsIter != m_loadingTorrents.end())
|
||||
{
|
||||
LoadTorrentParams params = loadingTorrentsIter.value();
|
||||
m_loadingTorrents.erase(loadingTorrentsIter);
|
||||
|
||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
||||
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
for (int i = 0; i < fileNames.size(); ++i)
|
||||
p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString();
|
||||
|
||||
loadTorrent(params);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the torrent handle, given its hash
|
||||
TorrentHandle *Session::findTorrent(const InfoHash &hash) const
|
||||
{
|
||||
@ -2137,15 +2162,20 @@ bool Session::addTorrent_impl(const AddTorrentParams &addTorrentParams, const Ma
|
||||
LoadTorrentParams loadTorrentParams = initLoadTorrentParams(addTorrentParams);
|
||||
lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
|
||||
|
||||
bool isFindingIncompleteFiles = false;
|
||||
|
||||
// If empty then Automatic mode, otherwise Manual mode
|
||||
QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
const QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
if (hasMetadata)
|
||||
{
|
||||
if (!loadTorrentParams.hasRootFolder)
|
||||
metadata.stripRootFolder();
|
||||
|
||||
if (!loadTorrentParams.hasSeedStatus)
|
||||
findIncompleteFiles(metadata, actualSavePath); // if needed points savePath to incomplete folder too
|
||||
{
|
||||
findIncompleteFiles(metadata, actualSavePath);
|
||||
isFindingIncompleteFiles = true;
|
||||
}
|
||||
|
||||
// if torrent name wasn't explicitly set we handle the case of
|
||||
// initial renaming of torrent content and rename torrent accordingly
|
||||
@ -2175,9 +2205,6 @@ bool Session::addTorrent_impl(const AddTorrentParams &addTorrentParams, const Ma
|
||||
|
||||
if (loadTorrentParams.name.isEmpty() && !p.name.empty())
|
||||
loadTorrentParams.name = QString::fromStdString(p.name);
|
||||
|
||||
if (isTempPathEnabled())
|
||||
actualSavePath = tempPath();
|
||||
}
|
||||
|
||||
p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
|
||||
@ -2209,7 +2236,11 @@ bool Session::addTorrent_impl(const AddTorrentParams &addTorrentParams, const Ma
|
||||
else
|
||||
p.flags |= lt::torrent_flags::auto_managed;
|
||||
|
||||
return loadTorrent(loadTorrentParams);
|
||||
if (!isFindingIncompleteFiles)
|
||||
return loadTorrent(loadTorrentParams);
|
||||
|
||||
m_loadingTorrents.insert(hash, loadTorrentParams);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add a torrent to the BitTorrent session
|
||||
@ -2234,37 +2265,22 @@ bool Session::loadTorrent(LoadTorrentParams params)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Session::findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const
|
||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath) const
|
||||
{
|
||||
auto findInDir = [](const QString &dirPath, TorrentInfo &torrentInfo) -> bool
|
||||
const InfoHash searchId = torrentInfo.hash();
|
||||
const QStringList originalFileNames = torrentInfo.filePaths();
|
||||
const QString completeSavePath = savePath;
|
||||
const QString incompleteSavePath = (isTempPathEnabled() ? torrentTempPath(torrentInfo) : QString {});
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||
QMetaObject::invokeMethod(m_fileSearcher, [=]()
|
||||
{
|
||||
const QDir dir(dirPath);
|
||||
bool found = false;
|
||||
for (int i = 0; i < torrentInfo.filesCount(); ++i)
|
||||
{
|
||||
const QString filePath = torrentInfo.filePath(i);
|
||||
if (dir.exists(filePath))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (dir.exists(filePath + QB_EXT))
|
||||
{
|
||||
found = true;
|
||||
torrentInfo.renameFile(i, filePath + QB_EXT);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
bool found = findInDir(savePath, torrentInfo);
|
||||
if (!found && isTempPathEnabled())
|
||||
{
|
||||
savePath = torrentTempPath(torrentInfo);
|
||||
found = findInDir(savePath, torrentInfo);
|
||||
}
|
||||
|
||||
return found;
|
||||
m_fileSearcher->search(searchId, originalFileNames, completeSavePath, incompleteSavePath);
|
||||
});
|
||||
#else
|
||||
QMetaObject::invokeMethod(m_fileSearcher, "search"
|
||||
, Q_ARG(InfoHash, searchId), Q_ARG(QStringList, originalFileNames)
|
||||
, Q_ARG(QString, completeSavePath), Q_ARG(QString, incompleteSavePath));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Add a torrent to libtorrent session in hidden mode
|
||||
@ -3870,8 +3886,6 @@ void Session::handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, con
|
||||
|
||||
void Session::handleTorrentMetadataReceived(TorrentHandleImpl *const torrent)
|
||||
{
|
||||
torrent->saveResumeData();
|
||||
|
||||
// Save metadata
|
||||
const QDir resumeDataDir {m_resumeFolderPath};
|
||||
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())};
|
||||
@ -3888,7 +3902,7 @@ void Session::handleTorrentMetadataReceived(TorrentHandleImpl *const torrent)
|
||||
.arg(torrentFileName, err.message()), Log::CRITICAL);
|
||||
}
|
||||
|
||||
emit torrentMetadataLoaded(torrent);
|
||||
emit torrentMetadataReceived(torrent);
|
||||
}
|
||||
|
||||
void Session::handleTorrentPaused(TorrentHandleImpl *const torrent)
|
||||
|
@ -64,6 +64,7 @@ class QTimer;
|
||||
class QUrl;
|
||||
|
||||
class BandwidthScheduler;
|
||||
class FileSearcher;
|
||||
class FilterParserThread;
|
||||
class ResumeDataSavingManager;
|
||||
class Statistics;
|
||||
@ -488,6 +489,8 @@ namespace BitTorrent
|
||||
|
||||
bool addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, MoveStorageMode mode);
|
||||
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath) const;
|
||||
|
||||
signals:
|
||||
void allTorrentsFinished();
|
||||
void categoryAdded(const QString &categoryName);
|
||||
@ -510,7 +513,7 @@ namespace BitTorrent
|
||||
void torrentFinished(TorrentHandle *torrent);
|
||||
void torrentFinishedChecking(TorrentHandle *torrent);
|
||||
void torrentLoaded(TorrentHandle *torrent);
|
||||
void torrentMetadataLoaded(TorrentHandle *torrent);
|
||||
void torrentMetadataReceived(TorrentHandle *torrent);
|
||||
void torrentPaused(TorrentHandle *torrent);
|
||||
void torrentResumed(TorrentHandle *torrent);
|
||||
void torrentSavePathChanged(TorrentHandle *torrent);
|
||||
@ -537,6 +540,7 @@ namespace BitTorrent
|
||||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
||||
void fileSearchFinished(const InfoHash &id, const QString &savePath, const QStringList &fileNames);
|
||||
|
||||
// Session reconfiguration triggers
|
||||
void networkOnlineStateChanged(bool online);
|
||||
@ -593,7 +597,6 @@ namespace BitTorrent
|
||||
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();
|
||||
void exportTorrentFile(const TorrentHandle *torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
|
||||
@ -763,6 +766,7 @@ namespace BitTorrent
|
||||
// fastresume data writing thread
|
||||
QThread *m_ioThread = nullptr;
|
||||
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
|
||||
FileSearcher *m_fileSearcher = nullptr;
|
||||
|
||||
QSet<InfoHash> m_downloadedMetadata;
|
||||
|
||||
|
@ -104,7 +104,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent)
|
||||
connect(m_propListDelegate, &PropListDelegate::filteredFilesChanged, this, &PropertiesWidget::filteredFilesChanged);
|
||||
connect(m_ui->stackedProperties, &QStackedWidget::currentChanged, this, &PropertiesWidget::loadDynamicData);
|
||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentSavePathChanged, this, &PropertiesWidget::updateSavePath);
|
||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentMetadataLoaded, this, &PropertiesWidget::updateTorrentInfos);
|
||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentMetadataReceived, this, &PropertiesWidget::updateTorrentInfos);
|
||||
connect(m_ui->filesList, &QAbstractItemView::clicked
|
||||
, m_ui->filesList, qOverload<const QModelIndex &>(&QAbstractItemView::edit));
|
||||
connect(m_ui->filesList, &QWidget::customContextMenuRequested, this, &PropertiesWidget::displayFilesListMenu);
|
||||
@ -404,7 +404,7 @@ void PropertiesWidget::loadDynamicData()
|
||||
switch (m_ui->stackedProperties->currentIndex())
|
||||
{
|
||||
case PropTabBar::MainTab:
|
||||
{
|
||||
{
|
||||
m_ui->labelWastedVal->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
|
||||
|
||||
m_ui->labelUpTotalVal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload())
|
||||
|
@ -142,7 +142,7 @@ TransferListModel::TransferListModel(QObject *parent)
|
||||
connect(Session::instance(), &Session::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated);
|
||||
|
||||
connect(Session::instance(), &Session::torrentFinished, this, &TransferListModel::handleTorrentStatusUpdated);
|
||||
connect(Session::instance(), &Session::torrentMetadataLoaded, this, &TransferListModel::handleTorrentStatusUpdated);
|
||||
connect(Session::instance(), &Session::torrentMetadataReceived, this, &TransferListModel::handleTorrentStatusUpdated);
|
||||
connect(Session::instance(), &Session::torrentResumed, this, &TransferListModel::handleTorrentStatusUpdated);
|
||||
connect(Session::instance(), &Session::torrentPaused, this, &TransferListModel::handleTorrentStatusUpdated);
|
||||
connect(Session::instance(), &Session::torrentFinishedChecking, this, &TransferListModel::handleTorrentStatusUpdated);
|
||||
|
Loading…
Reference in New Issue
Block a user