Implement class for handling filesystem paths

PR #15915.
This commit is contained in:
Vladimir Golovnev
2022-02-08 06:03:48 +03:00
committed by GitHub
parent facfa26eed
commit dd1bd8ad10
131 changed files with 2252 additions and 1868 deletions

View File

@@ -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);
}
}

View File

@@ -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);
};
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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}
};
}

View File

@@ -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);

View File

@@ -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 &params
CustomStorage::CustomStorage(const lt::storage_params &params, 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);
}
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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())
{

View File

@@ -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);
};

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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 &params)
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 &currentJob = 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();
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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 = {});
}

View File

@@ -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();

View File

@@ -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 &params);
#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:

View File

@@ -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

View File

@@ -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 &params);
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;

View File

@@ -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;
}

View File

@@ -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;