mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-05 23:22:31 -06:00
committed by
GitHub
parent
facfa26eed
commit
dd1bd8ad10
@@ -33,93 +33,63 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/fs.h"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseInsensitive};
|
||||
#else
|
||||
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseSensitive};
|
||||
#endif
|
||||
|
||||
namespace
|
||||
void BitTorrent::AbstractFileStorage::renameFile(const Path &oldPath, const Path &newPath)
|
||||
{
|
||||
bool areSameFileNames(QString first, QString second)
|
||||
{
|
||||
return QString::compare(first, second, CASE_SENSITIVITY) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BitTorrent::AbstractFileStorage::renameFile(const QString &oldPath, const QString &newPath)
|
||||
{
|
||||
if (!Utils::Fs::isValidFileSystemName(oldPath, true))
|
||||
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
||||
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
||||
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
||||
|
||||
const QString oldFilePath = Utils::Fs::toUniformPath(oldPath);
|
||||
if (oldFilePath.endsWith(QLatin1Char {'/'}))
|
||||
throw RuntimeError {tr("Invalid file path: '%1'.").arg(oldFilePath)};
|
||||
|
||||
const QString newFilePath = Utils::Fs::toUniformPath(newPath);
|
||||
if (newFilePath.endsWith(QLatin1Char {'/'}))
|
||||
throw RuntimeError {tr("Invalid file path: '%1'.").arg(newFilePath)};
|
||||
if (QDir().isAbsolutePath(newFilePath))
|
||||
throw RuntimeError {tr("Absolute path isn't allowed: '%1'.").arg(newFilePath)};
|
||||
if (!oldPath.isValid())
|
||||
throw RuntimeError(tr("The old path is invalid: '%1'.").arg(oldPath.toString()));
|
||||
if (!newPath.isValid())
|
||||
throw RuntimeError(tr("The new path is invalid: '%1'.").arg(newPath.toString()));
|
||||
if (newPath.isAbsolute())
|
||||
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newPath.toString()));
|
||||
|
||||
int renamingFileIndex = -1;
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString path = filePath(i);
|
||||
const Path path = filePath(i);
|
||||
|
||||
if ((renamingFileIndex < 0) && areSameFileNames(path, oldFilePath))
|
||||
if ((renamingFileIndex < 0) && (path == oldPath))
|
||||
renamingFileIndex = i;
|
||||
else if (areSameFileNames(path, newFilePath))
|
||||
throw RuntimeError {tr("The file already exists: '%1'.").arg(newFilePath)};
|
||||
else if (path == newPath)
|
||||
throw RuntimeError(tr("The file already exists: '%1'.").arg(newPath.toString()));
|
||||
}
|
||||
|
||||
if (renamingFileIndex < 0)
|
||||
throw RuntimeError {tr("No such file: '%1'.").arg(oldFilePath)};
|
||||
throw RuntimeError(tr("No such file: '%1'.").arg(oldPath.toString()));
|
||||
|
||||
renameFile(renamingFileIndex, newFilePath);
|
||||
renameFile(renamingFileIndex, newPath);
|
||||
}
|
||||
|
||||
void BitTorrent::AbstractFileStorage::renameFolder(const QString &oldPath, const QString &newPath)
|
||||
void BitTorrent::AbstractFileStorage::renameFolder(const Path &oldFolderPath, const Path &newFolderPath)
|
||||
{
|
||||
if (!Utils::Fs::isValidFileSystemName(oldPath, true))
|
||||
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
||||
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
||||
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
||||
|
||||
const auto cleanFolderPath = [](const QString &path) -> QString
|
||||
{
|
||||
const QString uniformPath = Utils::Fs::toUniformPath(path);
|
||||
return (uniformPath.endsWith(QLatin1Char {'/'}) ? uniformPath : uniformPath + QLatin1Char {'/'});
|
||||
};
|
||||
|
||||
const QString oldFolderPath = cleanFolderPath(oldPath);
|
||||
const QString newFolderPath = cleanFolderPath(newPath);
|
||||
if (QDir().isAbsolutePath(newFolderPath))
|
||||
throw RuntimeError {tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath)};
|
||||
if (!oldFolderPath.isValid())
|
||||
throw RuntimeError(tr("The old path is invalid: '%1'.").arg(oldFolderPath.toString()));
|
||||
if (!newFolderPath.isValid())
|
||||
throw RuntimeError(tr("The new path is invalid: '%1'.").arg(newFolderPath.toString()));
|
||||
if (newFolderPath.isAbsolute())
|
||||
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath.toString()));
|
||||
|
||||
QVector<int> renamingFileIndexes;
|
||||
renamingFileIndexes.reserve(filesCount());
|
||||
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString path = filePath(i);
|
||||
const Path path = filePath(i);
|
||||
|
||||
if (path.startsWith(oldFolderPath, CASE_SENSITIVITY))
|
||||
if (path.hasAncestor(oldFolderPath))
|
||||
renamingFileIndexes.append(i);
|
||||
else if (path.startsWith(newFolderPath, CASE_SENSITIVITY))
|
||||
throw RuntimeError {tr("The folder already exists: '%1'.").arg(newFolderPath)};
|
||||
else if (path.hasAncestor(newFolderPath))
|
||||
throw RuntimeError(tr("The folder already exists: '%1'.").arg(newFolderPath.toString()));
|
||||
}
|
||||
|
||||
if (renamingFileIndexes.isEmpty())
|
||||
throw RuntimeError {tr("No such folder: '%1'.").arg(oldFolderPath)};
|
||||
throw RuntimeError(tr("No such folder: '%1'.").arg(oldFolderPath.toString()));
|
||||
|
||||
for (const int index : renamingFileIndexes)
|
||||
{
|
||||
const QString newFilePath = newFolderPath + filePath(index).mid(oldFolderPath.size());
|
||||
const Path newFilePath = newFolderPath / oldFolderPath.relativePathOf(filePath(index));
|
||||
renameFile(index, newFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QString;
|
||||
|
||||
namespace BitTorrent
|
||||
@@ -43,12 +45,12 @@ namespace BitTorrent
|
||||
virtual ~AbstractFileStorage() = default;
|
||||
|
||||
virtual int filesCount() const = 0;
|
||||
virtual QString filePath(int index) const = 0;
|
||||
virtual Path filePath(int index) const = 0;
|
||||
virtual qlonglong fileSize(int index) const = 0;
|
||||
|
||||
virtual void renameFile(int index, const QString &name) = 0;
|
||||
virtual void renameFile(int index, const Path &newPath) = 0;
|
||||
|
||||
void renameFile(const QString &oldPath, const QString &newPath);
|
||||
void renameFolder(const QString &oldPath, const QString &newPath);
|
||||
void renameFile(const Path &oldPath, const Path &newPath);
|
||||
void renameFolder(const Path &oldFolderPath, const Path &newFolderPath);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
@@ -47,14 +48,14 @@ namespace BitTorrent
|
||||
QString name;
|
||||
QString category;
|
||||
TagSet tags;
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
std::optional<bool> useDownloadPath;
|
||||
QString downloadPath;
|
||||
Path downloadPath;
|
||||
bool sequential = false;
|
||||
bool firstLastPiecePriority = false;
|
||||
bool addForced = false;
|
||||
std::optional<bool> addPaused;
|
||||
QStringList filePaths; // used if TorrentInfo is set
|
||||
PathList filePaths; // used if TorrentInfo is set
|
||||
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
|
||||
bool skipChecking = false;
|
||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||
|
||||
@@ -58,14 +58,14 @@ namespace BitTorrent
|
||||
Q_DISABLE_COPY_MOVE(Worker)
|
||||
|
||||
public:
|
||||
explicit Worker(const QDir &resumeDataDir);
|
||||
explicit Worker(const Path &resumeDataDir);
|
||||
|
||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
||||
void remove(const TorrentID &id) const;
|
||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||
|
||||
private:
|
||||
const QDir m_resumeDataDir;
|
||||
const Path m_resumeDataDir;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,20 +89,22 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &path, QObject *parent)
|
||||
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path, QObject *parent)
|
||||
: ResumeDataStorage {parent}
|
||||
, m_resumeDataDir {path}
|
||||
, m_resumeDataPath {path}
|
||||
, m_ioThread {new QThread {this}}
|
||||
, m_asyncWorker {new Worker {m_resumeDataDir}}
|
||||
, m_asyncWorker {new Worker(m_resumeDataPath)}
|
||||
{
|
||||
if (!m_resumeDataDir.exists() && !m_resumeDataDir.mkpath(m_resumeDataDir.absolutePath()))
|
||||
Q_ASSERT(path.isAbsolute());
|
||||
|
||||
if (!m_resumeDataPath.exists() && !Utils::Fs::mkpath(m_resumeDataPath))
|
||||
{
|
||||
throw RuntimeError {tr("Cannot create torrent resume folder: \"%1\"")
|
||||
.arg(Utils::Fs::toNativePath(m_resumeDataDir.absolutePath()))};
|
||||
throw RuntimeError(tr("Cannot create torrent resume folder: \"%1\"")
|
||||
.arg(m_resumeDataPath.toString()));
|
||||
}
|
||||
|
||||
const QRegularExpression filenamePattern {QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$")};
|
||||
const QStringList filenames = m_resumeDataDir.entryList(QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
const QStringList filenames = QDir(m_resumeDataPath.data()).entryList(QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
|
||||
m_registeredTorrents.reserve(filenames.size());
|
||||
for (const QString &filename : filenames)
|
||||
@@ -112,7 +114,7 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &pa
|
||||
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
|
||||
}
|
||||
|
||||
loadQueue(m_resumeDataDir.absoluteFilePath(QLatin1String("queue")));
|
||||
loadQueue(m_resumeDataPath / Path("queue"));
|
||||
|
||||
qDebug() << "Registered torrents count: " << m_registeredTorrents.size();
|
||||
|
||||
@@ -135,20 +137,20 @@ QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredT
|
||||
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::load(const TorrentID &id) const
|
||||
{
|
||||
const QString idString = id.toString();
|
||||
const QString fastresumePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(idString));
|
||||
const QString torrentFilePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(idString));
|
||||
const Path fastresumePath = m_resumeDataPath / Path(idString + QLatin1String(".fastresume"));
|
||||
const Path torrentFilePath = m_resumeDataPath / Path(idString + QLatin1String(".torrent"));
|
||||
|
||||
QFile resumeDataFile {fastresumePath};
|
||||
QFile resumeDataFile {fastresumePath.data()};
|
||||
if (!resumeDataFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath, resumeDataFile.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath.toString(), resumeDataFile.errorString()), Log::WARNING);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
QFile metadataFile {torrentFilePath};
|
||||
QFile metadataFile {torrentFilePath.data()};
|
||||
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(torrentFilePath, metadataFile.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(torrentFilePath.toString(), metadataFile.errorString()), Log::WARNING);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -178,12 +180,12 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
|
||||
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
|
||||
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||
Path(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||
torrentParams.useAutoTMM = torrentParams.savePath.isEmpty();
|
||||
if (!torrentParams.useAutoTMM)
|
||||
{
|
||||
torrentParams.downloadPath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||
Path(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||
}
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
||||
@@ -224,7 +226,8 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
|
||||
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();
|
||||
p.save_path = Profile::instance()->fromPortablePath(
|
||||
Path(fromLTString(p.save_path))).toString().toStdString();
|
||||
|
||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||
{
|
||||
@@ -273,9 +276,9 @@ void BitTorrent::BencodeResumeDataStorage::storeQueue(const QVector<TorrentID> &
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const QString &queueFilename)
|
||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||
{
|
||||
QFile queueFile {queueFilename};
|
||||
QFile queueFile {queueFilename.data()};
|
||||
if (!queueFile.exists())
|
||||
return;
|
||||
|
||||
@@ -306,7 +309,7 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const QString &queueFilenam
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::Worker::Worker(const QDir &resumeDataDir)
|
||||
BitTorrent::BencodeResumeDataStorage::Worker::Worker(const Path &resumeDataDir)
|
||||
: m_resumeDataDir {resumeDataDir}
|
||||
{
|
||||
}
|
||||
@@ -315,7 +318,8 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||
{
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->toPortablePath(Path(p.save_path))
|
||||
.toString().toStdString();
|
||||
if (resumeData.stopped)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
@@ -349,12 +353,12 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||
metadataDict.insert(dataDict.extract("created by"));
|
||||
metadataDict.insert(dataDict.extract("comment"));
|
||||
|
||||
const QString torrentFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(id.toString()));
|
||||
const Path torrentFilepath = m_resumeDataDir / Path(QString::fromLatin1("%1.torrent").arg(id.toString()));
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(torrentFilepath, metadata);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
|
||||
.arg(torrentFilepath, result.error()), Log::CRITICAL);
|
||||
.arg(torrentFilepath.toString(), result.error()), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -370,26 +374,26 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
|
||||
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).toStdString();
|
||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).data().toStdString();
|
||||
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).data().toStdString();
|
||||
}
|
||||
|
||||
const QString resumeFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
||||
const Path resumeFilepath = m_resumeDataDir / Path(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
|
||||
.arg(resumeFilepath, result.error()), Log::CRITICAL);
|
||||
.arg(resumeFilepath.toString(), result.error()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
|
||||
{
|
||||
const QString resumeFilename = QString::fromLatin1("%1.fastresume").arg(id.toString());
|
||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(resumeFilename));
|
||||
const Path resumeFilename {QString::fromLatin1("%1.fastresume").arg(id.toString())};
|
||||
Utils::Fs::removeFile(m_resumeDataDir / resumeFilename);
|
||||
|
||||
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(id.toString());
|
||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(torrentFilename));
|
||||
const Path torrentFilename {QString::fromLatin1("%1.torrent").arg(id.toString())};
|
||||
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
|
||||
@@ -399,11 +403,11 @@ void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<Torr
|
||||
for (const BitTorrent::TorrentID &torrentID : queue)
|
||||
data += (torrentID.toString().toLatin1() + '\n');
|
||||
|
||||
const QString filepath = m_resumeDataDir.absoluteFilePath(QLatin1String("queue"));
|
||||
const Path filepath = m_resumeDataDir / Path("queue");
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filepath, data);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
||||
.arg(filepath, result.error()), Log::CRITICAL);
|
||||
.arg(filepath.toString(), result.error()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <QDir>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QByteArray;
|
||||
@@ -44,7 +45,7 @@ namespace BitTorrent
|
||||
Q_DISABLE_COPY_MOVE(BencodeResumeDataStorage)
|
||||
|
||||
public:
|
||||
explicit BencodeResumeDataStorage(const QString &path, QObject *parent = nullptr);
|
||||
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
||||
~BencodeResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
@@ -54,10 +55,10 @@ namespace BitTorrent
|
||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||
|
||||
private:
|
||||
void loadQueue(const QString &queueFilename);
|
||||
void loadQueue(const Path &queueFilename);
|
||||
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
||||
|
||||
const QDir m_resumeDataDir;
|
||||
const Path m_resumeDataPath;
|
||||
QVector<TorrentID> m_registeredTorrents;
|
||||
QThread *m_ioThread = nullptr;
|
||||
|
||||
|
||||
@@ -37,13 +37,13 @@ const QString OPTION_DOWNLOADPATH {QStringLiteral("download_path")};
|
||||
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
||||
{
|
||||
CategoryOptions options;
|
||||
options.savePath = jsonObj.value(OPTION_SAVEPATH).toString();
|
||||
options.savePath = Path(jsonObj.value(OPTION_SAVEPATH).toString());
|
||||
|
||||
const QJsonValue downloadPathValue = jsonObj.value(OPTION_DOWNLOADPATH);
|
||||
if (downloadPathValue.isBool())
|
||||
options.downloadPath = {downloadPathValue.toBool(), {}};
|
||||
else if (downloadPathValue.isString())
|
||||
options.downloadPath = {true, downloadPathValue.toString()};
|
||||
options.downloadPath = {true, Path(downloadPathValue.toString())};
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -54,13 +54,13 @@ QJsonObject BitTorrent::CategoryOptions::toJSON() const
|
||||
if (downloadPath)
|
||||
{
|
||||
if (downloadPath->enabled)
|
||||
downloadPathValue = downloadPath->path;
|
||||
downloadPathValue = downloadPath->path.data();
|
||||
else
|
||||
downloadPathValue = false;
|
||||
}
|
||||
|
||||
return {
|
||||
{OPTION_SAVEPATH, savePath},
|
||||
{OPTION_SAVEPATH, savePath.data()},
|
||||
{OPTION_DOWNLOADPATH, downloadPathValue}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace BitTorrent
|
||||
@@ -41,10 +43,10 @@ namespace BitTorrent
|
||||
struct DownloadPathOption
|
||||
{
|
||||
bool enabled;
|
||||
QString path;
|
||||
Path path;
|
||||
};
|
||||
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
std::optional<DownloadPathOption> downloadPath;
|
||||
|
||||
static CategoryOptions fromJSON(const QJsonObject &jsonObj);
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
|
||||
#include <libtorrent/download_priority.hpp>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include "base/utils/fs.h"
|
||||
#include "common.h"
|
||||
|
||||
@@ -53,12 +51,13 @@ lt::storage_holder CustomDiskIOThread::new_torrent(const lt::storage_params &sto
|
||||
{
|
||||
lt::storage_holder storageHolder = m_nativeDiskIO->new_torrent(storageParams, torrent);
|
||||
|
||||
const QString savePath = Utils::Fs::expandPathAbs(QString::fromStdString(storageParams.path));
|
||||
const Path savePath {storageParams.path};
|
||||
m_storageData[storageHolder] =
|
||||
{
|
||||
savePath
|
||||
, storageParams.mapped_files ? *storageParams.mapped_files : storageParams.files
|
||||
, storageParams.priorities};
|
||||
savePath,
|
||||
storageParams.mapped_files ? *storageParams.mapped_files : storageParams.files,
|
||||
storageParams.priorities
|
||||
};
|
||||
|
||||
return storageHolder;
|
||||
}
|
||||
@@ -99,7 +98,7 @@ void CustomDiskIOThread::async_hash2(lt::storage_index_t storage, lt::piece_inde
|
||||
void CustomDiskIOThread::async_move_storage(lt::storage_index_t storage, std::string path, lt::move_flags_t flags
|
||||
, std::function<void (lt::status_t, const std::string &, const lt::storage_error &)> handler)
|
||||
{
|
||||
const QString newSavePath {Utils::Fs::expandPathAbs(QString::fromStdString(path))};
|
||||
const Path newSavePath {path};
|
||||
|
||||
if (flags == lt::move_flags_t::dont_replace)
|
||||
handleCompleteFiles(storage, newSavePath);
|
||||
@@ -192,9 +191,8 @@ void CustomDiskIOThread::settings_updated()
|
||||
m_nativeDiskIO->settings_updated();
|
||||
}
|
||||
|
||||
void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const QString &savePath)
|
||||
void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const Path &savePath)
|
||||
{
|
||||
const QDir saveDir {savePath};
|
||||
const StorageData storageData = m_storageData[storage];
|
||||
const lt::file_storage &fileStorage = storageData.files;
|
||||
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
||||
@@ -206,16 +204,16 @@ void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const
|
||||
// ignore pad files
|
||||
if (fileStorage.pad_file_at(fileIndex)) continue;
|
||||
|
||||
const QString filePath = QString::fromStdString(fileStorage.file_path(fileIndex));
|
||||
if (filePath.endsWith(QB_EXT))
|
||||
const Path filePath {fileStorage.file_path(fileIndex)};
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
{
|
||||
const QString completeFilePath = filePath.left(filePath.size() - QB_EXT.size());
|
||||
QFile completeFile {saveDir.absoluteFilePath(completeFilePath)};
|
||||
if (completeFile.exists())
|
||||
const Path incompleteFilePath = savePath / filePath;
|
||||
Path completeFilePath = incompleteFilePath;
|
||||
completeFilePath.removeExtension();
|
||||
if (completeFilePath.exists())
|
||||
{
|
||||
QFile incompleteFile {saveDir.absoluteFilePath(filePath)};
|
||||
incompleteFile.remove();
|
||||
completeFile.rename(incompleteFile.fileName());
|
||||
Utils::Fs::removeFile(incompleteFilePath);
|
||||
Utils::Fs::renameFile(completeFilePath, incompleteFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +228,7 @@ lt::storage_interface *customStorageConstructor(const lt::storage_params ¶ms
|
||||
|
||||
CustomStorage::CustomStorage(const lt::storage_params ¶ms, lt::file_pool &filePool)
|
||||
: lt::default_storage {params, filePool}
|
||||
, m_savePath {Utils::Fs::expandPathAbs(QString::fromStdString(params.path))}
|
||||
, m_savePath {params.path}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -248,7 +246,7 @@ void CustomStorage::set_file_priority(lt::aux::vector<lt::download_priority_t, l
|
||||
|
||||
lt::status_t CustomStorage::move_storage(const std::string &savePath, lt::move_flags_t flags, lt::storage_error &ec)
|
||||
{
|
||||
const QString newSavePath {Utils::Fs::expandPathAbs(QString::fromStdString(savePath))};
|
||||
const Path newSavePath {savePath};
|
||||
|
||||
if (flags == lt::move_flags_t::dont_replace)
|
||||
handleCompleteFiles(newSavePath);
|
||||
@@ -260,10 +258,8 @@ lt::status_t CustomStorage::move_storage(const std::string &savePath, lt::move_f
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CustomStorage::handleCompleteFiles(const QString &savePath)
|
||||
void CustomStorage::handleCompleteFiles(const Path &savePath)
|
||||
{
|
||||
const QDir saveDir {savePath};
|
||||
|
||||
const lt::file_storage &fileStorage = files();
|
||||
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
||||
{
|
||||
@@ -274,16 +270,16 @@ void CustomStorage::handleCompleteFiles(const QString &savePath)
|
||||
// ignore pad files
|
||||
if (fileStorage.pad_file_at(fileIndex)) continue;
|
||||
|
||||
const QString filePath = QString::fromStdString(fileStorage.file_path(fileIndex));
|
||||
if (filePath.endsWith(QB_EXT))
|
||||
const Path filePath {fileStorage.file_path(fileIndex)};
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
{
|
||||
const QString completeFilePath = filePath.left(filePath.size() - QB_EXT.size());
|
||||
QFile completeFile {saveDir.absoluteFilePath(completeFilePath)};
|
||||
if (completeFile.exists())
|
||||
const Path incompleteFilePath = savePath / filePath;
|
||||
Path completeFilePath = incompleteFilePath;
|
||||
completeFilePath.removeExtension();
|
||||
if (completeFilePath.exists())
|
||||
{
|
||||
QFile incompleteFile {saveDir.absoluteFilePath(filePath)};
|
||||
incompleteFile.remove();
|
||||
completeFile.rename(incompleteFile.fileName());
|
||||
Utils::Fs::removeFile(incompleteFilePath);
|
||||
Utils::Fs::renameFile(completeFilePath, incompleteFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
#include <libtorrent/disk_interface.hpp>
|
||||
#include <libtorrent/file_storage.hpp>
|
||||
@@ -86,13 +88,13 @@ public:
|
||||
void settings_updated() override;
|
||||
|
||||
private:
|
||||
void handleCompleteFiles(libtorrent::storage_index_t storage, const QString &savePath);
|
||||
void handleCompleteFiles(libtorrent::storage_index_t storage, const Path &savePath);
|
||||
|
||||
std::unique_ptr<lt::disk_interface> m_nativeDiskIO;
|
||||
|
||||
struct StorageData
|
||||
{
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
lt::file_storage files;
|
||||
lt::aux::vector<lt::download_priority_t, lt::file_index_t> filePriorities;
|
||||
};
|
||||
@@ -113,9 +115,9 @@ public:
|
||||
lt::status_t move_storage(const std::string &savePath, lt::move_flags_t flags, lt::storage_error &ec) override;
|
||||
|
||||
private:
|
||||
void handleCompleteFiles(const QString &savePath);
|
||||
void handleCompleteFiles(const Path &savePath);
|
||||
|
||||
lt::aux::vector<lt::download_priority_t, lt::file_index_t> m_filePriorities;
|
||||
QString m_savePath;
|
||||
Path m_savePath;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "base/exceptions.h"
|
||||
#include "base/global.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/path.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/string.h"
|
||||
@@ -173,7 +174,7 @@ namespace BitTorrent
|
||||
Q_DISABLE_COPY_MOVE(Worker)
|
||||
|
||||
public:
|
||||
Worker(const QString &dbPath, const QString &dbConnectionName);
|
||||
Worker(const Path &dbPath, const QString &dbConnectionName);
|
||||
|
||||
void openDatabase() const;
|
||||
void closeDatabase() const;
|
||||
@@ -183,19 +184,19 @@ namespace BitTorrent
|
||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||
|
||||
private:
|
||||
const QString m_path;
|
||||
const Path m_path;
|
||||
const QString m_connectionName;
|
||||
};
|
||||
}
|
||||
|
||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const QString &dbPath, QObject *parent)
|
||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
||||
: ResumeDataStorage {parent}
|
||||
, m_ioThread {new QThread(this)}
|
||||
{
|
||||
const bool needCreateDB = !QFile::exists(dbPath);
|
||||
const bool needCreateDB = !dbPath.exists();
|
||||
|
||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), DB_CONNECTION_NAME);
|
||||
db.setDatabaseName(dbPath);
|
||||
db.setDatabaseName(dbPath.data());
|
||||
if (!db.open())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
|
||||
@@ -308,12 +309,12 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
||||
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||
|
||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||
}
|
||||
|
||||
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
||||
@@ -325,13 +326,11 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
||||
lt::error_code ec;
|
||||
const lt::bdecode_node root = lt::bdecode(allData, ec);
|
||||
|
||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||
|
||||
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||
|
||||
p = lt::read_resume_data(root, ec);
|
||||
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
|
||||
.toString().toStdString();
|
||||
|
||||
return resumeData;
|
||||
}
|
||||
@@ -485,7 +484,7 @@ void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QString &dbConnectionName)
|
||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, const QString &dbConnectionName)
|
||||
: m_path {dbPath}
|
||||
, m_connectionName {dbConnectionName}
|
||||
{
|
||||
@@ -494,7 +493,7 @@ BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QSt
|
||||
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
|
||||
{
|
||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
|
||||
db.setDatabaseName(m_path);
|
||||
db.setDatabaseName(m_path.data());
|
||||
if (!db.open())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
}
|
||||
@@ -508,7 +507,8 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
||||
{
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->toPortablePath(Path(p.save_path))
|
||||
.toString().toStdString();
|
||||
if (resumeData.stopped)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
@@ -603,8 +603,8 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
||||
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
|
||||
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath));
|
||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath).data());
|
||||
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath).data());
|
||||
}
|
||||
|
||||
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QThread;
|
||||
@@ -40,7 +41,7 @@ namespace BitTorrent
|
||||
Q_DISABLE_COPY_MOVE(DBResumeDataStorage)
|
||||
|
||||
public:
|
||||
explicit DBResumeDataStorage(const QString &dbPath, QObject *parent = nullptr);
|
||||
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
||||
~DBResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
|
||||
@@ -27,37 +27,33 @@
|
||||
*/
|
||||
|
||||
#include "filesearcher.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include "base/bittorrent/common.h"
|
||||
#include "base/bittorrent/infohash.h"
|
||||
|
||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
||||
, const QString &savePath, const QString &downloadPath)
|
||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||
, const Path &savePath, const Path &downloadPath)
|
||||
{
|
||||
const auto findInDir = [](const QString &dirPath, QStringList &fileNames) -> bool
|
||||
const auto findInDir = [](const Path &dirPath, PathList &fileNames) -> bool
|
||||
{
|
||||
const QDir dir {dirPath};
|
||||
bool found = false;
|
||||
for (QString &fileName : fileNames)
|
||||
for (Path &fileName : fileNames)
|
||||
{
|
||||
if (dir.exists(fileName))
|
||||
if ((dirPath / fileName).exists())
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (dir.exists(fileName + QB_EXT))
|
||||
else if ((dirPath / fileName + QB_EXT).exists())
|
||||
{
|
||||
found = true;
|
||||
fileName += QB_EXT;
|
||||
fileName = fileName + QB_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
QString usedPath = savePath;
|
||||
QStringList adjustedFileNames = originalFileNames;
|
||||
Path usedPath = savePath;
|
||||
PathList adjustedFileNames = originalFileNames;
|
||||
const bool found = findInDir(usedPath, adjustedFileNames);
|
||||
if (!found && !downloadPath.isEmpty())
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentID;
|
||||
@@ -44,9 +46,9 @@ public:
|
||||
FileSearcher() = default;
|
||||
|
||||
public slots:
|
||||
void search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
||||
, const QString &savePath, const QString &downloadPath);
|
||||
void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||
, const Path &savePath, const Path &downloadPath);
|
||||
|
||||
signals:
|
||||
void searchFinished(const BitTorrent::TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
||||
void searchFinished(const BitTorrent::TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||
};
|
||||
|
||||
@@ -124,7 +124,7 @@ FilterParserThread::~FilterParserThread()
|
||||
int FilterParserThread::parseDATFilterFile()
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
QFile file {m_filePath.data()};
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
@@ -288,7 +288,7 @@ int FilterParserThread::parseDATFilterFile()
|
||||
int FilterParserThread::parseP2PFilterFile()
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
QFile file {m_filePath.data()};
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
@@ -469,7 +469,7 @@ int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name,
|
||||
int FilterParserThread::parseP2BFilterFile()
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
QFile file {m_filePath.data()};
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
@@ -592,7 +592,7 @@ int FilterParserThread::parseP2BFilterFile()
|
||||
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
||||
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
||||
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
|
||||
void FilterParserThread::processFilterFile(const QString &filePath)
|
||||
void FilterParserThread::processFilterFile(const Path &filePath)
|
||||
{
|
||||
if (isRunning())
|
||||
{
|
||||
@@ -617,17 +617,17 @@ void FilterParserThread::run()
|
||||
{
|
||||
qDebug("Processing filter file");
|
||||
int ruleCount = 0;
|
||||
if (m_filePath.endsWith(".p2p", Qt::CaseInsensitive))
|
||||
if (m_filePath.hasExtension(QLatin1String(".p2p")))
|
||||
{
|
||||
// PeerGuardian p2p file
|
||||
ruleCount = parseP2PFilterFile();
|
||||
}
|
||||
else if (m_filePath.endsWith(".p2b", Qt::CaseInsensitive))
|
||||
else if (m_filePath.hasExtension(QLatin1String(".p2b")))
|
||||
{
|
||||
// PeerGuardian p2b file
|
||||
ruleCount = parseP2BFilterFile();
|
||||
}
|
||||
else if (m_filePath.endsWith(".dat", Qt::CaseInsensitive))
|
||||
else if (m_filePath.hasExtension(QLatin1String(".dat")))
|
||||
{
|
||||
// eMule DAT format
|
||||
ruleCount = parseDATFilterFile();
|
||||
|
||||
@@ -32,16 +32,19 @@
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
class QDataStream;
|
||||
|
||||
class FilterParserThread final : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(FilterParserThread)
|
||||
|
||||
public:
|
||||
FilterParserThread(QObject *parent = nullptr);
|
||||
~FilterParserThread();
|
||||
void processFilterFile(const QString &filePath);
|
||||
void processFilterFile(const Path &filePath);
|
||||
lt::ip_filter IPfilter();
|
||||
|
||||
signals:
|
||||
@@ -60,6 +63,6 @@ private:
|
||||
int parseP2BFilterFile();
|
||||
|
||||
bool m_abort;
|
||||
QString m_filePath;
|
||||
Path m_filePath;
|
||||
lt::ip_filter m_filter;
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
@@ -45,8 +46,8 @@ namespace BitTorrent
|
||||
QString name;
|
||||
QString category;
|
||||
TagSet tags;
|
||||
QString savePath;
|
||||
QString downloadPath;
|
||||
Path savePath;
|
||||
Path downloadPath;
|
||||
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
||||
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
||||
bool useAutoTMM = false;
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
const QString CATEGORIES_FILE_NAME {QStringLiteral("categories.json")};
|
||||
const Path CATEGORIES_FILE_NAME {"categories.json"};
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -394,8 +394,8 @@ Session::Session(QObject *parent)
|
||||
, clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
|
||||
, m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
|
||||
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
|
||||
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), Utils::Fs::toUniformPath)
|
||||
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp"), Utils::Fs::toUniformPath)
|
||||
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads))
|
||||
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), (savePath() / Path("temp")))
|
||||
, m_isDownloadPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
|
||||
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
|
||||
, m_useCategoryPathsInManualMode(BITTORRENT_SESSION_KEY("UseCategoryPathsInManualMode"), false)
|
||||
@@ -594,36 +594,34 @@ void Session::setPreallocationEnabled(const bool enabled)
|
||||
m_isPreallocationEnabled = enabled;
|
||||
}
|
||||
|
||||
QString Session::torrentExportDirectory() const
|
||||
Path Session::torrentExportDirectory() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(m_torrentExportDirectory);
|
||||
return m_torrentExportDirectory;
|
||||
}
|
||||
|
||||
void Session::setTorrentExportDirectory(QString path)
|
||||
void Session::setTorrentExportDirectory(const Path &path)
|
||||
{
|
||||
path = Utils::Fs::toUniformPath(path);
|
||||
if (path != torrentExportDirectory())
|
||||
m_torrentExportDirectory = path;
|
||||
}
|
||||
|
||||
QString Session::finishedTorrentExportDirectory() const
|
||||
Path Session::finishedTorrentExportDirectory() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(m_finishedTorrentExportDirectory);
|
||||
return m_finishedTorrentExportDirectory;
|
||||
}
|
||||
|
||||
void Session::setFinishedTorrentExportDirectory(QString path)
|
||||
void Session::setFinishedTorrentExportDirectory(const Path &path)
|
||||
{
|
||||
path = Utils::Fs::toUniformPath(path);
|
||||
if (path != finishedTorrentExportDirectory())
|
||||
m_finishedTorrentExportDirectory = path;
|
||||
}
|
||||
|
||||
QString Session::savePath() const
|
||||
Path Session::savePath() const
|
||||
{
|
||||
return m_savePath;
|
||||
}
|
||||
|
||||
QString Session::downloadPath() const
|
||||
Path Session::downloadPath() const
|
||||
{
|
||||
return m_downloadPath;
|
||||
}
|
||||
@@ -667,20 +665,20 @@ CategoryOptions Session::categoryOptions(const QString &categoryName) const
|
||||
return m_categories.value(categoryName);
|
||||
}
|
||||
|
||||
QString Session::categorySavePath(const QString &categoryName) const
|
||||
Path Session::categorySavePath(const QString &categoryName) const
|
||||
{
|
||||
const QString basePath = savePath();
|
||||
const Path basePath = savePath();
|
||||
if (categoryName.isEmpty())
|
||||
return basePath;
|
||||
|
||||
QString path = m_categories.value(categoryName).savePath;
|
||||
Path path = m_categories.value(categoryName).savePath;
|
||||
if (path.isEmpty()) // use implicit save path
|
||||
path = Utils::Fs::toValidFileSystemName(categoryName, true);
|
||||
path = Utils::Fs::toValidPath(categoryName);
|
||||
|
||||
return (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
return (path.isAbsolute() ? path : (basePath / path));
|
||||
}
|
||||
|
||||
QString Session::categoryDownloadPath(const QString &categoryName) const
|
||||
Path Session::categoryDownloadPath(const QString &categoryName) const
|
||||
{
|
||||
const CategoryOptions categoryOptions = m_categories.value(categoryName);
|
||||
const CategoryOptions::DownloadPathOption downloadPathOption =
|
||||
@@ -688,15 +686,15 @@ QString Session::categoryDownloadPath(const QString &categoryName) const
|
||||
if (!downloadPathOption.enabled)
|
||||
return {};
|
||||
|
||||
const QString basePath = downloadPath();
|
||||
const Path basePath = downloadPath();
|
||||
if (categoryName.isEmpty())
|
||||
return basePath;
|
||||
|
||||
const QString path = (!downloadPathOption.path.isEmpty()
|
||||
const Path path = (!downloadPathOption.path.isEmpty()
|
||||
? downloadPathOption.path
|
||||
: Utils::Fs::toValidFileSystemName(categoryName, true)); // use implicit download path
|
||||
: Utils::Fs::toValidPath(categoryName)); // use implicit download path
|
||||
|
||||
return (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
return (path.isAbsolute() ? path : (basePath / path));
|
||||
}
|
||||
|
||||
bool Session::addCategory(const QString &name, const CategoryOptions &options)
|
||||
@@ -1722,7 +1720,7 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
|
||||
}
|
||||
}
|
||||
|
||||
void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames)
|
||||
void Session::fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
TorrentImpl *torrent = m_torrents.value(id);
|
||||
if (torrent)
|
||||
@@ -1739,11 +1737,11 @@ void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, c
|
||||
|
||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
||||
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
const TorrentInfo torrentInfo {*p.ti};
|
||||
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||
for (int i = 0; i < fileNames.size(); ++i)
|
||||
p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString();
|
||||
p.renamed_files[nativeIndexes[i]] = fileNames[i].toString().toStdString();
|
||||
|
||||
loadTorrent(params);
|
||||
}
|
||||
@@ -1811,7 +1809,7 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
|
||||
// Remove it from session
|
||||
if (deleteOption == DeleteTorrent)
|
||||
{
|
||||
m_removingTorrents[torrent->id()] = {torrent->name(), "", deleteOption};
|
||||
m_removingTorrents[torrent->id()] = {torrent->name(), {}, deleteOption};
|
||||
|
||||
const lt::torrent_handle nativeHandle {torrent->nativeHandle()};
|
||||
const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
|
||||
@@ -2033,8 +2031,9 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams ¶ms)
|
||||
if (magnetUri.isValid())
|
||||
return addTorrent(magnetUri, params);
|
||||
|
||||
TorrentFileGuard guard {source};
|
||||
const nonstd::expected<TorrentInfo, QString> loadResult = TorrentInfo::loadFromFile(source);
|
||||
const Path path {source};
|
||||
TorrentFileGuard guard {path};
|
||||
const nonstd::expected<TorrentInfo, QString> loadResult = TorrentInfo::loadFromFile(path);
|
||||
if (!loadResult)
|
||||
{
|
||||
LogMsg(tr("Couldn't load torrent: %1").arg(loadResult.error()), Log::WARNING);
|
||||
@@ -2079,27 +2078,27 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
|
||||
|
||||
if (!loadTorrentParams.useAutoTMM)
|
||||
{
|
||||
if (QDir::isAbsolutePath(addTorrentParams.savePath))
|
||||
if (addTorrentParams.savePath.isAbsolute())
|
||||
{
|
||||
loadTorrentParams.savePath = addTorrentParams.savePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
||||
loadTorrentParams.savePath = Utils::Fs::resolvePath(addTorrentParams.savePath, basePath);
|
||||
const Path basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
||||
loadTorrentParams.savePath = basePath / addTorrentParams.savePath;
|
||||
}
|
||||
|
||||
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(isDownloadPathEnabled());
|
||||
if (useDownloadPath)
|
||||
{
|
||||
if (QDir::isAbsolutePath(addTorrentParams.downloadPath))
|
||||
if (addTorrentParams.downloadPath.isAbsolute())
|
||||
{
|
||||
loadTorrentParams.downloadPath = addTorrentParams.downloadPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
||||
loadTorrentParams.downloadPath = Utils::Fs::resolvePath(addTorrentParams.downloadPath, basePath);
|
||||
const Path basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
||||
loadTorrentParams.downloadPath = basePath / addTorrentParams.downloadPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2165,7 +2164,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||
bool isFindingIncompleteFiles = false;
|
||||
|
||||
const bool useAutoTMM = loadTorrentParams.useAutoTMM;
|
||||
const QString actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
const Path actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
|
||||
if (hasMetadata)
|
||||
{
|
||||
@@ -2175,16 +2174,16 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||
|
||||
const TorrentContentLayout contentLayout = ((loadTorrentParams.contentLayout == TorrentContentLayout::Original)
|
||||
? detectContentLayout(torrentInfo.filePaths()) : loadTorrentParams.contentLayout);
|
||||
QStringList filePaths = (!addTorrentParams.filePaths.isEmpty() ? addTorrentParams.filePaths : torrentInfo.filePaths());
|
||||
applyContentLayout(filePaths, contentLayout, Utils::Fs::findRootFolder(torrentInfo.filePaths()));
|
||||
PathList filePaths = (!addTorrentParams.filePaths.isEmpty() ? addTorrentParams.filePaths : torrentInfo.filePaths());
|
||||
applyContentLayout(filePaths, contentLayout, Path::findRootFolder(torrentInfo.filePaths()));
|
||||
|
||||
// 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 = Utils::Fs::findRootFolder(filePaths);
|
||||
QString contentName = Path::findRootFolder(filePaths).toString();
|
||||
if (contentName.isEmpty() && (filePaths.size() == 1))
|
||||
contentName = Utils::Fs::fileName(filePaths.at(0));
|
||||
contentName = filePaths.at(0).filename();
|
||||
|
||||
if (!contentName.isEmpty() && (contentName != torrentInfo.name()))
|
||||
loadTorrentParams.name = contentName;
|
||||
@@ -2192,7 +2191,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||
|
||||
if (!loadTorrentParams.hasSeedStatus)
|
||||
{
|
||||
const QString actualDownloadPath = useAutoTMM
|
||||
const Path actualDownloadPath = useAutoTMM
|
||||
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||
findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths);
|
||||
isFindingIncompleteFiles = true;
|
||||
@@ -2202,7 +2201,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||
if (!filePaths.isEmpty())
|
||||
{
|
||||
for (int index = 0; index < addTorrentParams.filePaths.size(); ++index)
|
||||
p.renamed_files[nativeIndexes[index]] = Utils::Fs::toNativePath(addTorrentParams.filePaths.at(index)).toStdString();
|
||||
p.renamed_files[nativeIndexes[index]] = addTorrentParams.filePaths.at(index).toString().toStdString();
|
||||
}
|
||||
|
||||
Q_ASSERT(p.file_priorities.empty());
|
||||
@@ -2224,7 +2223,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||
loadTorrentParams.name = QString::fromStdString(p.name);
|
||||
}
|
||||
|
||||
p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
|
||||
p.save_path = actualSavePath.toString().toStdString();
|
||||
|
||||
p.upload_limit = addTorrentParams.uploadLimit;
|
||||
p.download_limit = addTorrentParams.downloadLimit;
|
||||
@@ -2288,13 +2287,13 @@ bool Session::loadTorrent(LoadTorrentParams params)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
||||
, const QString &downloadPath, const QStringList &filePaths) const
|
||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths) const
|
||||
{
|
||||
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
||||
|
||||
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
|
||||
const QStringList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
||||
const PathList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
||||
QMetaObject::invokeMethod(m_fileSearcher, [=]()
|
||||
{
|
||||
m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath);
|
||||
@@ -2333,8 +2332,8 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
|
||||
p.max_connections = maxConnectionsPerTorrent();
|
||||
p.max_uploads = maxUploadsPerTorrent();
|
||||
|
||||
const QString savePath = Utils::Fs::tempPath() + id.toString();
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
const Path savePath = Utils::Fs::tempPath() / Path(id.toString());
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
|
||||
// Forced start
|
||||
p.flags &= ~lt::torrent_flags::paused;
|
||||
@@ -2360,28 +2359,27 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Session::exportTorrentFile(const TorrentInfo &torrentInfo, const QString &folderPath, const QString &baseName)
|
||||
void Session::exportTorrentFile(const TorrentInfo &torrentInfo, const Path &folderPath, const QString &baseName)
|
||||
{
|
||||
const QString validName = Utils::Fs::toValidFileSystemName(baseName);
|
||||
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
||||
const QDir exportDir {folderPath};
|
||||
if (exportDir.exists() || exportDir.mkpath(exportDir.absolutePath()))
|
||||
{
|
||||
QString newTorrentPath = exportDir.absoluteFilePath(torrentExportFilename);
|
||||
int counter = 0;
|
||||
while (QFile::exists(newTorrentPath))
|
||||
{
|
||||
// Append number to torrent name to make it unique
|
||||
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
|
||||
newTorrentPath = exportDir.absoluteFilePath(torrentExportFilename);
|
||||
}
|
||||
if (!folderPath.exists() && !Utils::Fs::mkpath(folderPath))
|
||||
return;
|
||||
|
||||
const nonstd::expected<void, QString> result = torrentInfo.saveToFile(newTorrentPath);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
|
||||
.arg(newTorrentPath, result.error()), Log::WARNING);
|
||||
}
|
||||
const QString validName = Utils::Fs::toValidFileName(baseName);
|
||||
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
||||
Path newTorrentPath = folderPath / Path(torrentExportFilename);
|
||||
int counter = 0;
|
||||
while (newTorrentPath.exists())
|
||||
{
|
||||
// Append number to torrent name to make it unique
|
||||
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
|
||||
newTorrentPath = folderPath / Path(torrentExportFilename);
|
||||
}
|
||||
|
||||
const nonstd::expected<void, QString> result = torrentInfo.saveToFile(newTorrentPath);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
|
||||
.arg(newTorrentPath.toString(), result.error()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2455,13 +2453,13 @@ void Session::removeTorrentsQueue() const
|
||||
m_resumeDataStorage->storeQueue({});
|
||||
}
|
||||
|
||||
void Session::setSavePath(const QString &path)
|
||||
void Session::setSavePath(const Path &path)
|
||||
{
|
||||
const QString baseSavePath = specialFolderLocation(SpecialFolder::Downloads);
|
||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseSavePath));
|
||||
if (resolvedPath == m_savePath) return;
|
||||
const auto newPath = (path.isAbsolute() ? path : (specialFolderLocation(SpecialFolder::Downloads) / path));
|
||||
if (newPath == m_savePath)
|
||||
return;
|
||||
|
||||
m_savePath = resolvedPath;
|
||||
m_savePath = newPath;
|
||||
|
||||
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
||||
{
|
||||
@@ -2475,13 +2473,12 @@ void Session::setSavePath(const QString &path)
|
||||
}
|
||||
}
|
||||
|
||||
void Session::setDownloadPath(const QString &path)
|
||||
void Session::setDownloadPath(const Path &path)
|
||||
{
|
||||
const QString baseDownloadPath = specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp");
|
||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseDownloadPath));
|
||||
if (resolvedPath != m_downloadPath)
|
||||
const Path newPath = (path.isAbsolute() ? path : (savePath() / Path("temp") / path));
|
||||
if (newPath != m_downloadPath)
|
||||
{
|
||||
m_downloadPath = resolvedPath;
|
||||
m_downloadPath = newPath;
|
||||
|
||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||
torrent->handleDownloadPathChanged();
|
||||
@@ -2953,14 +2950,13 @@ void Session::setIPFilteringEnabled(const bool enabled)
|
||||
}
|
||||
}
|
||||
|
||||
QString Session::IPFilterFile() const
|
||||
Path Session::IPFilterFile() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(m_IPFilterFile);
|
||||
return m_IPFilterFile;
|
||||
}
|
||||
|
||||
void Session::setIPFilterFile(QString path)
|
||||
void Session::setIPFilterFile(const Path &path)
|
||||
{
|
||||
path = Utils::Fs::toUniformPath(path);
|
||||
if (path != IPFilterFile())
|
||||
{
|
||||
m_IPFilterFile = path;
|
||||
@@ -3994,13 +3990,13 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
||||
|
||||
qDebug("Checking if the torrent contains torrent files to download");
|
||||
// Check if there are torrent files inside
|
||||
for (const QString &torrentRelpath : asConst(torrent->filePaths()))
|
||||
for (const Path &torrentRelpath : asConst(torrent->filePaths()))
|
||||
{
|
||||
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
|
||||
if (torrentRelpath.hasExtension(QLatin1String(".torrent")))
|
||||
{
|
||||
qDebug("Found possible recursive torrent download.");
|
||||
const QString torrentFullpath = torrent->actualStorageLocation() + '/' + torrentRelpath;
|
||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
||||
const Path torrentFullpath = torrent->actualStorageLocation() / torrentRelpath;
|
||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath.toString()));
|
||||
if (TorrentInfo::loadFromFile(torrentFullpath))
|
||||
{
|
||||
qDebug("emitting recursiveTorrentDownloadPossible()");
|
||||
@@ -4010,7 +4006,7 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
||||
else
|
||||
{
|
||||
qDebug("Caught error loading torrent");
|
||||
LogMsg(tr("Unable to decode '%1' torrent file.").arg(Utils::Fs::toNativePath(torrentFullpath)), Log::CRITICAL);
|
||||
LogMsg(tr("Unable to decode '%1' torrent file.").arg(torrentFullpath.toString()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4047,12 +4043,12 @@ void Session::handleTorrentTrackerError(TorrentImpl *const torrent, const QStrin
|
||||
emit trackerError(torrent, trackerUrl);
|
||||
}
|
||||
|
||||
bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, const MoveStorageMode mode)
|
||||
bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, const MoveStorageMode mode)
|
||||
{
|
||||
Q_ASSERT(torrent);
|
||||
|
||||
const lt::torrent_handle torrentHandle = torrent->nativeHandle();
|
||||
const QString currentLocation = Utils::Fs::toNativePath(torrent->actualStorageLocation());
|
||||
const Path currentLocation = torrent->actualStorageLocation();
|
||||
|
||||
if (m_moveStorageQueue.size() > 1)
|
||||
{
|
||||
@@ -4065,7 +4061,7 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
|
||||
if (iter != m_moveStorageQueue.end())
|
||||
{
|
||||
// remove existing inactive job
|
||||
LogMsg(tr("Cancelled moving \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation, iter->path));
|
||||
LogMsg(tr("Cancelled moving \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation.toString(), iter->path.toString()));
|
||||
iter = m_moveStorageQueue.erase(iter);
|
||||
|
||||
iter = std::find_if(iter, m_moveStorageQueue.end(), [&torrentHandle](const MoveStorageJob &job)
|
||||
@@ -4082,26 +4078,26 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
|
||||
{
|
||||
// if there is active job for this torrent prevent creating meaningless
|
||||
// job that will move torrent to the same location as current one
|
||||
if (QDir {m_moveStorageQueue.first().path} == QDir {newPath})
|
||||
if (m_moveStorageQueue.first().path == newPath)
|
||||
{
|
||||
LogMsg(tr("Couldn't enqueue move of \"%1\" to \"%2\". Torrent is currently moving to the same destination location.")
|
||||
.arg(torrent->name(), newPath));
|
||||
.arg(torrent->name(), newPath.toString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (QDir {currentLocation} == QDir {newPath})
|
||||
if (currentLocation == newPath)
|
||||
{
|
||||
LogMsg(tr("Couldn't enqueue move of \"%1\" from \"%2\" to \"%3\". Both paths point to the same location.")
|
||||
.arg(torrent->name(), currentLocation, newPath));
|
||||
.arg(torrent->name(), currentLocation.toString(), newPath.toString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode};
|
||||
m_moveStorageQueue << moveStorageJob;
|
||||
LogMsg(tr("Enqueued to move \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation, newPath));
|
||||
LogMsg(tr("Enqueued to move \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation.toString(), newPath.toString()));
|
||||
|
||||
if (m_moveStorageQueue.size() == 1)
|
||||
moveTorrentStorage(moveStorageJob);
|
||||
@@ -4118,9 +4114,9 @@ void Session::moveTorrentStorage(const MoveStorageJob &job) const
|
||||
#endif
|
||||
const TorrentImpl *torrent = m_torrents.value(id);
|
||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
||||
LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path));
|
||||
LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path.toString()));
|
||||
|
||||
job.torrentHandle.move_storage(job.path.toUtf8().constData()
|
||||
job.torrentHandle.move_storage(job.path.toString().toStdString()
|
||||
, ((job.mode == MoveStorageMode::Overwrite)
|
||||
? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace));
|
||||
}
|
||||
@@ -4164,13 +4160,13 @@ void Session::storeCategories() const
|
||||
jsonObj[categoryName] = categoryOptions.toJSON();
|
||||
}
|
||||
|
||||
const QString path = QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CATEGORIES_FILE_NAME);
|
||||
const Path path = specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME;
|
||||
const QByteArray data = QJsonDocument(jsonObj).toJson();
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't store Categories configuration to %1. Error: %2")
|
||||
.arg(path, result.error()), Log::WARNING);
|
||||
.arg(path.toString(), result.error()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4181,7 +4177,7 @@ void Session::upgradeCategories()
|
||||
{
|
||||
const QString categoryName = it.key();
|
||||
CategoryOptions categoryOptions;
|
||||
categoryOptions.savePath = it.value().toString();
|
||||
categoryOptions.savePath = Path(it.value().toString());
|
||||
m_categories[categoryName] = categoryOptions;
|
||||
}
|
||||
|
||||
@@ -4192,7 +4188,7 @@ void Session::loadCategories()
|
||||
{
|
||||
m_categories.clear();
|
||||
|
||||
QFile confFile {QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CATEGORIES_FILE_NAME)};
|
||||
QFile confFile {(specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME).data()};
|
||||
if (!confFile.exists())
|
||||
{
|
||||
// TODO: Remove the following upgrade code in v4.5
|
||||
@@ -4308,14 +4304,14 @@ void Session::recursiveTorrentDownload(const TorrentID &id)
|
||||
TorrentImpl *const torrent = m_torrents.value(id);
|
||||
if (!torrent) return;
|
||||
|
||||
for (const QString &torrentRelpath : asConst(torrent->filePaths()))
|
||||
for (const Path &torrentRelpath : asConst(torrent->filePaths()))
|
||||
{
|
||||
if (torrentRelpath.endsWith(QLatin1String(".torrent")))
|
||||
if (torrentRelpath.hasExtension(QLatin1String(".torrent")))
|
||||
{
|
||||
LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"
|
||||
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
|
||||
.arg(Utils::Fs::toNativePath(torrentRelpath), torrent->name()));
|
||||
const QString torrentFullpath = torrent->savePath() + '/' + torrentRelpath;
|
||||
.arg(torrentRelpath.toString(), torrent->name()));
|
||||
const Path torrentFullpath = torrent->savePath() / torrentRelpath;
|
||||
|
||||
AddTorrentParams params;
|
||||
// Passing the save path along to the sub torrent file
|
||||
@@ -4343,9 +4339,8 @@ void Session::startUpTorrents()
|
||||
{
|
||||
qDebug("Initializing torrents resume data storage...");
|
||||
|
||||
const QString dbPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/torrents.db"));
|
||||
const bool dbStorageExists = QFile::exists(dbPath);
|
||||
const Path dbPath = specialFolderLocation(SpecialFolder::Data) / Path("torrents.db");
|
||||
const bool dbStorageExists = dbPath.exists();
|
||||
|
||||
ResumeDataStorage *startupStorage = nullptr;
|
||||
if (resumeDataStorageType() == ResumeDataStorageType::SQLite)
|
||||
@@ -4354,15 +4349,13 @@ void Session::startUpTorrents()
|
||||
|
||||
if (!dbStorageExists)
|
||||
{
|
||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/BT_backup"));
|
||||
const Path dataPath = specialFolderLocation(SpecialFolder::Data) / Path("BT_backup");
|
||||
startupStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/BT_backup"));
|
||||
const Path dataPath = specialFolderLocation(SpecialFolder::Data) / Path("BT_backup");
|
||||
m_resumeDataStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||
|
||||
if (dbStorageExists)
|
||||
@@ -4491,7 +4484,7 @@ void Session::startUpTorrents()
|
||||
{
|
||||
delete startupStorage;
|
||||
if (resumeDataStorageType() == ResumeDataStorageType::Legacy)
|
||||
Utils::Fs::forceRemove(dbPath);
|
||||
Utils::Fs::removeFile(dbPath);
|
||||
|
||||
if (isQueueingSystemEnabled())
|
||||
m_resumeDataStorage->storeQueue(queue);
|
||||
@@ -5064,7 +5057,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
||||
const MoveStorageJob ¤tJob = m_moveStorageQueue.first();
|
||||
Q_ASSERT(currentJob.torrentHandle == p->handle);
|
||||
|
||||
const QString newPath {p->storage_path()};
|
||||
const Path newPath {QString::fromUtf8(p->storage_path())};
|
||||
Q_ASSERT(newPath == currentJob.path);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
@@ -5075,7 +5068,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
||||
|
||||
TorrentImpl *torrent = m_torrents.value(id);
|
||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
||||
LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath));
|
||||
LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath.toString()));
|
||||
|
||||
handleMoveTorrentStorageJobFinished();
|
||||
}
|
||||
@@ -5098,7 +5091,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
|
||||
const QString currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path);
|
||||
const QString errorMessage = QString::fromStdString(p->message());
|
||||
LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.")
|
||||
.arg(torrentName, currentLocation, currentJob.path, errorMessage), Log::CRITICAL);
|
||||
.arg(torrentName, currentLocation, currentJob.path.toString(), errorMessage), Log::CRITICAL);
|
||||
|
||||
handleMoveTorrentStorageJobFinished();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <QtContainerFwd>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
#include "base/types.h"
|
||||
#include "addtorrentparams.h"
|
||||
@@ -202,7 +203,7 @@ namespace BitTorrent
|
||||
} disk;
|
||||
};
|
||||
|
||||
class Session : public QObject
|
||||
class Session final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Session)
|
||||
@@ -212,10 +213,10 @@ namespace BitTorrent
|
||||
static void freeInstance();
|
||||
static Session *instance();
|
||||
|
||||
QString savePath() const;
|
||||
void setSavePath(const QString &path);
|
||||
QString downloadPath() const;
|
||||
void setDownloadPath(const QString &path);
|
||||
Path savePath() const;
|
||||
void setSavePath(const Path &path);
|
||||
Path downloadPath() const;
|
||||
void setDownloadPath(const Path &path);
|
||||
bool isDownloadPathEnabled() const;
|
||||
void setDownloadPathEnabled(bool enabled);
|
||||
|
||||
@@ -225,8 +226,8 @@ namespace BitTorrent
|
||||
|
||||
QStringList categories() const;
|
||||
CategoryOptions categoryOptions(const QString &categoryName) const;
|
||||
QString categorySavePath(const QString &categoryName) const;
|
||||
QString categoryDownloadPath(const QString &categoryName) const;
|
||||
Path categorySavePath(const QString &categoryName) const;
|
||||
Path categoryDownloadPath(const QString &categoryName) const;
|
||||
bool addCategory(const QString &name, const CategoryOptions &options = {});
|
||||
bool editCategory(const QString &name, const CategoryOptions &options);
|
||||
bool removeCategory(const QString &name);
|
||||
@@ -283,10 +284,10 @@ namespace BitTorrent
|
||||
void setRefreshInterval(int value);
|
||||
bool isPreallocationEnabled() const;
|
||||
void setPreallocationEnabled(bool enabled);
|
||||
QString torrentExportDirectory() const;
|
||||
void setTorrentExportDirectory(QString path);
|
||||
QString finishedTorrentExportDirectory() const;
|
||||
void setFinishedTorrentExportDirectory(QString path);
|
||||
Path torrentExportDirectory() const;
|
||||
void setTorrentExportDirectory(const Path &path);
|
||||
Path finishedTorrentExportDirectory() const;
|
||||
void setFinishedTorrentExportDirectory(const Path &path);
|
||||
|
||||
int globalDownloadSpeedLimit() const;
|
||||
void setGlobalDownloadSpeedLimit(int limit);
|
||||
@@ -329,8 +330,8 @@ namespace BitTorrent
|
||||
void setAdditionalTrackers(const QString &trackers);
|
||||
bool isIPFilteringEnabled() const;
|
||||
void setIPFilteringEnabled(bool enabled);
|
||||
QString IPFilterFile() const;
|
||||
void setIPFilterFile(QString path);
|
||||
Path IPFilterFile() const;
|
||||
void setIPFilterFile(const Path &path);
|
||||
bool announceToAllTrackers() const;
|
||||
void setAnnounceToAllTrackers(bool val);
|
||||
bool announceToAllTiers() const;
|
||||
@@ -501,10 +502,10 @@ namespace BitTorrent
|
||||
void handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl);
|
||||
void handleTorrentTrackerError(TorrentImpl *const torrent, const QString &trackerUrl);
|
||||
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, MoveStorageMode mode);
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode);
|
||||
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
||||
, const QString &downloadPath, const QStringList &filePaths = {}) const;
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||
|
||||
signals:
|
||||
void allTorrentsFinished();
|
||||
@@ -553,7 +554,7 @@ namespace BitTorrent
|
||||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
||||
void fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
||||
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Session reconfiguration triggers
|
||||
@@ -565,14 +566,14 @@ namespace BitTorrent
|
||||
struct MoveStorageJob
|
||||
{
|
||||
lt::torrent_handle torrentHandle;
|
||||
QString path;
|
||||
Path path;
|
||||
MoveStorageMode mode;
|
||||
};
|
||||
|
||||
struct RemovingTorrentData
|
||||
{
|
||||
QString name;
|
||||
QString pathToRemove;
|
||||
Path pathToRemove;
|
||||
DeleteOption deleteOption;
|
||||
};
|
||||
|
||||
@@ -611,7 +612,7 @@ namespace BitTorrent
|
||||
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
|
||||
|
||||
void updateSeedingLimitTimer();
|
||||
void exportTorrentFile(const TorrentInfo &torrentInfo, const QString &folderPath, const QString &baseName);
|
||||
void exportTorrentFile(const TorrentInfo &torrentInfo, const Path &folderPath, const QString &baseName);
|
||||
|
||||
void handleAlert(const lt::alert *a);
|
||||
void dispatchTorrentAlert(const lt::alert *a);
|
||||
@@ -663,7 +664,7 @@ namespace BitTorrent
|
||||
CachedSettingValue<bool> m_isPeXEnabled;
|
||||
CachedSettingValue<bool> m_isIPFilteringEnabled;
|
||||
CachedSettingValue<bool> m_isTrackerFilteringEnabled;
|
||||
CachedSettingValue<QString> m_IPFilterFile;
|
||||
CachedSettingValue<Path> m_IPFilterFile;
|
||||
CachedSettingValue<bool> m_announceToAllTrackers;
|
||||
CachedSettingValue<bool> m_announceToAllTiers;
|
||||
CachedSettingValue<int> m_asyncIOThreads;
|
||||
@@ -721,8 +722,8 @@ namespace BitTorrent
|
||||
CachedSettingValue<bool> m_isAppendExtensionEnabled;
|
||||
CachedSettingValue<int> m_refreshInterval;
|
||||
CachedSettingValue<bool> m_isPreallocationEnabled;
|
||||
CachedSettingValue<QString> m_torrentExportDirectory;
|
||||
CachedSettingValue<QString> m_finishedTorrentExportDirectory;
|
||||
CachedSettingValue<Path> m_torrentExportDirectory;
|
||||
CachedSettingValue<Path> m_finishedTorrentExportDirectory;
|
||||
CachedSettingValue<int> m_globalDownloadSpeedLimit;
|
||||
CachedSettingValue<int> m_globalUploadSpeedLimit;
|
||||
CachedSettingValue<int> m_altGlobalDownloadSpeedLimit;
|
||||
@@ -740,8 +741,8 @@ namespace BitTorrent
|
||||
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
||||
CachedSettingValue<QStringList> m_storedTags;
|
||||
CachedSettingValue<int> m_maxRatioAction;
|
||||
CachedSettingValue<QString> m_savePath;
|
||||
CachedSettingValue<QString> m_downloadPath;
|
||||
CachedSettingValue<Path> m_savePath;
|
||||
CachedSettingValue<Path> m_downloadPath;
|
||||
CachedSettingValue<bool> m_isDownloadPathEnabled;
|
||||
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
||||
CachedSettingValue<bool> m_useCategoryPathsInManualMode;
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QtContainerFwd>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/tagset.h"
|
||||
#include "abstractfilestorage.h"
|
||||
|
||||
@@ -169,13 +170,13 @@ namespace BitTorrent
|
||||
|
||||
virtual bool isAutoTMMEnabled() const = 0;
|
||||
virtual void setAutoTMMEnabled(bool enabled) = 0;
|
||||
virtual QString savePath() const = 0;
|
||||
virtual void setSavePath(const QString &savePath) = 0;
|
||||
virtual QString downloadPath() const = 0;
|
||||
virtual void setDownloadPath(const QString &downloadPath) = 0;
|
||||
virtual QString actualStorageLocation() const = 0;
|
||||
virtual QString rootPath() const = 0;
|
||||
virtual QString contentPath() const = 0;
|
||||
virtual Path savePath() const = 0;
|
||||
virtual void setSavePath(const Path &savePath) = 0;
|
||||
virtual Path downloadPath() const = 0;
|
||||
virtual void setDownloadPath(const Path &downloadPath) = 0;
|
||||
virtual Path actualStorageLocation() const = 0;
|
||||
virtual Path rootPath() const = 0;
|
||||
virtual Path contentPath() const = 0;
|
||||
virtual QString category() const = 0;
|
||||
virtual bool belongsToCategory(const QString &category) const = 0;
|
||||
virtual bool setCategory(const QString &category) = 0;
|
||||
@@ -193,8 +194,8 @@ namespace BitTorrent
|
||||
virtual qreal ratioLimit() const = 0;
|
||||
virtual int seedingTimeLimit() const = 0;
|
||||
|
||||
virtual QString actualFilePath(int index) const = 0;
|
||||
virtual QStringList filePaths() const = 0;
|
||||
virtual Path actualFilePath(int index) const = 0;
|
||||
virtual PathList filePaths() const = 0;
|
||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||
|
||||
virtual TorrentInfo info() const = 0;
|
||||
|
||||
@@ -32,36 +32,35 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
QString removeExtension(const QString &fileName)
|
||||
Path removeExtension(const Path &fileName)
|
||||
{
|
||||
const QString extension = Utils::Fs::fileExtension(fileName);
|
||||
return extension.isEmpty()
|
||||
? fileName
|
||||
: fileName.chopped(extension.size() + 1);
|
||||
Path result = fileName;
|
||||
result.removeExtension();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::TorrentContentLayout BitTorrent::detectContentLayout(const QStringList &filePaths)
|
||||
BitTorrent::TorrentContentLayout BitTorrent::detectContentLayout(const PathList &filePaths)
|
||||
{
|
||||
const QString rootFolder = Utils::Fs::findRootFolder(filePaths);
|
||||
const Path rootFolder = Path::findRootFolder(filePaths);
|
||||
return (rootFolder.isEmpty()
|
||||
? TorrentContentLayout::NoSubfolder
|
||||
: TorrentContentLayout::Subfolder);
|
||||
}
|
||||
|
||||
void BitTorrent::applyContentLayout(QStringList &filePaths, const BitTorrent::TorrentContentLayout contentLayout, const QString &rootFolder)
|
||||
void BitTorrent::applyContentLayout(PathList &filePaths, const BitTorrent::TorrentContentLayout contentLayout, const Path &rootFolder)
|
||||
{
|
||||
Q_ASSERT(!filePaths.isEmpty());
|
||||
|
||||
switch (contentLayout)
|
||||
{
|
||||
case TorrentContentLayout::Subfolder:
|
||||
if (Utils::Fs::findRootFolder(filePaths).isEmpty())
|
||||
Utils::Fs::addRootFolder(filePaths, !rootFolder.isEmpty() ? rootFolder : removeExtension(filePaths.at(0)));
|
||||
if (Path::findRootFolder(filePaths).isEmpty())
|
||||
Path::addRootFolder(filePaths, !rootFolder.isEmpty() ? rootFolder : removeExtension(filePaths.at(0)));
|
||||
break;
|
||||
|
||||
case TorrentContentLayout::NoSubfolder:
|
||||
Utils::Fs::stripRootFolder(filePaths);
|
||||
Path::stripRootFolder(filePaths);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -28,8 +28,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
||||
@@ -49,6 +52,6 @@ namespace BitTorrent
|
||||
Q_ENUM_NS(TorrentContentLayout)
|
||||
}
|
||||
|
||||
TorrentContentLayout detectContentLayout(const QStringList &filePaths);
|
||||
void applyContentLayout(QStringList &filePaths, TorrentContentLayout contentLayout, const QString &rootFolder = {});
|
||||
TorrentContentLayout detectContentLayout(const PathList &filePaths);
|
||||
void applyContentLayout(PathList &filePaths, TorrentContentLayout contentLayout, const Path &rootFolder = {});
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace
|
||||
// name starts with a .
|
||||
bool fileFilter(const std::string &f)
|
||||
{
|
||||
return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
|
||||
return !Path(f).filename().startsWith('.');
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
@@ -108,50 +108,50 @@ void TorrentCreatorThread::run()
|
||||
|
||||
try
|
||||
{
|
||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
|
||||
const Path parentPath = m_params.inputPath.parentPath();
|
||||
const Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> naturalLessThan {};
|
||||
|
||||
// Adding files to the torrent
|
||||
lt::file_storage fs;
|
||||
if (QFileInfo(m_params.inputPath).isFile())
|
||||
if (QFileInfo(m_params.inputPath.data()).isFile())
|
||||
{
|
||||
lt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
|
||||
lt::add_files(fs, m_params.inputPath.toString().toStdString(), fileFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// need to sort the file names by natural sort order
|
||||
QStringList dirs = {m_params.inputPath};
|
||||
QStringList dirs = {m_params.inputPath.data()};
|
||||
|
||||
QDirIterator dirIter {m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
QDirIterator dirIter {m_params.inputPath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
dirIter.next();
|
||||
dirs += dirIter.filePath();
|
||||
dirs.append(dirIter.filePath());
|
||||
}
|
||||
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
||||
|
||||
QStringList fileNames;
|
||||
QHash<QString, qint64> fileSizeMap;
|
||||
|
||||
for (const auto &dir : asConst(dirs))
|
||||
for (const QString &dir : asConst(dirs))
|
||||
{
|
||||
QStringList tmpNames; // natural sort files within each dir
|
||||
|
||||
QDirIterator fileIter(dir, QDir::Files);
|
||||
QDirIterator fileIter {dir, QDir::Files};
|
||||
while (fileIter.hasNext())
|
||||
{
|
||||
fileIter.next();
|
||||
|
||||
const QString relFilePath = fileIter.filePath().mid(parentPath.length());
|
||||
tmpNames += relFilePath;
|
||||
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
|
||||
const auto relFilePath = parentPath.relativePathOf(Path(fileIter.filePath()));
|
||||
tmpNames.append(relFilePath.toString());
|
||||
fileSizeMap[tmpNames.last()] = fileIter.fileInfo().size();
|
||||
}
|
||||
|
||||
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
||||
fileNames += tmpNames;
|
||||
}
|
||||
|
||||
for (const auto &fileName : asConst(fileNames))
|
||||
for (const QString &fileName : asConst(fileNames))
|
||||
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ void TorrentCreatorThread::run()
|
||||
}
|
||||
|
||||
// calculate the hash for all pieces
|
||||
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
|
||||
lt::set_piece_hashes(newTorrent, parentPath.toString().toStdString()
|
||||
, [this, &newTorrent](const lt::piece_index_t n)
|
||||
{
|
||||
checkInterruptionRequested();
|
||||
@@ -225,16 +225,16 @@ void TorrentCreatorThread::run()
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat)
|
||||
int TorrentCreatorThread::calculateTotalPieces(const Path &inputPath, const int pieceSize, const TorrentFormat torrentFormat)
|
||||
#else
|
||||
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized, const int paddedFileSizeLimit)
|
||||
int TorrentCreatorThread::calculateTotalPieces(const Path &inputPath, const int pieceSize, const bool isAlignmentOptimized, const int paddedFileSizeLimit)
|
||||
#endif
|
||||
{
|
||||
if (inputPath.isEmpty())
|
||||
return 0;
|
||||
|
||||
lt::file_storage fs;
|
||||
lt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
|
||||
lt::add_files(fs, inputPath.toString().toStdString(), fileFilter);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
return lt::create_torrent {fs, pieceSize, toNativeTorrentFormatFlag(torrentFormat)}.num_pieces();
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
@@ -52,8 +54,8 @@ namespace BitTorrent
|
||||
int paddedFileSizeLimit;
|
||||
#endif
|
||||
int pieceSize;
|
||||
QString inputPath;
|
||||
QString savePath;
|
||||
Path inputPath;
|
||||
Path savePath;
|
||||
QString comment;
|
||||
QString source;
|
||||
QStringList trackers;
|
||||
@@ -72,15 +74,15 @@ namespace BitTorrent
|
||||
void create(const TorrentCreatorParams ¶ms);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat);
|
||||
static int calculateTotalPieces(const Path &inputPath, const int pieceSize, const TorrentFormat torrentFormat);
|
||||
#else
|
||||
static int calculateTotalPieces(const QString &inputPath
|
||||
static int calculateTotalPieces(const Path &inputPath
|
||||
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void creationFailure(const QString &msg);
|
||||
void creationSuccess(const QString &path, const QString &branchPath);
|
||||
void creationSuccess(const Path &path, const Path &branchPath);
|
||||
void updateProgress(int progress);
|
||||
|
||||
private:
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
@@ -282,8 +281,10 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||
{
|
||||
const lt::file_index_t nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||
m_indexMap[nativeIndex] = i;
|
||||
const QString filePath = Utils::Fs::toUniformPath(QString::fromStdString(fileStorage.file_path(nativeIndex)));
|
||||
m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
|
||||
Path filePath {fileStorage.file_path(nativeIndex)};
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
filePath.removeExtension();
|
||||
m_filePaths.append(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,24 +296,22 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||
|
||||
// TODO: Remove the following upgrade code in v4.4
|
||||
// == BEGIN UPGRADE CODE ==
|
||||
const QString spath = actualStorageLocation();
|
||||
const Path spath = actualStorageLocation();
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString filepath = filePath(i);
|
||||
const Path filepath = filePath(i);
|
||||
// Move "unwanted" files back to their original folder
|
||||
const QString parentRelPath = Utils::Fs::branchPath(filepath);
|
||||
if (QDir(parentRelPath).dirName() == ".unwanted")
|
||||
const Path parentRelPath = filepath.parentPath();
|
||||
if (parentRelPath.filename() == QLatin1String(".unwanted"))
|
||||
{
|
||||
const QString oldName = Utils::Fs::fileName(filepath);
|
||||
const QString newRelPath = Utils::Fs::branchPath(parentRelPath);
|
||||
if (newRelPath.isEmpty())
|
||||
renameFile(i, oldName);
|
||||
else
|
||||
renameFile(i, QDir(newRelPath).filePath(oldName));
|
||||
const QString oldName = filepath.filename();
|
||||
const Path newRelPath = parentRelPath.parentPath();
|
||||
renameFile(i, (newRelPath / Path(oldName)));
|
||||
|
||||
// Remove .unwanted directory if empty
|
||||
qDebug() << "Attempting to remove \".unwanted\" folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
|
||||
QDir(spath + '/' + newRelPath).rmdir(".unwanted");
|
||||
const Path newPath = spath / newRelPath;
|
||||
qDebug() << "Attempting to remove \".unwanted\" folder at " << (newPath / Path(".unwanted")).toString();
|
||||
Utils::Fs::rmdir(newPath / Path(".unwanted"));
|
||||
}
|
||||
}
|
||||
// == END UPGRADE CODE ==
|
||||
@@ -396,18 +395,18 @@ QString TorrentImpl::currentTracker() const
|
||||
return QString::fromStdString(m_nativeStatus.current_tracker);
|
||||
}
|
||||
|
||||
QString TorrentImpl::savePath() const
|
||||
Path TorrentImpl::savePath() const
|
||||
{
|
||||
return isAutoTMMEnabled() ? m_session->categorySavePath(category()) : m_savePath;
|
||||
}
|
||||
|
||||
void TorrentImpl::setSavePath(const QString &path)
|
||||
void TorrentImpl::setSavePath(const Path &path)
|
||||
{
|
||||
Q_ASSERT(!isAutoTMMEnabled());
|
||||
|
||||
const QString basePath = m_session->useCategoryPathsInManualMode()
|
||||
const Path basePath = m_session->useCategoryPathsInManualMode()
|
||||
? m_session->categorySavePath(category()) : m_session->savePath();
|
||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
const Path resolvedPath = (path.isAbsolute() ? path : (basePath / path));
|
||||
if (resolvedPath == savePath())
|
||||
return;
|
||||
|
||||
@@ -420,18 +419,18 @@ void TorrentImpl::setSavePath(const QString &path)
|
||||
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
||||
}
|
||||
|
||||
QString TorrentImpl::downloadPath() const
|
||||
Path TorrentImpl::downloadPath() const
|
||||
{
|
||||
return isAutoTMMEnabled() ? m_session->categoryDownloadPath(category()) : m_downloadPath;
|
||||
}
|
||||
|
||||
void TorrentImpl::setDownloadPath(const QString &path)
|
||||
void TorrentImpl::setDownloadPath(const Path &path)
|
||||
{
|
||||
Q_ASSERT(!isAutoTMMEnabled());
|
||||
|
||||
const QString basePath = m_session->useCategoryPathsInManualMode()
|
||||
const Path basePath = m_session->useCategoryPathsInManualMode()
|
||||
? m_session->categoryDownloadPath(category()) : m_session->downloadPath();
|
||||
const QString resolvedPath = ((path.isEmpty() || QDir::isAbsolutePath(path)) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
const Path resolvedPath = (path.isEmpty() || path.isAbsolute()) ? path : (basePath / path);
|
||||
if (resolvedPath == m_downloadPath)
|
||||
return;
|
||||
|
||||
@@ -444,27 +443,27 @@ void TorrentImpl::setDownloadPath(const QString &path)
|
||||
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
||||
}
|
||||
|
||||
QString TorrentImpl::rootPath() const
|
||||
Path TorrentImpl::rootPath() const
|
||||
{
|
||||
if (!hasMetadata())
|
||||
return {};
|
||||
|
||||
const QString relativeRootPath = Utils::Fs::findRootFolder(filePaths());
|
||||
const Path relativeRootPath = Path::findRootFolder(filePaths());
|
||||
if (relativeRootPath.isEmpty())
|
||||
return {};
|
||||
|
||||
return QDir(actualStorageLocation()).absoluteFilePath(relativeRootPath);
|
||||
return (actualStorageLocation() / relativeRootPath);
|
||||
}
|
||||
|
||||
QString TorrentImpl::contentPath() const
|
||||
Path TorrentImpl::contentPath() const
|
||||
{
|
||||
if (!hasMetadata())
|
||||
return {};
|
||||
|
||||
if (filesCount() == 1)
|
||||
return QDir(actualStorageLocation()).absoluteFilePath(filePath(0));
|
||||
return (actualStorageLocation() / filePath(0));
|
||||
|
||||
const QString rootPath = this->rootPath();
|
||||
const Path rootPath = this->rootPath();
|
||||
return (rootPath.isEmpty() ? actualStorageLocation() : rootPath);
|
||||
}
|
||||
|
||||
@@ -491,9 +490,9 @@ void TorrentImpl::setAutoTMMEnabled(bool enabled)
|
||||
adjustStorageLocation();
|
||||
}
|
||||
|
||||
QString TorrentImpl::actualStorageLocation() const
|
||||
Path TorrentImpl::actualStorageLocation() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(QString::fromStdString(m_nativeStatus.save_path));
|
||||
return Path(m_nativeStatus.save_path);
|
||||
}
|
||||
|
||||
void TorrentImpl::setAutoManaged(const bool enable)
|
||||
@@ -799,16 +798,15 @@ int TorrentImpl::seedingTimeLimit() const
|
||||
return m_seedingTimeLimit;
|
||||
}
|
||||
|
||||
QString TorrentImpl::filePath(const int index) const
|
||||
Path TorrentImpl::filePath(const int index) const
|
||||
{
|
||||
return m_filePaths.at(index);
|
||||
}
|
||||
|
||||
QString TorrentImpl::actualFilePath(const int index) const
|
||||
Path TorrentImpl::actualFilePath(const int index) const
|
||||
{
|
||||
const auto nativeIndex = m_torrentInfo.nativeIndexes().at(index);
|
||||
const std::string filePath = m_nativeHandle.torrent_file()->files().file_path(nativeIndex);
|
||||
return Utils::Fs::toUniformPath(QString::fromStdString(filePath));
|
||||
return Path(m_nativeHandle.torrent_file()->files().file_path(nativeIndex));
|
||||
}
|
||||
|
||||
qlonglong TorrentImpl::fileSize(const int index) const
|
||||
@@ -816,7 +814,7 @@ qlonglong TorrentImpl::fileSize(const int index) const
|
||||
return m_torrentInfo.fileSize(index);
|
||||
}
|
||||
|
||||
QStringList TorrentImpl::filePaths() const
|
||||
PathList TorrentImpl::filePaths() const
|
||||
{
|
||||
return m_filePaths;
|
||||
}
|
||||
@@ -1481,12 +1479,12 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled, const QVector<
|
||||
m_nativeHandle.prioritize_pieces(piecePriorities);
|
||||
}
|
||||
|
||||
void TorrentImpl::fileSearchFinished(const QString &savePath, const QStringList &fileNames)
|
||||
void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
endReceivedMetadataHandling(savePath, fileNames);
|
||||
}
|
||||
|
||||
void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames)
|
||||
void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
Q_ASSERT(m_filePaths.isEmpty());
|
||||
Q_ASSERT(m_indexMap.isEmpty());
|
||||
@@ -1502,11 +1500,14 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
|
||||
const auto nativeIndex = nativeIndexes.at(i);
|
||||
m_indexMap[nativeIndex] = i;
|
||||
|
||||
const QString filePath = fileNames.at(i);
|
||||
m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
|
||||
p.renamed_files[nativeIndex] = filePath.toStdString();
|
||||
Path filePath = fileNames.at(i);
|
||||
p.renamed_files[nativeIndex] = filePath.toString().toStdString();
|
||||
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
filePath.removeExtension();
|
||||
m_filePaths.append(filePath);
|
||||
}
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
p.ti = metadata;
|
||||
|
||||
const int internalFilesCount = p.ti->files().num_files(); // including .pad files
|
||||
@@ -1613,19 +1614,20 @@ void TorrentImpl::resume(const TorrentOperatingMode mode)
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
|
||||
void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode)
|
||||
{
|
||||
if (m_session->addMoveTorrentStorageJob(this, Utils::Fs::toNativePath(newPath), mode))
|
||||
if (m_session->addMoveTorrentStorageJob(this, newPath, mode))
|
||||
{
|
||||
m_storageIsMoving = true;
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentImpl::renameFile(const int index, const QString &path)
|
||||
void TorrentImpl::renameFile(const int index, const Path &path)
|
||||
{
|
||||
++m_renameCount;
|
||||
m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes().at(index), Utils::Fs::toNativePath(path).toStdString());
|
||||
m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes().at(index)
|
||||
, path.toString().toStdString());
|
||||
}
|
||||
|
||||
void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||
@@ -1790,7 +1792,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
||||
|
||||
TorrentInfo metadata = TorrentInfo(*m_nativeHandle.torrent_file());
|
||||
|
||||
QStringList filePaths = metadata.filePaths();
|
||||
PathList filePaths = metadata.filePaths();
|
||||
applyContentLayout(filePaths, m_contentLayout);
|
||||
m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths);
|
||||
}
|
||||
@@ -1876,37 +1878,23 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
|
||||
// Remove empty leftover folders
|
||||
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
|
||||
// be removed if they are empty
|
||||
const QString oldFilePath = m_filePaths.at(fileIndex);
|
||||
const QString newFilePath = Utils::Fs::toUniformPath(p->new_name());
|
||||
const Path oldFilePath = m_filePaths.at(fileIndex);
|
||||
const Path newFilePath {QString(p->new_name())};
|
||||
|
||||
// Check if ".!qB" extension was just added or removed
|
||||
if ((oldFilePath != newFilePath) && (oldFilePath != newFilePath.chopped(QB_EXT.size())))
|
||||
// We should compare path in a case sensitive manner even on case insensitive
|
||||
// platforms since it can be renamed by only changing case of some character(s)
|
||||
if ((oldFilePath.data() != newFilePath.data())
|
||||
&& ((oldFilePath + QB_EXT) != newFilePath))
|
||||
{
|
||||
m_filePaths[fileIndex] = newFilePath;
|
||||
|
||||
QList<QStringView> oldPathParts = QStringView(oldFilePath).split('/', Qt::SkipEmptyParts);
|
||||
oldPathParts.removeLast(); // drop file name part
|
||||
QList<QStringView> newPathParts = QStringView(newFilePath).split('/', Qt::SkipEmptyParts);
|
||||
newPathParts.removeLast(); // drop file name part
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
|
||||
#else
|
||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
||||
#endif
|
||||
|
||||
int pathIdx = 0;
|
||||
while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size()))
|
||||
Path oldParentPath = oldFilePath.parentPath();
|
||||
const Path commonBasePath = Path::commonPath(oldParentPath, newFilePath.parentPath());
|
||||
while (oldParentPath != commonBasePath)
|
||||
{
|
||||
if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
|
||||
break;
|
||||
++pathIdx;
|
||||
}
|
||||
|
||||
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i)
|
||||
{
|
||||
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QString::fromLatin1("/")));
|
||||
oldPathParts.removeLast();
|
||||
Utils::Fs::rmdir(actualStorageLocation() / oldParentPath);
|
||||
oldParentPath = oldParentPath.parentPath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1923,7 +1911,7 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
|
||||
Q_ASSERT(fileIndex >= 0);
|
||||
|
||||
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
|
||||
.arg(name(), filePath(fileIndex), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
|
||||
.arg(name(), filePath(fileIndex).toString(), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
|
||||
|
||||
--m_renameCount;
|
||||
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
||||
@@ -1939,11 +1927,11 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
||||
const int fileIndex = m_indexMap.value(p->index, -1);
|
||||
Q_ASSERT(fileIndex >= 0);
|
||||
|
||||
const QString path = filePath(fileIndex);
|
||||
const QString actualPath = actualFilePath(fileIndex);
|
||||
const Path path = filePath(fileIndex);
|
||||
const Path actualPath = actualFilePath(fileIndex);
|
||||
if (actualPath != path)
|
||||
{
|
||||
qDebug("Renaming %s to %s", qUtf8Printable(actualPath), qUtf8Printable(path));
|
||||
qDebug("Renaming %s to %s", qUtf8Printable(actualPath.toString()), qUtf8Printable(path.toString()));
|
||||
renameFile(fileIndex, path);
|
||||
}
|
||||
}
|
||||
@@ -2064,18 +2052,17 @@ void TorrentImpl::manageIncompleteFiles()
|
||||
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString path = filePath(i);
|
||||
const Path path = filePath(i);
|
||||
|
||||
const auto nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||
const QString actualPath = Utils::Fs::toUniformPath(
|
||||
QString::fromStdString(nativeFiles.file_path(nativeIndex)));
|
||||
const Path actualPath {nativeFiles.file_path(nativeIndex)};
|
||||
|
||||
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
|
||||
{
|
||||
const QString wantedPath = path + QB_EXT;
|
||||
const Path wantedPath = path + QB_EXT;
|
||||
if (actualPath != wantedPath)
|
||||
{
|
||||
qDebug() << "Renaming" << actualPath << "to" << wantedPath;
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << wantedPath.toString();
|
||||
renameFile(i, wantedPath);
|
||||
}
|
||||
}
|
||||
@@ -2083,7 +2070,7 @@ void TorrentImpl::manageIncompleteFiles()
|
||||
{
|
||||
if (actualPath != path)
|
||||
{
|
||||
qDebug() << "Renaming" << actualPath << "to" << path;
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << path.toString();
|
||||
renameFile(i, path);
|
||||
}
|
||||
}
|
||||
@@ -2092,12 +2079,12 @@ void TorrentImpl::manageIncompleteFiles()
|
||||
|
||||
void TorrentImpl::adjustStorageLocation()
|
||||
{
|
||||
const QString downloadPath = this->downloadPath();
|
||||
const Path downloadPath = this->downloadPath();
|
||||
const bool isFinished = isSeed() || m_hasSeedStatus;
|
||||
const QDir targetDir {((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath)};
|
||||
const Path targetPath = ((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath);
|
||||
|
||||
if ((targetDir != QDir(actualStorageLocation())) || isMoveInProgress())
|
||||
moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
|
||||
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
||||
moveStorage(targetPath, MoveStorageMode::Overwrite);
|
||||
}
|
||||
|
||||
lt::torrent_handle TorrentImpl::nativeHandle() const
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "infohash.h"
|
||||
#include "speedmonitor.h"
|
||||
@@ -102,13 +103,13 @@ namespace BitTorrent
|
||||
|
||||
bool isAutoTMMEnabled() const override;
|
||||
void setAutoTMMEnabled(bool enabled) override;
|
||||
QString savePath() const override;
|
||||
void setSavePath(const QString &path) override;
|
||||
QString downloadPath() const override;
|
||||
void setDownloadPath(const QString &path) override;
|
||||
QString actualStorageLocation() const override;
|
||||
QString rootPath() const override;
|
||||
QString contentPath() const override;
|
||||
Path savePath() const override;
|
||||
void setSavePath(const Path &path) override;
|
||||
Path downloadPath() const override;
|
||||
void setDownloadPath(const Path &path) override;
|
||||
Path actualStorageLocation() const override;
|
||||
Path rootPath() const override;
|
||||
Path contentPath() const override;
|
||||
QString category() const override;
|
||||
bool belongsToCategory(const QString &category) const override;
|
||||
bool setCategory(const QString &category) override;
|
||||
@@ -127,10 +128,10 @@ namespace BitTorrent
|
||||
qreal ratioLimit() const override;
|
||||
int seedingTimeLimit() const override;
|
||||
|
||||
QString filePath(int index) const override;
|
||||
QString actualFilePath(int index) const override;
|
||||
Path filePath(int index) const override;
|
||||
Path actualFilePath(int index) const override;
|
||||
qlonglong fileSize(int index) const override;
|
||||
QStringList filePaths() const override;
|
||||
PathList filePaths() const override;
|
||||
QVector<DownloadPriority> filePriorities() const override;
|
||||
|
||||
TorrentInfo info() const override;
|
||||
@@ -205,7 +206,7 @@ namespace BitTorrent
|
||||
void forceReannounce(int index = -1) override;
|
||||
void forceDHTAnnounce() override;
|
||||
void forceRecheck() override;
|
||||
void renameFile(int index, const QString &path) override;
|
||||
void renameFile(int index, const Path &path) override;
|
||||
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
||||
void setRatioLimit(qreal limit) override;
|
||||
void setSeedingTimeLimit(int limit) override;
|
||||
@@ -237,7 +238,7 @@ namespace BitTorrent
|
||||
void handleAppendExtensionToggled();
|
||||
void saveResumeData();
|
||||
void handleMoveStorageJobFinished(bool hasOutstandingJob);
|
||||
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);
|
||||
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||
|
||||
private:
|
||||
using EventTrigger = std::function<void ()>;
|
||||
@@ -271,12 +272,12 @@ namespace BitTorrent
|
||||
void setAutoManaged(bool enable);
|
||||
|
||||
void adjustStorageLocation();
|
||||
void moveStorage(const QString &newPath, MoveStorageMode mode);
|
||||
void moveStorage(const Path &newPath, MoveStorageMode mode);
|
||||
void manageIncompleteFiles();
|
||||
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
||||
|
||||
void prepareResumeData(const lt::add_torrent_params ¶ms);
|
||||
void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames);
|
||||
void endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames);
|
||||
void reload();
|
||||
|
||||
Session *const m_session;
|
||||
@@ -285,7 +286,7 @@ namespace BitTorrent
|
||||
lt::torrent_status m_nativeStatus;
|
||||
TorrentState m_state = TorrentState::Unknown;
|
||||
TorrentInfo m_torrentInfo;
|
||||
QStringList m_filePaths;
|
||||
PathList m_filePaths;
|
||||
QHash<lt::file_index_t, int> m_indexMap;
|
||||
SpeedMonitor m_speedMonitor;
|
||||
|
||||
@@ -304,8 +305,8 @@ namespace BitTorrent
|
||||
|
||||
// Persistent data
|
||||
QString m_name;
|
||||
QString m_savePath;
|
||||
QString m_downloadPath;
|
||||
Path m_savePath;
|
||||
Path m_downloadPath;
|
||||
QString m_category;
|
||||
TagSet m_tags;
|
||||
qreal m_ratioLimit;
|
||||
|
||||
@@ -99,16 +99,16 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)
|
||||
if (ec)
|
||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||
|
||||
lt::torrent_info nativeInfo {node, ec};
|
||||
const lt::torrent_info nativeInfo {node, ec};
|
||||
if (ec)
|
||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||
|
||||
return TorrentInfo(nativeInfo);
|
||||
}
|
||||
|
||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const QString &path) noexcept
|
||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const Path &path) noexcept
|
||||
{
|
||||
QFile file {path};
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return nonstd::make_unexpected(file.errorString());
|
||||
|
||||
@@ -133,7 +133,7 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const QString &
|
||||
return load(data);
|
||||
}
|
||||
|
||||
nonstd::expected<void, QString> TorrentInfo::saveToFile(const QString &path) const
|
||||
nonstd::expected<void, QString> TorrentInfo::saveToFile(const Path &path) const
|
||||
{
|
||||
if (!isValid())
|
||||
return nonstd::make_unexpected(tr("Invalid metadata"));
|
||||
@@ -236,17 +236,16 @@ int TorrentInfo::piecesCount() const
|
||||
return m_nativeInfo->num_pieces();
|
||||
}
|
||||
|
||||
QString TorrentInfo::filePath(const int index) const
|
||||
Path TorrentInfo::filePath(const int index) const
|
||||
{
|
||||
if (!isValid()) return {};
|
||||
|
||||
return Utils::Fs::toUniformPath(
|
||||
QString::fromStdString(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index])));
|
||||
return Path(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index]));
|
||||
}
|
||||
|
||||
QStringList TorrentInfo::filePaths() const
|
||||
PathList TorrentInfo::filePaths() const
|
||||
{
|
||||
QStringList list;
|
||||
PathList list;
|
||||
list.reserve(filesCount());
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
list << filePath(i);
|
||||
@@ -312,15 +311,15 @@ QByteArray TorrentInfo::metadata() const
|
||||
#endif
|
||||
}
|
||||
|
||||
QStringList TorrentInfo::filesForPiece(const int pieceIndex) const
|
||||
PathList TorrentInfo::filesForPiece(const int pieceIndex) const
|
||||
{
|
||||
// no checks here because fileIndicesForPiece() will return an empty list
|
||||
const QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||
|
||||
QStringList res;
|
||||
PathList res;
|
||||
res.reserve(fileIndices.size());
|
||||
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
|
||||
[this](int i) { return filePath(i); });
|
||||
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res)
|
||||
, [this](int i) { return filePath(i); });
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -359,15 +358,15 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
|
||||
return hashes;
|
||||
}
|
||||
|
||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString &file) const
|
||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const Path &filePath) const
|
||||
{
|
||||
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||
return {};
|
||||
|
||||
const int index = fileIndex(file);
|
||||
const int index = fileIndex(filePath);
|
||||
if (index == -1)
|
||||
{
|
||||
qDebug() << "Filename" << file << "was not found in torrent" << name();
|
||||
qDebug() << "Filename" << filePath.toString() << "was not found in torrent" << name();
|
||||
return {};
|
||||
}
|
||||
return filePieces(index);
|
||||
@@ -396,13 +395,13 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
|
||||
return makeInterval(beginIdx, endIdx);
|
||||
}
|
||||
|
||||
int TorrentInfo::fileIndex(const QString &fileName) const
|
||||
int TorrentInfo::fileIndex(const Path &filePath) const
|
||||
{
|
||||
// the check whether the object is valid is not needed here
|
||||
// because if filesCount() returns -1 the loop exits immediately
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
if (fileName == filePath(i))
|
||||
if (filePath == this->filePath(i))
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "base/3rdparty/expected.hpp"
|
||||
#include "base/indexrange.h"
|
||||
#include "base/pathfwd.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
class QByteArray;
|
||||
@@ -58,8 +59,8 @@ namespace BitTorrent
|
||||
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
||||
|
||||
static nonstd::expected<TorrentInfo, QString> load(const QByteArray &data) noexcept;
|
||||
static nonstd::expected<TorrentInfo, QString> loadFromFile(const QString &path) noexcept;
|
||||
nonstd::expected<void, QString> saveToFile(const QString &path) const;
|
||||
static nonstd::expected<TorrentInfo, QString> loadFromFile(const Path &path) noexcept;
|
||||
nonstd::expected<void, QString> saveToFile(const Path &path) const;
|
||||
|
||||
TorrentInfo &operator=(const TorrentInfo &other);
|
||||
|
||||
@@ -75,21 +76,21 @@ namespace BitTorrent
|
||||
int pieceLength() const;
|
||||
int pieceLength(int index) const;
|
||||
int piecesCount() const;
|
||||
QString filePath(int index) const;
|
||||
QStringList filePaths() const;
|
||||
Path filePath(int index) const;
|
||||
PathList filePaths() const;
|
||||
qlonglong fileSize(int index) const;
|
||||
qlonglong fileOffset(int index) const;
|
||||
QVector<TrackerEntry> trackers() const;
|
||||
QVector<QUrl> urlSeeds() const;
|
||||
QByteArray metadata() const;
|
||||
QStringList filesForPiece(int pieceIndex) const;
|
||||
PathList filesForPiece(int pieceIndex) const;
|
||||
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
||||
QVector<QByteArray> pieceHashes() const;
|
||||
|
||||
using PieceRange = IndexRange<int>;
|
||||
// returns pair of the first and the last pieces into which
|
||||
// the given file extends (maybe partially).
|
||||
PieceRange filePieces(const QString &file) const;
|
||||
PieceRange filePieces(const Path &filePath) const;
|
||||
PieceRange filePieces(int fileIndex) const;
|
||||
|
||||
std::shared_ptr<lt::torrent_info> nativeInfo() const;
|
||||
@@ -97,7 +98,7 @@ namespace BitTorrent
|
||||
|
||||
private:
|
||||
// returns file index or -1 if fileName is not found
|
||||
int fileIndex(const QString &fileName) const;
|
||||
int fileIndex(const Path &filePath) const;
|
||||
TorrentContentLayout contentLayout() const;
|
||||
|
||||
std::shared_ptr<const lt::torrent_info> m_nativeInfo;
|
||||
|
||||
Reference in New Issue
Block a user