parent
facfa26eed
commit
dd1bd8ad10
|
@ -45,7 +45,6 @@
|
|||
|
||||
#include <QAtomicInt>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QLibraryInfo>
|
||||
#include <QProcess>
|
||||
|
||||
|
@ -80,6 +79,7 @@
|
|||
#include "base/torrentfileswatcher.h"
|
||||
#include "base/utils/compare.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "base/version.h"
|
||||
#include "applicationinstancemanager.h"
|
||||
|
@ -105,7 +105,7 @@ namespace
|
|||
const QString LOG_FOLDER = QStringLiteral("logs");
|
||||
const QChar PARAMS_SEPARATOR = QLatin1Char('|');
|
||||
|
||||
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QStringLiteral("profile");
|
||||
const Path DEFAULT_PORTABLE_MODE_PROFILE_DIR {QStringLiteral("profile")};
|
||||
|
||||
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
||||
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
||||
|
@ -143,16 +143,16 @@ Application::Application(int &argc, char **argv)
|
|||
QPixmapCache::setCacheLimit(PIXMAP_CACHE_SIZE);
|
||||
#endif
|
||||
|
||||
const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty()
|
||||
&& QDir(QCoreApplication::applicationDirPath()).exists(DEFAULT_PORTABLE_MODE_PROFILE_DIR);
|
||||
const auto portableProfilePath = Path(QCoreApplication::applicationDirPath()) / DEFAULT_PORTABLE_MODE_PROFILE_DIR;
|
||||
const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty() && portableProfilePath.exists();
|
||||
|
||||
const QString profileDir = portableModeEnabled
|
||||
? QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(DEFAULT_PORTABLE_MODE_PROFILE_DIR)
|
||||
const Path profileDir = portableModeEnabled
|
||||
? portableProfilePath
|
||||
: m_commandLineArgs.profileDir;
|
||||
Profile::initInstance(profileDir, m_commandLineArgs.configurationName,
|
||||
(m_commandLineArgs.relativeFastresumePaths || portableModeEnabled));
|
||||
|
||||
m_instanceManager = new ApplicationInstanceManager {Profile::instance()->location(SpecialFolder::Config), this};
|
||||
m_instanceManager = new ApplicationInstanceManager(Profile::instance()->location(SpecialFolder::Config), this);
|
||||
|
||||
Logger::initInstance();
|
||||
SettingsStorage::initInstance();
|
||||
|
@ -175,13 +175,13 @@ Application::Application(int &argc, char **argv)
|
|||
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(QBT_VERSION));
|
||||
if (portableModeEnabled)
|
||||
{
|
||||
Logger::instance()->addMessage(tr("Running in portable mode. Auto detected profile folder at: %1").arg(profileDir));
|
||||
Logger::instance()->addMessage(tr("Running in portable mode. Auto detected profile folder at: %1").arg(profileDir.toString()));
|
||||
if (m_commandLineArgs.relativeFastresumePaths)
|
||||
Logger::instance()->addMessage(tr("Redundant command line flag detected: \"%1\". Portable mode implies relative fastresume.").arg("--relative-fastresume"), Log::WARNING); // to avoid translating the `--relative-fastresume` string
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::instance()->addMessage(tr("Using config directory: %1").arg(Profile::instance()->location(SpecialFolder::Config)));
|
||||
Logger::instance()->addMessage(tr("Using config directory: %1").arg(Profile::instance()->location(SpecialFolder::Config).toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,12 +218,12 @@ void Application::setFileLoggerEnabled(const bool value)
|
|||
m_storeFileLoggerEnabled = value;
|
||||
}
|
||||
|
||||
QString Application::fileLoggerPath() const
|
||||
Path Application::fileLoggerPath() const
|
||||
{
|
||||
return m_storeFileLoggerPath.get(QDir(specialFolderLocation(SpecialFolder::Data)).absoluteFilePath(LOG_FOLDER));
|
||||
return m_storeFileLoggerPath.get(specialFolderLocation(SpecialFolder::Data) / Path(LOG_FOLDER));
|
||||
}
|
||||
|
||||
void Application::setFileLoggerPath(const QString &path)
|
||||
void Application::setFileLoggerPath(const Path &path)
|
||||
{
|
||||
if (m_fileLogger)
|
||||
m_fileLogger->changePath(path);
|
||||
|
@ -327,16 +327,16 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
|
|||
break;
|
||||
case u'D':
|
||||
#if defined(Q_OS_WIN)
|
||||
program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->savePath())));
|
||||
program.replace(i, 2, chopPathSep(torrent->savePath().toString()));
|
||||
#else
|
||||
program.replace(i, 2, Utils::Fs::toNativePath(torrent->savePath()));
|
||||
program.replace(i, 2, torrent->savePath().toString());
|
||||
#endif
|
||||
break;
|
||||
case u'F':
|
||||
#if defined(Q_OS_WIN)
|
||||
program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->contentPath())));
|
||||
program.replace(i, 2, chopPathSep(torrent->contentPath().toString()));
|
||||
#else
|
||||
program.replace(i, 2, Utils::Fs::toNativePath(torrent->contentPath()));
|
||||
program.replace(i, 2, torrent->contentPath().toString());
|
||||
#endif
|
||||
break;
|
||||
case u'G':
|
||||
|
@ -359,9 +359,9 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
|
|||
break;
|
||||
case u'R':
|
||||
#if defined(Q_OS_WIN)
|
||||
program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->rootPath())));
|
||||
program.replace(i, 2, chopPathSep(torrent->rootPath().toString()));
|
||||
#else
|
||||
program.replace(i, 2, Utils::Fs::toNativePath(torrent->rootPath()));
|
||||
program.replace(i, 2, torrent->rootPath().toString());
|
||||
#endif
|
||||
break;
|
||||
case u'T':
|
||||
|
@ -439,7 +439,7 @@ void Application::sendNotificationEmail(const BitTorrent::Torrent *torrent)
|
|||
// Prepare mail content
|
||||
const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
|
||||
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + '\n'
|
||||
+ tr("Save path: %1").arg(torrent->savePath()) + "\n\n"
|
||||
+ tr("Save path: %1").arg(torrent->savePath().toString()) + "\n\n"
|
||||
+ tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds")
|
||||
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
|
||||
+ tr("Thank you for using qBittorrent.") + '\n';
|
||||
|
@ -545,7 +545,7 @@ void Application::processParams(const QStringList ¶ms)
|
|||
|
||||
if (param.startsWith(QLatin1String("@savePath=")))
|
||||
{
|
||||
torrentParams.savePath = param.mid(10);
|
||||
torrentParams.savePath = Path(param.mid(10));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -821,7 +821,7 @@ void Application::cleanup()
|
|||
Logger::freeInstance();
|
||||
IconProvider::freeInstance();
|
||||
SearchPluginManager::freeInstance();
|
||||
Utils::Fs::removeDirRecursive(Utils::Fs::tempPath());
|
||||
Utils::Fs::removeDirRecursively(Utils::Fs::tempPath());
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
if (m_window)
|
||||
|
|
|
@ -47,6 +47,7 @@ class QSessionManager;
|
|||
using BaseApplication = QCoreApplication;
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
#include "base/types.h"
|
||||
#include "cmdoptions.h"
|
||||
|
@ -91,8 +92,8 @@ public:
|
|||
// FileLogger properties
|
||||
bool isFileLoggerEnabled() const;
|
||||
void setFileLoggerEnabled(bool value);
|
||||
QString fileLoggerPath() const;
|
||||
void setFileLoggerPath(const QString &path);
|
||||
Path fileLoggerPath() const;
|
||||
void setFileLoggerPath(const Path &path);
|
||||
bool isFileLoggerBackup() const;
|
||||
void setFileLoggerBackup(bool value);
|
||||
bool isFileLoggerDeleteOld() const;
|
||||
|
@ -152,5 +153,5 @@ private:
|
|||
SettingValue<int> m_storeFileLoggerMaxSize;
|
||||
SettingValue<int> m_storeFileLoggerAge;
|
||||
SettingValue<int> m_storeFileLoggerAgeType;
|
||||
SettingValue<QString> m_storeFileLoggerPath;
|
||||
SettingValue<Path> m_storeFileLoggerPath;
|
||||
};
|
||||
|
|
|
@ -37,18 +37,19 @@
|
|||
#include <QSharedMemory>
|
||||
#endif
|
||||
|
||||
#include "base/path.h"
|
||||
#include "qtlocalpeer/qtlocalpeer.h"
|
||||
|
||||
ApplicationInstanceManager::ApplicationInstanceManager(const QString &instancePath, QObject *parent)
|
||||
ApplicationInstanceManager::ApplicationInstanceManager(const Path &instancePath, QObject *parent)
|
||||
: QObject {parent}
|
||||
, m_peer {new QtLocalPeer {instancePath, this}}
|
||||
, m_peer {new QtLocalPeer(instancePath.data(), this)}
|
||||
, m_isFirstInstance {!m_peer->isClient()}
|
||||
{
|
||||
connect(m_peer, &QtLocalPeer::messageReceived, this, &ApplicationInstanceManager::messageReceived);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const QString sharedMemoryKey = instancePath + QLatin1String {"/shared-memory"};
|
||||
auto sharedMem = new QSharedMemory {sharedMemoryKey, this};
|
||||
const QString sharedMemoryKey = instancePath.data() + QLatin1String("/shared-memory");
|
||||
auto sharedMem = new QSharedMemory(sharedMemoryKey, this);
|
||||
if (m_isFirstInstance)
|
||||
{
|
||||
// First instance creates shared memory and store PID
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QtLocalPeer;
|
||||
|
||||
class ApplicationInstanceManager final : public QObject
|
||||
|
@ -38,7 +40,7 @@ class ApplicationInstanceManager final : public QObject
|
|||
Q_DISABLE_COPY_MOVE(ApplicationInstanceManager)
|
||||
|
||||
public:
|
||||
explicit ApplicationInstanceManager(const QString &instancePath, QObject *parent = nullptr);
|
||||
explicit ApplicationInstanceManager(const Path &instancePath, QObject *parent = nullptr);
|
||||
|
||||
bool isFirstInstance() const;
|
||||
|
||||
|
|
|
@ -372,7 +372,7 @@ QStringList QBtCommandLineParameters::paramList() const
|
|||
// torrent paths or URLs.
|
||||
|
||||
if (!savePath.isEmpty())
|
||||
result.append(QLatin1String("@savePath=") + savePath);
|
||||
result.append(QLatin1String("@savePath=") + savePath.data());
|
||||
|
||||
if (addPaused.has_value())
|
||||
result.append(*addPaused ? QLatin1String {"@addPaused=1"} : QLatin1String {"@addPaused=0"});
|
||||
|
@ -438,7 +438,7 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||
#endif
|
||||
else if (arg == PROFILE_OPTION)
|
||||
{
|
||||
result.profileDir = PROFILE_OPTION.value(arg);
|
||||
result.profileDir = Path(PROFILE_OPTION.value(arg));
|
||||
}
|
||||
else if (arg == RELATIVE_FASTRESUME)
|
||||
{
|
||||
|
@ -450,7 +450,7 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||
}
|
||||
else if (arg == SAVE_PATH_OPTION)
|
||||
{
|
||||
result.savePath = SAVE_PATH_OPTION.value(arg);
|
||||
result.savePath = Path(SAVE_PATH_OPTION.value(arg));
|
||||
}
|
||||
else if (arg == PAUSED_OPTION)
|
||||
{
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <QStringList>
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/path.h"
|
||||
|
||||
class QProcessEnvironment;
|
||||
|
||||
|
@ -58,9 +59,9 @@ struct QBtCommandLineParameters
|
|||
std::optional<bool> addPaused;
|
||||
std::optional<bool> skipDialog;
|
||||
QStringList torrents;
|
||||
QString profileDir;
|
||||
Path profileDir;
|
||||
QString configurationName;
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
QString category;
|
||||
QString unknownParameter;
|
||||
|
||||
|
|
|
@ -44,7 +44,9 @@ namespace
|
|||
const std::chrono::seconds FLUSH_INTERVAL {2};
|
||||
}
|
||||
|
||||
FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize, const bool deleteOld, const int age, const FileLogAgeType ageType)
|
||||
FileLogger::FileLogger(const Path &path, const bool backup
|
||||
, const int maxSize, const bool deleteOld, const int age
|
||||
, const FileLogAgeType ageType)
|
||||
: m_backup(backup)
|
||||
, m_maxSize(maxSize)
|
||||
{
|
||||
|
@ -68,26 +70,25 @@ FileLogger::~FileLogger()
|
|||
closeLogFile();
|
||||
}
|
||||
|
||||
void FileLogger::changePath(const QString &newPath)
|
||||
void FileLogger::changePath(const Path &newPath)
|
||||
{
|
||||
const QDir dir(newPath);
|
||||
dir.mkpath(newPath);
|
||||
const QString tmpPath = dir.absoluteFilePath("qbittorrent.log");
|
||||
// compare paths as strings to perform case sensitive comparison on all the platforms
|
||||
if (newPath.data() == m_path.parentPath().data())
|
||||
return;
|
||||
|
||||
if (tmpPath != m_path)
|
||||
{
|
||||
m_path = tmpPath;
|
||||
closeLogFile();
|
||||
|
||||
closeLogFile();
|
||||
m_logFile.setFileName(m_path);
|
||||
openLogFile();
|
||||
}
|
||||
m_path = newPath / Path("qbittorrent.log");
|
||||
m_logFile.setFileName(m_path.data());
|
||||
|
||||
Utils::Fs::mkpath(newPath);
|
||||
openLogFile();
|
||||
}
|
||||
|
||||
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
||||
{
|
||||
const QDateTime date = QDateTime::currentDateTime();
|
||||
const QDir dir(Utils::Fs::branchPath(m_path));
|
||||
const QDir dir {m_path.parentPath().data()};
|
||||
const QFileInfoList fileList = dir.entryInfoList(QStringList("qbittorrent.log.bak*")
|
||||
, (QDir::Files | QDir::Writable), (QDir::Time | QDir::Reversed));
|
||||
|
||||
|
@ -107,7 +108,7 @@ void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
|||
}
|
||||
if (modificationDate > date)
|
||||
break;
|
||||
Utils::Fs::forceRemove(file.absoluteFilePath());
|
||||
Utils::Fs::removeFile(Path(file.absoluteFilePath()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,15 +152,15 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
|
|||
{
|
||||
closeLogFile();
|
||||
int counter = 0;
|
||||
QString backupLogFilename = m_path + ".bak";
|
||||
Path backupLogFilename = m_path + ".bak";
|
||||
|
||||
while (QFile::exists(backupLogFilename))
|
||||
while (backupLogFilename.exists())
|
||||
{
|
||||
++counter;
|
||||
backupLogFilename = m_path + ".bak" + QString::number(counter);
|
||||
}
|
||||
|
||||
QFile::rename(m_path, backupLogFilename);
|
||||
Utils::Fs::renameFile(m_path, backupLogFilename);
|
||||
openLogFile();
|
||||
}
|
||||
else
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace Log
|
||||
{
|
||||
struct Msg;
|
||||
|
@ -50,10 +52,10 @@ public:
|
|||
YEARS
|
||||
};
|
||||
|
||||
FileLogger(const QString &path, bool backup, int maxSize, bool deleteOld, int age, FileLogAgeType ageType);
|
||||
FileLogger(const Path &path, bool backup, int maxSize, bool deleteOld, int age, FileLogAgeType ageType);
|
||||
~FileLogger();
|
||||
|
||||
void changePath(const QString &newPath);
|
||||
void changePath(const Path &newPath);
|
||||
void deleteOld(int age, FileLogAgeType ageType);
|
||||
void setBackup(bool value);
|
||||
void setMaxSize(int value);
|
||||
|
@ -66,7 +68,7 @@ private:
|
|||
void openLogFile();
|
||||
void closeLogFile();
|
||||
|
||||
QString m_path;
|
||||
Path m_path;
|
||||
bool m_backup;
|
||||
int m_maxSize;
|
||||
QFile m_logFile;
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace
|
|||
|
||||
void exportWebUIHttpsFiles()
|
||||
{
|
||||
const auto migrate = [](const QString &oldKey, const QString &newKey, const QString &savePath)
|
||||
const auto migrate = [](const QString &oldKey, const QString &newKey, const Path &savePath)
|
||||
{
|
||||
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
||||
const auto oldData {settingsStorage->loadValue<QByteArray>(oldKey)};
|
||||
|
@ -61,24 +61,24 @@ namespace
|
|||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(savePath, oldData);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(errorMsgFormat.arg(savePath, result.error()) , Log::WARNING);
|
||||
LogMsg(errorMsgFormat.arg(savePath.toString(), result.error()) , Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
settingsStorage->storeValue(newKey, savePath);
|
||||
settingsStorage->removeValue(oldKey);
|
||||
|
||||
LogMsg(QObject::tr("Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath)
|
||||
LogMsg(QObject::tr("Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath.toString())
|
||||
, Log::INFO);
|
||||
};
|
||||
|
||||
const QString configPath {specialFolderLocation(SpecialFolder::Config)};
|
||||
const Path configPath = specialFolderLocation(SpecialFolder::Config);
|
||||
migrate(QLatin1String("Preferences/WebUI/HTTPS/Certificate")
|
||||
, QLatin1String("Preferences/WebUI/HTTPS/CertificatePath")
|
||||
, Utils::Fs::toNativePath(configPath + QLatin1String("/WebUICertificate.crt")));
|
||||
, (configPath / Path("WebUICertificate.crt")));
|
||||
migrate(QLatin1String("Preferences/WebUI/HTTPS/Key")
|
||||
, QLatin1String("Preferences/WebUI/HTTPS/KeyPath")
|
||||
, Utils::Fs::toNativePath(configPath + QLatin1String("/WebUIPrivateKey.pem")));
|
||||
, (configPath / Path("WebUIPrivateKey.pem")));
|
||||
}
|
||||
|
||||
void upgradeTorrentContentLayout()
|
||||
|
|
|
@ -62,6 +62,8 @@ add_library(qbt_base STATIC
|
|||
net/reverseresolution.h
|
||||
net/smtp.h
|
||||
orderedset.h
|
||||
path.h
|
||||
pathfwd.h
|
||||
preferences.h
|
||||
profile.h
|
||||
profile_p.h
|
||||
|
@ -144,6 +146,7 @@ add_library(qbt_base STATIC
|
|||
net/proxyconfigurationmanager.cpp
|
||||
net/reverseresolution.cpp
|
||||
net/smtp.cpp
|
||||
path.cpp
|
||||
preferences.cpp
|
||||
profile.cpp
|
||||
profile_p.cpp
|
||||
|
|
|
@ -31,21 +31,22 @@
|
|||
#include <QDebug>
|
||||
#include <QMetaObject>
|
||||
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/io.h"
|
||||
|
||||
AsyncFileStorage::AsyncFileStorage(const QString &storageFolderPath, QObject *parent)
|
||||
AsyncFileStorage::AsyncFileStorage(const Path &storageFolderPath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_storageDir(storageFolderPath)
|
||||
, m_lockFile(m_storageDir.absoluteFilePath(QStringLiteral("storage.lock")))
|
||||
, m_lockFile((m_storageDir / Path(QStringLiteral("storage.lock"))).data())
|
||||
{
|
||||
if (!m_storageDir.mkpath(m_storageDir.absolutePath()))
|
||||
throw AsyncFileStorageError
|
||||
{tr("Could not create directory '%1'.")
|
||||
.arg(m_storageDir.absolutePath())};
|
||||
Q_ASSERT(m_storageDir.isAbsolute());
|
||||
|
||||
if (!Utils::Fs::mkpath(m_storageDir))
|
||||
throw AsyncFileStorageError(tr("Could not create directory '%1'.").arg(m_storageDir.toString()));
|
||||
|
||||
// TODO: This folder locking approach does not work for UNIX systems. Implement it.
|
||||
if (!m_lockFile.open(QFile::WriteOnly))
|
||||
throw AsyncFileStorageError {m_lockFile.errorString()};
|
||||
throw AsyncFileStorageError(m_lockFile.errorString());
|
||||
}
|
||||
|
||||
AsyncFileStorage::~AsyncFileStorage()
|
||||
|
@ -54,21 +55,21 @@ AsyncFileStorage::~AsyncFileStorage()
|
|||
m_lockFile.remove();
|
||||
}
|
||||
|
||||
void AsyncFileStorage::store(const QString &fileName, const QByteArray &data)
|
||||
void AsyncFileStorage::store(const Path &filePath, const QByteArray &data)
|
||||
{
|
||||
QMetaObject::invokeMethod(this, [this, data, fileName]() { store_impl(fileName, data); }
|
||||
QMetaObject::invokeMethod(this, [this, data, filePath]() { store_impl(filePath, data); }
|
||||
, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QDir AsyncFileStorage::storageDir() const
|
||||
Path AsyncFileStorage::storageDir() const
|
||||
{
|
||||
return m_storageDir;
|
||||
}
|
||||
|
||||
void AsyncFileStorage::store_impl(const QString &fileName, const QByteArray &data)
|
||||
void AsyncFileStorage::store_impl(const Path &fileName, const QByteArray &data)
|
||||
{
|
||||
const QString filePath = m_storageDir.absoluteFilePath(fileName);
|
||||
qDebug() << "AsyncFileStorage: Saving data to" << filePath;
|
||||
const Path filePath = m_storageDir / fileName;
|
||||
qDebug() << "AsyncFileStorage: Saving data to" << filePath.toString();
|
||||
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filePath, data);
|
||||
if (!result)
|
||||
|
|
|
@ -28,11 +28,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QObject>
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/path.h"
|
||||
|
||||
class AsyncFileStorageError : public RuntimeError
|
||||
{
|
||||
|
@ -46,19 +46,19 @@ class AsyncFileStorage : public QObject
|
|||
Q_DISABLE_COPY_MOVE(AsyncFileStorage)
|
||||
|
||||
public:
|
||||
explicit AsyncFileStorage(const QString &storageFolderPath, QObject *parent = nullptr);
|
||||
explicit AsyncFileStorage(const Path &storageFolderPath, QObject *parent = nullptr);
|
||||
~AsyncFileStorage() override;
|
||||
|
||||
void store(const QString &fileName, const QByteArray &data);
|
||||
void store(const Path &filePath, const QByteArray &data);
|
||||
|
||||
QDir storageDir() const;
|
||||
Path storageDir() const;
|
||||
|
||||
signals:
|
||||
void failed(const QString &fileName, const QString &errorString);
|
||||
void failed(const Path &filePath, const QString &errorString);
|
||||
|
||||
private:
|
||||
Q_INVOKABLE void store_impl(const QString &fileName, const QByteArray &data);
|
||||
Q_INVOKABLE void store_impl(const Path &fileName, const QByteArray &data);
|
||||
|
||||
QDir m_storageDir;
|
||||
Path m_storageDir;
|
||||
QFile m_lockFile;
|
||||
};
|
||||
|
|
|
@ -61,6 +61,8 @@ HEADERS += \
|
|||
$$PWD/net/reverseresolution.h \
|
||||
$$PWD/net/smtp.h \
|
||||
$$PWD/orderedset.h \
|
||||
$$PWD/path.h \
|
||||
$$PWD/pathfwd.h \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/profile.h \
|
||||
$$PWD/profile_p.h \
|
||||
|
@ -144,6 +146,7 @@ SOURCES += \
|
|||
$$PWD/net/proxyconfigurationmanager.cpp \
|
||||
$$PWD/net/reverseresolution.cpp \
|
||||
$$PWD/net/smtp.cpp \
|
||||
$$PWD/path.cpp \
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/profile.cpp \
|
||||
$$PWD/profile_p.cpp \
|
||||
|
|
|
@ -33,93 +33,63 @@
|
|||
#include <QVector>
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/fs.h"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseInsensitive};
|
||||
#else
|
||||
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseSensitive};
|
||||
#endif
|
||||
|
||||
namespace
|
||||
void BitTorrent::AbstractFileStorage::renameFile(const Path &oldPath, const Path &newPath)
|
||||
{
|
||||
bool areSameFileNames(QString first, QString second)
|
||||
{
|
||||
return QString::compare(first, second, CASE_SENSITIVITY) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BitTorrent::AbstractFileStorage::renameFile(const QString &oldPath, const QString &newPath)
|
||||
{
|
||||
if (!Utils::Fs::isValidFileSystemName(oldPath, true))
|
||||
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
||||
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
||||
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
||||
|
||||
const QString oldFilePath = Utils::Fs::toUniformPath(oldPath);
|
||||
if (oldFilePath.endsWith(QLatin1Char {'/'}))
|
||||
throw RuntimeError {tr("Invalid file path: '%1'.").arg(oldFilePath)};
|
||||
|
||||
const QString newFilePath = Utils::Fs::toUniformPath(newPath);
|
||||
if (newFilePath.endsWith(QLatin1Char {'/'}))
|
||||
throw RuntimeError {tr("Invalid file path: '%1'.").arg(newFilePath)};
|
||||
if (QDir().isAbsolutePath(newFilePath))
|
||||
throw RuntimeError {tr("Absolute path isn't allowed: '%1'.").arg(newFilePath)};
|
||||
if (!oldPath.isValid())
|
||||
throw RuntimeError(tr("The old path is invalid: '%1'.").arg(oldPath.toString()));
|
||||
if (!newPath.isValid())
|
||||
throw RuntimeError(tr("The new path is invalid: '%1'.").arg(newPath.toString()));
|
||||
if (newPath.isAbsolute())
|
||||
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newPath.toString()));
|
||||
|
||||
int renamingFileIndex = -1;
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString path = filePath(i);
|
||||
const Path path = filePath(i);
|
||||
|
||||
if ((renamingFileIndex < 0) && areSameFileNames(path, oldFilePath))
|
||||
if ((renamingFileIndex < 0) && (path == oldPath))
|
||||
renamingFileIndex = i;
|
||||
else if (areSameFileNames(path, newFilePath))
|
||||
throw RuntimeError {tr("The file already exists: '%1'.").arg(newFilePath)};
|
||||
else if (path == newPath)
|
||||
throw RuntimeError(tr("The file already exists: '%1'.").arg(newPath.toString()));
|
||||
}
|
||||
|
||||
if (renamingFileIndex < 0)
|
||||
throw RuntimeError {tr("No such file: '%1'.").arg(oldFilePath)};
|
||||
throw RuntimeError(tr("No such file: '%1'.").arg(oldPath.toString()));
|
||||
|
||||
renameFile(renamingFileIndex, newFilePath);
|
||||
renameFile(renamingFileIndex, newPath);
|
||||
}
|
||||
|
||||
void BitTorrent::AbstractFileStorage::renameFolder(const QString &oldPath, const QString &newPath)
|
||||
void BitTorrent::AbstractFileStorage::renameFolder(const Path &oldFolderPath, const Path &newFolderPath)
|
||||
{
|
||||
if (!Utils::Fs::isValidFileSystemName(oldPath, true))
|
||||
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
||||
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
||||
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
||||
|
||||
const auto cleanFolderPath = [](const QString &path) -> QString
|
||||
{
|
||||
const QString uniformPath = Utils::Fs::toUniformPath(path);
|
||||
return (uniformPath.endsWith(QLatin1Char {'/'}) ? uniformPath : uniformPath + QLatin1Char {'/'});
|
||||
};
|
||||
|
||||
const QString oldFolderPath = cleanFolderPath(oldPath);
|
||||
const QString newFolderPath = cleanFolderPath(newPath);
|
||||
if (QDir().isAbsolutePath(newFolderPath))
|
||||
throw RuntimeError {tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath)};
|
||||
if (!oldFolderPath.isValid())
|
||||
throw RuntimeError(tr("The old path is invalid: '%1'.").arg(oldFolderPath.toString()));
|
||||
if (!newFolderPath.isValid())
|
||||
throw RuntimeError(tr("The new path is invalid: '%1'.").arg(newFolderPath.toString()));
|
||||
if (newFolderPath.isAbsolute())
|
||||
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath.toString()));
|
||||
|
||||
QVector<int> renamingFileIndexes;
|
||||
renamingFileIndexes.reserve(filesCount());
|
||||
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString path = filePath(i);
|
||||
const Path path = filePath(i);
|
||||
|
||||
if (path.startsWith(oldFolderPath, CASE_SENSITIVITY))
|
||||
if (path.hasAncestor(oldFolderPath))
|
||||
renamingFileIndexes.append(i);
|
||||
else if (path.startsWith(newFolderPath, CASE_SENSITIVITY))
|
||||
throw RuntimeError {tr("The folder already exists: '%1'.").arg(newFolderPath)};
|
||||
else if (path.hasAncestor(newFolderPath))
|
||||
throw RuntimeError(tr("The folder already exists: '%1'.").arg(newFolderPath.toString()));
|
||||
}
|
||||
|
||||
if (renamingFileIndexes.isEmpty())
|
||||
throw RuntimeError {tr("No such folder: '%1'.").arg(oldFolderPath)};
|
||||
throw RuntimeError(tr("No such folder: '%1'.").arg(oldFolderPath.toString()));
|
||||
|
||||
for (const int index : renamingFileIndexes)
|
||||
{
|
||||
const QString newFilePath = newFolderPath + filePath(index).mid(oldFolderPath.size());
|
||||
const Path newFilePath = newFolderPath / oldFolderPath.relativePathOf(filePath(index));
|
||||
renameFile(index, newFilePath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QString;
|
||||
|
||||
namespace BitTorrent
|
||||
|
@ -43,12 +45,12 @@ namespace BitTorrent
|
|||
virtual ~AbstractFileStorage() = default;
|
||||
|
||||
virtual int filesCount() const = 0;
|
||||
virtual QString filePath(int index) const = 0;
|
||||
virtual Path filePath(int index) const = 0;
|
||||
virtual qlonglong fileSize(int index) const = 0;
|
||||
|
||||
virtual void renameFile(int index, const QString &name) = 0;
|
||||
virtual void renameFile(int index, const Path &newPath) = 0;
|
||||
|
||||
void renameFile(const QString &oldPath, const QString &newPath);
|
||||
void renameFolder(const QString &oldPath, const QString &newPath);
|
||||
void renameFile(const Path &oldPath, const Path &newPath);
|
||||
void renameFolder(const Path &oldFolderPath, const Path &newFolderPath);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
@ -47,14 +48,14 @@ namespace BitTorrent
|
|||
QString name;
|
||||
QString category;
|
||||
TagSet tags;
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
std::optional<bool> useDownloadPath;
|
||||
QString downloadPath;
|
||||
Path downloadPath;
|
||||
bool sequential = false;
|
||||
bool firstLastPiecePriority = false;
|
||||
bool addForced = false;
|
||||
std::optional<bool> addPaused;
|
||||
QStringList filePaths; // used if TorrentInfo is set
|
||||
PathList filePaths; // used if TorrentInfo is set
|
||||
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
|
||||
bool skipChecking = false;
|
||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||
|
|
|
@ -58,14 +58,14 @@ namespace BitTorrent
|
|||
Q_DISABLE_COPY_MOVE(Worker)
|
||||
|
||||
public:
|
||||
explicit Worker(const QDir &resumeDataDir);
|
||||
explicit Worker(const Path &resumeDataDir);
|
||||
|
||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
||||
void remove(const TorrentID &id) const;
|
||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||
|
||||
private:
|
||||
const QDir m_resumeDataDir;
|
||||
const Path m_resumeDataDir;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -89,20 +89,22 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &path, QObject *parent)
|
||||
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path, QObject *parent)
|
||||
: ResumeDataStorage {parent}
|
||||
, m_resumeDataDir {path}
|
||||
, m_resumeDataPath {path}
|
||||
, m_ioThread {new QThread {this}}
|
||||
, m_asyncWorker {new Worker {m_resumeDataDir}}
|
||||
, m_asyncWorker {new Worker(m_resumeDataPath)}
|
||||
{
|
||||
if (!m_resumeDataDir.exists() && !m_resumeDataDir.mkpath(m_resumeDataDir.absolutePath()))
|
||||
Q_ASSERT(path.isAbsolute());
|
||||
|
||||
if (!m_resumeDataPath.exists() && !Utils::Fs::mkpath(m_resumeDataPath))
|
||||
{
|
||||
throw RuntimeError {tr("Cannot create torrent resume folder: \"%1\"")
|
||||
.arg(Utils::Fs::toNativePath(m_resumeDataDir.absolutePath()))};
|
||||
throw RuntimeError(tr("Cannot create torrent resume folder: \"%1\"")
|
||||
.arg(m_resumeDataPath.toString()));
|
||||
}
|
||||
|
||||
const QRegularExpression filenamePattern {QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$")};
|
||||
const QStringList filenames = m_resumeDataDir.entryList(QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
const QStringList filenames = QDir(m_resumeDataPath.data()).entryList(QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
|
||||
m_registeredTorrents.reserve(filenames.size());
|
||||
for (const QString &filename : filenames)
|
||||
|
@ -112,7 +114,7 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &pa
|
|||
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
|
||||
}
|
||||
|
||||
loadQueue(m_resumeDataDir.absoluteFilePath(QLatin1String("queue")));
|
||||
loadQueue(m_resumeDataPath / Path("queue"));
|
||||
|
||||
qDebug() << "Registered torrents count: " << m_registeredTorrents.size();
|
||||
|
||||
|
@ -135,20 +137,20 @@ QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredT
|
|||
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::load(const TorrentID &id) const
|
||||
{
|
||||
const QString idString = id.toString();
|
||||
const QString fastresumePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(idString));
|
||||
const QString torrentFilePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(idString));
|
||||
const Path fastresumePath = m_resumeDataPath / Path(idString + QLatin1String(".fastresume"));
|
||||
const Path torrentFilePath = m_resumeDataPath / Path(idString + QLatin1String(".torrent"));
|
||||
|
||||
QFile resumeDataFile {fastresumePath};
|
||||
QFile resumeDataFile {fastresumePath.data()};
|
||||
if (!resumeDataFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath, resumeDataFile.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath.toString(), resumeDataFile.errorString()), Log::WARNING);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
QFile metadataFile {torrentFilePath};
|
||||
QFile metadataFile {torrentFilePath.data()};
|
||||
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(torrentFilePath, metadataFile.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(torrentFilePath.toString(), metadataFile.errorString()), Log::WARNING);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -178,12 +180,12 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
|
|||
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
|
||||
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||
Path(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||
torrentParams.useAutoTMM = torrentParams.savePath.isEmpty();
|
||||
if (!torrentParams.useAutoTMM)
|
||||
{
|
||||
torrentParams.downloadPath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||
Path(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||
}
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
||||
|
@ -224,7 +226,8 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
|
|||
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
|
||||
|
||||
p = lt::read_resume_data(root, ec);
|
||||
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->fromPortablePath(
|
||||
Path(fromLTString(p.save_path))).toString().toStdString();
|
||||
|
||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||
{
|
||||
|
@ -273,9 +276,9 @@ void BitTorrent::BencodeResumeDataStorage::storeQueue(const QVector<TorrentID> &
|
|||
});
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const QString &queueFilename)
|
||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||
{
|
||||
QFile queueFile {queueFilename};
|
||||
QFile queueFile {queueFilename.data()};
|
||||
if (!queueFile.exists())
|
||||
return;
|
||||
|
||||
|
@ -306,7 +309,7 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const QString &queueFilenam
|
|||
}
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::Worker::Worker(const QDir &resumeDataDir)
|
||||
BitTorrent::BencodeResumeDataStorage::Worker::Worker(const Path &resumeDataDir)
|
||||
: m_resumeDataDir {resumeDataDir}
|
||||
{
|
||||
}
|
||||
|
@ -315,7 +318,8 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||
{
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->toPortablePath(Path(p.save_path))
|
||||
.toString().toStdString();
|
||||
if (resumeData.stopped)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
|
@ -349,12 +353,12 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||
metadataDict.insert(dataDict.extract("created by"));
|
||||
metadataDict.insert(dataDict.extract("comment"));
|
||||
|
||||
const QString torrentFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(id.toString()));
|
||||
const Path torrentFilepath = m_resumeDataDir / Path(QString::fromLatin1("%1.torrent").arg(id.toString()));
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(torrentFilepath, metadata);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
|
||||
.arg(torrentFilepath, result.error()), Log::CRITICAL);
|
||||
.arg(torrentFilepath.toString(), result.error()), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -370,26 +374,26 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
|
||||
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).toStdString();
|
||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).data().toStdString();
|
||||
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).data().toStdString();
|
||||
}
|
||||
|
||||
const QString resumeFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
||||
const Path resumeFilepath = m_resumeDataDir / Path(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
|
||||
.arg(resumeFilepath, result.error()), Log::CRITICAL);
|
||||
.arg(resumeFilepath.toString(), result.error()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
|
||||
{
|
||||
const QString resumeFilename = QString::fromLatin1("%1.fastresume").arg(id.toString());
|
||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(resumeFilename));
|
||||
const Path resumeFilename {QString::fromLatin1("%1.fastresume").arg(id.toString())};
|
||||
Utils::Fs::removeFile(m_resumeDataDir / resumeFilename);
|
||||
|
||||
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(id.toString());
|
||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(torrentFilename));
|
||||
const Path torrentFilename {QString::fromLatin1("%1.torrent").arg(id.toString())};
|
||||
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
|
||||
|
@ -399,11 +403,11 @@ void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<Torr
|
|||
for (const BitTorrent::TorrentID &torrentID : queue)
|
||||
data += (torrentID.toString().toLatin1() + '\n');
|
||||
|
||||
const QString filepath = m_resumeDataDir.absoluteFilePath(QLatin1String("queue"));
|
||||
const Path filepath = m_resumeDataDir / Path("queue");
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filepath, data);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
||||
.arg(filepath, result.error()), Log::CRITICAL);
|
||||
.arg(filepath.toString(), result.error()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <QDir>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QByteArray;
|
||||
|
@ -44,7 +45,7 @@ namespace BitTorrent
|
|||
Q_DISABLE_COPY_MOVE(BencodeResumeDataStorage)
|
||||
|
||||
public:
|
||||
explicit BencodeResumeDataStorage(const QString &path, QObject *parent = nullptr);
|
||||
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
||||
~BencodeResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
|
@ -54,10 +55,10 @@ namespace BitTorrent
|
|||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||
|
||||
private:
|
||||
void loadQueue(const QString &queueFilename);
|
||||
void loadQueue(const Path &queueFilename);
|
||||
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
||||
|
||||
const QDir m_resumeDataDir;
|
||||
const Path m_resumeDataPath;
|
||||
QVector<TorrentID> m_registeredTorrents;
|
||||
QThread *m_ioThread = nullptr;
|
||||
|
||||
|
|
|
@ -37,13 +37,13 @@ const QString OPTION_DOWNLOADPATH {QStringLiteral("download_path")};
|
|||
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
||||
{
|
||||
CategoryOptions options;
|
||||
options.savePath = jsonObj.value(OPTION_SAVEPATH).toString();
|
||||
options.savePath = Path(jsonObj.value(OPTION_SAVEPATH).toString());
|
||||
|
||||
const QJsonValue downloadPathValue = jsonObj.value(OPTION_DOWNLOADPATH);
|
||||
if (downloadPathValue.isBool())
|
||||
options.downloadPath = {downloadPathValue.toBool(), {}};
|
||||
else if (downloadPathValue.isString())
|
||||
options.downloadPath = {true, downloadPathValue.toString()};
|
||||
options.downloadPath = {true, Path(downloadPathValue.toString())};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -54,13 +54,13 @@ QJsonObject BitTorrent::CategoryOptions::toJSON() const
|
|||
if (downloadPath)
|
||||
{
|
||||
if (downloadPath->enabled)
|
||||
downloadPathValue = downloadPath->path;
|
||||
downloadPathValue = downloadPath->path.data();
|
||||
else
|
||||
downloadPathValue = false;
|
||||
}
|
||||
|
||||
return {
|
||||
{OPTION_SAVEPATH, savePath},
|
||||
{OPTION_SAVEPATH, savePath.data()},
|
||||
{OPTION_DOWNLOADPATH, downloadPathValue}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace BitTorrent
|
||||
|
@ -41,10 +43,10 @@ namespace BitTorrent
|
|||
struct DownloadPathOption
|
||||
{
|
||||
bool enabled;
|
||||
QString path;
|
||||
Path path;
|
||||
};
|
||||
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
std::optional<DownloadPathOption> downloadPath;
|
||||
|
||||
static CategoryOptions fromJSON(const QJsonObject &jsonObj);
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include <libtorrent/download_priority.hpp>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include "base/utils/fs.h"
|
||||
#include "common.h"
|
||||
|
||||
|
@ -53,12 +51,13 @@ lt::storage_holder CustomDiskIOThread::new_torrent(const lt::storage_params &sto
|
|||
{
|
||||
lt::storage_holder storageHolder = m_nativeDiskIO->new_torrent(storageParams, torrent);
|
||||
|
||||
const QString savePath = Utils::Fs::expandPathAbs(QString::fromStdString(storageParams.path));
|
||||
const Path savePath {storageParams.path};
|
||||
m_storageData[storageHolder] =
|
||||
{
|
||||
savePath
|
||||
, storageParams.mapped_files ? *storageParams.mapped_files : storageParams.files
|
||||
, storageParams.priorities};
|
||||
savePath,
|
||||
storageParams.mapped_files ? *storageParams.mapped_files : storageParams.files,
|
||||
storageParams.priorities
|
||||
};
|
||||
|
||||
return storageHolder;
|
||||
}
|
||||
|
@ -99,7 +98,7 @@ void CustomDiskIOThread::async_hash2(lt::storage_index_t storage, lt::piece_inde
|
|||
void CustomDiskIOThread::async_move_storage(lt::storage_index_t storage, std::string path, lt::move_flags_t flags
|
||||
, std::function<void (lt::status_t, const std::string &, const lt::storage_error &)> handler)
|
||||
{
|
||||
const QString newSavePath {Utils::Fs::expandPathAbs(QString::fromStdString(path))};
|
||||
const Path newSavePath {path};
|
||||
|
||||
if (flags == lt::move_flags_t::dont_replace)
|
||||
handleCompleteFiles(storage, newSavePath);
|
||||
|
@ -192,9 +191,8 @@ void CustomDiskIOThread::settings_updated()
|
|||
m_nativeDiskIO->settings_updated();
|
||||
}
|
||||
|
||||
void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const QString &savePath)
|
||||
void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const Path &savePath)
|
||||
{
|
||||
const QDir saveDir {savePath};
|
||||
const StorageData storageData = m_storageData[storage];
|
||||
const lt::file_storage &fileStorage = storageData.files;
|
||||
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
||||
|
@ -206,16 +204,16 @@ void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const
|
|||
// ignore pad files
|
||||
if (fileStorage.pad_file_at(fileIndex)) continue;
|
||||
|
||||
const QString filePath = QString::fromStdString(fileStorage.file_path(fileIndex));
|
||||
if (filePath.endsWith(QB_EXT))
|
||||
const Path filePath {fileStorage.file_path(fileIndex)};
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
{
|
||||
const QString completeFilePath = filePath.left(filePath.size() - QB_EXT.size());
|
||||
QFile completeFile {saveDir.absoluteFilePath(completeFilePath)};
|
||||
if (completeFile.exists())
|
||||
const Path incompleteFilePath = savePath / filePath;
|
||||
Path completeFilePath = incompleteFilePath;
|
||||
completeFilePath.removeExtension();
|
||||
if (completeFilePath.exists())
|
||||
{
|
||||
QFile incompleteFile {saveDir.absoluteFilePath(filePath)};
|
||||
incompleteFile.remove();
|
||||
completeFile.rename(incompleteFile.fileName());
|
||||
Utils::Fs::removeFile(incompleteFilePath);
|
||||
Utils::Fs::renameFile(completeFilePath, incompleteFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +228,7 @@ lt::storage_interface *customStorageConstructor(const lt::storage_params ¶ms
|
|||
|
||||
CustomStorage::CustomStorage(const lt::storage_params ¶ms, lt::file_pool &filePool)
|
||||
: lt::default_storage {params, filePool}
|
||||
, m_savePath {Utils::Fs::expandPathAbs(QString::fromStdString(params.path))}
|
||||
, m_savePath {params.path}
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -248,7 +246,7 @@ void CustomStorage::set_file_priority(lt::aux::vector<lt::download_priority_t, l
|
|||
|
||||
lt::status_t CustomStorage::move_storage(const std::string &savePath, lt::move_flags_t flags, lt::storage_error &ec)
|
||||
{
|
||||
const QString newSavePath {Utils::Fs::expandPathAbs(QString::fromStdString(savePath))};
|
||||
const Path newSavePath {savePath};
|
||||
|
||||
if (flags == lt::move_flags_t::dont_replace)
|
||||
handleCompleteFiles(newSavePath);
|
||||
|
@ -260,10 +258,8 @@ lt::status_t CustomStorage::move_storage(const std::string &savePath, lt::move_f
|
|||
return ret;
|
||||
}
|
||||
|
||||
void CustomStorage::handleCompleteFiles(const QString &savePath)
|
||||
void CustomStorage::handleCompleteFiles(const Path &savePath)
|
||||
{
|
||||
const QDir saveDir {savePath};
|
||||
|
||||
const lt::file_storage &fileStorage = files();
|
||||
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
||||
{
|
||||
|
@ -274,16 +270,16 @@ void CustomStorage::handleCompleteFiles(const QString &savePath)
|
|||
// ignore pad files
|
||||
if (fileStorage.pad_file_at(fileIndex)) continue;
|
||||
|
||||
const QString filePath = QString::fromStdString(fileStorage.file_path(fileIndex));
|
||||
if (filePath.endsWith(QB_EXT))
|
||||
const Path filePath {fileStorage.file_path(fileIndex)};
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
{
|
||||
const QString completeFilePath = filePath.left(filePath.size() - QB_EXT.size());
|
||||
QFile completeFile {saveDir.absoluteFilePath(completeFilePath)};
|
||||
if (completeFile.exists())
|
||||
const Path incompleteFilePath = savePath / filePath;
|
||||
Path completeFilePath = incompleteFilePath;
|
||||
completeFilePath.removeExtension();
|
||||
if (completeFilePath.exists())
|
||||
{
|
||||
QFile incompleteFile {saveDir.absoluteFilePath(filePath)};
|
||||
incompleteFile.remove();
|
||||
completeFile.rename(incompleteFile.fileName());
|
||||
Utils::Fs::removeFile(incompleteFilePath);
|
||||
Utils::Fs::renameFile(completeFilePath, incompleteFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
#include <libtorrent/disk_interface.hpp>
|
||||
#include <libtorrent/file_storage.hpp>
|
||||
|
@ -86,13 +88,13 @@ public:
|
|||
void settings_updated() override;
|
||||
|
||||
private:
|
||||
void handleCompleteFiles(libtorrent::storage_index_t storage, const QString &savePath);
|
||||
void handleCompleteFiles(libtorrent::storage_index_t storage, const Path &savePath);
|
||||
|
||||
std::unique_ptr<lt::disk_interface> m_nativeDiskIO;
|
||||
|
||||
struct StorageData
|
||||
{
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
lt::file_storage files;
|
||||
lt::aux::vector<lt::download_priority_t, lt::file_index_t> filePriorities;
|
||||
};
|
||||
|
@ -113,9 +115,9 @@ public:
|
|||
lt::status_t move_storage(const std::string &savePath, lt::move_flags_t flags, lt::storage_error &ec) override;
|
||||
|
||||
private:
|
||||
void handleCompleteFiles(const QString &savePath);
|
||||
void handleCompleteFiles(const Path &savePath);
|
||||
|
||||
lt::aux::vector<lt::download_priority_t, lt::file_index_t> m_filePriorities;
|
||||
QString m_savePath;
|
||||
Path m_savePath;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "base/exceptions.h"
|
||||
#include "base/global.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/path.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/string.h"
|
||||
|
@ -173,7 +174,7 @@ namespace BitTorrent
|
|||
Q_DISABLE_COPY_MOVE(Worker)
|
||||
|
||||
public:
|
||||
Worker(const QString &dbPath, const QString &dbConnectionName);
|
||||
Worker(const Path &dbPath, const QString &dbConnectionName);
|
||||
|
||||
void openDatabase() const;
|
||||
void closeDatabase() const;
|
||||
|
@ -183,19 +184,19 @@ namespace BitTorrent
|
|||
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||
|
||||
private:
|
||||
const QString m_path;
|
||||
const Path m_path;
|
||||
const QString m_connectionName;
|
||||
};
|
||||
}
|
||||
|
||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const QString &dbPath, QObject *parent)
|
||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
||||
: ResumeDataStorage {parent}
|
||||
, m_ioThread {new QThread(this)}
|
||||
{
|
||||
const bool needCreateDB = !QFile::exists(dbPath);
|
||||
const bool needCreateDB = !dbPath.exists();
|
||||
|
||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), DB_CONNECTION_NAME);
|
||||
db.setDatabaseName(dbPath);
|
||||
db.setDatabaseName(dbPath.data());
|
||||
if (!db.open())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
|
||||
|
@ -308,12 +309,12 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
|||
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||
|
||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||
}
|
||||
|
||||
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
||||
|
@ -325,13 +326,11 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
|||
lt::error_code ec;
|
||||
const lt::bdecode_node root = lt::bdecode(allData, ec);
|
||||
|
||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||
|
||||
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||
|
||||
p = lt::read_resume_data(root, ec);
|
||||
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
|
||||
.toString().toStdString();
|
||||
|
||||
return resumeData;
|
||||
}
|
||||
|
@ -485,7 +484,7 @@ void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const
|
|||
}
|
||||
}
|
||||
|
||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QString &dbConnectionName)
|
||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, const QString &dbConnectionName)
|
||||
: m_path {dbPath}
|
||||
, m_connectionName {dbConnectionName}
|
||||
{
|
||||
|
@ -494,7 +493,7 @@ BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QSt
|
|||
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
|
||||
{
|
||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
|
||||
db.setDatabaseName(m_path);
|
||||
db.setDatabaseName(m_path.data());
|
||||
if (!db.open())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
}
|
||||
|
@ -508,7 +507,8 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
|||
{
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
p.save_path = Profile::instance()->toPortablePath(Path(p.save_path))
|
||||
.toString().toStdString();
|
||||
if (resumeData.stopped)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
|
@ -603,8 +603,8 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
|||
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
|
||||
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath));
|
||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath).data());
|
||||
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath).data());
|
||||
}
|
||||
|
||||
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QThread;
|
||||
|
@ -40,7 +41,7 @@ namespace BitTorrent
|
|||
Q_DISABLE_COPY_MOVE(DBResumeDataStorage)
|
||||
|
||||
public:
|
||||
explicit DBResumeDataStorage(const QString &dbPath, QObject *parent = nullptr);
|
||||
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
||||
~DBResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
|
|
|
@ -27,37 +27,33 @@
|
|||
*/
|
||||
|
||||
#include "filesearcher.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include "base/bittorrent/common.h"
|
||||
#include "base/bittorrent/infohash.h"
|
||||
|
||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
||||
, const QString &savePath, const QString &downloadPath)
|
||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||
, const Path &savePath, const Path &downloadPath)
|
||||
{
|
||||
const auto findInDir = [](const QString &dirPath, QStringList &fileNames) -> bool
|
||||
const auto findInDir = [](const Path &dirPath, PathList &fileNames) -> bool
|
||||
{
|
||||
const QDir dir {dirPath};
|
||||
bool found = false;
|
||||
for (QString &fileName : fileNames)
|
||||
for (Path &fileName : fileNames)
|
||||
{
|
||||
if (dir.exists(fileName))
|
||||
if ((dirPath / fileName).exists())
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (dir.exists(fileName + QB_EXT))
|
||||
else if ((dirPath / fileName + QB_EXT).exists())
|
||||
{
|
||||
found = true;
|
||||
fileName += QB_EXT;
|
||||
fileName = fileName + QB_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
QString usedPath = savePath;
|
||||
QStringList adjustedFileNames = originalFileNames;
|
||||
Path usedPath = savePath;
|
||||
PathList adjustedFileNames = originalFileNames;
|
||||
const bool found = findInDir(usedPath, adjustedFileNames);
|
||||
if (!found && !downloadPath.isEmpty())
|
||||
{
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentID;
|
||||
|
@ -44,9 +46,9 @@ public:
|
|||
FileSearcher() = default;
|
||||
|
||||
public slots:
|
||||
void search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
||||
, const QString &savePath, const QString &downloadPath);
|
||||
void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||
, const Path &savePath, const Path &downloadPath);
|
||||
|
||||
signals:
|
||||
void searchFinished(const BitTorrent::TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
||||
void searchFinished(const BitTorrent::TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||
};
|
||||
|
|
|
@ -124,7 +124,7 @@ FilterParserThread::~FilterParserThread()
|
|||
int FilterParserThread::parseDATFilterFile()
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
QFile file {m_filePath.data()};
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
|
@ -288,7 +288,7 @@ int FilterParserThread::parseDATFilterFile()
|
|||
int FilterParserThread::parseP2PFilterFile()
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
QFile file {m_filePath.data()};
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
|
@ -469,7 +469,7 @@ int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name,
|
|||
int FilterParserThread::parseP2BFilterFile()
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
QFile file {m_filePath.data()};
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
|
@ -592,7 +592,7 @@ int FilterParserThread::parseP2BFilterFile()
|
|||
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
||||
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
||||
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
|
||||
void FilterParserThread::processFilterFile(const QString &filePath)
|
||||
void FilterParserThread::processFilterFile(const Path &filePath)
|
||||
{
|
||||
if (isRunning())
|
||||
{
|
||||
|
@ -617,17 +617,17 @@ void FilterParserThread::run()
|
|||
{
|
||||
qDebug("Processing filter file");
|
||||
int ruleCount = 0;
|
||||
if (m_filePath.endsWith(".p2p", Qt::CaseInsensitive))
|
||||
if (m_filePath.hasExtension(QLatin1String(".p2p")))
|
||||
{
|
||||
// PeerGuardian p2p file
|
||||
ruleCount = parseP2PFilterFile();
|
||||
}
|
||||
else if (m_filePath.endsWith(".p2b", Qt::CaseInsensitive))
|
||||
else if (m_filePath.hasExtension(QLatin1String(".p2b")))
|
||||
{
|
||||
// PeerGuardian p2b file
|
||||
ruleCount = parseP2BFilterFile();
|
||||
}
|
||||
else if (m_filePath.endsWith(".dat", Qt::CaseInsensitive))
|
||||
else if (m_filePath.hasExtension(QLatin1String(".dat")))
|
||||
{
|
||||
// eMule DAT format
|
||||
ruleCount = parseDATFilterFile();
|
||||
|
|
|
@ -32,16 +32,19 @@
|
|||
|
||||
#include <QThread>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
class QDataStream;
|
||||
|
||||
class FilterParserThread final : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(FilterParserThread)
|
||||
|
||||
public:
|
||||
FilterParserThread(QObject *parent = nullptr);
|
||||
~FilterParserThread();
|
||||
void processFilterFile(const QString &filePath);
|
||||
void processFilterFile(const Path &filePath);
|
||||
lt::ip_filter IPfilter();
|
||||
|
||||
signals:
|
||||
|
@ -60,6 +63,6 @@ private:
|
|||
int parseP2BFilterFile();
|
||||
|
||||
bool m_abort;
|
||||
QString m_filePath;
|
||||
Path m_filePath;
|
||||
lt::ip_filter m_filter;
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
@ -45,8 +46,8 @@ namespace BitTorrent
|
|||
QString name;
|
||||
QString category;
|
||||
TagSet tags;
|
||||
QString savePath;
|
||||
QString downloadPath;
|
||||
Path savePath;
|
||||
Path downloadPath;
|
||||
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
||||
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
||||
bool useAutoTMM = false;
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
|
||||
using namespace BitTorrent;
|
||||
|
||||
const QString CATEGORIES_FILE_NAME {QStringLiteral("categories.json")};
|
||||
const Path CATEGORIES_FILE_NAME {"categories.json"};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -394,8 +394,8 @@ Session::Session(QObject *parent)
|
|||
, clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
|
||||
, m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
|
||||
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
|
||||
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), Utils::Fs::toUniformPath)
|
||||
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp"), Utils::Fs::toUniformPath)
|
||||
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads))
|
||||
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), (savePath() / Path("temp")))
|
||||
, m_isDownloadPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
|
||||
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
|
||||
, m_useCategoryPathsInManualMode(BITTORRENT_SESSION_KEY("UseCategoryPathsInManualMode"), false)
|
||||
|
@ -594,36 +594,34 @@ void Session::setPreallocationEnabled(const bool enabled)
|
|||
m_isPreallocationEnabled = enabled;
|
||||
}
|
||||
|
||||
QString Session::torrentExportDirectory() const
|
||||
Path Session::torrentExportDirectory() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(m_torrentExportDirectory);
|
||||
return m_torrentExportDirectory;
|
||||
}
|
||||
|
||||
void Session::setTorrentExportDirectory(QString path)
|
||||
void Session::setTorrentExportDirectory(const Path &path)
|
||||
{
|
||||
path = Utils::Fs::toUniformPath(path);
|
||||
if (path != torrentExportDirectory())
|
||||
m_torrentExportDirectory = path;
|
||||
}
|
||||
|
||||
QString Session::finishedTorrentExportDirectory() const
|
||||
Path Session::finishedTorrentExportDirectory() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(m_finishedTorrentExportDirectory);
|
||||
return m_finishedTorrentExportDirectory;
|
||||
}
|
||||
|
||||
void Session::setFinishedTorrentExportDirectory(QString path)
|
||||
void Session::setFinishedTorrentExportDirectory(const Path &path)
|
||||
{
|
||||
path = Utils::Fs::toUniformPath(path);
|
||||
if (path != finishedTorrentExportDirectory())
|
||||
m_finishedTorrentExportDirectory = path;
|
||||
}
|
||||
|
||||
QString Session::savePath() const
|
||||
Path Session::savePath() const
|
||||
{
|
||||
return m_savePath;
|
||||
}
|
||||
|
||||
QString Session::downloadPath() const
|
||||
Path Session::downloadPath() const
|
||||
{
|
||||
return m_downloadPath;
|
||||
}
|
||||
|
@ -667,20 +665,20 @@ CategoryOptions Session::categoryOptions(const QString &categoryName) const
|
|||
return m_categories.value(categoryName);
|
||||
}
|
||||
|
||||
QString Session::categorySavePath(const QString &categoryName) const
|
||||
Path Session::categorySavePath(const QString &categoryName) const
|
||||
{
|
||||
const QString basePath = savePath();
|
||||
const Path basePath = savePath();
|
||||
if (categoryName.isEmpty())
|
||||
return basePath;
|
||||
|
||||
QString path = m_categories.value(categoryName).savePath;
|
||||
Path path = m_categories.value(categoryName).savePath;
|
||||
if (path.isEmpty()) // use implicit save path
|
||||
path = Utils::Fs::toValidFileSystemName(categoryName, true);
|
||||
path = Utils::Fs::toValidPath(categoryName);
|
||||
|
||||
return (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
return (path.isAbsolute() ? path : (basePath / path));
|
||||
}
|
||||
|
||||
QString Session::categoryDownloadPath(const QString &categoryName) const
|
||||
Path Session::categoryDownloadPath(const QString &categoryName) const
|
||||
{
|
||||
const CategoryOptions categoryOptions = m_categories.value(categoryName);
|
||||
const CategoryOptions::DownloadPathOption downloadPathOption =
|
||||
|
@ -688,15 +686,15 @@ QString Session::categoryDownloadPath(const QString &categoryName) const
|
|||
if (!downloadPathOption.enabled)
|
||||
return {};
|
||||
|
||||
const QString basePath = downloadPath();
|
||||
const Path basePath = downloadPath();
|
||||
if (categoryName.isEmpty())
|
||||
return basePath;
|
||||
|
||||
const QString path = (!downloadPathOption.path.isEmpty()
|
||||
const Path path = (!downloadPathOption.path.isEmpty()
|
||||
? downloadPathOption.path
|
||||
: Utils::Fs::toValidFileSystemName(categoryName, true)); // use implicit download path
|
||||
: Utils::Fs::toValidPath(categoryName)); // use implicit download path
|
||||
|
||||
return (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
return (path.isAbsolute() ? path : (basePath / path));
|
||||
}
|
||||
|
||||
bool Session::addCategory(const QString &name, const CategoryOptions &options)
|
||||
|
@ -1722,7 +1720,7 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
|
|||
}
|
||||
}
|
||||
|
||||
void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames)
|
||||
void Session::fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
TorrentImpl *torrent = m_torrents.value(id);
|
||||
if (torrent)
|
||||
|
@ -1739,11 +1737,11 @@ void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, c
|
|||
|
||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
||||
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
const TorrentInfo torrentInfo {*p.ti};
|
||||
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||
for (int i = 0; i < fileNames.size(); ++i)
|
||||
p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString();
|
||||
p.renamed_files[nativeIndexes[i]] = fileNames[i].toString().toStdString();
|
||||
|
||||
loadTorrent(params);
|
||||
}
|
||||
|
@ -1811,7 +1809,7 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
|
|||
// Remove it from session
|
||||
if (deleteOption == DeleteTorrent)
|
||||
{
|
||||
m_removingTorrents[torrent->id()] = {torrent->name(), "", deleteOption};
|
||||
m_removingTorrents[torrent->id()] = {torrent->name(), {}, deleteOption};
|
||||
|
||||
const lt::torrent_handle nativeHandle {torrent->nativeHandle()};
|
||||
const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
|
||||
|
@ -2033,8 +2031,9 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams ¶ms)
|
|||
if (magnetUri.isValid())
|
||||
return addTorrent(magnetUri, params);
|
||||
|
||||
TorrentFileGuard guard {source};
|
||||
const nonstd::expected<TorrentInfo, QString> loadResult = TorrentInfo::loadFromFile(source);
|
||||
const Path path {source};
|
||||
TorrentFileGuard guard {path};
|
||||
const nonstd::expected<TorrentInfo, QString> loadResult = TorrentInfo::loadFromFile(path);
|
||||
if (!loadResult)
|
||||
{
|
||||
LogMsg(tr("Couldn't load torrent: %1").arg(loadResult.error()), Log::WARNING);
|
||||
|
@ -2079,27 +2078,27 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
|
|||
|
||||
if (!loadTorrentParams.useAutoTMM)
|
||||
{
|
||||
if (QDir::isAbsolutePath(addTorrentParams.savePath))
|
||||
if (addTorrentParams.savePath.isAbsolute())
|
||||
{
|
||||
loadTorrentParams.savePath = addTorrentParams.savePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
||||
loadTorrentParams.savePath = Utils::Fs::resolvePath(addTorrentParams.savePath, basePath);
|
||||
const Path basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
||||
loadTorrentParams.savePath = basePath / addTorrentParams.savePath;
|
||||
}
|
||||
|
||||
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(isDownloadPathEnabled());
|
||||
if (useDownloadPath)
|
||||
{
|
||||
if (QDir::isAbsolutePath(addTorrentParams.downloadPath))
|
||||
if (addTorrentParams.downloadPath.isAbsolute())
|
||||
{
|
||||
loadTorrentParams.downloadPath = addTorrentParams.downloadPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
||||
loadTorrentParams.downloadPath = Utils::Fs::resolvePath(addTorrentParams.downloadPath, basePath);
|
||||
const Path basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
||||
loadTorrentParams.downloadPath = basePath / addTorrentParams.downloadPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2165,7 +2164,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
|||
bool isFindingIncompleteFiles = false;
|
||||
|
||||
const bool useAutoTMM = loadTorrentParams.useAutoTMM;
|
||||
const QString actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
const Path actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
|
||||
if (hasMetadata)
|
||||
{
|
||||
|
@ -2175,16 +2174,16 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
|||
|
||||
const TorrentContentLayout contentLayout = ((loadTorrentParams.contentLayout == TorrentContentLayout::Original)
|
||||
? detectContentLayout(torrentInfo.filePaths()) : loadTorrentParams.contentLayout);
|
||||
QStringList filePaths = (!addTorrentParams.filePaths.isEmpty() ? addTorrentParams.filePaths : torrentInfo.filePaths());
|
||||
applyContentLayout(filePaths, contentLayout, Utils::Fs::findRootFolder(torrentInfo.filePaths()));
|
||||
PathList filePaths = (!addTorrentParams.filePaths.isEmpty() ? addTorrentParams.filePaths : torrentInfo.filePaths());
|
||||
applyContentLayout(filePaths, contentLayout, Path::findRootFolder(torrentInfo.filePaths()));
|
||||
|
||||
// if torrent name wasn't explicitly set we handle the case of
|
||||
// initial renaming of torrent content and rename torrent accordingly
|
||||
if (loadTorrentParams.name.isEmpty())
|
||||
{
|
||||
QString contentName = Utils::Fs::findRootFolder(filePaths);
|
||||
QString contentName = Path::findRootFolder(filePaths).toString();
|
||||
if (contentName.isEmpty() && (filePaths.size() == 1))
|
||||
contentName = Utils::Fs::fileName(filePaths.at(0));
|
||||
contentName = filePaths.at(0).filename();
|
||||
|
||||
if (!contentName.isEmpty() && (contentName != torrentInfo.name()))
|
||||
loadTorrentParams.name = contentName;
|
||||
|
@ -2192,7 +2191,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
|||
|
||||
if (!loadTorrentParams.hasSeedStatus)
|
||||
{
|
||||
const QString actualDownloadPath = useAutoTMM
|
||||
const Path actualDownloadPath = useAutoTMM
|
||||
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||
findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths);
|
||||
isFindingIncompleteFiles = true;
|
||||
|
@ -2202,7 +2201,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
|||
if (!filePaths.isEmpty())
|
||||
{
|
||||
for (int index = 0; index < addTorrentParams.filePaths.size(); ++index)
|
||||
p.renamed_files[nativeIndexes[index]] = Utils::Fs::toNativePath(addTorrentParams.filePaths.at(index)).toStdString();
|
||||
p.renamed_files[nativeIndexes[index]] = addTorrentParams.filePaths.at(index).toString().toStdString();
|
||||
}
|
||||
|
||||
Q_ASSERT(p.file_priorities.empty());
|
||||
|
@ -2224,7 +2223,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
|||
loadTorrentParams.name = QString::fromStdString(p.name);
|
||||
}
|
||||
|
||||
p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
|
||||
p.save_path = actualSavePath.toString().toStdString();
|
||||
|
||||
p.upload_limit = addTorrentParams.uploadLimit;
|
||||
p.download_limit = addTorrentParams.downloadLimit;
|
||||
|
@ -2288,13 +2287,13 @@ bool Session::loadTorrent(LoadTorrentParams params)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
||||
, const QString &downloadPath, const QStringList &filePaths) const
|
||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths) const
|
||||
{
|
||||
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
||||
|
||||
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
|
||||
const QStringList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
||||
const PathList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
||||
QMetaObject::invokeMethod(m_fileSearcher, [=]()
|
||||
{
|
||||
m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath);
|
||||
|
@ -2333,8 +2332,8 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
|
|||
p.max_connections = maxConnectionsPerTorrent();
|
||||
p.max_uploads = maxUploadsPerTorrent();
|
||||
|
||||
const QString savePath = Utils::Fs::tempPath() + id.toString();
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
const Path savePath = Utils::Fs::tempPath() / Path(id.toString());
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
|
||||
// Forced start
|
||||
p.flags &= ~lt::torrent_flags::paused;
|
||||
|
@ -2360,28 +2359,27 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Session::exportTorrentFile(const TorrentInfo &torrentInfo, const QString &folderPath, const QString &baseName)
|
||||
void Session::exportTorrentFile(const TorrentInfo &torrentInfo, const Path &folderPath, const QString &baseName)
|
||||
{
|
||||
const QString validName = Utils::Fs::toValidFileSystemName(baseName);
|
||||
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
||||
const QDir exportDir {folderPath};
|
||||
if (exportDir.exists() || exportDir.mkpath(exportDir.absolutePath()))
|
||||
{
|
||||
QString newTorrentPath = exportDir.absoluteFilePath(torrentExportFilename);
|
||||
int counter = 0;
|
||||
while (QFile::exists(newTorrentPath))
|
||||
{
|
||||
// Append number to torrent name to make it unique
|
||||
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
|
||||
newTorrentPath = exportDir.absoluteFilePath(torrentExportFilename);
|
||||
}
|
||||
if (!folderPath.exists() && !Utils::Fs::mkpath(folderPath))
|
||||
return;
|
||||
|
||||
const nonstd::expected<void, QString> result = torrentInfo.saveToFile(newTorrentPath);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
|
||||
.arg(newTorrentPath, result.error()), Log::WARNING);
|
||||
}
|
||||
const QString validName = Utils::Fs::toValidFileName(baseName);
|
||||
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
||||
Path newTorrentPath = folderPath / Path(torrentExportFilename);
|
||||
int counter = 0;
|
||||
while (newTorrentPath.exists())
|
||||
{
|
||||
// Append number to torrent name to make it unique
|
||||
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
|
||||
newTorrentPath = folderPath / Path(torrentExportFilename);
|
||||
}
|
||||
|
||||
const nonstd::expected<void, QString> result = torrentInfo.saveToFile(newTorrentPath);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
|
||||
.arg(newTorrentPath.toString(), result.error()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2455,13 +2453,13 @@ void Session::removeTorrentsQueue() const
|
|||
m_resumeDataStorage->storeQueue({});
|
||||
}
|
||||
|
||||
void Session::setSavePath(const QString &path)
|
||||
void Session::setSavePath(const Path &path)
|
||||
{
|
||||
const QString baseSavePath = specialFolderLocation(SpecialFolder::Downloads);
|
||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseSavePath));
|
||||
if (resolvedPath == m_savePath) return;
|
||||
const auto newPath = (path.isAbsolute() ? path : (specialFolderLocation(SpecialFolder::Downloads) / path));
|
||||
if (newPath == m_savePath)
|
||||
return;
|
||||
|
||||
m_savePath = resolvedPath;
|
||||
m_savePath = newPath;
|
||||
|
||||
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
||||
{
|
||||
|
@ -2475,13 +2473,12 @@ void Session::setSavePath(const QString &path)
|
|||
}
|
||||
}
|
||||
|
||||
void Session::setDownloadPath(const QString &path)
|
||||
void Session::setDownloadPath(const Path &path)
|
||||
{
|
||||
const QString baseDownloadPath = specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp");
|
||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseDownloadPath));
|
||||
if (resolvedPath != m_downloadPath)
|
||||
const Path newPath = (path.isAbsolute() ? path : (savePath() / Path("temp") / path));
|
||||
if (newPath != m_downloadPath)
|
||||
{
|
||||
m_downloadPath = resolvedPath;
|
||||
m_downloadPath = newPath;
|
||||
|
||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||
torrent->handleDownloadPathChanged();
|
||||
|
@ -2953,14 +2950,13 @@ void Session::setIPFilteringEnabled(const bool enabled)
|
|||
}
|
||||
}
|
||||
|
||||
QString Session::IPFilterFile() const
|
||||
Path Session::IPFilterFile() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(m_IPFilterFile);
|
||||
return m_IPFilterFile;
|
||||
}
|
||||
|
||||
void Session::setIPFilterFile(QString path)
|
||||
void Session::setIPFilterFile(const Path &path)
|
||||
{
|
||||
path = Utils::Fs::toUniformPath(path);
|
||||
if (path != IPFilterFile())
|
||||
{
|
||||
m_IPFilterFile = path;
|
||||
|
@ -3994,13 +3990,13 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
|||
|
||||
qDebug("Checking if the torrent contains torrent files to download");
|
||||
// Check if there are torrent files inside
|
||||
for (const QString &torrentRelpath : asConst(torrent->filePaths()))
|
||||
for (const Path &torrentRelpath : asConst(torrent->filePaths()))
|
||||
{
|
||||
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
|
||||
if (torrentRelpath.hasExtension(QLatin1String(".torrent")))
|
||||
{
|
||||
qDebug("Found possible recursive torrent download.");
|
||||
const QString torrentFullpath = torrent->actualStorageLocation() + '/' + torrentRelpath;
|
||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
||||
const Path torrentFullpath = torrent->actualStorageLocation() / torrentRelpath;
|
||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath.toString()));
|
||||
if (TorrentInfo::loadFromFile(torrentFullpath))
|
||||
{
|
||||
qDebug("emitting recursiveTorrentDownloadPossible()");
|
||||
|
@ -4010,7 +4006,7 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
|||
else
|
||||
{
|
||||
qDebug("Caught error loading torrent");
|
||||
LogMsg(tr("Unable to decode '%1' torrent file.").arg(Utils::Fs::toNativePath(torrentFullpath)), Log::CRITICAL);
|
||||
LogMsg(tr("Unable to decode '%1' torrent file.").arg(torrentFullpath.toString()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4047,12 +4043,12 @@ void Session::handleTorrentTrackerError(TorrentImpl *const torrent, const QStrin
|
|||
emit trackerError(torrent, trackerUrl);
|
||||
}
|
||||
|
||||
bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, const MoveStorageMode mode)
|
||||
bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, const MoveStorageMode mode)
|
||||
{
|
||||
Q_ASSERT(torrent);
|
||||
|
||||
const lt::torrent_handle torrentHandle = torrent->nativeHandle();
|
||||
const QString currentLocation = Utils::Fs::toNativePath(torrent->actualStorageLocation());
|
||||
const Path currentLocation = torrent->actualStorageLocation();
|
||||
|
||||
if (m_moveStorageQueue.size() > 1)
|
||||
{
|
||||
|
@ -4065,7 +4061,7 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
|
|||
if (iter != m_moveStorageQueue.end())
|
||||
{
|
||||
// remove existing inactive job
|
||||
LogMsg(tr("Cancelled moving \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation, iter->path));
|
||||
LogMsg(tr("Cancelled moving \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation.toString(), iter->path.toString()));
|
||||
iter = m_moveStorageQueue.erase(iter);
|
||||
|
||||
iter = std::find_if(iter, m_moveStorageQueue.end(), [&torrentHandle](const MoveStorageJob &job)
|
||||
|
@ -4082,26 +4078,26 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
|
|||
{
|
||||
// if there is active job for this torrent prevent creating meaningless
|
||||
// job that will move torrent to the same location as current one
|
||||
if (QDir {m_moveStorageQueue.first().path} == QDir {newPath})
|
||||
if (m_moveStorageQueue.first().path == newPath)
|
||||
{
|
||||
LogMsg(tr("Couldn't enqueue move of \"%1\" to \"%2\". Torrent is currently moving to the same destination location.")
|
||||
.arg(torrent->name(), newPath));
|
||||
.arg(torrent->name(), newPath.toString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (QDir {currentLocation} == QDir {newPath})
|
||||
if (currentLocation == newPath)
|
||||
{
|
||||
LogMsg(tr("Couldn't enqueue move of \"%1\" from \"%2\" to \"%3\". Both paths point to the same location.")
|
||||
.arg(torrent->name(), currentLocation, newPath));
|
||||
.arg(torrent->name(), currentLocation.toString(), newPath.toString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode};
|
||||
m_moveStorageQueue << moveStorageJob;
|
||||
LogMsg(tr("Enqueued to move \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation, newPath));
|
||||
LogMsg(tr("Enqueued to move \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation.toString(), newPath.toString()));
|
||||
|
||||
if (m_moveStorageQueue.size() == 1)
|
||||
moveTorrentStorage(moveStorageJob);
|
||||
|
@ -4118,9 +4114,9 @@ void Session::moveTorrentStorage(const MoveStorageJob &job) const
|
|||
#endif
|
||||
const TorrentImpl *torrent = m_torrents.value(id);
|
||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
||||
LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path));
|
||||
LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path.toString()));
|
||||
|
||||
job.torrentHandle.move_storage(job.path.toUtf8().constData()
|
||||
job.torrentHandle.move_storage(job.path.toString().toStdString()
|
||||
, ((job.mode == MoveStorageMode::Overwrite)
|
||||
? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace));
|
||||
}
|
||||
|
@ -4164,13 +4160,13 @@ void Session::storeCategories() const
|
|||
jsonObj[categoryName] = categoryOptions.toJSON();
|
||||
}
|
||||
|
||||
const QString path = QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CATEGORIES_FILE_NAME);
|
||||
const Path path = specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME;
|
||||
const QByteArray data = QJsonDocument(jsonObj).toJson();
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Couldn't store Categories configuration to %1. Error: %2")
|
||||
.arg(path, result.error()), Log::WARNING);
|
||||
.arg(path.toString(), result.error()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4181,7 +4177,7 @@ void Session::upgradeCategories()
|
|||
{
|
||||
const QString categoryName = it.key();
|
||||
CategoryOptions categoryOptions;
|
||||
categoryOptions.savePath = it.value().toString();
|
||||
categoryOptions.savePath = Path(it.value().toString());
|
||||
m_categories[categoryName] = categoryOptions;
|
||||
}
|
||||
|
||||
|
@ -4192,7 +4188,7 @@ void Session::loadCategories()
|
|||
{
|
||||
m_categories.clear();
|
||||
|
||||
QFile confFile {QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CATEGORIES_FILE_NAME)};
|
||||
QFile confFile {(specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME).data()};
|
||||
if (!confFile.exists())
|
||||
{
|
||||
// TODO: Remove the following upgrade code in v4.5
|
||||
|
@ -4308,14 +4304,14 @@ void Session::recursiveTorrentDownload(const TorrentID &id)
|
|||
TorrentImpl *const torrent = m_torrents.value(id);
|
||||
if (!torrent) return;
|
||||
|
||||
for (const QString &torrentRelpath : asConst(torrent->filePaths()))
|
||||
for (const Path &torrentRelpath : asConst(torrent->filePaths()))
|
||||
{
|
||||
if (torrentRelpath.endsWith(QLatin1String(".torrent")))
|
||||
if (torrentRelpath.hasExtension(QLatin1String(".torrent")))
|
||||
{
|
||||
LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"
|
||||
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
|
||||
.arg(Utils::Fs::toNativePath(torrentRelpath), torrent->name()));
|
||||
const QString torrentFullpath = torrent->savePath() + '/' + torrentRelpath;
|
||||
.arg(torrentRelpath.toString(), torrent->name()));
|
||||
const Path torrentFullpath = torrent->savePath() / torrentRelpath;
|
||||
|
||||
AddTorrentParams params;
|
||||
// Passing the save path along to the sub torrent file
|
||||
|
@ -4343,9 +4339,8 @@ void Session::startUpTorrents()
|
|||
{
|
||||
qDebug("Initializing torrents resume data storage...");
|
||||
|
||||
const QString dbPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/torrents.db"));
|
||||
const bool dbStorageExists = QFile::exists(dbPath);
|
||||
const Path dbPath = specialFolderLocation(SpecialFolder::Data) / Path("torrents.db");
|
||||
const bool dbStorageExists = dbPath.exists();
|
||||
|
||||
ResumeDataStorage *startupStorage = nullptr;
|
||||
if (resumeDataStorageType() == ResumeDataStorageType::SQLite)
|
||||
|
@ -4354,15 +4349,13 @@ void Session::startUpTorrents()
|
|||
|
||||
if (!dbStorageExists)
|
||||
{
|
||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/BT_backup"));
|
||||
const Path dataPath = specialFolderLocation(SpecialFolder::Data) / Path("BT_backup");
|
||||
startupStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/BT_backup"));
|
||||
const Path dataPath = specialFolderLocation(SpecialFolder::Data) / Path("BT_backup");
|
||||
m_resumeDataStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||
|
||||
if (dbStorageExists)
|
||||
|
@ -4491,7 +4484,7 @@ void Session::startUpTorrents()
|
|||
{
|
||||
delete startupStorage;
|
||||
if (resumeDataStorageType() == ResumeDataStorageType::Legacy)
|
||||
Utils::Fs::forceRemove(dbPath);
|
||||
Utils::Fs::removeFile(dbPath);
|
||||
|
||||
if (isQueueingSystemEnabled())
|
||||
m_resumeDataStorage->storeQueue(queue);
|
||||
|
@ -5064,7 +5057,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
|||
const MoveStorageJob ¤tJob = m_moveStorageQueue.first();
|
||||
Q_ASSERT(currentJob.torrentHandle == p->handle);
|
||||
|
||||
const QString newPath {p->storage_path()};
|
||||
const Path newPath {QString::fromUtf8(p->storage_path())};
|
||||
Q_ASSERT(newPath == currentJob.path);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
|
@ -5075,7 +5068,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
|||
|
||||
TorrentImpl *torrent = m_torrents.value(id);
|
||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
||||
LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath));
|
||||
LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath.toString()));
|
||||
|
||||
handleMoveTorrentStorageJobFinished();
|
||||
}
|
||||
|
@ -5098,7 +5091,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
|
|||
const QString currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path);
|
||||
const QString errorMessage = QString::fromStdString(p->message());
|
||||
LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.")
|
||||
.arg(torrentName, currentLocation, currentJob.path, errorMessage), Log::CRITICAL);
|
||||
.arg(torrentName, currentLocation, currentJob.path.toString(), errorMessage), Log::CRITICAL);
|
||||
|
||||
handleMoveTorrentStorageJobFinished();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <QtContainerFwd>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
#include "base/types.h"
|
||||
#include "addtorrentparams.h"
|
||||
|
@ -202,7 +203,7 @@ namespace BitTorrent
|
|||
} disk;
|
||||
};
|
||||
|
||||
class Session : public QObject
|
||||
class Session final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Session)
|
||||
|
@ -212,10 +213,10 @@ namespace BitTorrent
|
|||
static void freeInstance();
|
||||
static Session *instance();
|
||||
|
||||
QString savePath() const;
|
||||
void setSavePath(const QString &path);
|
||||
QString downloadPath() const;
|
||||
void setDownloadPath(const QString &path);
|
||||
Path savePath() const;
|
||||
void setSavePath(const Path &path);
|
||||
Path downloadPath() const;
|
||||
void setDownloadPath(const Path &path);
|
||||
bool isDownloadPathEnabled() const;
|
||||
void setDownloadPathEnabled(bool enabled);
|
||||
|
||||
|
@ -225,8 +226,8 @@ namespace BitTorrent
|
|||
|
||||
QStringList categories() const;
|
||||
CategoryOptions categoryOptions(const QString &categoryName) const;
|
||||
QString categorySavePath(const QString &categoryName) const;
|
||||
QString categoryDownloadPath(const QString &categoryName) const;
|
||||
Path categorySavePath(const QString &categoryName) const;
|
||||
Path categoryDownloadPath(const QString &categoryName) const;
|
||||
bool addCategory(const QString &name, const CategoryOptions &options = {});
|
||||
bool editCategory(const QString &name, const CategoryOptions &options);
|
||||
bool removeCategory(const QString &name);
|
||||
|
@ -283,10 +284,10 @@ namespace BitTorrent
|
|||
void setRefreshInterval(int value);
|
||||
bool isPreallocationEnabled() const;
|
||||
void setPreallocationEnabled(bool enabled);
|
||||
QString torrentExportDirectory() const;
|
||||
void setTorrentExportDirectory(QString path);
|
||||
QString finishedTorrentExportDirectory() const;
|
||||
void setFinishedTorrentExportDirectory(QString path);
|
||||
Path torrentExportDirectory() const;
|
||||
void setTorrentExportDirectory(const Path &path);
|
||||
Path finishedTorrentExportDirectory() const;
|
||||
void setFinishedTorrentExportDirectory(const Path &path);
|
||||
|
||||
int globalDownloadSpeedLimit() const;
|
||||
void setGlobalDownloadSpeedLimit(int limit);
|
||||
|
@ -329,8 +330,8 @@ namespace BitTorrent
|
|||
void setAdditionalTrackers(const QString &trackers);
|
||||
bool isIPFilteringEnabled() const;
|
||||
void setIPFilteringEnabled(bool enabled);
|
||||
QString IPFilterFile() const;
|
||||
void setIPFilterFile(QString path);
|
||||
Path IPFilterFile() const;
|
||||
void setIPFilterFile(const Path &path);
|
||||
bool announceToAllTrackers() const;
|
||||
void setAnnounceToAllTrackers(bool val);
|
||||
bool announceToAllTiers() const;
|
||||
|
@ -501,10 +502,10 @@ namespace BitTorrent
|
|||
void handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl);
|
||||
void handleTorrentTrackerError(TorrentImpl *const torrent, const QString &trackerUrl);
|
||||
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, MoveStorageMode mode);
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode);
|
||||
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
||||
, const QString &downloadPath, const QStringList &filePaths = {}) const;
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||
|
||||
signals:
|
||||
void allTorrentsFinished();
|
||||
|
@ -553,7 +554,7 @@ namespace BitTorrent
|
|||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
||||
void fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
||||
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Session reconfiguration triggers
|
||||
|
@ -565,14 +566,14 @@ namespace BitTorrent
|
|||
struct MoveStorageJob
|
||||
{
|
||||
lt::torrent_handle torrentHandle;
|
||||
QString path;
|
||||
Path path;
|
||||
MoveStorageMode mode;
|
||||
};
|
||||
|
||||
struct RemovingTorrentData
|
||||
{
|
||||
QString name;
|
||||
QString pathToRemove;
|
||||
Path pathToRemove;
|
||||
DeleteOption deleteOption;
|
||||
};
|
||||
|
||||
|
@ -611,7 +612,7 @@ namespace BitTorrent
|
|||
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
|
||||
|
||||
void updateSeedingLimitTimer();
|
||||
void exportTorrentFile(const TorrentInfo &torrentInfo, const QString &folderPath, const QString &baseName);
|
||||
void exportTorrentFile(const TorrentInfo &torrentInfo, const Path &folderPath, const QString &baseName);
|
||||
|
||||
void handleAlert(const lt::alert *a);
|
||||
void dispatchTorrentAlert(const lt::alert *a);
|
||||
|
@ -663,7 +664,7 @@ namespace BitTorrent
|
|||
CachedSettingValue<bool> m_isPeXEnabled;
|
||||
CachedSettingValue<bool> m_isIPFilteringEnabled;
|
||||
CachedSettingValue<bool> m_isTrackerFilteringEnabled;
|
||||
CachedSettingValue<QString> m_IPFilterFile;
|
||||
CachedSettingValue<Path> m_IPFilterFile;
|
||||
CachedSettingValue<bool> m_announceToAllTrackers;
|
||||
CachedSettingValue<bool> m_announceToAllTiers;
|
||||
CachedSettingValue<int> m_asyncIOThreads;
|
||||
|
@ -721,8 +722,8 @@ namespace BitTorrent
|
|||
CachedSettingValue<bool> m_isAppendExtensionEnabled;
|
||||
CachedSettingValue<int> m_refreshInterval;
|
||||
CachedSettingValue<bool> m_isPreallocationEnabled;
|
||||
CachedSettingValue<QString> m_torrentExportDirectory;
|
||||
CachedSettingValue<QString> m_finishedTorrentExportDirectory;
|
||||
CachedSettingValue<Path> m_torrentExportDirectory;
|
||||
CachedSettingValue<Path> m_finishedTorrentExportDirectory;
|
||||
CachedSettingValue<int> m_globalDownloadSpeedLimit;
|
||||
CachedSettingValue<int> m_globalUploadSpeedLimit;
|
||||
CachedSettingValue<int> m_altGlobalDownloadSpeedLimit;
|
||||
|
@ -740,8 +741,8 @@ namespace BitTorrent
|
|||
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
||||
CachedSettingValue<QStringList> m_storedTags;
|
||||
CachedSettingValue<int> m_maxRatioAction;
|
||||
CachedSettingValue<QString> m_savePath;
|
||||
CachedSettingValue<QString> m_downloadPath;
|
||||
CachedSettingValue<Path> m_savePath;
|
||||
CachedSettingValue<Path> m_downloadPath;
|
||||
CachedSettingValue<bool> m_isDownloadPathEnabled;
|
||||
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
||||
CachedSettingValue<bool> m_useCategoryPathsInManualMode;
|
||||
|
|
|
@ -29,10 +29,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QtContainerFwd>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/tagset.h"
|
||||
#include "abstractfilestorage.h"
|
||||
|
||||
|
@ -169,13 +170,13 @@ namespace BitTorrent
|
|||
|
||||
virtual bool isAutoTMMEnabled() const = 0;
|
||||
virtual void setAutoTMMEnabled(bool enabled) = 0;
|
||||
virtual QString savePath() const = 0;
|
||||
virtual void setSavePath(const QString &savePath) = 0;
|
||||
virtual QString downloadPath() const = 0;
|
||||
virtual void setDownloadPath(const QString &downloadPath) = 0;
|
||||
virtual QString actualStorageLocation() const = 0;
|
||||
virtual QString rootPath() const = 0;
|
||||
virtual QString contentPath() const = 0;
|
||||
virtual Path savePath() const = 0;
|
||||
virtual void setSavePath(const Path &savePath) = 0;
|
||||
virtual Path downloadPath() const = 0;
|
||||
virtual void setDownloadPath(const Path &downloadPath) = 0;
|
||||
virtual Path actualStorageLocation() const = 0;
|
||||
virtual Path rootPath() const = 0;
|
||||
virtual Path contentPath() const = 0;
|
||||
virtual QString category() const = 0;
|
||||
virtual bool belongsToCategory(const QString &category) const = 0;
|
||||
virtual bool setCategory(const QString &category) = 0;
|
||||
|
@ -193,8 +194,8 @@ namespace BitTorrent
|
|||
virtual qreal ratioLimit() const = 0;
|
||||
virtual int seedingTimeLimit() const = 0;
|
||||
|
||||
virtual QString actualFilePath(int index) const = 0;
|
||||
virtual QStringList filePaths() const = 0;
|
||||
virtual Path actualFilePath(int index) const = 0;
|
||||
virtual PathList filePaths() const = 0;
|
||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||
|
||||
virtual TorrentInfo info() const = 0;
|
||||
|
|
|
@ -32,36 +32,35 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
QString removeExtension(const QString &fileName)
|
||||
Path removeExtension(const Path &fileName)
|
||||
{
|
||||
const QString extension = Utils::Fs::fileExtension(fileName);
|
||||
return extension.isEmpty()
|
||||
? fileName
|
||||
: fileName.chopped(extension.size() + 1);
|
||||
Path result = fileName;
|
||||
result.removeExtension();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::TorrentContentLayout BitTorrent::detectContentLayout(const QStringList &filePaths)
|
||||
BitTorrent::TorrentContentLayout BitTorrent::detectContentLayout(const PathList &filePaths)
|
||||
{
|
||||
const QString rootFolder = Utils::Fs::findRootFolder(filePaths);
|
||||
const Path rootFolder = Path::findRootFolder(filePaths);
|
||||
return (rootFolder.isEmpty()
|
||||
? TorrentContentLayout::NoSubfolder
|
||||
: TorrentContentLayout::Subfolder);
|
||||
}
|
||||
|
||||
void BitTorrent::applyContentLayout(QStringList &filePaths, const BitTorrent::TorrentContentLayout contentLayout, const QString &rootFolder)
|
||||
void BitTorrent::applyContentLayout(PathList &filePaths, const BitTorrent::TorrentContentLayout contentLayout, const Path &rootFolder)
|
||||
{
|
||||
Q_ASSERT(!filePaths.isEmpty());
|
||||
|
||||
switch (contentLayout)
|
||||
{
|
||||
case TorrentContentLayout::Subfolder:
|
||||
if (Utils::Fs::findRootFolder(filePaths).isEmpty())
|
||||
Utils::Fs::addRootFolder(filePaths, !rootFolder.isEmpty() ? rootFolder : removeExtension(filePaths.at(0)));
|
||||
if (Path::findRootFolder(filePaths).isEmpty())
|
||||
Path::addRootFolder(filePaths, !rootFolder.isEmpty() ? rootFolder : removeExtension(filePaths.at(0)));
|
||||
break;
|
||||
|
||||
case TorrentContentLayout::NoSubfolder:
|
||||
Utils::Fs::stripRootFolder(filePaths);
|
||||
Path::stripRootFolder(filePaths);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -28,8 +28,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
||||
|
@ -49,6 +52,6 @@ namespace BitTorrent
|
|||
Q_ENUM_NS(TorrentContentLayout)
|
||||
}
|
||||
|
||||
TorrentContentLayout detectContentLayout(const QStringList &filePaths);
|
||||
void applyContentLayout(QStringList &filePaths, TorrentContentLayout contentLayout, const QString &rootFolder = {});
|
||||
TorrentContentLayout detectContentLayout(const PathList &filePaths);
|
||||
void applyContentLayout(PathList &filePaths, TorrentContentLayout contentLayout, const Path &rootFolder = {});
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace
|
|||
// name starts with a .
|
||||
bool fileFilter(const std::string &f)
|
||||
{
|
||||
return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
|
||||
return !Path(f).filename().startsWith('.');
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
|
@ -108,50 +108,50 @@ void TorrentCreatorThread::run()
|
|||
|
||||
try
|
||||
{
|
||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
|
||||
const Path parentPath = m_params.inputPath.parentPath();
|
||||
const Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> naturalLessThan {};
|
||||
|
||||
// Adding files to the torrent
|
||||
lt::file_storage fs;
|
||||
if (QFileInfo(m_params.inputPath).isFile())
|
||||
if (QFileInfo(m_params.inputPath.data()).isFile())
|
||||
{
|
||||
lt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
|
||||
lt::add_files(fs, m_params.inputPath.toString().toStdString(), fileFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// need to sort the file names by natural sort order
|
||||
QStringList dirs = {m_params.inputPath};
|
||||
QStringList dirs = {m_params.inputPath.data()};
|
||||
|
||||
QDirIterator dirIter {m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
QDirIterator dirIter {m_params.inputPath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
dirIter.next();
|
||||
dirs += dirIter.filePath();
|
||||
dirs.append(dirIter.filePath());
|
||||
}
|
||||
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
||||
|
||||
QStringList fileNames;
|
||||
QHash<QString, qint64> fileSizeMap;
|
||||
|
||||
for (const auto &dir : asConst(dirs))
|
||||
for (const QString &dir : asConst(dirs))
|
||||
{
|
||||
QStringList tmpNames; // natural sort files within each dir
|
||||
|
||||
QDirIterator fileIter(dir, QDir::Files);
|
||||
QDirIterator fileIter {dir, QDir::Files};
|
||||
while (fileIter.hasNext())
|
||||
{
|
||||
fileIter.next();
|
||||
|
||||
const QString relFilePath = fileIter.filePath().mid(parentPath.length());
|
||||
tmpNames += relFilePath;
|
||||
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
|
||||
const auto relFilePath = parentPath.relativePathOf(Path(fileIter.filePath()));
|
||||
tmpNames.append(relFilePath.toString());
|
||||
fileSizeMap[tmpNames.last()] = fileIter.fileInfo().size();
|
||||
}
|
||||
|
||||
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
||||
fileNames += tmpNames;
|
||||
}
|
||||
|
||||
for (const auto &fileName : asConst(fileNames))
|
||||
for (const QString &fileName : asConst(fileNames))
|
||||
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ void TorrentCreatorThread::run()
|
|||
}
|
||||
|
||||
// calculate the hash for all pieces
|
||||
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
|
||||
lt::set_piece_hashes(newTorrent, parentPath.toString().toStdString()
|
||||
, [this, &newTorrent](const lt::piece_index_t n)
|
||||
{
|
||||
checkInterruptionRequested();
|
||||
|
@ -225,16 +225,16 @@ void TorrentCreatorThread::run()
|
|||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat)
|
||||
int TorrentCreatorThread::calculateTotalPieces(const Path &inputPath, const int pieceSize, const TorrentFormat torrentFormat)
|
||||
#else
|
||||
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized, const int paddedFileSizeLimit)
|
||||
int TorrentCreatorThread::calculateTotalPieces(const Path &inputPath, const int pieceSize, const bool isAlignmentOptimized, const int paddedFileSizeLimit)
|
||||
#endif
|
||||
{
|
||||
if (inputPath.isEmpty())
|
||||
return 0;
|
||||
|
||||
lt::file_storage fs;
|
||||
lt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
|
||||
lt::add_files(fs, inputPath.toString().toStdString(), fileFilter);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
return lt::create_torrent {fs, pieceSize, toNativeTorrentFormatFlag(torrentFormat)}.num_pieces();
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <QStringList>
|
||||
#include <QThread>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
|
@ -52,8 +54,8 @@ namespace BitTorrent
|
|||
int paddedFileSizeLimit;
|
||||
#endif
|
||||
int pieceSize;
|
||||
QString inputPath;
|
||||
QString savePath;
|
||||
Path inputPath;
|
||||
Path savePath;
|
||||
QString comment;
|
||||
QString source;
|
||||
QStringList trackers;
|
||||
|
@ -72,15 +74,15 @@ namespace BitTorrent
|
|||
void create(const TorrentCreatorParams ¶ms);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat);
|
||||
static int calculateTotalPieces(const Path &inputPath, const int pieceSize, const TorrentFormat torrentFormat);
|
||||
#else
|
||||
static int calculateTotalPieces(const QString &inputPath
|
||||
static int calculateTotalPieces(const Path &inputPath
|
||||
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void creationFailure(const QString &msg);
|
||||
void creationSuccess(const QString &path, const QString &branchPath);
|
||||
void creationSuccess(const Path &path, const Path &branchPath);
|
||||
void updateProgress(int progress);
|
||||
|
||||
private:
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
@ -282,8 +281,10 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
|||
{
|
||||
const lt::file_index_t nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||
m_indexMap[nativeIndex] = i;
|
||||
const QString filePath = Utils::Fs::toUniformPath(QString::fromStdString(fileStorage.file_path(nativeIndex)));
|
||||
m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
|
||||
Path filePath {fileStorage.file_path(nativeIndex)};
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
filePath.removeExtension();
|
||||
m_filePaths.append(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,24 +296,22 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
|||
|
||||
// TODO: Remove the following upgrade code in v4.4
|
||||
// == BEGIN UPGRADE CODE ==
|
||||
const QString spath = actualStorageLocation();
|
||||
const Path spath = actualStorageLocation();
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString filepath = filePath(i);
|
||||
const Path filepath = filePath(i);
|
||||
// Move "unwanted" files back to their original folder
|
||||
const QString parentRelPath = Utils::Fs::branchPath(filepath);
|
||||
if (QDir(parentRelPath).dirName() == ".unwanted")
|
||||
const Path parentRelPath = filepath.parentPath();
|
||||
if (parentRelPath.filename() == QLatin1String(".unwanted"))
|
||||
{
|
||||
const QString oldName = Utils::Fs::fileName(filepath);
|
||||
const QString newRelPath = Utils::Fs::branchPath(parentRelPath);
|
||||
if (newRelPath.isEmpty())
|
||||
renameFile(i, oldName);
|
||||
else
|
||||
renameFile(i, QDir(newRelPath).filePath(oldName));
|
||||
const QString oldName = filepath.filename();
|
||||
const Path newRelPath = parentRelPath.parentPath();
|
||||
renameFile(i, (newRelPath / Path(oldName)));
|
||||
|
||||
// Remove .unwanted directory if empty
|
||||
qDebug() << "Attempting to remove \".unwanted\" folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
|
||||
QDir(spath + '/' + newRelPath).rmdir(".unwanted");
|
||||
const Path newPath = spath / newRelPath;
|
||||
qDebug() << "Attempting to remove \".unwanted\" folder at " << (newPath / Path(".unwanted")).toString();
|
||||
Utils::Fs::rmdir(newPath / Path(".unwanted"));
|
||||
}
|
||||
}
|
||||
// == END UPGRADE CODE ==
|
||||
|
@ -396,18 +395,18 @@ QString TorrentImpl::currentTracker() const
|
|||
return QString::fromStdString(m_nativeStatus.current_tracker);
|
||||
}
|
||||
|
||||
QString TorrentImpl::savePath() const
|
||||
Path TorrentImpl::savePath() const
|
||||
{
|
||||
return isAutoTMMEnabled() ? m_session->categorySavePath(category()) : m_savePath;
|
||||
}
|
||||
|
||||
void TorrentImpl::setSavePath(const QString &path)
|
||||
void TorrentImpl::setSavePath(const Path &path)
|
||||
{
|
||||
Q_ASSERT(!isAutoTMMEnabled());
|
||||
|
||||
const QString basePath = m_session->useCategoryPathsInManualMode()
|
||||
const Path basePath = m_session->useCategoryPathsInManualMode()
|
||||
? m_session->categorySavePath(category()) : m_session->savePath();
|
||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
const Path resolvedPath = (path.isAbsolute() ? path : (basePath / path));
|
||||
if (resolvedPath == savePath())
|
||||
return;
|
||||
|
||||
|
@ -420,18 +419,18 @@ void TorrentImpl::setSavePath(const QString &path)
|
|||
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
||||
}
|
||||
|
||||
QString TorrentImpl::downloadPath() const
|
||||
Path TorrentImpl::downloadPath() const
|
||||
{
|
||||
return isAutoTMMEnabled() ? m_session->categoryDownloadPath(category()) : m_downloadPath;
|
||||
}
|
||||
|
||||
void TorrentImpl::setDownloadPath(const QString &path)
|
||||
void TorrentImpl::setDownloadPath(const Path &path)
|
||||
{
|
||||
Q_ASSERT(!isAutoTMMEnabled());
|
||||
|
||||
const QString basePath = m_session->useCategoryPathsInManualMode()
|
||||
const Path basePath = m_session->useCategoryPathsInManualMode()
|
||||
? m_session->categoryDownloadPath(category()) : m_session->downloadPath();
|
||||
const QString resolvedPath = ((path.isEmpty() || QDir::isAbsolutePath(path)) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||
const Path resolvedPath = (path.isEmpty() || path.isAbsolute()) ? path : (basePath / path);
|
||||
if (resolvedPath == m_downloadPath)
|
||||
return;
|
||||
|
||||
|
@ -444,27 +443,27 @@ void TorrentImpl::setDownloadPath(const QString &path)
|
|||
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
||||
}
|
||||
|
||||
QString TorrentImpl::rootPath() const
|
||||
Path TorrentImpl::rootPath() const
|
||||
{
|
||||
if (!hasMetadata())
|
||||
return {};
|
||||
|
||||
const QString relativeRootPath = Utils::Fs::findRootFolder(filePaths());
|
||||
const Path relativeRootPath = Path::findRootFolder(filePaths());
|
||||
if (relativeRootPath.isEmpty())
|
||||
return {};
|
||||
|
||||
return QDir(actualStorageLocation()).absoluteFilePath(relativeRootPath);
|
||||
return (actualStorageLocation() / relativeRootPath);
|
||||
}
|
||||
|
||||
QString TorrentImpl::contentPath() const
|
||||
Path TorrentImpl::contentPath() const
|
||||
{
|
||||
if (!hasMetadata())
|
||||
return {};
|
||||
|
||||
if (filesCount() == 1)
|
||||
return QDir(actualStorageLocation()).absoluteFilePath(filePath(0));
|
||||
return (actualStorageLocation() / filePath(0));
|
||||
|
||||
const QString rootPath = this->rootPath();
|
||||
const Path rootPath = this->rootPath();
|
||||
return (rootPath.isEmpty() ? actualStorageLocation() : rootPath);
|
||||
}
|
||||
|
||||
|
@ -491,9 +490,9 @@ void TorrentImpl::setAutoTMMEnabled(bool enabled)
|
|||
adjustStorageLocation();
|
||||
}
|
||||
|
||||
QString TorrentImpl::actualStorageLocation() const
|
||||
Path TorrentImpl::actualStorageLocation() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(QString::fromStdString(m_nativeStatus.save_path));
|
||||
return Path(m_nativeStatus.save_path);
|
||||
}
|
||||
|
||||
void TorrentImpl::setAutoManaged(const bool enable)
|
||||
|
@ -799,16 +798,15 @@ int TorrentImpl::seedingTimeLimit() const
|
|||
return m_seedingTimeLimit;
|
||||
}
|
||||
|
||||
QString TorrentImpl::filePath(const int index) const
|
||||
Path TorrentImpl::filePath(const int index) const
|
||||
{
|
||||
return m_filePaths.at(index);
|
||||
}
|
||||
|
||||
QString TorrentImpl::actualFilePath(const int index) const
|
||||
Path TorrentImpl::actualFilePath(const int index) const
|
||||
{
|
||||
const auto nativeIndex = m_torrentInfo.nativeIndexes().at(index);
|
||||
const std::string filePath = m_nativeHandle.torrent_file()->files().file_path(nativeIndex);
|
||||
return Utils::Fs::toUniformPath(QString::fromStdString(filePath));
|
||||
return Path(m_nativeHandle.torrent_file()->files().file_path(nativeIndex));
|
||||
}
|
||||
|
||||
qlonglong TorrentImpl::fileSize(const int index) const
|
||||
|
@ -816,7 +814,7 @@ qlonglong TorrentImpl::fileSize(const int index) const
|
|||
return m_torrentInfo.fileSize(index);
|
||||
}
|
||||
|
||||
QStringList TorrentImpl::filePaths() const
|
||||
PathList TorrentImpl::filePaths() const
|
||||
{
|
||||
return m_filePaths;
|
||||
}
|
||||
|
@ -1481,12 +1479,12 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled, const QVector<
|
|||
m_nativeHandle.prioritize_pieces(piecePriorities);
|
||||
}
|
||||
|
||||
void TorrentImpl::fileSearchFinished(const QString &savePath, const QStringList &fileNames)
|
||||
void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
endReceivedMetadataHandling(savePath, fileNames);
|
||||
}
|
||||
|
||||
void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames)
|
||||
void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
Q_ASSERT(m_filePaths.isEmpty());
|
||||
Q_ASSERT(m_indexMap.isEmpty());
|
||||
|
@ -1502,11 +1500,14 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
|
|||
const auto nativeIndex = nativeIndexes.at(i);
|
||||
m_indexMap[nativeIndex] = i;
|
||||
|
||||
const QString filePath = fileNames.at(i);
|
||||
m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
|
||||
p.renamed_files[nativeIndex] = filePath.toStdString();
|
||||
Path filePath = fileNames.at(i);
|
||||
p.renamed_files[nativeIndex] = filePath.toString().toStdString();
|
||||
|
||||
if (filePath.hasExtension(QB_EXT))
|
||||
filePath.removeExtension();
|
||||
m_filePaths.append(filePath);
|
||||
}
|
||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
p.ti = metadata;
|
||||
|
||||
const int internalFilesCount = p.ti->files().num_files(); // including .pad files
|
||||
|
@ -1613,19 +1614,20 @@ void TorrentImpl::resume(const TorrentOperatingMode mode)
|
|||
}
|
||||
}
|
||||
|
||||
void TorrentImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
|
||||
void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode)
|
||||
{
|
||||
if (m_session->addMoveTorrentStorageJob(this, Utils::Fs::toNativePath(newPath), mode))
|
||||
if (m_session->addMoveTorrentStorageJob(this, newPath, mode))
|
||||
{
|
||||
m_storageIsMoving = true;
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentImpl::renameFile(const int index, const QString &path)
|
||||
void TorrentImpl::renameFile(const int index, const Path &path)
|
||||
{
|
||||
++m_renameCount;
|
||||
m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes().at(index), Utils::Fs::toNativePath(path).toStdString());
|
||||
m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes().at(index)
|
||||
, path.toString().toStdString());
|
||||
}
|
||||
|
||||
void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||
|
@ -1790,7 +1792,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
|||
|
||||
TorrentInfo metadata = TorrentInfo(*m_nativeHandle.torrent_file());
|
||||
|
||||
QStringList filePaths = metadata.filePaths();
|
||||
PathList filePaths = metadata.filePaths();
|
||||
applyContentLayout(filePaths, m_contentLayout);
|
||||
m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths);
|
||||
}
|
||||
|
@ -1876,37 +1878,23 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
|
|||
// Remove empty leftover folders
|
||||
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
|
||||
// be removed if they are empty
|
||||
const QString oldFilePath = m_filePaths.at(fileIndex);
|
||||
const QString newFilePath = Utils::Fs::toUniformPath(p->new_name());
|
||||
const Path oldFilePath = m_filePaths.at(fileIndex);
|
||||
const Path newFilePath {QString(p->new_name())};
|
||||
|
||||
// Check if ".!qB" extension was just added or removed
|
||||
if ((oldFilePath != newFilePath) && (oldFilePath != newFilePath.chopped(QB_EXT.size())))
|
||||
// We should compare path in a case sensitive manner even on case insensitive
|
||||
// platforms since it can be renamed by only changing case of some character(s)
|
||||
if ((oldFilePath.data() != newFilePath.data())
|
||||
&& ((oldFilePath + QB_EXT) != newFilePath))
|
||||
{
|
||||
m_filePaths[fileIndex] = newFilePath;
|
||||
|
||||
QList<QStringView> oldPathParts = QStringView(oldFilePath).split('/', Qt::SkipEmptyParts);
|
||||
oldPathParts.removeLast(); // drop file name part
|
||||
QList<QStringView> newPathParts = QStringView(newFilePath).split('/', Qt::SkipEmptyParts);
|
||||
newPathParts.removeLast(); // drop file name part
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
|
||||
#else
|
||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
||||
#endif
|
||||
|
||||
int pathIdx = 0;
|
||||
while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size()))
|
||||
Path oldParentPath = oldFilePath.parentPath();
|
||||
const Path commonBasePath = Path::commonPath(oldParentPath, newFilePath.parentPath());
|
||||
while (oldParentPath != commonBasePath)
|
||||
{
|
||||
if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
|
||||
break;
|
||||
++pathIdx;
|
||||
}
|
||||
|
||||
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i)
|
||||
{
|
||||
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QString::fromLatin1("/")));
|
||||
oldPathParts.removeLast();
|
||||
Utils::Fs::rmdir(actualStorageLocation() / oldParentPath);
|
||||
oldParentPath = oldParentPath.parentPath();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1923,7 +1911,7 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
|
|||
Q_ASSERT(fileIndex >= 0);
|
||||
|
||||
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
|
||||
.arg(name(), filePath(fileIndex), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
|
||||
.arg(name(), filePath(fileIndex).toString(), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
|
||||
|
||||
--m_renameCount;
|
||||
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
||||
|
@ -1939,11 +1927,11 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
|||
const int fileIndex = m_indexMap.value(p->index, -1);
|
||||
Q_ASSERT(fileIndex >= 0);
|
||||
|
||||
const QString path = filePath(fileIndex);
|
||||
const QString actualPath = actualFilePath(fileIndex);
|
||||
const Path path = filePath(fileIndex);
|
||||
const Path actualPath = actualFilePath(fileIndex);
|
||||
if (actualPath != path)
|
||||
{
|
||||
qDebug("Renaming %s to %s", qUtf8Printable(actualPath), qUtf8Printable(path));
|
||||
qDebug("Renaming %s to %s", qUtf8Printable(actualPath.toString()), qUtf8Printable(path.toString()));
|
||||
renameFile(fileIndex, path);
|
||||
}
|
||||
}
|
||||
|
@ -2064,18 +2052,17 @@ void TorrentImpl::manageIncompleteFiles()
|
|||
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
const QString path = filePath(i);
|
||||
const Path path = filePath(i);
|
||||
|
||||
const auto nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||
const QString actualPath = Utils::Fs::toUniformPath(
|
||||
QString::fromStdString(nativeFiles.file_path(nativeIndex)));
|
||||
const Path actualPath {nativeFiles.file_path(nativeIndex)};
|
||||
|
||||
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
|
||||
{
|
||||
const QString wantedPath = path + QB_EXT;
|
||||
const Path wantedPath = path + QB_EXT;
|
||||
if (actualPath != wantedPath)
|
||||
{
|
||||
qDebug() << "Renaming" << actualPath << "to" << wantedPath;
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << wantedPath.toString();
|
||||
renameFile(i, wantedPath);
|
||||
}
|
||||
}
|
||||
|
@ -2083,7 +2070,7 @@ void TorrentImpl::manageIncompleteFiles()
|
|||
{
|
||||
if (actualPath != path)
|
||||
{
|
||||
qDebug() << "Renaming" << actualPath << "to" << path;
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << path.toString();
|
||||
renameFile(i, path);
|
||||
}
|
||||
}
|
||||
|
@ -2092,12 +2079,12 @@ void TorrentImpl::manageIncompleteFiles()
|
|||
|
||||
void TorrentImpl::adjustStorageLocation()
|
||||
{
|
||||
const QString downloadPath = this->downloadPath();
|
||||
const Path downloadPath = this->downloadPath();
|
||||
const bool isFinished = isSeed() || m_hasSeedStatus;
|
||||
const QDir targetDir {((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath)};
|
||||
const Path targetPath = ((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath);
|
||||
|
||||
if ((targetDir != QDir(actualStorageLocation())) || isMoveInProgress())
|
||||
moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
|
||||
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
||||
moveStorage(targetPath, MoveStorageMode::Overwrite);
|
||||
}
|
||||
|
||||
lt::torrent_handle TorrentImpl::nativeHandle() const
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "infohash.h"
|
||||
#include "speedmonitor.h"
|
||||
|
@ -102,13 +103,13 @@ namespace BitTorrent
|
|||
|
||||
bool isAutoTMMEnabled() const override;
|
||||
void setAutoTMMEnabled(bool enabled) override;
|
||||
QString savePath() const override;
|
||||
void setSavePath(const QString &path) override;
|
||||
QString downloadPath() const override;
|
||||
void setDownloadPath(const QString &path) override;
|
||||
QString actualStorageLocation() const override;
|
||||
QString rootPath() const override;
|
||||
QString contentPath() const override;
|
||||
Path savePath() const override;
|
||||
void setSavePath(const Path &path) override;
|
||||
Path downloadPath() const override;
|
||||
void setDownloadPath(const Path &path) override;
|
||||
Path actualStorageLocation() const override;
|
||||
Path rootPath() const override;
|
||||
Path contentPath() const override;
|
||||
QString category() const override;
|
||||
bool belongsToCategory(const QString &category) const override;
|
||||
bool setCategory(const QString &category) override;
|
||||
|
@ -127,10 +128,10 @@ namespace BitTorrent
|
|||
qreal ratioLimit() const override;
|
||||
int seedingTimeLimit() const override;
|
||||
|
||||
QString filePath(int index) const override;
|
||||
QString actualFilePath(int index) const override;
|
||||
Path filePath(int index) const override;
|
||||
Path actualFilePath(int index) const override;
|
||||
qlonglong fileSize(int index) const override;
|
||||
QStringList filePaths() const override;
|
||||
PathList filePaths() const override;
|
||||
QVector<DownloadPriority> filePriorities() const override;
|
||||
|
||||
TorrentInfo info() const override;
|
||||
|
@ -205,7 +206,7 @@ namespace BitTorrent
|
|||
void forceReannounce(int index = -1) override;
|
||||
void forceDHTAnnounce() override;
|
||||
void forceRecheck() override;
|
||||
void renameFile(int index, const QString &path) override;
|
||||
void renameFile(int index, const Path &path) override;
|
||||
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
||||
void setRatioLimit(qreal limit) override;
|
||||
void setSeedingTimeLimit(int limit) override;
|
||||
|
@ -237,7 +238,7 @@ namespace BitTorrent
|
|||
void handleAppendExtensionToggled();
|
||||
void saveResumeData();
|
||||
void handleMoveStorageJobFinished(bool hasOutstandingJob);
|
||||
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);
|
||||
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||
|
||||
private:
|
||||
using EventTrigger = std::function<void ()>;
|
||||
|
@ -271,12 +272,12 @@ namespace BitTorrent
|
|||
void setAutoManaged(bool enable);
|
||||
|
||||
void adjustStorageLocation();
|
||||
void moveStorage(const QString &newPath, MoveStorageMode mode);
|
||||
void moveStorage(const Path &newPath, MoveStorageMode mode);
|
||||
void manageIncompleteFiles();
|
||||
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
||||
|
||||
void prepareResumeData(const lt::add_torrent_params ¶ms);
|
||||
void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames);
|
||||
void endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames);
|
||||
void reload();
|
||||
|
||||
Session *const m_session;
|
||||
|
@ -285,7 +286,7 @@ namespace BitTorrent
|
|||
lt::torrent_status m_nativeStatus;
|
||||
TorrentState m_state = TorrentState::Unknown;
|
||||
TorrentInfo m_torrentInfo;
|
||||
QStringList m_filePaths;
|
||||
PathList m_filePaths;
|
||||
QHash<lt::file_index_t, int> m_indexMap;
|
||||
SpeedMonitor m_speedMonitor;
|
||||
|
||||
|
@ -304,8 +305,8 @@ namespace BitTorrent
|
|||
|
||||
// Persistent data
|
||||
QString m_name;
|
||||
QString m_savePath;
|
||||
QString m_downloadPath;
|
||||
Path m_savePath;
|
||||
Path m_downloadPath;
|
||||
QString m_category;
|
||||
TagSet m_tags;
|
||||
qreal m_ratioLimit;
|
||||
|
|
|
@ -99,16 +99,16 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)
|
|||
if (ec)
|
||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||
|
||||
lt::torrent_info nativeInfo {node, ec};
|
||||
const lt::torrent_info nativeInfo {node, ec};
|
||||
if (ec)
|
||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||
|
||||
return TorrentInfo(nativeInfo);
|
||||
}
|
||||
|
||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const QString &path) noexcept
|
||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const Path &path) noexcept
|
||||
{
|
||||
QFile file {path};
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return nonstd::make_unexpected(file.errorString());
|
||||
|
||||
|
@ -133,7 +133,7 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const QString &
|
|||
return load(data);
|
||||
}
|
||||
|
||||
nonstd::expected<void, QString> TorrentInfo::saveToFile(const QString &path) const
|
||||
nonstd::expected<void, QString> TorrentInfo::saveToFile(const Path &path) const
|
||||
{
|
||||
if (!isValid())
|
||||
return nonstd::make_unexpected(tr("Invalid metadata"));
|
||||
|
@ -236,17 +236,16 @@ int TorrentInfo::piecesCount() const
|
|||
return m_nativeInfo->num_pieces();
|
||||
}
|
||||
|
||||
QString TorrentInfo::filePath(const int index) const
|
||||
Path TorrentInfo::filePath(const int index) const
|
||||
{
|
||||
if (!isValid()) return {};
|
||||
|
||||
return Utils::Fs::toUniformPath(
|
||||
QString::fromStdString(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index])));
|
||||
return Path(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index]));
|
||||
}
|
||||
|
||||
QStringList TorrentInfo::filePaths() const
|
||||
PathList TorrentInfo::filePaths() const
|
||||
{
|
||||
QStringList list;
|
||||
PathList list;
|
||||
list.reserve(filesCount());
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
list << filePath(i);
|
||||
|
@ -312,15 +311,15 @@ QByteArray TorrentInfo::metadata() const
|
|||
#endif
|
||||
}
|
||||
|
||||
QStringList TorrentInfo::filesForPiece(const int pieceIndex) const
|
||||
PathList TorrentInfo::filesForPiece(const int pieceIndex) const
|
||||
{
|
||||
// no checks here because fileIndicesForPiece() will return an empty list
|
||||
const QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||
|
||||
QStringList res;
|
||||
PathList res;
|
||||
res.reserve(fileIndices.size());
|
||||
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
|
||||
[this](int i) { return filePath(i); });
|
||||
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res)
|
||||
, [this](int i) { return filePath(i); });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -359,15 +358,15 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
|
|||
return hashes;
|
||||
}
|
||||
|
||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString &file) const
|
||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const Path &filePath) const
|
||||
{
|
||||
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||
return {};
|
||||
|
||||
const int index = fileIndex(file);
|
||||
const int index = fileIndex(filePath);
|
||||
if (index == -1)
|
||||
{
|
||||
qDebug() << "Filename" << file << "was not found in torrent" << name();
|
||||
qDebug() << "Filename" << filePath.toString() << "was not found in torrent" << name();
|
||||
return {};
|
||||
}
|
||||
return filePieces(index);
|
||||
|
@ -396,13 +395,13 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
|
|||
return makeInterval(beginIdx, endIdx);
|
||||
}
|
||||
|
||||
int TorrentInfo::fileIndex(const QString &fileName) const
|
||||
int TorrentInfo::fileIndex(const Path &filePath) const
|
||||
{
|
||||
// the check whether the object is valid is not needed here
|
||||
// because if filesCount() returns -1 the loop exits immediately
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
if (fileName == filePath(i))
|
||||
if (filePath == this->filePath(i))
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "base/3rdparty/expected.hpp"
|
||||
#include "base/indexrange.h"
|
||||
#include "base/pathfwd.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
class QByteArray;
|
||||
|
@ -58,8 +59,8 @@ namespace BitTorrent
|
|||
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
||||
|
||||
static nonstd::expected<TorrentInfo, QString> load(const QByteArray &data) noexcept;
|
||||
static nonstd::expected<TorrentInfo, QString> loadFromFile(const QString &path) noexcept;
|
||||
nonstd::expected<void, QString> saveToFile(const QString &path) const;
|
||||
static nonstd::expected<TorrentInfo, QString> loadFromFile(const Path &path) noexcept;
|
||||
nonstd::expected<void, QString> saveToFile(const Path &path) const;
|
||||
|
||||
TorrentInfo &operator=(const TorrentInfo &other);
|
||||
|
||||
|
@ -75,21 +76,21 @@ namespace BitTorrent
|
|||
int pieceLength() const;
|
||||
int pieceLength(int index) const;
|
||||
int piecesCount() const;
|
||||
QString filePath(int index) const;
|
||||
QStringList filePaths() const;
|
||||
Path filePath(int index) const;
|
||||
PathList filePaths() const;
|
||||
qlonglong fileSize(int index) const;
|
||||
qlonglong fileOffset(int index) const;
|
||||
QVector<TrackerEntry> trackers() const;
|
||||
QVector<QUrl> urlSeeds() const;
|
||||
QByteArray metadata() const;
|
||||
QStringList filesForPiece(int pieceIndex) const;
|
||||
PathList filesForPiece(int pieceIndex) const;
|
||||
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
||||
QVector<QByteArray> pieceHashes() const;
|
||||
|
||||
using PieceRange = IndexRange<int>;
|
||||
// returns pair of the first and the last pieces into which
|
||||
// the given file extends (maybe partially).
|
||||
PieceRange filePieces(const QString &file) const;
|
||||
PieceRange filePieces(const Path &filePath) const;
|
||||
PieceRange filePieces(int fileIndex) const;
|
||||
|
||||
std::shared_ptr<lt::torrent_info> nativeInfo() const;
|
||||
|
@ -97,7 +98,7 @@ namespace BitTorrent
|
|||
|
||||
private:
|
||||
// returns file index or -1 if fileName is not found
|
||||
int fileIndex(const QString &fileName) const;
|
||||
int fileIndex(const Path &filePath) const;
|
||||
TorrentContentLayout contentLayout() const;
|
||||
|
||||
std::shared_ptr<const lt::torrent_info> m_nativeInfo;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include "base/path.h"
|
||||
|
||||
IconProvider::IconProvider(QObject *parent)
|
||||
: QObject(parent)
|
||||
|
@ -55,14 +55,14 @@ IconProvider *IconProvider::instance()
|
|||
return m_instance;
|
||||
}
|
||||
|
||||
QString IconProvider::getIconPath(const QString &iconId) const
|
||||
Path IconProvider::getIconPath(const QString &iconId) const
|
||||
{
|
||||
// there are a few icons not available in svg
|
||||
const QString pathSvg = ":/icons/" + iconId + ".svg";
|
||||
if (QFileInfo::exists(pathSvg))
|
||||
const Path pathSvg {":/icons/" + iconId + ".svg"};
|
||||
if (pathSvg.exists())
|
||||
return pathSvg;
|
||||
|
||||
const QString pathPng = ":/icons/" + iconId + ".png";
|
||||
const Path pathPng {":/icons/" + iconId + ".png"};
|
||||
return pathPng;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QString;
|
||||
|
||||
class IconProvider : public QObject
|
||||
|
@ -42,7 +44,7 @@ public:
|
|||
static void freeInstance();
|
||||
static IconProvider *instance();
|
||||
|
||||
virtual QString getIconPath(const QString &iconId) const;
|
||||
virtual Path getIconPath(const QString &iconId) const;
|
||||
|
||||
protected:
|
||||
explicit IconProvider(QObject *parent = nullptr);
|
||||
|
|
|
@ -42,14 +42,14 @@ const int MAX_REDIRECTIONS = 20; // the common value for web browsers
|
|||
|
||||
namespace
|
||||
{
|
||||
nonstd::expected<QString, QString> saveToTempFile(const QByteArray &data)
|
||||
nonstd::expected<Path, QString> saveToTempFile(const QByteArray &data)
|
||||
{
|
||||
QTemporaryFile file {Utils::Fs::tempPath()};
|
||||
QTemporaryFile file {Utils::Fs::tempPath().data()};
|
||||
if (!file.open() || (file.write(data) != data.length()) || !file.flush())
|
||||
return nonstd::make_unexpected(file.errorString());
|
||||
|
||||
file.setAutoRemove(false);
|
||||
return file.fileName();
|
||||
return Path(file.fileName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,10 +127,10 @@ void DownloadHandlerImpl::processFinishedDownload()
|
|||
|
||||
if (m_downloadRequest.saveToFile())
|
||||
{
|
||||
const QString destinationPath = m_downloadRequest.destFileName();
|
||||
const Path destinationPath = m_downloadRequest.destFileName();
|
||||
if (destinationPath.isEmpty())
|
||||
{
|
||||
const nonstd::expected<QString, QString> result = saveToTempFile(m_result.data);
|
||||
const nonstd::expected<Path, QString> result = saveToTempFile(m_result.data);
|
||||
if (result)
|
||||
m_result.filePath = result.value();
|
||||
else
|
||||
|
|
|
@ -348,12 +348,12 @@ Net::DownloadRequest &Net::DownloadRequest::saveToFile(const bool value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
QString Net::DownloadRequest::destFileName() const
|
||||
Path Net::DownloadRequest::destFileName() const
|
||||
{
|
||||
return m_destFileName;
|
||||
}
|
||||
|
||||
Net::DownloadRequest &Net::DownloadRequest::destFileName(const QString &value)
|
||||
Net::DownloadRequest &Net::DownloadRequest::destFileName(const Path &value)
|
||||
{
|
||||
m_destFileName = value;
|
||||
return *this;
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include <QQueue>
|
||||
#include <QSet>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
class QNetworkCookie;
|
||||
class QNetworkReply;
|
||||
class QSslError;
|
||||
|
@ -81,15 +83,15 @@ namespace Net
|
|||
// if saveToFile is set, the file is saved in destFileName
|
||||
// (deprecated) if destFileName is not provided, the file will be saved
|
||||
// in a temporary file, the name of file is set in DownloadResult::filePath
|
||||
QString destFileName() const;
|
||||
DownloadRequest &destFileName(const QString &value);
|
||||
Path destFileName() const;
|
||||
DownloadRequest &destFileName(const Path &value);
|
||||
|
||||
private:
|
||||
QString m_url;
|
||||
QString m_userAgent;
|
||||
qint64 m_limit = 0;
|
||||
bool m_saveToFile = false;
|
||||
QString m_destFileName;
|
||||
Path m_destFileName;
|
||||
};
|
||||
|
||||
struct DownloadResult
|
||||
|
@ -98,7 +100,7 @@ namespace Net
|
|||
DownloadStatus status;
|
||||
QString errorString;
|
||||
QByteArray data;
|
||||
QString filePath;
|
||||
Path filePath;
|
||||
QString magnet;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,13 +26,15 @@
|
|||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "geoipdatabase.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
#include <QVariant>
|
||||
|
||||
#include "geoipdatabase.h"
|
||||
#include "base/path.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -84,10 +86,10 @@ GeoIPDatabase::GeoIPDatabase(const quint32 size)
|
|||
{
|
||||
}
|
||||
|
||||
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
||||
GeoIPDatabase *GeoIPDatabase::load(const Path &filename, QString &error)
|
||||
{
|
||||
GeoIPDatabase *db = nullptr;
|
||||
QFile file(filename);
|
||||
QFile file {filename.data()};
|
||||
if (file.size() > MAX_FILE_SIZE)
|
||||
{
|
||||
error = tr("Unsupported database file size.");
|
||||
|
|
|
@ -28,11 +28,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QVariant>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QByteArray;
|
||||
class QDateTime;
|
||||
class QHostAddress;
|
||||
class QString;
|
||||
|
||||
|
@ -43,7 +47,7 @@ class GeoIPDatabase
|
|||
Q_DECLARE_TR_FUNCTIONS(GeoIPDatabase)
|
||||
|
||||
public:
|
||||
static GeoIPDatabase *load(const QString &filename, QString &error);
|
||||
static GeoIPDatabase *load(const Path &filename, QString &error);
|
||||
static GeoIPDatabase *load(const QByteArray &data, QString &error);
|
||||
|
||||
~GeoIPDatabase();
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "geoipmanager.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QHostAddress>
|
||||
#include <QLocale>
|
||||
|
||||
|
@ -43,9 +42,9 @@
|
|||
#include "downloadmanager.h"
|
||||
#include "geoipdatabase.h"
|
||||
|
||||
static const QString DATABASE_URL = QStringLiteral("https://download.db-ip.com/free/dbip-country-lite-%1.mmdb.gz");
|
||||
static const char GEODB_FOLDER[] = "GeoDB";
|
||||
static const char GEODB_FILENAME[] = "dbip-country-lite.mmdb";
|
||||
const QString DATABASE_URL = QStringLiteral("https://download.db-ip.com/free/dbip-country-lite-%1.mmdb.gz");
|
||||
const char GEODB_FOLDER[] = "GeoDB";
|
||||
const char GEODB_FILENAME[] = "dbip-country-lite.mmdb";
|
||||
|
||||
using namespace Net;
|
||||
|
||||
|
@ -88,17 +87,21 @@ void GeoIPManager::loadDatabase()
|
|||
delete m_geoIPDatabase;
|
||||
m_geoIPDatabase = nullptr;
|
||||
|
||||
const QString filepath = Utils::Fs::expandPathAbs(
|
||||
QString::fromLatin1("%1/%2/%3").arg(specialFolderLocation(SpecialFolder::Data), GEODB_FOLDER, GEODB_FILENAME));
|
||||
const Path filepath = specialFolderLocation(SpecialFolder::Data)
|
||||
/ Path(GEODB_FOLDER) / Path(GEODB_FILENAME);
|
||||
|
||||
QString error;
|
||||
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
|
||||
if (m_geoIPDatabase)
|
||||
{
|
||||
Logger::instance()->addMessage(tr("IP geolocation database loaded. Type: %1. Build time: %2.")
|
||||
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
||||
Log::INFO);
|
||||
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
||||
Log::INFO);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::instance()->addMessage(tr("Couldn't load IP geolocation database. Reason: %1").arg(error), Log::WARNING);
|
||||
}
|
||||
|
||||
manageDatabaseUpdate();
|
||||
}
|
||||
|
@ -445,14 +448,13 @@ void GeoIPManager::downloadFinished(const DownloadResult &result)
|
|||
delete m_geoIPDatabase;
|
||||
m_geoIPDatabase = geoIPDatabase;
|
||||
LogMsg(tr("IP geolocation database loaded. Type: %1. Build time: %2.")
|
||||
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
||||
Log::INFO);
|
||||
const QString targetPath = Utils::Fs::expandPathAbs(
|
||||
QDir(specialFolderLocation(SpecialFolder::Data)).absoluteFilePath(GEODB_FOLDER));
|
||||
if (!QDir(targetPath).exists())
|
||||
QDir().mkpath(targetPath);
|
||||
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString())
|
||||
, Log::INFO);
|
||||
const Path targetPath = specialFolderLocation(SpecialFolder::Data) / Path(GEODB_FOLDER);
|
||||
if (!targetPath.exists())
|
||||
Utils::Fs::mkpath(targetPath);
|
||||
|
||||
const auto path = QString::fromLatin1("%1/%2").arg(targetPath, GEODB_FILENAME);
|
||||
const auto path = targetPath / Path(GEODB_FILENAME);
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||
if (result)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "path.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
#include <QMimeDatabase>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const Qt::CaseSensitivity CASE_SENSITIVITY = Qt::CaseInsensitive;
|
||||
#else
|
||||
const Qt::CaseSensitivity CASE_SENSITIVITY = Qt::CaseSensitive;
|
||||
#endif
|
||||
|
||||
const int PATHLIST_TYPEID = qRegisterMetaType<PathList>();
|
||||
|
||||
Path::Path(const QString &pathStr)
|
||||
: m_pathStr {QDir::cleanPath(pathStr)}
|
||||
{
|
||||
}
|
||||
|
||||
Path::Path(const std::string &pathStr)
|
||||
: Path(QString::fromStdString(pathStr))
|
||||
{
|
||||
}
|
||||
|
||||
Path::Path(const char pathStr[])
|
||||
: Path(QString::fromLatin1(pathStr))
|
||||
{
|
||||
}
|
||||
|
||||
bool Path::isValid() const
|
||||
{
|
||||
if (isEmpty())
|
||||
return false;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const QRegularExpression regex {QLatin1String("[:?\"*<>|]")};
|
||||
#elif defined(Q_OS_MACOS)
|
||||
const QRegularExpression regex {QLatin1String("[\\0:]")};
|
||||
#else
|
||||
const QRegularExpression regex {QLatin1String("[\\0]")};
|
||||
#endif
|
||||
return !m_pathStr.contains(regex);
|
||||
}
|
||||
|
||||
bool Path::isEmpty() const
|
||||
{
|
||||
return m_pathStr.isEmpty();
|
||||
}
|
||||
|
||||
bool Path::isAbsolute() const
|
||||
{
|
||||
return QDir::isAbsolutePath(m_pathStr);
|
||||
}
|
||||
|
||||
bool Path::isRelative() const
|
||||
{
|
||||
return QDir::isRelativePath(m_pathStr);
|
||||
}
|
||||
|
||||
bool Path::exists() const
|
||||
{
|
||||
return !isEmpty() && QFileInfo::exists(m_pathStr);
|
||||
}
|
||||
|
||||
Path Path::rootItem() const
|
||||
{
|
||||
const int slashIndex = m_pathStr.indexOf(QLatin1Char('/'));
|
||||
if (slashIndex < 0)
|
||||
return *this;
|
||||
|
||||
if (slashIndex == 0) // *nix absolute path
|
||||
return createUnchecked(QLatin1String("/"));
|
||||
|
||||
return createUnchecked(m_pathStr.left(slashIndex));
|
||||
}
|
||||
|
||||
Path Path::parentPath() const
|
||||
{
|
||||
const int slashIndex = m_pathStr.lastIndexOf(QLatin1Char('/'));
|
||||
if (slashIndex == -1)
|
||||
return {};
|
||||
|
||||
if (slashIndex == 0) // *nix absolute path
|
||||
return (m_pathStr.size() == 1) ? Path() : createUnchecked(QLatin1String("/"));
|
||||
|
||||
return createUnchecked(m_pathStr.left(slashIndex));
|
||||
}
|
||||
|
||||
QString Path::filename() const
|
||||
{
|
||||
const int slashIndex = m_pathStr.lastIndexOf('/');
|
||||
if (slashIndex == -1)
|
||||
return m_pathStr;
|
||||
|
||||
return m_pathStr.mid(slashIndex + 1);
|
||||
}
|
||||
|
||||
QString Path::extension() const
|
||||
{
|
||||
const QString suffix = QMimeDatabase().suffixForFileName(m_pathStr);
|
||||
if (!suffix.isEmpty())
|
||||
return (QLatin1String(".") + suffix);
|
||||
|
||||
const int slashIndex = m_pathStr.lastIndexOf(QLatin1Char('/'));
|
||||
const auto filename = QStringView(m_pathStr).mid(slashIndex + 1);
|
||||
const int dotIndex = filename.lastIndexOf(QLatin1Char('.'));
|
||||
return ((dotIndex == -1) ? QString() : filename.mid(dotIndex).toString());
|
||||
}
|
||||
|
||||
bool Path::hasExtension(const QString &ext) const
|
||||
{
|
||||
return (extension().compare(ext, Qt::CaseInsensitive) == 0);
|
||||
}
|
||||
|
||||
bool Path::hasAncestor(const Path &other) const
|
||||
{
|
||||
if (other.isEmpty() || (m_pathStr.size() <= other.m_pathStr.size()))
|
||||
return false;
|
||||
|
||||
return (m_pathStr[other.m_pathStr.size()] == QLatin1Char('/'))
|
||||
&& m_pathStr.startsWith(other.m_pathStr, CASE_SENSITIVITY);
|
||||
}
|
||||
|
||||
Path Path::relativePathOf(const Path &childPath) const
|
||||
{
|
||||
// If both paths are relative, we assume that they have the same base path
|
||||
if (isRelative() && childPath.isRelative())
|
||||
return Path(QDir(QDir::home().absoluteFilePath(m_pathStr)).relativeFilePath(QDir::home().absoluteFilePath(childPath.data())));
|
||||
|
||||
return Path(QDir(m_pathStr).relativeFilePath(childPath.data()));
|
||||
}
|
||||
|
||||
void Path::removeExtension()
|
||||
{
|
||||
m_pathStr.chop(extension().size());
|
||||
}
|
||||
|
||||
QString Path::data() const
|
||||
{
|
||||
return m_pathStr;
|
||||
}
|
||||
|
||||
QString Path::toString() const
|
||||
{
|
||||
return QDir::toNativeSeparators(m_pathStr);
|
||||
}
|
||||
|
||||
Path &Path::operator/=(const Path &other)
|
||||
{
|
||||
*this = *this / other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Path &Path::operator+=(const QString &str)
|
||||
{
|
||||
*this = *this + str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Path &Path::operator+=(const char str[])
|
||||
{
|
||||
return (*this += QString::fromLatin1(str));
|
||||
}
|
||||
|
||||
Path &Path::operator+=(const std::string &str)
|
||||
{
|
||||
return (*this += QString::fromStdString(str));
|
||||
}
|
||||
|
||||
Path Path::commonPath(const Path &left, const Path &right)
|
||||
{
|
||||
if (left.isEmpty() || right.isEmpty())
|
||||
return {};
|
||||
|
||||
const QList<QStringView> leftPathItems = QStringView(left.m_pathStr).split(u'/');
|
||||
const QList<QStringView> rightPathItems = QStringView(right.m_pathStr).split(u'/');
|
||||
int commonItemsCount = 0;
|
||||
qsizetype commonPathSize = 0;
|
||||
while ((commonItemsCount < leftPathItems.size()) && (commonItemsCount < rightPathItems.size()))
|
||||
{
|
||||
const QStringView leftPathItem = leftPathItems[commonItemsCount];
|
||||
const QStringView rightPathItem = rightPathItems[commonItemsCount];
|
||||
if (leftPathItem.compare(rightPathItem, CASE_SENSITIVITY) != 0)
|
||||
break;
|
||||
|
||||
++commonItemsCount;
|
||||
commonPathSize += leftPathItem.size();
|
||||
}
|
||||
|
||||
if (commonItemsCount > 0)
|
||||
commonPathSize += (commonItemsCount - 1); // size of intermediate separators
|
||||
|
||||
return Path::createUnchecked(left.m_pathStr.left(commonPathSize));
|
||||
}
|
||||
|
||||
Path Path::findRootFolder(const PathList &filePaths)
|
||||
{
|
||||
Path rootFolder;
|
||||
for (const Path &filePath : filePaths)
|
||||
{
|
||||
const auto filePathElements = QStringView(filePath.m_pathStr).split(u'/');
|
||||
// if at least one file has no root folder, no common root folder exists
|
||||
if (filePathElements.count() <= 1)
|
||||
return {};
|
||||
|
||||
if (rootFolder.isEmpty())
|
||||
rootFolder.m_pathStr = filePathElements.at(0).toString();
|
||||
else if (rootFolder.m_pathStr != filePathElements.at(0))
|
||||
return {};
|
||||
}
|
||||
|
||||
return rootFolder;
|
||||
}
|
||||
|
||||
void Path::stripRootFolder(PathList &filePaths)
|
||||
{
|
||||
const Path commonRootFolder = findRootFolder(filePaths);
|
||||
if (commonRootFolder.isEmpty())
|
||||
return;
|
||||
|
||||
for (Path &filePath : filePaths)
|
||||
filePath.m_pathStr = filePath.m_pathStr.mid(commonRootFolder.m_pathStr.size() + 1);
|
||||
}
|
||||
|
||||
void Path::addRootFolder(PathList &filePaths, const Path &rootFolder)
|
||||
{
|
||||
Q_ASSERT(!rootFolder.isEmpty());
|
||||
|
||||
for (Path &filePath : filePaths)
|
||||
filePath = rootFolder / filePath;
|
||||
}
|
||||
|
||||
Path Path::createUnchecked(const QString &pathStr)
|
||||
{
|
||||
Path path;
|
||||
path.m_pathStr = pathStr;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool operator==(const Path &lhs, const Path &rhs)
|
||||
{
|
||||
return (lhs.m_pathStr.compare(rhs.m_pathStr, CASE_SENSITIVITY) == 0);
|
||||
}
|
||||
|
||||
bool operator!=(const Path &lhs, const Path &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
Path operator/(const Path &lhs, const Path &rhs)
|
||||
{
|
||||
if (rhs.isEmpty())
|
||||
return lhs;
|
||||
|
||||
if (lhs.isEmpty())
|
||||
return rhs;
|
||||
|
||||
return Path(lhs.m_pathStr + QLatin1Char('/') + rhs.m_pathStr);
|
||||
}
|
||||
|
||||
Path operator+(const Path &lhs, const QString &rhs)
|
||||
{
|
||||
return Path(lhs.m_pathStr + rhs);
|
||||
}
|
||||
|
||||
Path operator+(const Path &lhs, const char rhs[])
|
||||
{
|
||||
return lhs + QString::fromLatin1(rhs);
|
||||
}
|
||||
|
||||
Path operator+(const Path &lhs, const std::string &rhs)
|
||||
{
|
||||
return lhs + QString::fromStdString(rhs);
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Path &path)
|
||||
{
|
||||
out << path.data();
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &in, Path &path)
|
||||
{
|
||||
QString pathStr;
|
||||
in >> pathStr;
|
||||
path = Path(pathStr);
|
||||
return in;
|
||||
}
|
||||
|
||||
uint qHash(const Path &key, const uint seed)
|
||||
{
|
||||
return ::qHash(key.data(), seed);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
|
||||
#include "pathfwd.h"
|
||||
|
||||
class Path final
|
||||
{
|
||||
public:
|
||||
Path() = default;
|
||||
|
||||
explicit Path(const QString &pathStr);
|
||||
explicit Path(const char pathStr[]);
|
||||
explicit Path(const std::string &pathStr);
|
||||
|
||||
bool isValid() const;
|
||||
bool isEmpty() const;
|
||||
bool isAbsolute() const;
|
||||
bool isRelative() const;
|
||||
|
||||
bool exists() const;
|
||||
|
||||
Path rootItem() const;
|
||||
Path parentPath() const;
|
||||
|
||||
QString filename() const;
|
||||
|
||||
QString extension() const;
|
||||
bool hasExtension(const QString &ext) const;
|
||||
void removeExtension();
|
||||
|
||||
bool hasAncestor(const Path &other) const;
|
||||
Path relativePathOf(const Path &childPath) const;
|
||||
|
||||
QString data() const;
|
||||
QString toString() const;
|
||||
|
||||
Path &operator/=(const Path &other);
|
||||
Path &operator+=(const QString &str);
|
||||
Path &operator+=(const char str[]);
|
||||
Path &operator+=(const std::string &str);
|
||||
|
||||
static Path commonPath(const Path &left, const Path &right);
|
||||
|
||||
static Path findRootFolder(const PathList &filePaths);
|
||||
static void stripRootFolder(PathList &filePaths);
|
||||
static void addRootFolder(PathList &filePaths, const Path &rootFolder);
|
||||
|
||||
friend bool operator==(const Path &lhs, const Path &rhs);
|
||||
friend Path operator/(const Path &lhs, const Path &rhs);
|
||||
friend Path operator+(const Path &lhs, const QString &rhs);
|
||||
|
||||
private:
|
||||
// this constructor doesn't perform any checks
|
||||
// so it's intended for internal use only
|
||||
static Path createUnchecked(const QString &pathStr);
|
||||
|
||||
QString m_pathStr;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Path)
|
||||
|
||||
bool operator!=(const Path &lhs, const Path &rhs);
|
||||
Path operator+(const Path &lhs, const char rhs[]);
|
||||
Path operator+(const Path &lhs, const std::string &rhs);
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Path &path);
|
||||
QDataStream &operator>>(QDataStream &in, Path &path);
|
||||
|
||||
uint qHash(const Path &key, uint seed);
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
|
||||
class Path;
|
||||
|
||||
using PathList = QList<Path>;
|
|
@ -54,6 +54,7 @@
|
|||
|
||||
#include "algorithm.h"
|
||||
#include "global.h"
|
||||
#include "path.h"
|
||||
#include "profile.h"
|
||||
#include "settingsstorage.h"
|
||||
#include "utils/fs.h"
|
||||
|
@ -85,11 +86,11 @@ namespace
|
|||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString makeProfileID(const QString &profilePath, const QString &profileName)
|
||||
QString makeProfileID(const Path &profilePath, const QString &profileName)
|
||||
{
|
||||
return profilePath.isEmpty()
|
||||
? profileName
|
||||
: profileName + QLatin1Char('@') + Utils::Fs::toValidFileSystemName(profilePath, false, {});
|
||||
: profileName + QLatin1Char('@') + Utils::Fs::toValidFileName(profilePath.data(), {});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -137,12 +138,12 @@ void Preferences::setUseCustomUITheme(const bool use)
|
|||
setValue("Preferences/General/UseCustomUITheme", use);
|
||||
}
|
||||
|
||||
QString Preferences::customUIThemePath() const
|
||||
Path Preferences::customUIThemePath() const
|
||||
{
|
||||
return value<QString>("Preferences/General/CustomUIThemePath");
|
||||
return value<Path>("Preferences/General/CustomUIThemePath");
|
||||
}
|
||||
|
||||
void Preferences::setCustomUIThemePath(const QString &path)
|
||||
void Preferences::setCustomUIThemePath(const Path &path)
|
||||
{
|
||||
setValue("Preferences/General/CustomUIThemePath", path);
|
||||
}
|
||||
|
@ -336,7 +337,7 @@ void Preferences::setPreventFromSuspendWhenSeeding(const bool b)
|
|||
bool Preferences::WinStartup() const
|
||||
{
|
||||
const QString profileName = Profile::instance()->profileName();
|
||||
const QString profilePath = Profile::instance()->rootPath();
|
||||
const Path profilePath = Profile::instance()->rootPath();
|
||||
const QString profileID = makeProfileID(profilePath, profileName);
|
||||
const QSettings settings {"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat};
|
||||
|
||||
|
@ -346,7 +347,7 @@ bool Preferences::WinStartup() const
|
|||
void Preferences::setWinStartup(const bool b)
|
||||
{
|
||||
const QString profileName = Profile::instance()->profileName();
|
||||
const QString profilePath = Profile::instance()->rootPath();
|
||||
const Path profilePath = Profile::instance()->rootPath();
|
||||
const QString profileID = makeProfileID(profilePath, profileName);
|
||||
QSettings settings {"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat};
|
||||
if (b)
|
||||
|
@ -354,7 +355,7 @@ void Preferences::setWinStartup(const bool b)
|
|||
const QString configuration = Profile::instance()->configurationName();
|
||||
|
||||
const auto cmd = QString::fromLatin1(R"("%1" "--profile=%2" "--configuration=%3")")
|
||||
.arg(Utils::Fs::toNativePath(qApp->applicationFilePath()), profilePath, configuration);
|
||||
.arg(Path(qApp->applicationFilePath()).toString(), profilePath.toString(), configuration);
|
||||
settings.setValue(profileID, cmd);
|
||||
}
|
||||
else
|
||||
|
@ -365,24 +366,14 @@ void Preferences::setWinStartup(const bool b)
|
|||
#endif // Q_OS_WIN
|
||||
|
||||
// Downloads
|
||||
QString Preferences::lastLocationPath() const
|
||||
Path Preferences::getScanDirsLastPath() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(value<QString>("Preferences/Downloads/LastLocationPath"));
|
||||
return value<Path>("Preferences/Downloads/ScanDirsLastPath");
|
||||
}
|
||||
|
||||
void Preferences::setLastLocationPath(const QString &path)
|
||||
void Preferences::setScanDirsLastPath(const Path &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/LastLocationPath", Utils::Fs::toUniformPath(path));
|
||||
}
|
||||
|
||||
QString Preferences::getScanDirsLastPath() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(value<QString>("Preferences/Downloads/ScanDirsLastPath"));
|
||||
}
|
||||
|
||||
void Preferences::setScanDirsLastPath(const QString &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/ScanDirsLastPath", Utils::Fs::toUniformPath(path));
|
||||
setValue("Preferences/Downloads/ScanDirsLastPath", path);
|
||||
}
|
||||
|
||||
bool Preferences::isMailNotificationEnabled() const
|
||||
|
@ -737,22 +728,22 @@ void Preferences::setWebUiHttpsEnabled(const bool enabled)
|
|||
setValue("Preferences/WebUI/HTTPS/Enabled", enabled);
|
||||
}
|
||||
|
||||
QString Preferences::getWebUIHttpsCertificatePath() const
|
||||
Path Preferences::getWebUIHttpsCertificatePath() const
|
||||
{
|
||||
return value<QString>("Preferences/WebUI/HTTPS/CertificatePath");
|
||||
return value<Path>("Preferences/WebUI/HTTPS/CertificatePath");
|
||||
}
|
||||
|
||||
void Preferences::setWebUIHttpsCertificatePath(const QString &path)
|
||||
void Preferences::setWebUIHttpsCertificatePath(const Path &path)
|
||||
{
|
||||
setValue("Preferences/WebUI/HTTPS/CertificatePath", path);
|
||||
}
|
||||
|
||||
QString Preferences::getWebUIHttpsKeyPath() const
|
||||
Path Preferences::getWebUIHttpsKeyPath() const
|
||||
{
|
||||
return value<QString>("Preferences/WebUI/HTTPS/KeyPath");
|
||||
return value<Path>("Preferences/WebUI/HTTPS/KeyPath");
|
||||
}
|
||||
|
||||
void Preferences::setWebUIHttpsKeyPath(const QString &path)
|
||||
void Preferences::setWebUIHttpsKeyPath(const Path &path)
|
||||
{
|
||||
setValue("Preferences/WebUI/HTTPS/KeyPath", path);
|
||||
}
|
||||
|
@ -767,12 +758,12 @@ void Preferences::setAltWebUiEnabled(const bool enabled)
|
|||
setValue("Preferences/WebUI/AlternativeUIEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Preferences::getWebUiRootFolder() const
|
||||
Path Preferences::getWebUiRootFolder() const
|
||||
{
|
||||
return value<QString>("Preferences/WebUI/RootFolder");
|
||||
return value<Path>("Preferences/WebUI/RootFolder");
|
||||
}
|
||||
|
||||
void Preferences::setWebUiRootFolder(const QString &path)
|
||||
void Preferences::setWebUiRootFolder(const Path &path)
|
||||
{
|
||||
setValue("Preferences/WebUI/RootFolder", path);
|
||||
}
|
||||
|
@ -1050,14 +1041,14 @@ bool Preferences::isMagnetLinkAssocSet()
|
|||
const QSettings settings("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
|
||||
|
||||
// Check magnet link assoc
|
||||
const QString shellCommand = Utils::Fs::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
||||
const QString shellCommand = settings.value("magnet/shell/open/command/Default", "").toString();
|
||||
|
||||
const QRegularExpressionMatch exeRegMatch = QRegularExpression("\"([^\"]+)\".*").match(shellCommand);
|
||||
if (!exeRegMatch.hasMatch())
|
||||
return false;
|
||||
|
||||
const QString assocExe = exeRegMatch.captured(1);
|
||||
if (assocExe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
||||
const Path assocExe {exeRegMatch.captured(1)};
|
||||
if (assocExe != Path(qApp->applicationFilePath()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -1090,15 +1081,16 @@ void Preferences::setMagnetLinkAssoc(const bool set)
|
|||
// Magnet association
|
||||
if (set)
|
||||
{
|
||||
const QString commandStr = '"' + qApp->applicationFilePath() + "\" \"%1\"";
|
||||
const QString iconStr = '"' + qApp->applicationFilePath() + "\",1";
|
||||
const QString applicationFilePath = Path(qApp->applicationFilePath()).toString();
|
||||
const QString commandStr = '"' + applicationFilePath + "\" \"%1\"";
|
||||
const QString iconStr = '"' + applicationFilePath + "\",1";
|
||||
|
||||
settings.setValue("magnet/Default", "URL:Magnet link");
|
||||
settings.setValue("magnet/Content Type", "application/x-magnet");
|
||||
settings.setValue("magnet/URL Protocol", "");
|
||||
settings.setValue("magnet/DefaultIcon/Default", Utils::Fs::toNativePath(iconStr));
|
||||
settings.setValue("magnet/DefaultIcon/Default", iconStr);
|
||||
settings.setValue("magnet/shell/Default", "open");
|
||||
settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(commandStr));
|
||||
settings.setValue("magnet/shell/open/command/Default", commandStr);
|
||||
}
|
||||
else if (isMagnetLinkAssocSet())
|
||||
{
|
||||
|
@ -1294,12 +1286,12 @@ void Preferences::setMainVSplitterState(const QByteArray &state)
|
|||
#endif
|
||||
}
|
||||
|
||||
QString Preferences::getMainLastDir() const
|
||||
Path Preferences::getMainLastDir() const
|
||||
{
|
||||
return value("MainWindow/LastDir", QDir::homePath());
|
||||
return value("MainWindow/LastDir", Utils::Fs::homePath());
|
||||
}
|
||||
|
||||
void Preferences::setMainLastDir(const QString &path)
|
||||
void Preferences::setMainLastDir(const Path &path)
|
||||
{
|
||||
setValue("MainWindow/LastDir", path);
|
||||
}
|
||||
|
|
|
@ -29,10 +29,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QtContainerFwd>
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/utils/net.h"
|
||||
|
||||
class QDateTime;
|
||||
|
@ -108,8 +109,8 @@ public:
|
|||
void setLocale(const QString &locale);
|
||||
bool useCustomUITheme() const;
|
||||
void setUseCustomUITheme(bool use);
|
||||
QString customUIThemePath() const;
|
||||
void setCustomUIThemePath(const QString &path);
|
||||
Path customUIThemePath() const;
|
||||
void setCustomUIThemePath(const Path &path);
|
||||
bool deleteTorrentFilesAsDefault() const;
|
||||
void setDeleteTorrentFilesAsDefault(bool del);
|
||||
bool confirmOnExit() const;
|
||||
|
@ -140,10 +141,8 @@ public:
|
|||
#endif
|
||||
|
||||
// Downloads
|
||||
QString lastLocationPath() const;
|
||||
void setLastLocationPath(const QString &path);
|
||||
QString getScanDirsLastPath() const;
|
||||
void setScanDirsLastPath(const QString &path);
|
||||
Path getScanDirsLastPath() const;
|
||||
void setScanDirsLastPath(const Path &path);
|
||||
bool isMailNotificationEnabled() const;
|
||||
void setMailNotificationEnabled(bool enabled);
|
||||
QString getMailNotificationSender() const;
|
||||
|
@ -220,14 +219,14 @@ public:
|
|||
// HTTPS
|
||||
bool isWebUiHttpsEnabled() const;
|
||||
void setWebUiHttpsEnabled(bool enabled);
|
||||
QString getWebUIHttpsCertificatePath() const;
|
||||
void setWebUIHttpsCertificatePath(const QString &path);
|
||||
QString getWebUIHttpsKeyPath() const;
|
||||
void setWebUIHttpsKeyPath(const QString &path);
|
||||
Path getWebUIHttpsCertificatePath() const;
|
||||
void setWebUIHttpsCertificatePath(const Path &path);
|
||||
Path getWebUIHttpsKeyPath() const;
|
||||
void setWebUIHttpsKeyPath(const Path &path);
|
||||
bool isAltWebUiEnabled() const;
|
||||
void setAltWebUiEnabled(bool enabled);
|
||||
QString getWebUiRootFolder() const;
|
||||
void setWebUiRootFolder(const QString &path);
|
||||
Path getWebUiRootFolder() const;
|
||||
void setWebUiRootFolder(const Path &path);
|
||||
|
||||
// WebUI custom HTTP headers
|
||||
bool isWebUICustomHTTPHeadersEnabled() const;
|
||||
|
@ -343,8 +342,8 @@ public:
|
|||
void setMainGeometry(const QByteArray &geometry);
|
||||
QByteArray getMainVSplitterState() const;
|
||||
void setMainVSplitterState(const QByteArray &state);
|
||||
QString getMainLastDir() const;
|
||||
void setMainLastDir(const QString &path);
|
||||
Path getMainLastDir() const;
|
||||
void setMainLastDir(const Path &path);
|
||||
QByteArray getPeerListState() const;
|
||||
void setPeerListState(const QByteArray &state);
|
||||
QString getPropSplitterSizes() const;
|
||||
|
|
|
@ -29,11 +29,13 @@
|
|||
|
||||
#include "profile.h"
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "profile_p.h"
|
||||
|
||||
Profile *Profile::m_instance = nullptr;
|
||||
|
||||
Profile::Profile(const QString &rootProfilePath, const QString &configurationName, const bool convertPathsToProfileRelative)
|
||||
Profile::Profile(const Path &rootProfilePath, const QString &configurationName, const bool convertPathsToProfileRelative)
|
||||
{
|
||||
if (rootProfilePath.isEmpty())
|
||||
m_profileImpl = std::make_unique<Private::DefaultProfile>(configurationName);
|
||||
|
@ -50,7 +52,7 @@ Profile::Profile(const QString &rootProfilePath, const QString &configurationNam
|
|||
m_pathConverterImpl = std::make_unique<Private::NoConvertConverter>();
|
||||
}
|
||||
|
||||
void Profile::initInstance(const QString &rootProfilePath, const QString &configurationName,
|
||||
void Profile::initInstance(const Path &rootProfilePath, const QString &configurationName,
|
||||
const bool convertPathsToProfileRelative)
|
||||
{
|
||||
if (m_instance)
|
||||
|
@ -69,29 +71,29 @@ const Profile *Profile::instance()
|
|||
return m_instance;
|
||||
}
|
||||
|
||||
QString Profile::location(const SpecialFolder folder) const
|
||||
Path Profile::location(const SpecialFolder folder) const
|
||||
{
|
||||
QString result;
|
||||
switch (folder)
|
||||
{
|
||||
case SpecialFolder::Cache:
|
||||
result = m_profileImpl->cacheLocation();
|
||||
break;
|
||||
return m_profileImpl->cacheLocation();
|
||||
|
||||
case SpecialFolder::Config:
|
||||
result = m_profileImpl->configLocation();
|
||||
break;
|
||||
return m_profileImpl->configLocation();
|
||||
|
||||
case SpecialFolder::Data:
|
||||
result = m_profileImpl->dataLocation();
|
||||
break;
|
||||
return m_profileImpl->dataLocation();
|
||||
|
||||
case SpecialFolder::Downloads:
|
||||
result = m_profileImpl->downloadLocation();
|
||||
break;
|
||||
return m_profileImpl->downloadLocation();
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown SpecialFolder value.");
|
||||
return {};
|
||||
}
|
||||
|
||||
QString Profile::rootPath() const
|
||||
Path Profile::rootPath() const
|
||||
{
|
||||
return m_profileImpl->rootPath();
|
||||
}
|
||||
|
@ -113,22 +115,22 @@ SettingsPtr Profile::applicationSettings(const QString &name) const
|
|||
|
||||
void Profile::ensureDirectoryExists(const SpecialFolder folder) const
|
||||
{
|
||||
const QString locationPath = location(folder);
|
||||
if (!locationPath.isEmpty() && !QDir().mkpath(locationPath))
|
||||
qFatal("Could not create required directory '%s'", qUtf8Printable(locationPath));
|
||||
const Path locationPath = location(folder);
|
||||
if (!locationPath.isEmpty() && !Utils::Fs::mkpath(locationPath))
|
||||
qFatal("Could not create required directory '%s'", qUtf8Printable(locationPath.toString()));
|
||||
}
|
||||
|
||||
QString Profile::toPortablePath(const QString &absolutePath) const
|
||||
Path Profile::toPortablePath(const Path &absolutePath) const
|
||||
{
|
||||
return m_pathConverterImpl->toPortablePath(absolutePath);
|
||||
}
|
||||
|
||||
QString Profile::fromPortablePath(const QString &portablePath) const
|
||||
Path Profile::fromPortablePath(const Path &portablePath) const
|
||||
{
|
||||
return m_pathConverterImpl->fromPortablePath(portablePath);
|
||||
}
|
||||
|
||||
QString specialFolderLocation(const SpecialFolder folder)
|
||||
Path specialFolderLocation(const SpecialFolder folder)
|
||||
{
|
||||
return Profile::instance()->location(folder);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include <QSettings>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QString;
|
||||
|
||||
namespace Private
|
||||
|
@ -54,26 +56,26 @@ enum class SpecialFolder
|
|||
class Profile
|
||||
{
|
||||
public:
|
||||
static void initInstance(const QString &rootProfilePath, const QString &configurationName,
|
||||
static void initInstance(const Path &rootProfilePath, const QString &configurationName,
|
||||
bool convertPathsToProfileRelative);
|
||||
static void freeInstance();
|
||||
static const Profile *instance();
|
||||
|
||||
QString location(SpecialFolder folder) const;
|
||||
Path location(SpecialFolder folder) const;
|
||||
SettingsPtr applicationSettings(const QString &name) const;
|
||||
|
||||
QString rootPath() const;
|
||||
Path rootPath() const;
|
||||
QString configurationName() const;
|
||||
|
||||
/// Returns either default name for configuration file (QCoreApplication::applicationName())
|
||||
/// or the value, supplied via parameters
|
||||
QString profileName() const;
|
||||
|
||||
QString toPortablePath(const QString &absolutePath) const;
|
||||
QString fromPortablePath(const QString &portablePath) const;
|
||||
Path toPortablePath(const Path &absolutePath) const;
|
||||
Path fromPortablePath(const Path &portablePath) const;
|
||||
|
||||
private:
|
||||
Profile(const QString &rootProfilePath, const QString &configurationName, bool convertPathsToProfileRelative);
|
||||
Profile(const Path &rootProfilePath, const QString &configurationName, bool convertPathsToProfileRelative);
|
||||
~Profile() = default; // to generate correct call to ProfilePrivate::~ProfileImpl()
|
||||
|
||||
void ensureDirectoryExists(SpecialFolder folder) const;
|
||||
|
@ -83,4 +85,4 @@ private:
|
|||
static Profile *m_instance;
|
||||
};
|
||||
|
||||
QString specialFolderLocation(SpecialFolder folder);
|
||||
Path specialFolderLocation(SpecialFolder folder);
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "base/utils/fs.h"
|
||||
|
||||
Private::Profile::Profile(const QString &configurationName)
|
||||
: m_configurationName {configurationName}
|
||||
{
|
||||
|
@ -56,22 +58,22 @@ Private::DefaultProfile::DefaultProfile(const QString &configurationName)
|
|||
{
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::rootPath() const
|
||||
Path Private::DefaultProfile::rootPath() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::basePath() const
|
||||
Path Private::DefaultProfile::basePath() const
|
||||
{
|
||||
return QDir::homePath();
|
||||
return Utils::Fs::homePath();
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::cacheLocation() const
|
||||
Path Private::DefaultProfile::cacheLocation() const
|
||||
{
|
||||
return locationWithConfigurationName(QStandardPaths::CacheLocation);
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::configLocation() const
|
||||
Path Private::DefaultProfile::configLocation() const
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
// On Windows QSettings stores files in FOLDERID_RoamingAppData\AppName
|
||||
|
@ -81,22 +83,22 @@ QString Private::DefaultProfile::configLocation() const
|
|||
#endif
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::dataLocation() const
|
||||
Path Private::DefaultProfile::dataLocation() const
|
||||
{
|
||||
#if defined(Q_OS_WIN) || defined (Q_OS_MACOS)
|
||||
return locationWithConfigurationName(QStandardPaths::AppLocalDataLocation);
|
||||
#else
|
||||
// On Linux keep using the legacy directory ~/.local/share/data/ if it exists
|
||||
const QString legacyDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
|
||||
+ QLatin1String("/data/") + profileName();
|
||||
const Path genericDataPath {QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)};
|
||||
const Path profilePath {profileName()};
|
||||
const Path legacyDir = genericDataPath / Path("data") / profilePath;
|
||||
|
||||
const QString dataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
|
||||
+ QLatin1Char('/') + profileName();
|
||||
const Path dataDir = genericDataPath / profilePath;
|
||||
|
||||
if (!QDir(dataDir).exists() && QDir(legacyDir).exists())
|
||||
if (!dataDir.exists() && legacyDir.exists())
|
||||
{
|
||||
qWarning("The legacy data directory '%s' is used. It is recommended to move its content to '%s'",
|
||||
qUtf8Printable(legacyDir), qUtf8Printable(dataDir));
|
||||
qUtf8Printable(legacyDir.toString()), qUtf8Printable(dataDir.toString()));
|
||||
|
||||
return legacyDir;
|
||||
}
|
||||
|
@ -105,9 +107,9 @@ QString Private::DefaultProfile::dataLocation() const
|
|||
#endif
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::downloadLocation() const
|
||||
Path Private::DefaultProfile::downloadLocation() const
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
return Path(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
}
|
||||
|
||||
SettingsPtr Private::DefaultProfile::applicationSettings(const QString &name) const
|
||||
|
@ -119,48 +121,48 @@ SettingsPtr Private::DefaultProfile::applicationSettings(const QString &name) co
|
|||
#endif
|
||||
}
|
||||
|
||||
QString Private::DefaultProfile::locationWithConfigurationName(const QStandardPaths::StandardLocation location) const
|
||||
Path Private::DefaultProfile::locationWithConfigurationName(const QStandardPaths::StandardLocation location) const
|
||||
{
|
||||
return QStandardPaths::writableLocation(location) + configurationSuffix();
|
||||
return Path(QStandardPaths::writableLocation(location) + configurationSuffix());
|
||||
}
|
||||
|
||||
Private::CustomProfile::CustomProfile(const QString &rootPath, const QString &configurationName)
|
||||
Private::CustomProfile::CustomProfile(const Path &rootPath, const QString &configurationName)
|
||||
: Profile {configurationName}
|
||||
, m_rootDir {rootPath}
|
||||
, m_baseDir {m_rootDir.absoluteFilePath(profileName())}
|
||||
, m_cacheLocation {m_baseDir.absoluteFilePath(QLatin1String("cache"))}
|
||||
, m_configLocation {m_baseDir.absoluteFilePath(QLatin1String("config"))}
|
||||
, m_dataLocation {m_baseDir.absoluteFilePath(QLatin1String("data"))}
|
||||
, m_downloadLocation {m_baseDir.absoluteFilePath(QLatin1String("downloads"))}
|
||||
, m_rootPath {rootPath}
|
||||
, m_basePath {m_rootPath / Path(profileName())}
|
||||
, m_cacheLocation {m_basePath / Path("cache")}
|
||||
, m_configLocation {m_basePath / Path("config")}
|
||||
, m_dataLocation {m_basePath / Path("data")}
|
||||
, m_downloadLocation {m_basePath / Path("downloads")}
|
||||
{
|
||||
}
|
||||
|
||||
QString Private::CustomProfile::rootPath() const
|
||||
Path Private::CustomProfile::rootPath() const
|
||||
{
|
||||
return m_rootDir.absolutePath();
|
||||
return m_rootPath;
|
||||
}
|
||||
|
||||
QString Private::CustomProfile::basePath() const
|
||||
Path Private::CustomProfile::basePath() const
|
||||
{
|
||||
return m_baseDir.absolutePath();
|
||||
return m_basePath;
|
||||
}
|
||||
|
||||
QString Private::CustomProfile::cacheLocation() const
|
||||
Path Private::CustomProfile::cacheLocation() const
|
||||
{
|
||||
return m_cacheLocation;
|
||||
}
|
||||
|
||||
QString Private::CustomProfile::configLocation() const
|
||||
Path Private::CustomProfile::configLocation() const
|
||||
{
|
||||
return m_configLocation;
|
||||
}
|
||||
|
||||
QString Private::CustomProfile::dataLocation() const
|
||||
Path Private::CustomProfile::dataLocation() const
|
||||
{
|
||||
return m_dataLocation;
|
||||
}
|
||||
|
||||
QString Private::CustomProfile::downloadLocation() const
|
||||
Path Private::CustomProfile::downloadLocation() const
|
||||
{
|
||||
return m_downloadLocation;
|
||||
}
|
||||
|
@ -173,48 +175,48 @@ SettingsPtr Private::CustomProfile::applicationSettings(const QString &name) con
|
|||
#else
|
||||
const char CONF_FILE_EXTENSION[] = ".conf";
|
||||
#endif
|
||||
const QString settingsFileName {QDir(configLocation()).absoluteFilePath(name + QLatin1String(CONF_FILE_EXTENSION))};
|
||||
return SettingsPtr(new QSettings(settingsFileName, QSettings::IniFormat));
|
||||
const Path settingsFilePath = configLocation() / Path(name + QLatin1String(CONF_FILE_EXTENSION));
|
||||
return SettingsPtr(new QSettings(settingsFilePath.data(), QSettings::IniFormat));
|
||||
}
|
||||
|
||||
QString Private::NoConvertConverter::fromPortablePath(const QString &portablePath) const
|
||||
Path Private::NoConvertConverter::fromPortablePath(const Path &portablePath) const
|
||||
{
|
||||
return portablePath;
|
||||
}
|
||||
|
||||
QString Private::NoConvertConverter::toPortablePath(const QString &path) const
|
||||
Path Private::NoConvertConverter::toPortablePath(const Path &path) const
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Private::Converter::Converter(const QString &basePath)
|
||||
: m_baseDir {basePath}
|
||||
Private::Converter::Converter(const Path &basePath)
|
||||
: m_basePath {basePath}
|
||||
{
|
||||
m_baseDir.makeAbsolute();
|
||||
Q_ASSERT(basePath.isAbsolute());
|
||||
}
|
||||
|
||||
QString Private::Converter::toPortablePath(const QString &path) const
|
||||
Path Private::Converter::toPortablePath(const Path &path) const
|
||||
{
|
||||
if (path.isEmpty() || m_baseDir.path().isEmpty())
|
||||
if (path.isEmpty())
|
||||
return path;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (QDir::isAbsolutePath(path))
|
||||
if (path.isAbsolute())
|
||||
{
|
||||
const QChar driveLeter = path[0].toUpper();
|
||||
const QChar baseDriveLetter = m_baseDir.path()[0].toUpper();
|
||||
const bool onSameDrive = (driveLeter.category() == QChar::Letter_Uppercase) && (driveLeter == baseDriveLetter);
|
||||
const QChar driveLetter = path.data()[0].toUpper();
|
||||
const QChar baseDriveLetter = m_basePath.data()[0].toUpper();
|
||||
const bool onSameDrive = (driveLetter.category() == QChar::Letter_Uppercase) && (driveLetter == baseDriveLetter);
|
||||
if (!onSameDrive)
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
return m_baseDir.relativeFilePath(path);
|
||||
return m_basePath.relativePathOf(path);
|
||||
}
|
||||
|
||||
QString Private::Converter::fromPortablePath(const QString &portablePath) const
|
||||
Path Private::Converter::fromPortablePath(const Path &portablePath) const
|
||||
{
|
||||
if (portablePath.isEmpty() || QDir::isAbsolutePath(portablePath))
|
||||
if (portablePath.isEmpty() || portablePath.isAbsolute())
|
||||
return portablePath;
|
||||
|
||||
return QDir::cleanPath(m_baseDir.absoluteFilePath(portablePath));
|
||||
return m_basePath / portablePath;
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "base/profile.h"
|
||||
#include "base/path.h"
|
||||
#include "profile.h"
|
||||
|
||||
namespace Private
|
||||
{
|
||||
|
@ -41,17 +41,17 @@ namespace Private
|
|||
public:
|
||||
virtual ~Profile() = default;
|
||||
|
||||
virtual QString rootPath() const = 0;
|
||||
virtual Path rootPath() const = 0;
|
||||
|
||||
/**
|
||||
* @brief The base path against to which portable (relative) paths are resolved
|
||||
*/
|
||||
virtual QString basePath() const = 0;
|
||||
virtual Path basePath() const = 0;
|
||||
|
||||
virtual QString cacheLocation() const = 0;
|
||||
virtual QString configLocation() const = 0;
|
||||
virtual QString dataLocation() const = 0;
|
||||
virtual QString downloadLocation() const = 0;
|
||||
virtual Path cacheLocation() const = 0;
|
||||
virtual Path configLocation() const = 0;
|
||||
virtual Path dataLocation() const = 0;
|
||||
virtual Path downloadLocation() const = 0;
|
||||
|
||||
virtual SettingsPtr applicationSettings(const QString &name) const = 0;
|
||||
|
||||
|
@ -77,12 +77,12 @@ namespace Private
|
|||
public:
|
||||
explicit DefaultProfile(const QString &configurationName);
|
||||
|
||||
QString rootPath() const override;
|
||||
QString basePath() const override;
|
||||
QString cacheLocation() const override;
|
||||
QString configLocation() const override;
|
||||
QString dataLocation() const override;
|
||||
QString downloadLocation() const override;
|
||||
Path rootPath() const override;
|
||||
Path basePath() const override;
|
||||
Path cacheLocation() const override;
|
||||
Path configLocation() const override;
|
||||
Path dataLocation() const override;
|
||||
Path downloadLocation() const override;
|
||||
SettingsPtr applicationSettings(const QString &name) const override;
|
||||
|
||||
private:
|
||||
|
@ -92,55 +92,55 @@ namespace Private
|
|||
* @param location location kind
|
||||
* @return QStandardPaths::writableLocation(location) / configurationName()
|
||||
*/
|
||||
QString locationWithConfigurationName(QStandardPaths::StandardLocation location) const;
|
||||
Path locationWithConfigurationName(QStandardPaths::StandardLocation location) const;
|
||||
};
|
||||
|
||||
/// Custom tree: creates directories under the specified root directory
|
||||
class CustomProfile final : public Profile
|
||||
{
|
||||
public:
|
||||
CustomProfile(const QString &rootPath, const QString &configurationName);
|
||||
CustomProfile(const Path &rootPath, const QString &configurationName);
|
||||
|
||||
QString rootPath() const override;
|
||||
QString basePath() const override;
|
||||
QString cacheLocation() const override;
|
||||
QString configLocation() const override;
|
||||
QString dataLocation() const override;
|
||||
QString downloadLocation() const override;
|
||||
Path rootPath() const override;
|
||||
Path basePath() const override;
|
||||
Path cacheLocation() const override;
|
||||
Path configLocation() const override;
|
||||
Path dataLocation() const override;
|
||||
Path downloadLocation() const override;
|
||||
SettingsPtr applicationSettings(const QString &name) const override;
|
||||
|
||||
private:
|
||||
const QDir m_rootDir;
|
||||
const QDir m_baseDir;
|
||||
const QString m_cacheLocation;
|
||||
const QString m_configLocation;
|
||||
const QString m_dataLocation;
|
||||
const QString m_downloadLocation;
|
||||
const Path m_rootPath;
|
||||
const Path m_basePath;
|
||||
const Path m_cacheLocation;
|
||||
const Path m_configLocation;
|
||||
const Path m_dataLocation;
|
||||
const Path m_downloadLocation;
|
||||
};
|
||||
|
||||
class PathConverter
|
||||
{
|
||||
public:
|
||||
virtual QString toPortablePath(const QString &path) const = 0;
|
||||
virtual QString fromPortablePath(const QString &portablePath) const = 0;
|
||||
virtual Path toPortablePath(const Path &path) const = 0;
|
||||
virtual Path fromPortablePath(const Path &portablePath) const = 0;
|
||||
virtual ~PathConverter() = default;
|
||||
};
|
||||
|
||||
class NoConvertConverter final : public PathConverter
|
||||
{
|
||||
public:
|
||||
QString toPortablePath(const QString &path) const override;
|
||||
QString fromPortablePath(const QString &portablePath) const override;
|
||||
Path toPortablePath(const Path &path) const override;
|
||||
Path fromPortablePath(const Path &portablePath) const override;
|
||||
};
|
||||
|
||||
class Converter final : public PathConverter
|
||||
{
|
||||
public:
|
||||
explicit Converter(const QString &basePath);
|
||||
QString toPortablePath(const QString &path) const override;
|
||||
QString fromPortablePath(const QString &portablePath) const override;
|
||||
explicit Converter(const Path &basePath);
|
||||
Path toPortablePath(const Path &path) const override;
|
||||
Path fromPortablePath(const Path &portablePath) const override;
|
||||
|
||||
private:
|
||||
QDir m_baseDir;
|
||||
Path m_basePath;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -58,8 +58,8 @@ struct ProcessingJob
|
|||
QVariantHash articleData;
|
||||
};
|
||||
|
||||
const QString ConfFolderName(QStringLiteral("rss"));
|
||||
const QString RulesFileName(QStringLiteral("download_rules.json"));
|
||||
const QString CONF_FOLDER_NAME {QStringLiteral("rss")};
|
||||
const QString RULES_FILE_NAME {QStringLiteral("download_rules.json")};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -107,17 +107,16 @@ AutoDownloader::AutoDownloader()
|
|||
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||
m_instance = this;
|
||||
|
||||
m_fileStorage = new AsyncFileStorage(
|
||||
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Config) + QLatin1Char('/') + ConfFolderName));
|
||||
m_fileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FOLDER_NAME));
|
||||
if (!m_fileStorage)
|
||||
throw RuntimeError(tr("Directory for RSS AutoDownloader data is unavailable."));
|
||||
|
||||
m_fileStorage->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_fileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString)
|
||||
connect(m_fileStorage, &AsyncFileStorage::failed, [](const Path &fileName, const QString &errorString)
|
||||
{
|
||||
LogMsg(tr("Couldn't save RSS AutoDownloader data in %1. Error: %2")
|
||||
.arg(fileName, errorString), Log::CRITICAL);
|
||||
.arg(fileName.toString(), errorString), Log::CRITICAL);
|
||||
});
|
||||
|
||||
m_ioThread->start();
|
||||
|
@ -414,7 +413,7 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
|
|||
|
||||
void AutoDownloader::load()
|
||||
{
|
||||
QFile rulesFile(m_fileStorage->storageDir().absoluteFilePath(RulesFileName));
|
||||
QFile rulesFile {(m_fileStorage->storageDir() / Path(RULES_FILE_NAME)).data()};
|
||||
|
||||
if (!rulesFile.exists())
|
||||
loadRulesLegacy();
|
||||
|
@ -463,7 +462,7 @@ void AutoDownloader::store()
|
|||
for (const auto &rule : asConst(m_rules))
|
||||
jsonObj.insert(rule.name(), rule.toJsonObject());
|
||||
|
||||
m_fileStorage->store(RulesFileName, QJsonDocument(jsonObj).toJson());
|
||||
m_fileStorage->store(Path(RULES_FILE_NAME), QJsonDocument(jsonObj).toJson());
|
||||
}
|
||||
|
||||
void AutoDownloader::storeDeferred()
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <QStringList>
|
||||
|
||||
#include "base/global.h"
|
||||
#include "base/path.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/string.h"
|
||||
|
@ -132,7 +133,7 @@ namespace RSS
|
|||
int ignoreDays = 0;
|
||||
QDateTime lastMatch;
|
||||
|
||||
QString savePath;
|
||||
Path savePath;
|
||||
QString category;
|
||||
std::optional<bool> addPaused;
|
||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||
|
@ -466,7 +467,7 @@ QJsonObject AutoDownloadRule::toJsonObject() const
|
|||
, {Str_MustNotContain, mustNotContain()}
|
||||
, {Str_EpisodeFilter, episodeFilter()}
|
||||
, {Str_AffectedFeeds, QJsonArray::fromStringList(feedURLs())}
|
||||
, {Str_SavePath, savePath()}
|
||||
, {Str_SavePath, savePath().toString()}
|
||||
, {Str_AssignedCategory, assignedCategory()}
|
||||
, {Str_LastMatch, lastMatch().toString(Qt::RFC2822Date)}
|
||||
, {Str_IgnoreDays, ignoreDays()}
|
||||
|
@ -485,7 +486,7 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
|
|||
rule.setMustNotContain(jsonObj.value(Str_MustNotContain).toString());
|
||||
rule.setEpisodeFilter(jsonObj.value(Str_EpisodeFilter).toString());
|
||||
rule.setEnabled(jsonObj.value(Str_Enabled).toBool(true));
|
||||
rule.setSavePath(jsonObj.value(Str_SavePath).toString());
|
||||
rule.setSavePath(Path(jsonObj.value(Str_SavePath).toString()));
|
||||
rule.setCategory(jsonObj.value(Str_AssignedCategory).toString());
|
||||
rule.setAddPaused(toOptionalBool(jsonObj.value(Str_AddPaused)));
|
||||
|
||||
|
@ -546,7 +547,7 @@ QVariantHash AutoDownloadRule::toLegacyDict() const
|
|||
return {{"name", name()},
|
||||
{"must_contain", mustContain()},
|
||||
{"must_not_contain", mustNotContain()},
|
||||
{"save_path", savePath()},
|
||||
{"save_path", savePath().toString()},
|
||||
{"affected_feeds", feedURLs()},
|
||||
{"enabled", isEnabled()},
|
||||
{"category_assigned", assignedCategory()},
|
||||
|
@ -567,7 +568,7 @@ AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
|
|||
rule.setEpisodeFilter(dict.value("episode_filter").toString());
|
||||
rule.setFeedURLs(dict.value("affected_feeds").toStringList());
|
||||
rule.setEnabled(dict.value("enabled", false).toBool());
|
||||
rule.setSavePath(dict.value("save_path").toString());
|
||||
rule.setSavePath(Path(dict.value("save_path").toString()));
|
||||
rule.setCategory(dict.value("category_assigned").toString());
|
||||
rule.setAddPaused(addPausedLegacyToOptionalBool(dict.value("add_paused").toInt()));
|
||||
rule.setLastMatch(dict.value("last_match").toDateTime());
|
||||
|
@ -624,14 +625,14 @@ void AutoDownloadRule::setName(const QString &name)
|
|||
m_dataPtr->name = name;
|
||||
}
|
||||
|
||||
QString AutoDownloadRule::savePath() const
|
||||
Path AutoDownloadRule::savePath() const
|
||||
{
|
||||
return m_dataPtr->savePath;
|
||||
}
|
||||
|
||||
void AutoDownloadRule::setSavePath(const QString &savePath)
|
||||
void AutoDownloadRule::setSavePath(const Path &savePath)
|
||||
{
|
||||
m_dataPtr->savePath = Utils::Fs::toUniformPath(savePath);
|
||||
m_dataPtr->savePath = savePath;
|
||||
}
|
||||
|
||||
std::optional<bool> AutoDownloadRule::addPaused() const
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include "base/bittorrent/torrentcontentlayout.h"
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QDateTime;
|
||||
class QJsonObject;
|
||||
|
@ -77,8 +78,8 @@ namespace RSS
|
|||
QStringList previouslyMatchedEpisodes() const;
|
||||
void setPreviouslyMatchedEpisodes(const QStringList &previouslyMatchedEpisodes);
|
||||
|
||||
QString savePath() const;
|
||||
void setSavePath(const QString &savePath);
|
||||
Path savePath() const;
|
||||
void setSavePath(const Path &savePath);
|
||||
std::optional<bool> addPaused() const;
|
||||
void setAddPaused(std::optional<bool> addPaused);
|
||||
std::optional<BitTorrent::TorrentContentLayout> torrentContentLayout() const;
|
||||
|
|
|
@ -68,17 +68,16 @@ Feed::Feed(const QUuid &uid, const QString &url, const QString &path, Session *s
|
|||
, m_url(url)
|
||||
{
|
||||
const auto uidHex = QString::fromLatin1(m_uid.toRfc4122().toHex());
|
||||
m_dataFileName = uidHex + QLatin1String(".json");
|
||||
m_dataFileName = Path(uidHex + QLatin1String(".json"));
|
||||
|
||||
// Move to new file naming scheme (since v4.1.2)
|
||||
const QString legacyFilename
|
||||
{Utils::Fs::toValidFileSystemName(m_url, false, QLatin1String("_"))
|
||||
+ QLatin1String(".json")};
|
||||
const QDir storageDir {m_session->dataFileStorage()->storageDir()};
|
||||
if (!QFile::exists(storageDir.absoluteFilePath(m_dataFileName)))
|
||||
QFile::rename(storageDir.absoluteFilePath(legacyFilename), storageDir.absoluteFilePath(m_dataFileName));
|
||||
const QString legacyFilename = Utils::Fs::toValidFileName(m_url, QLatin1String("_")) + QLatin1String(".json");
|
||||
const Path storageDir = m_session->dataFileStorage()->storageDir();
|
||||
const Path dataFilePath = storageDir / m_dataFileName;
|
||||
if (!dataFilePath.exists())
|
||||
Utils::Fs::renameFile((storageDir / Path(legacyFilename)), dataFilePath);
|
||||
|
||||
m_iconPath = Utils::Fs::toUniformPath(storageDir.absoluteFilePath(uidHex + QLatin1String(".ico")));
|
||||
m_iconPath = storageDir / Path(uidHex + QLatin1String(".ico"));
|
||||
|
||||
m_parser = new Private::Parser(m_lastBuildDate);
|
||||
m_parser->moveToThread(m_session->workingThread());
|
||||
|
@ -139,7 +138,7 @@ void Feed::refresh()
|
|||
m_downloadHandler = Net::DownloadManager::instance()->download(m_url);
|
||||
connect(m_downloadHandler, &Net::DownloadHandler::finished, this, &Feed::handleDownloadFinished);
|
||||
|
||||
if (!QFile::exists(m_iconPath))
|
||||
if (!m_iconPath.exists())
|
||||
downloadIcon();
|
||||
|
||||
m_isLoading = true;
|
||||
|
@ -262,7 +261,7 @@ void Feed::handleParsingFinished(const RSS::Private::ParsingResult &result)
|
|||
|
||||
void Feed::load()
|
||||
{
|
||||
QFile file(m_session->dataFileStorage()->storageDir().absoluteFilePath(m_dataFileName));
|
||||
QFile file {(m_session->dataFileStorage()->storageDir() / m_dataFileName).data()};
|
||||
|
||||
if (!file.exists())
|
||||
{
|
||||
|
@ -278,7 +277,7 @@ void Feed::load()
|
|||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
|
||||
.arg(m_dataFileName, file.errorString())
|
||||
.arg(m_dataFileName.toString(), file.errorString())
|
||||
, Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
@ -500,7 +499,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
|
|||
return newArticlesCount;
|
||||
}
|
||||
|
||||
QString Feed::iconPath() const
|
||||
Path Feed::iconPath() const
|
||||
{
|
||||
return m_iconPath;
|
||||
}
|
||||
|
@ -549,8 +548,8 @@ void Feed::handleArticleRead(Article *article)
|
|||
|
||||
void Feed::cleanup()
|
||||
{
|
||||
Utils::Fs::forceRemove(m_session->dataFileStorage()->storageDir().absoluteFilePath(m_dataFileName));
|
||||
Utils::Fs::forceRemove(m_iconPath);
|
||||
Utils::Fs::removeFile(m_session->dataFileStorage()->storageDir() / m_dataFileName);
|
||||
Utils::Fs::removeFile(m_iconPath);
|
||||
}
|
||||
|
||||
void Feed::timerEvent(QTimerEvent *event)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <QList>
|
||||
#include <QUuid>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "rss_item.h"
|
||||
|
||||
class AsyncFileStorage;
|
||||
|
@ -79,7 +80,7 @@ namespace RSS
|
|||
bool hasError() const;
|
||||
bool isLoading() const;
|
||||
Article *articleByGUID(const QString &guid) const;
|
||||
QString iconPath() const;
|
||||
Path iconPath() const;
|
||||
|
||||
QJsonValue toJsonValue(bool withData = false) const override;
|
||||
|
||||
|
@ -122,8 +123,8 @@ namespace RSS
|
|||
QHash<QString, Article *> m_articles;
|
||||
QList<Article *> m_articlesByDate;
|
||||
int m_unreadCount = 0;
|
||||
QString m_iconPath;
|
||||
QString m_dataFileName;
|
||||
Path m_iconPath;
|
||||
Path m_dataFileName;
|
||||
QBasicTimer m_savingTimer;
|
||||
bool m_dirty = false;
|
||||
Net::DownloadHandler *m_downloadHandler = nullptr;
|
||||
|
|
|
@ -49,9 +49,9 @@
|
|||
#include "rss_item.h"
|
||||
|
||||
const int MsecsPerMin = 60000;
|
||||
const QString ConfFolderName(QStringLiteral("rss"));
|
||||
const QString DataFolderName(QStringLiteral("rss/articles"));
|
||||
const QString FeedsFileName(QStringLiteral("feeds.json"));
|
||||
const QString CONF_FOLDER_NAME(QStringLiteral("rss"));
|
||||
const QString DATA_FOLDER_NAME(QStringLiteral("rss/articles"));
|
||||
const QString FEEDS_FILE_NAME(QStringLiteral("feeds.json"));
|
||||
|
||||
using namespace RSS;
|
||||
|
||||
|
@ -66,24 +66,22 @@ Session::Session()
|
|||
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||
m_instance = this;
|
||||
|
||||
m_confFileStorage = new AsyncFileStorage(
|
||||
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Config) + QLatin1Char('/') + ConfFolderName));
|
||||
m_confFileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FOLDER_NAME));
|
||||
m_confFileStorage->moveToThread(m_workingThread);
|
||||
connect(m_workingThread, &QThread::finished, m_confFileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_confFileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString)
|
||||
connect(m_confFileStorage, &AsyncFileStorage::failed, [](const Path &fileName, const QString &errorString)
|
||||
{
|
||||
Logger::instance()->addMessage(QString("Couldn't save RSS Session configuration in %1. Error: %2")
|
||||
.arg(fileName, errorString), Log::WARNING);
|
||||
LogMsg(tr("Couldn't save RSS Session configuration in %1. Error: %2")
|
||||
.arg(fileName.toString(), errorString), Log::WARNING);
|
||||
});
|
||||
|
||||
m_dataFileStorage = new AsyncFileStorage(
|
||||
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + QLatin1Char('/') + DataFolderName));
|
||||
m_dataFileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Data) / Path(DATA_FOLDER_NAME));
|
||||
m_dataFileStorage->moveToThread(m_workingThread);
|
||||
connect(m_workingThread, &QThread::finished, m_dataFileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_dataFileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString)
|
||||
connect(m_dataFileStorage, &AsyncFileStorage::failed, [](const Path &fileName, const QString &errorString)
|
||||
{
|
||||
Logger::instance()->addMessage(QString("Couldn't save RSS Session data in %1. Error: %2")
|
||||
.arg(fileName, errorString), Log::WARNING);
|
||||
LogMsg(tr("Couldn't save RSS Session data in %1. Error: %2")
|
||||
.arg(fileName.toString(), errorString), Log::WARNING);
|
||||
});
|
||||
|
||||
m_itemsByPath.insert("", new Folder); // root folder
|
||||
|
@ -233,7 +231,7 @@ Item *Session::itemByPath(const QString &path) const
|
|||
|
||||
void Session::load()
|
||||
{
|
||||
QFile itemsFile(m_confFileStorage->storageDir().absoluteFilePath(FeedsFileName));
|
||||
QFile itemsFile {(m_confFileStorage->storageDir() / Path(FEEDS_FILE_NAME)).data()};
|
||||
if (!itemsFile.exists())
|
||||
{
|
||||
loadLegacy();
|
||||
|
@ -372,7 +370,8 @@ void Session::loadLegacy()
|
|||
|
||||
void Session::store()
|
||||
{
|
||||
m_confFileStorage->store(FeedsFileName, QJsonDocument(rootFolder()->toJsonValue().toObject()).toJson());
|
||||
m_confFileStorage->store(Path(FEEDS_FILE_NAME)
|
||||
, QJsonDocument(rootFolder()->toJsonValue().toObject()).toJson());
|
||||
}
|
||||
|
||||
nonstd::expected<Folder *, QString> Session::prepareItemDest(const QString &path)
|
||||
|
|
|
@ -30,8 +30,9 @@
|
|||
|
||||
#include <QProcess>
|
||||
|
||||
#include "../utils/foreignapps.h"
|
||||
#include "../utils/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/foreignapps.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "searchpluginmanager.h"
|
||||
|
||||
SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager)
|
||||
|
@ -44,7 +45,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QStri
|
|||
, this, &SearchDownloadHandler::downloadProcessFinished);
|
||||
const QStringList params
|
||||
{
|
||||
Utils::Fs::toNativePath(m_manager->engineLocation() + "/nova2dl.py"),
|
||||
(m_manager->engineLocation() / Path("nova2dl.py")).toString(),
|
||||
siteUrl,
|
||||
url
|
||||
};
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <QVector>
|
||||
|
||||
#include "base/global.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/foreignapps.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "searchpluginmanager.h"
|
||||
|
@ -67,7 +68,7 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co
|
|||
|
||||
const QStringList params
|
||||
{
|
||||
Utils::Fs::toNativePath(m_manager->engineLocation() + "/nova2.py"),
|
||||
(m_manager->engineLocation() / Path("nova2.py")).toString(),
|
||||
m_usedPlugins.join(','),
|
||||
m_category
|
||||
};
|
||||
|
|
|
@ -52,30 +52,31 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
void clearPythonCache(const QString &path)
|
||||
void clearPythonCache(const Path &path)
|
||||
{
|
||||
// remove python cache artifacts in `path` and subdirs
|
||||
|
||||
QStringList dirs = {path};
|
||||
QDirIterator iter {path, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
PathList dirs = {path};
|
||||
QDirIterator iter {path.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
while (iter.hasNext())
|
||||
dirs += iter.next();
|
||||
dirs += Path(iter.next());
|
||||
|
||||
for (const QString &dir : asConst(dirs))
|
||||
for (const Path &dir : asConst(dirs))
|
||||
{
|
||||
// python 3: remove "__pycache__" folders
|
||||
if (dir.endsWith("/__pycache__"))
|
||||
if (dir.filename() == QLatin1String("__pycache__"))
|
||||
{
|
||||
Utils::Fs::removeDirRecursive(dir);
|
||||
Utils::Fs::removeDirRecursively(dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
// python 2: remove "*.pyc" files
|
||||
const QStringList files = QDir(dir).entryList(QDir::Files);
|
||||
const QStringList files = QDir(dir.data()).entryList(QDir::Files);
|
||||
for (const QString &file : files)
|
||||
{
|
||||
if (file.endsWith(".pyc"))
|
||||
Utils::Fs::forceRemove(file);
|
||||
const Path path {file};
|
||||
if (path.hasExtension(QLatin1String(".pyc")))
|
||||
Utils::Fs::removeFile(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,21 +211,22 @@ void SearchPluginManager::installPlugin(const QString &source)
|
|||
}
|
||||
else
|
||||
{
|
||||
QString path = source;
|
||||
if (path.startsWith("file:", Qt::CaseInsensitive))
|
||||
path = QUrl(path).toLocalFile();
|
||||
const Path path {source.startsWith("file:", Qt::CaseInsensitive) ? QUrl(source).toLocalFile() : source};
|
||||
|
||||
QString pluginName = Utils::Fs::fileName(path);
|
||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf('.'));
|
||||
|
||||
if (!path.endsWith(".py", Qt::CaseInsensitive))
|
||||
emit pluginInstallationFailed(pluginName, tr("Unknown search engine plugin file format."));
|
||||
else
|
||||
QString pluginName = path.filename();
|
||||
if (pluginName.endsWith(".py", Qt::CaseInsensitive))
|
||||
{
|
||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf('.'));
|
||||
installPlugin_impl(pluginName, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit pluginInstallationFailed(pluginName, tr("Unknown search engine plugin file format."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchPluginManager::installPlugin_impl(const QString &name, const QString &path)
|
||||
void SearchPluginManager::installPlugin_impl(const QString &name, const Path &path)
|
||||
{
|
||||
const PluginVersion newVersion = getPluginVersion(path);
|
||||
const PluginInfo *plugin = pluginInfo(name);
|
||||
|
@ -236,30 +238,31 @@ void SearchPluginManager::installPlugin_impl(const QString &name, const QString
|
|||
}
|
||||
|
||||
// Process with install
|
||||
const QString destPath = pluginPath(name);
|
||||
const Path destPath = pluginPath(name);
|
||||
const Path backupPath = destPath + ".bak";
|
||||
bool updated = false;
|
||||
if (QFile::exists(destPath))
|
||||
if (destPath.exists())
|
||||
{
|
||||
// Backup in case install fails
|
||||
QFile::copy(destPath, destPath + ".bak");
|
||||
Utils::Fs::forceRemove(destPath);
|
||||
Utils::Fs::copyFile(destPath, backupPath);
|
||||
Utils::Fs::removeFile(destPath);
|
||||
updated = true;
|
||||
}
|
||||
// Copy the plugin
|
||||
QFile::copy(path, destPath);
|
||||
Utils::Fs::copyFile(path, destPath);
|
||||
// Update supported plugins
|
||||
update();
|
||||
// Check if this was correctly installed
|
||||
if (!m_plugins.contains(name))
|
||||
{
|
||||
// Remove broken file
|
||||
Utils::Fs::forceRemove(destPath);
|
||||
Utils::Fs::removeFile(destPath);
|
||||
LogMsg(tr("Plugin %1 is not supported.").arg(name), Log::INFO);
|
||||
if (updated)
|
||||
{
|
||||
// restore backup
|
||||
QFile::copy(destPath + ".bak", destPath);
|
||||
Utils::Fs::forceRemove(destPath + ".bak");
|
||||
Utils::Fs::copyFile(backupPath, destPath);
|
||||
Utils::Fs::removeFile(backupPath);
|
||||
// Update supported plugins
|
||||
update();
|
||||
emit pluginUpdateFailed(name, tr("Plugin is not supported."));
|
||||
|
@ -275,7 +278,7 @@ void SearchPluginManager::installPlugin_impl(const QString &name, const QString
|
|||
if (updated)
|
||||
{
|
||||
LogMsg(tr("Plugin %1 has been successfully updated.").arg(name), Log::INFO);
|
||||
Utils::Fs::forceRemove(destPath + ".bak");
|
||||
Utils::Fs::removeFile(backupPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,12 +288,11 @@ bool SearchPluginManager::uninstallPlugin(const QString &name)
|
|||
clearPythonCache(engineLocation());
|
||||
|
||||
// remove it from hard drive
|
||||
const QDir pluginsFolder(pluginsLocation());
|
||||
QStringList filters;
|
||||
filters << name + ".*";
|
||||
const QStringList files = pluginsFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||
const Path pluginsPath = pluginsLocation();
|
||||
const QStringList filters {name + QLatin1String(".*")};
|
||||
const QStringList files = QDir(pluginsPath.data()).entryList(filters, QDir::Files, QDir::Unsorted);
|
||||
for (const QString &file : files)
|
||||
Utils::Fs::forceRemove(pluginsFolder.absoluteFilePath(file));
|
||||
Utils::Fs::removeFile(pluginsPath / Path(file));
|
||||
// Remove it from supported engines
|
||||
delete m_plugins.take(name);
|
||||
|
||||
|
@ -301,15 +303,17 @@ bool SearchPluginManager::uninstallPlugin(const QString &name)
|
|||
void SearchPluginManager::updateIconPath(PluginInfo *const plugin)
|
||||
{
|
||||
if (!plugin) return;
|
||||
QString iconPath = QString::fromLatin1("%1/%2.png").arg(pluginsLocation(), plugin->name);
|
||||
if (QFile::exists(iconPath))
|
||||
|
||||
const Path pluginsPath = pluginsLocation();
|
||||
Path iconPath = pluginsPath / Path(plugin->name + QLatin1String(".png"));
|
||||
if (iconPath.exists())
|
||||
{
|
||||
plugin->iconPath = iconPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
iconPath = QString::fromLatin1("%1/%2.ico").arg(pluginsLocation(), plugin->name);
|
||||
if (QFile::exists(iconPath))
|
||||
iconPath = pluginsPath / Path(plugin->name + QLatin1String(".ico"));
|
||||
if (iconPath.exists())
|
||||
plugin->iconPath = iconPath;
|
||||
}
|
||||
}
|
||||
|
@ -357,20 +361,18 @@ QString SearchPluginManager::pluginFullName(const QString &pluginName)
|
|||
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
|
||||
}
|
||||
|
||||
QString SearchPluginManager::pluginsLocation()
|
||||
Path SearchPluginManager::pluginsLocation()
|
||||
{
|
||||
return QString::fromLatin1("%1/engines").arg(engineLocation());
|
||||
return (engineLocation() / Path("engines"));
|
||||
}
|
||||
|
||||
QString SearchPluginManager::engineLocation()
|
||||
Path SearchPluginManager::engineLocation()
|
||||
{
|
||||
static QString location;
|
||||
static Path location;
|
||||
if (location.isEmpty())
|
||||
{
|
||||
location = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + "/nova3");
|
||||
|
||||
const QDir locationDir(location);
|
||||
locationDir.mkpath(locationDir.absolutePath());
|
||||
location = specialFolderLocation(SpecialFolder::Data) / Path("nova3");
|
||||
Utils::Fs::mkpath(location);
|
||||
}
|
||||
|
||||
return location;
|
||||
|
@ -388,12 +390,12 @@ void SearchPluginManager::pluginDownloadFinished(const Net::DownloadResult &resu
|
|||
{
|
||||
if (result.status == Net::DownloadStatus::Success)
|
||||
{
|
||||
const QString filePath = Utils::Fs::toUniformPath(result.filePath);
|
||||
const Path filePath = result.filePath;
|
||||
|
||||
QString pluginName = Utils::Fs::fileName(result.url);
|
||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf('.')); // Remove extension
|
||||
installPlugin_impl(pluginName, filePath);
|
||||
Utils::Fs::forceRemove(filePath);
|
||||
Path pluginPath {QUrl(result.url).path()};
|
||||
pluginPath.removeExtension(); // Remove extension
|
||||
installPlugin_impl(pluginPath.filename(), filePath);
|
||||
Utils::Fs::removeFile(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -412,37 +414,37 @@ void SearchPluginManager::pluginDownloadFinished(const Net::DownloadResult &resu
|
|||
void SearchPluginManager::updateNova()
|
||||
{
|
||||
// create nova directory if necessary
|
||||
const QDir searchDir(engineLocation());
|
||||
const Path enginePath = engineLocation();
|
||||
|
||||
QFile packageFile(searchDir.absoluteFilePath("__init__.py"));
|
||||
QFile packageFile {(enginePath / Path("__init__.py")).data()};
|
||||
packageFile.open(QIODevice::WriteOnly);
|
||||
packageFile.close();
|
||||
|
||||
searchDir.mkdir("engines");
|
||||
Utils::Fs::mkdir(enginePath / Path("engines"));
|
||||
|
||||
QFile packageFile2(searchDir.absolutePath() + "/engines/__init__.py");
|
||||
QFile packageFile2 {(enginePath / Path("engines/__init__.py")).data()};
|
||||
packageFile2.open(QIODevice::WriteOnly);
|
||||
packageFile2.close();
|
||||
|
||||
// Copy search plugin files (if necessary)
|
||||
const auto updateFile = [](const QString &filename, const bool compareVersion)
|
||||
const auto updateFile = [&enginePath](const Path &filename, const bool compareVersion)
|
||||
{
|
||||
const QString filePathBundled = ":/searchengine/nova3/" + filename;
|
||||
const QString filePathDisk = QDir(engineLocation()).absoluteFilePath(filename);
|
||||
const Path filePathBundled = Path(":/searchengine/nova3") / filename;
|
||||
const Path filePathDisk = enginePath / filename;
|
||||
|
||||
if (compareVersion && (getPluginVersion(filePathBundled) <= getPluginVersion(filePathDisk)))
|
||||
return;
|
||||
|
||||
Utils::Fs::forceRemove(filePathDisk);
|
||||
QFile::copy(filePathBundled, filePathDisk);
|
||||
Utils::Fs::removeFile(filePathDisk);
|
||||
Utils::Fs::copyFile(filePathBundled, filePathDisk);
|
||||
};
|
||||
|
||||
updateFile("helpers.py", true);
|
||||
updateFile("nova2.py", true);
|
||||
updateFile("nova2dl.py", true);
|
||||
updateFile("novaprinter.py", true);
|
||||
updateFile("sgmllib3.py", false);
|
||||
updateFile("socks.py", false);
|
||||
updateFile(Path("helpers.py"), true);
|
||||
updateFile(Path("nova2.py"), true);
|
||||
updateFile(Path("nova2dl.py"), true);
|
||||
updateFile(Path("novaprinter.py"), true);
|
||||
updateFile(Path("sgmllib3.py"), false);
|
||||
updateFile(Path("socks.py"), false);
|
||||
}
|
||||
|
||||
void SearchPluginManager::update()
|
||||
|
@ -450,7 +452,7 @@ void SearchPluginManager::update()
|
|||
QProcess nova;
|
||||
nova.setProcessEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
|
||||
const QStringList params {Utils::Fs::toNativePath(engineLocation() + "/nova2.py"), "--capabilities"};
|
||||
const QStringList params {(engineLocation() / Path("/nova2.py")).toString(), QLatin1String("--capabilities")};
|
||||
nova.start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly);
|
||||
nova.waitForFinished();
|
||||
|
||||
|
@ -559,14 +561,14 @@ bool SearchPluginManager::isUpdateNeeded(const QString &pluginName, const Plugin
|
|||
return (newVersion > oldVersion);
|
||||
}
|
||||
|
||||
QString SearchPluginManager::pluginPath(const QString &name)
|
||||
Path SearchPluginManager::pluginPath(const QString &name)
|
||||
{
|
||||
return QString::fromLatin1("%1/%2.py").arg(pluginsLocation(), name);
|
||||
return (pluginsLocation() / Path(name + QLatin1String(".py")));
|
||||
}
|
||||
|
||||
PluginVersion SearchPluginManager::getPluginVersion(const QString &filePath)
|
||||
PluginVersion SearchPluginManager::getPluginVersion(const Path &filePath)
|
||||
{
|
||||
QFile pluginFile(filePath);
|
||||
QFile pluginFile {filePath.data()};
|
||||
if (!pluginFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return {};
|
||||
|
||||
|
@ -581,7 +583,7 @@ PluginVersion SearchPluginManager::getPluginVersion(const QString &filePath)
|
|||
return version;
|
||||
|
||||
LogMsg(tr("Search plugin '%1' contains invalid version string ('%2')")
|
||||
.arg(Utils::Fs::fileName(filePath), versionStr), Log::MsgType::WARNING);
|
||||
.arg(filePath.filename(), versionStr), Log::MsgType::WARNING);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/utils/version.h"
|
||||
|
||||
using PluginVersion = Utils::Version<unsigned short, 2>;
|
||||
|
@ -50,7 +51,7 @@ struct PluginInfo
|
|||
QString fullName;
|
||||
QString url;
|
||||
QStringList supportedCategories;
|
||||
QString iconPath;
|
||||
Path iconPath;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
|
@ -85,11 +86,11 @@ public:
|
|||
SearchHandler *startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
|
||||
SearchDownloadHandler *downloadTorrent(const QString &siteUrl, const QString &url);
|
||||
|
||||
static PluginVersion getPluginVersion(const QString &filePath);
|
||||
static PluginVersion getPluginVersion(const Path &filePath);
|
||||
static QString categoryFullName(const QString &categoryName);
|
||||
QString pluginFullName(const QString &pluginName);
|
||||
static QString pluginsLocation();
|
||||
static QString engineLocation();
|
||||
static Path pluginsLocation();
|
||||
static Path engineLocation();
|
||||
|
||||
signals:
|
||||
void pluginEnabled(const QString &name, bool enabled);
|
||||
|
@ -106,13 +107,13 @@ private:
|
|||
void update();
|
||||
void updateNova();
|
||||
void parseVersionInfo(const QByteArray &info);
|
||||
void installPlugin_impl(const QString &name, const QString &path);
|
||||
void installPlugin_impl(const QString &name, const Path &path);
|
||||
bool isUpdateNeeded(const QString &pluginName, PluginVersion newVersion) const;
|
||||
|
||||
void versionInfoDownloadFinished(const Net::DownloadResult &result);
|
||||
void pluginDownloadFinished(const Net::DownloadResult &result);
|
||||
|
||||
static QString pluginPath(const QString &name);
|
||||
static Path pluginPath(const QString &name);
|
||||
|
||||
static QPointer<SearchPluginManager> m_instance;
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "global.h"
|
||||
#include "logger.h"
|
||||
#include "path.h"
|
||||
#include "profile.h"
|
||||
#include "utils/fs.h"
|
||||
|
||||
|
@ -59,8 +60,8 @@ namespace
|
|||
// there is no other way to get that name except
|
||||
// actually create a QSettings object.
|
||||
// if serialization operation was not successful we return empty string
|
||||
QString deserialize(const QString &name, QVariantHash &data) const;
|
||||
QString serialize(const QString &name, const QVariantHash &data) const;
|
||||
Path deserialize(const QString &name, QVariantHash &data) const;
|
||||
Path serialize(const QString &name, const QVariantHash &data) const;
|
||||
|
||||
const QString m_name;
|
||||
};
|
||||
|
@ -156,7 +157,7 @@ QVariantHash TransactionalSettings::read() const
|
|||
{
|
||||
QVariantHash res;
|
||||
|
||||
const QString newPath = deserialize(m_name + QLatin1String("_new"), res);
|
||||
const Path newPath = deserialize(m_name + QLatin1String("_new"), res);
|
||||
if (!newPath.isEmpty())
|
||||
{ // "_new" file is NOT empty
|
||||
// This means that the PC closed either due to power outage
|
||||
|
@ -164,15 +165,16 @@ QVariantHash TransactionalSettings::read() const
|
|||
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
|
||||
// contains the most recent settings.
|
||||
Logger::instance()->addMessage(QObject::tr("Detected unclean program exit. Using fallback file to restore settings: %1")
|
||||
.arg(Utils::Fs::toNativePath(newPath))
|
||||
.arg(newPath.toString())
|
||||
, Log::WARNING);
|
||||
|
||||
QString finalPath = newPath;
|
||||
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
finalPath.remove(index, 4);
|
||||
QString finalPathStr = newPath.data();
|
||||
const int index = finalPathStr.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
finalPathStr.remove(index, 4);
|
||||
|
||||
Utils::Fs::forceRemove(finalPath);
|
||||
QFile::rename(newPath, finalPath);
|
||||
const Path finalPath {finalPathStr};
|
||||
Utils::Fs::removeFile(finalPath);
|
||||
Utils::Fs::renameFile(newPath, finalPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -189,22 +191,23 @@ bool TransactionalSettings::write(const QVariantHash &data) const
|
|||
// between deleting the file and recreating it. This is a safety measure.
|
||||
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
|
||||
// replace qBittorrent.ini/qBittorrent.conf with it.
|
||||
const QString newPath = serialize(m_name + QLatin1String("_new"), data);
|
||||
const Path newPath = serialize(m_name + QLatin1String("_new"), data);
|
||||
if (newPath.isEmpty())
|
||||
{
|
||||
Utils::Fs::forceRemove(newPath);
|
||||
Utils::Fs::removeFile(newPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString finalPath = newPath;
|
||||
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
finalPath.remove(index, 4);
|
||||
QString finalPathStr = newPath.data();
|
||||
const int index = finalPathStr.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
finalPathStr.remove(index, 4);
|
||||
|
||||
Utils::Fs::forceRemove(finalPath);
|
||||
return QFile::rename(newPath, finalPath);
|
||||
const Path finalPath {finalPathStr};
|
||||
Utils::Fs::removeFile(finalPath);
|
||||
return Utils::Fs::renameFile(newPath, finalPath);
|
||||
}
|
||||
|
||||
QString TransactionalSettings::deserialize(const QString &name, QVariantHash &data) const
|
||||
Path TransactionalSettings::deserialize(const QString &name, QVariantHash &data) const
|
||||
{
|
||||
SettingsPtr settings = Profile::instance()->applicationSettings(name);
|
||||
|
||||
|
@ -221,10 +224,10 @@ QString TransactionalSettings::deserialize(const QString &name, QVariantHash &da
|
|||
data[key] = value;
|
||||
}
|
||||
|
||||
return settings->fileName();
|
||||
return Path(settings->fileName());
|
||||
}
|
||||
|
||||
QString TransactionalSettings::serialize(const QString &name, const QVariantHash &data) const
|
||||
Path TransactionalSettings::serialize(const QString &name, const QVariantHash &data) const
|
||||
{
|
||||
SettingsPtr settings = Profile::instance()->applicationSettings(name);
|
||||
for (auto i = data.begin(); i != data.end(); ++i)
|
||||
|
@ -235,7 +238,7 @@ QString TransactionalSettings::serialize(const QString &name, const QVariantHash
|
|||
switch (settings->status())
|
||||
{
|
||||
case QSettings::NoError:
|
||||
return settings->fileName();
|
||||
return Path(settings->fileName());
|
||||
case QSettings::AccessError:
|
||||
Logger::instance()->addMessage(QObject::tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
break;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <QTimer>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include "path.h"
|
||||
#include "utils/string.h"
|
||||
|
||||
template <typename T>
|
||||
|
@ -68,6 +69,11 @@ public:
|
|||
const typename T::Int value = loadValue(key, static_cast<typename T::Int>(defaultValue));
|
||||
return T {value};
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, Path>)
|
||||
{
|
||||
const auto value = loadValue<QString>(key, defaultValue.toString());
|
||||
return Path(value);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, QVariant>)
|
||||
{
|
||||
// fast path for loading QVariant
|
||||
|
@ -88,8 +94,10 @@ public:
|
|||
storeValueImpl(key, Utils::String::fromEnum(value));
|
||||
else if constexpr (IsQFlags<T>::value)
|
||||
storeValueImpl(key, static_cast<typename T::Int>(value));
|
||||
else if constexpr (std::is_same_v<T, Path>)
|
||||
storeValueImpl(key, value.toString());
|
||||
else
|
||||
storeValueImpl(key, value);
|
||||
storeValueImpl(key, QVariant::fromValue(value));
|
||||
}
|
||||
|
||||
void removeValue(const QString &key);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include "settingvalue.h"
|
||||
#include "utils/fs.h"
|
||||
|
||||
FileGuard::FileGuard(const QString &path)
|
||||
FileGuard::FileGuard(const Path &path)
|
||||
: m_path {path}
|
||||
, m_remove {true}
|
||||
{
|
||||
|
@ -45,17 +45,17 @@ void FileGuard::setAutoRemove(const bool remove) noexcept
|
|||
FileGuard::~FileGuard()
|
||||
{
|
||||
if (m_remove && !m_path.isEmpty())
|
||||
Utils::Fs::forceRemove(m_path); // forceRemove() checks for file existence
|
||||
Utils::Fs::removeFile(m_path); // removeFile() checks for file existence
|
||||
}
|
||||
|
||||
TorrentFileGuard::TorrentFileGuard(const QString &path, const TorrentFileGuard::AutoDeleteMode mode)
|
||||
: FileGuard {mode != Never ? path : QString()}
|
||||
TorrentFileGuard::TorrentFileGuard(const Path &path, const TorrentFileGuard::AutoDeleteMode mode)
|
||||
: FileGuard {mode != Never ? path : Path()}
|
||||
, m_mode {mode}
|
||||
, m_wasAdded {false}
|
||||
{
|
||||
}
|
||||
|
||||
TorrentFileGuard::TorrentFileGuard(const QString &path)
|
||||
TorrentFileGuard::TorrentFileGuard(const Path &path)
|
||||
: TorrentFileGuard {path, autoDeleteMode()}
|
||||
{
|
||||
}
|
||||
|
|
|
@ -31,20 +31,22 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
template <typename T> class SettingValue;
|
||||
|
||||
/// Utility class to defer file deletion
|
||||
class FileGuard
|
||||
{
|
||||
public:
|
||||
explicit FileGuard(const QString &path = {});
|
||||
explicit FileGuard(const Path &path = {});
|
||||
~FileGuard();
|
||||
|
||||
/// Cancels or re-enables deferred file deletion
|
||||
void setAutoRemove(bool remove) noexcept;
|
||||
|
||||
private:
|
||||
QString m_path;
|
||||
Path m_path;
|
||||
bool m_remove;
|
||||
};
|
||||
|
||||
|
@ -55,7 +57,7 @@ class TorrentFileGuard : private FileGuard
|
|||
Q_GADGET
|
||||
|
||||
public:
|
||||
explicit TorrentFileGuard(const QString &path = {});
|
||||
explicit TorrentFileGuard(const Path &path = {});
|
||||
~TorrentFileGuard();
|
||||
|
||||
/// marks the torrent file as loaded (added) into the BitTorrent::Session
|
||||
|
@ -74,7 +76,7 @@ public:
|
|||
static void setAutoDeleteMode(AutoDeleteMode mode);
|
||||
|
||||
private:
|
||||
TorrentFileGuard(const QString &path, AutoDeleteMode mode);
|
||||
TorrentFileGuard(const Path &path, AutoDeleteMode mode);
|
||||
static SettingValue<AutoDeleteMode> &autoDeleteModeSetting();
|
||||
|
||||
Q_ENUM(AutoDeleteMode)
|
||||
|
|
|
@ -137,9 +137,9 @@ namespace
|
|||
BitTorrent::AddTorrentParams params;
|
||||
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
||||
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
|
||||
params.savePath = jsonObj.value(PARAM_SAVEPATH).toString();
|
||||
params.savePath = Path(jsonObj.value(PARAM_SAVEPATH).toString());
|
||||
params.useDownloadPath = getOptionalBool(jsonObj, PARAM_USEDOWNLOADPATH);
|
||||
params.downloadPath = jsonObj.value(PARAM_DOWNLOADPATH).toString();
|
||||
params.downloadPath = Path(jsonObj.value(PARAM_DOWNLOADPATH).toString());
|
||||
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
||||
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
||||
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
||||
|
@ -158,8 +158,8 @@ namespace
|
|||
QJsonObject jsonObj {
|
||||
{PARAM_CATEGORY, params.category},
|
||||
{PARAM_TAGS, serializeTagSet(params.tags)},
|
||||
{PARAM_SAVEPATH, params.savePath},
|
||||
{PARAM_DOWNLOADPATH, params.downloadPath},
|
||||
{PARAM_SAVEPATH, params.savePath.data()},
|
||||
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||
|
@ -208,8 +208,8 @@ public:
|
|||
Worker();
|
||||
|
||||
public slots:
|
||||
void setWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void removeWatchedFolder(const QString &path);
|
||||
void setWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void removeWatchedFolder(const Path &path);
|
||||
|
||||
signals:
|
||||
void magnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
|
||||
|
@ -217,21 +217,21 @@ signals:
|
|||
|
||||
private:
|
||||
void onTimeout();
|
||||
void scheduleWatchedFolderProcessing(const QString &path);
|
||||
void processWatchedFolder(const QString &path);
|
||||
void processFolder(const QString &path, const QString &watchedFolderPath, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void scheduleWatchedFolderProcessing(const Path &path);
|
||||
void processWatchedFolder(const Path &path);
|
||||
void processFolder(const Path &path, const Path &watchedFolderPath, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void processFailedTorrents();
|
||||
void addWatchedFolder(const QString &watchedFolderID, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void updateWatchedFolder(const QString &watchedFolderID, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void addWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void updateWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
|
||||
QFileSystemWatcher *m_watcher = nullptr;
|
||||
QTimer *m_watchTimer = nullptr;
|
||||
QHash<QString, TorrentFilesWatcher::WatchedFolderOptions> m_watchedFolders;
|
||||
QSet<QString> m_watchedByTimeoutFolders;
|
||||
QHash<Path, TorrentFilesWatcher::WatchedFolderOptions> m_watchedFolders;
|
||||
QSet<Path> m_watchedByTimeoutFolders;
|
||||
|
||||
// Failed torrents
|
||||
QTimer *m_retryTorrentTimer = nullptr;
|
||||
QHash<QString, QHash<QString, int>> m_failedTorrents;
|
||||
QHash<Path, QHash<Path, int>> m_failedTorrents;
|
||||
};
|
||||
|
||||
TorrentFilesWatcher *TorrentFilesWatcher::m_instance = nullptr;
|
||||
|
@ -274,20 +274,9 @@ TorrentFilesWatcher::~TorrentFilesWatcher()
|
|||
delete m_asyncWorker;
|
||||
}
|
||||
|
||||
QString TorrentFilesWatcher::makeCleanPath(const QString &path)
|
||||
{
|
||||
if (path.isEmpty())
|
||||
throw InvalidArgument(tr("Watched folder path cannot be empty."));
|
||||
|
||||
if (QDir::isRelativePath(path))
|
||||
throw InvalidArgument(tr("Watched folder path cannot be relative."));
|
||||
|
||||
return QDir::cleanPath(path);
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::load()
|
||||
{
|
||||
QFile confFile {QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CONF_FILE_NAME)};
|
||||
QFile confFile {(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FILE_NAME)).data()};
|
||||
if (!confFile.exists())
|
||||
{
|
||||
loadLegacy();
|
||||
|
@ -320,7 +309,7 @@ void TorrentFilesWatcher::load()
|
|||
const QJsonObject jsonObj = jsonDoc.object();
|
||||
for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it)
|
||||
{
|
||||
const QString &watchedFolder = it.key();
|
||||
const Path watchedFolder {it.key()};
|
||||
const WatchedFolderOptions options = parseWatchedFolderOptions(it.value().toObject());
|
||||
try
|
||||
{
|
||||
|
@ -337,13 +326,13 @@ void TorrentFilesWatcher::loadLegacy()
|
|||
{
|
||||
const auto dirs = SettingsStorage::instance()->loadValue<QVariantHash>("Preferences/Downloads/ScanDirsV2");
|
||||
|
||||
for (auto i = dirs.cbegin(); i != dirs.cend(); ++i)
|
||||
for (auto it = dirs.cbegin(); it != dirs.cend(); ++it)
|
||||
{
|
||||
const QString watchedFolder = i.key();
|
||||
const Path watchedFolder {it.key()};
|
||||
BitTorrent::AddTorrentParams params;
|
||||
if (i.value().type() == QVariant::Int)
|
||||
if (it.value().type() == QVariant::Int)
|
||||
{
|
||||
if (i.value().toInt() == 0)
|
||||
if (it.value().toInt() == 0)
|
||||
{
|
||||
params.savePath = watchedFolder;
|
||||
params.useAutoTMM = false;
|
||||
|
@ -351,7 +340,7 @@ void TorrentFilesWatcher::loadLegacy()
|
|||
}
|
||||
else
|
||||
{
|
||||
const QString customSavePath = i.value().toString();
|
||||
const Path customSavePath {it.value().toString()};
|
||||
params.savePath = customSavePath;
|
||||
params.useAutoTMM = false;
|
||||
}
|
||||
|
@ -375,56 +364,60 @@ void TorrentFilesWatcher::store() const
|
|||
QJsonObject jsonObj;
|
||||
for (auto it = m_watchedFolders.cbegin(); it != m_watchedFolders.cend(); ++it)
|
||||
{
|
||||
const QString &watchedFolder = it.key();
|
||||
const Path &watchedFolder = it.key();
|
||||
const WatchedFolderOptions &options = it.value();
|
||||
jsonObj[watchedFolder] = serializeWatchedFolderOptions(options);
|
||||
jsonObj[watchedFolder.data()] = serializeWatchedFolderOptions(options);
|
||||
}
|
||||
|
||||
const QString path = QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CONF_FILE_NAME);
|
||||
const Path path = specialFolderLocation(SpecialFolder::Config) / Path(CONF_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 Watched Folders configuration to %1. Error: %2")
|
||||
.arg(path, result.error()), Log::WARNING);
|
||||
.arg(path.toString(), result.error()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
QHash<QString, TorrentFilesWatcher::WatchedFolderOptions> TorrentFilesWatcher::folders() const
|
||||
QHash<Path, TorrentFilesWatcher::WatchedFolderOptions> TorrentFilesWatcher::folders() const
|
||||
{
|
||||
return m_watchedFolders;
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::setWatchedFolder(const QString &path, const WatchedFolderOptions &options)
|
||||
void TorrentFilesWatcher::setWatchedFolder(const Path &path, const WatchedFolderOptions &options)
|
||||
{
|
||||
doSetWatchedFolder(path, options);
|
||||
store();
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::doSetWatchedFolder(const QString &path, const WatchedFolderOptions &options)
|
||||
void TorrentFilesWatcher::doSetWatchedFolder(const Path &path, const WatchedFolderOptions &options)
|
||||
{
|
||||
const QString cleanPath = makeCleanPath(path);
|
||||
m_watchedFolders[cleanPath] = options;
|
||||
if (path.isEmpty())
|
||||
throw InvalidArgument(tr("Watched folder Path cannot be empty."));
|
||||
|
||||
if (path.isRelative())
|
||||
throw InvalidArgument(tr("Watched folder Path cannot be relative."));
|
||||
|
||||
m_watchedFolders[path] = options;
|
||||
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, path, options]()
|
||||
{
|
||||
m_asyncWorker->setWatchedFolder(path, options);
|
||||
});
|
||||
|
||||
emit watchedFolderSet(cleanPath, options);
|
||||
emit watchedFolderSet(path, options);
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::removeWatchedFolder(const QString &path)
|
||||
void TorrentFilesWatcher::removeWatchedFolder(const Path &path)
|
||||
{
|
||||
const QString cleanPath = makeCleanPath(path);
|
||||
if (m_watchedFolders.remove(cleanPath))
|
||||
if (m_watchedFolders.remove(path))
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, cleanPath]()
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, path]()
|
||||
{
|
||||
m_asyncWorker->removeWatchedFolder(cleanPath);
|
||||
m_asyncWorker->removeWatchedFolder(path);
|
||||
});
|
||||
|
||||
emit watchedFolderRemoved(cleanPath);
|
||||
emit watchedFolderRemoved(path);
|
||||
|
||||
store();
|
||||
}
|
||||
|
@ -447,7 +440,10 @@ TorrentFilesWatcher::Worker::Worker()
|
|||
, m_watchTimer {new QTimer(this)}
|
||||
, m_retryTorrentTimer {new QTimer(this)}
|
||||
{
|
||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &Worker::scheduleWatchedFolderProcessing);
|
||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &path)
|
||||
{
|
||||
scheduleWatchedFolderProcessing(Path(path));
|
||||
});
|
||||
connect(m_watchTimer, &QTimer::timeout, this, &Worker::onTimeout);
|
||||
|
||||
connect(m_retryTorrentTimer, &QTimer::timeout, this, &Worker::processFailedTorrents);
|
||||
|
@ -455,11 +451,11 @@ TorrentFilesWatcher::Worker::Worker()
|
|||
|
||||
void TorrentFilesWatcher::Worker::onTimeout()
|
||||
{
|
||||
for (const QString &path : asConst(m_watchedByTimeoutFolders))
|
||||
for (const Path &path : asConst(m_watchedByTimeoutFolders))
|
||||
processWatchedFolder(path);
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::setWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
void TorrentFilesWatcher::Worker::setWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
{
|
||||
if (m_watchedFolders.contains(path))
|
||||
updateWatchedFolder(path, options);
|
||||
|
@ -467,11 +463,11 @@ void TorrentFilesWatcher::Worker::setWatchedFolder(const QString &path, const To
|
|||
addWatchedFolder(path, options);
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::removeWatchedFolder(const QString &path)
|
||||
void TorrentFilesWatcher::Worker::removeWatchedFolder(const Path &path)
|
||||
{
|
||||
m_watchedFolders.remove(path);
|
||||
|
||||
m_watcher->removePath(path);
|
||||
m_watcher->removePath(path.data());
|
||||
m_watchedByTimeoutFolders.remove(path);
|
||||
if (m_watchedByTimeoutFolders.isEmpty())
|
||||
m_watchTimer->stop();
|
||||
|
@ -481,7 +477,7 @@ void TorrentFilesWatcher::Worker::removeWatchedFolder(const QString &path)
|
|||
m_retryTorrentTimer->stop();
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const QString &path)
|
||||
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const Path &path)
|
||||
{
|
||||
QTimer::singleShot(2000, this, [this, path]()
|
||||
{
|
||||
|
@ -489,7 +485,7 @@ void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const QString
|
|||
});
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::processWatchedFolder(const QString &path)
|
||||
void TorrentFilesWatcher::Worker::processWatchedFolder(const Path &path)
|
||||
{
|
||||
const TorrentFilesWatcher::WatchedFolderOptions options = m_watchedFolders.value(path);
|
||||
processFolder(path, path, options);
|
||||
|
@ -498,34 +494,32 @@ void TorrentFilesWatcher::Worker::processWatchedFolder(const QString &path)
|
|||
m_retryTorrentTimer->start(WATCH_INTERVAL);
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QString &watchedFolderPath
|
||||
void TorrentFilesWatcher::Worker::processFolder(const Path &path, const Path &watchedFolderPath
|
||||
, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
{
|
||||
const QDir watchedDir {watchedFolderPath};
|
||||
|
||||
QDirIterator dirIter {path, {"*.torrent", "*.magnet"}, QDir::Files};
|
||||
QDirIterator dirIter {path.data(), {"*.torrent", "*.magnet"}, QDir::Files};
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
const QString filePath = dirIter.next();
|
||||
const Path filePath {dirIter.next()};
|
||||
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
||||
if (path != watchedFolderPath)
|
||||
{
|
||||
const QString subdirPath = watchedDir.relativeFilePath(path);
|
||||
const Path subdirPath = watchedFolderPath.relativePathOf(path);
|
||||
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
|
||||
if (useAutoTMM)
|
||||
{
|
||||
addTorrentParams.category = addTorrentParams.category.isEmpty()
|
||||
? subdirPath : (addTorrentParams.category + QLatin1Char('/') + subdirPath);
|
||||
? subdirPath.data() : (addTorrentParams.category + QLatin1Char('/') + subdirPath.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
addTorrentParams.savePath = QDir::cleanPath(QDir(addTorrentParams.savePath).filePath(subdirPath));
|
||||
addTorrentParams.savePath = addTorrentParams.savePath / subdirPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (filePath.endsWith(QLatin1String(".magnet"), Qt::CaseInsensitive))
|
||||
if (filePath.hasExtension(QLatin1String(".magnet")))
|
||||
{
|
||||
QFile file {filePath};
|
||||
QFile file {filePath.data()};
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
QTextStream str {&file};
|
||||
|
@ -533,7 +527,7 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
|||
emit magnetFound(BitTorrent::MagnetUri(str.readLine()), addTorrentParams);
|
||||
|
||||
file.close();
|
||||
Utils::Fs::forceRemove(filePath);
|
||||
Utils::Fs::removeFile(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -546,7 +540,7 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
|||
if (result)
|
||||
{
|
||||
emit torrentFound(result.value(), addTorrentParams);
|
||||
Utils::Fs::forceRemove(filePath);
|
||||
Utils::Fs::removeFile(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -560,10 +554,10 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
|||
|
||||
if (options.recursive)
|
||||
{
|
||||
QDirIterator dirIter {path, (QDir::Dirs | QDir::NoDot | QDir::NoDotDot)};
|
||||
QDirIterator dirIter {path.data(), (QDir::Dirs | QDir::NoDot | QDir::NoDotDot)};
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
const QString folderPath = dirIter.next();
|
||||
const Path folderPath {dirIter.next()};
|
||||
// Skip processing of subdirectory that is explicitly set as watched folder
|
||||
if (!m_watchedFolders.contains(folderPath))
|
||||
processFolder(folderPath, watchedFolderPath, options);
|
||||
|
@ -574,45 +568,43 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
|||
void TorrentFilesWatcher::Worker::processFailedTorrents()
|
||||
{
|
||||
// Check which torrents are still partial
|
||||
Algorithm::removeIf(m_failedTorrents, [this](const QString &watchedFolderPath, QHash<QString, int> &partialTorrents)
|
||||
Algorithm::removeIf(m_failedTorrents, [this](const Path &watchedFolderPath, QHash<Path, int> &partialTorrents)
|
||||
{
|
||||
const QDir dir {watchedFolderPath};
|
||||
const TorrentFilesWatcher::WatchedFolderOptions options = m_watchedFolders.value(watchedFolderPath);
|
||||
Algorithm::removeIf(partialTorrents, [this, &dir, &options](const QString &torrentPath, int &value)
|
||||
Algorithm::removeIf(partialTorrents, [this, &watchedFolderPath, &options](const Path &torrentPath, int &value)
|
||||
{
|
||||
if (!QFile::exists(torrentPath))
|
||||
if (!torrentPath.exists())
|
||||
return true;
|
||||
|
||||
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(torrentPath);
|
||||
if (result)
|
||||
{
|
||||
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
||||
const QString exactDirPath = QFileInfo(torrentPath).canonicalPath();
|
||||
if (exactDirPath != dir.path())
|
||||
if (torrentPath != watchedFolderPath)
|
||||
{
|
||||
const QString subdirPath = dir.relativeFilePath(exactDirPath);
|
||||
const Path subdirPath = watchedFolderPath.relativePathOf(torrentPath);
|
||||
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
|
||||
if (useAutoTMM)
|
||||
{
|
||||
addTorrentParams.category = addTorrentParams.category.isEmpty()
|
||||
? subdirPath : (addTorrentParams.category + QLatin1Char('/') + subdirPath);
|
||||
? subdirPath.data() : (addTorrentParams.category + QLatin1Char('/') + subdirPath.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
addTorrentParams.savePath = QDir(addTorrentParams.savePath).filePath(subdirPath);
|
||||
addTorrentParams.savePath = addTorrentParams.savePath / subdirPath;
|
||||
}
|
||||
}
|
||||
|
||||
emit torrentFound(result.value(), addTorrentParams);
|
||||
Utils::Fs::forceRemove(torrentPath);
|
||||
Utils::Fs::removeFile(torrentPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value >= MAX_FAILED_RETRIES)
|
||||
{
|
||||
LogMsg(tr("Rejecting failed torrent file: %1").arg(torrentPath));
|
||||
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
|
||||
LogMsg(tr("Rejecting failed torrent file: %1").arg(torrentPath.toString()));
|
||||
Utils::Fs::renameFile(torrentPath, (torrentPath + ".qbt_rejected"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -633,14 +625,10 @@ void TorrentFilesWatcher::Worker::processFailedTorrents()
|
|||
m_retryTorrentTimer->start(WATCH_INTERVAL);
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::addWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
void TorrentFilesWatcher::Worker::addWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
{
|
||||
#if !defined Q_OS_HAIKU
|
||||
// Check if the path points to a network file system or not
|
||||
// Check if the `path` points to a network file system or not
|
||||
if (Utils::Fs::isNetworkFileSystem(path) || options.recursive)
|
||||
#else
|
||||
if (options.recursive)
|
||||
#endif
|
||||
{
|
||||
m_watchedByTimeoutFolders.insert(path);
|
||||
if (!m_watchTimer->isActive())
|
||||
|
@ -648,27 +636,23 @@ void TorrentFilesWatcher::Worker::addWatchedFolder(const QString &path, const To
|
|||
}
|
||||
else
|
||||
{
|
||||
m_watcher->addPath(path);
|
||||
m_watcher->addPath(path.data());
|
||||
scheduleWatchedFolderProcessing(path);
|
||||
}
|
||||
|
||||
m_watchedFolders[path] = options;
|
||||
|
||||
LogMsg(tr("Watching folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
|
||||
LogMsg(tr("Watching folder: \"%1\"").arg(path.toString()));
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::updateWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
void TorrentFilesWatcher::Worker::updateWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
{
|
||||
const bool recursiveModeChanged = (m_watchedFolders[path].recursive != options.recursive);
|
||||
#if !defined Q_OS_HAIKU
|
||||
if (recursiveModeChanged && !Utils::Fs::isNetworkFileSystem(path))
|
||||
#else
|
||||
if (recursiveModeChanged)
|
||||
#endif
|
||||
{
|
||||
if (options.recursive)
|
||||
{
|
||||
m_watcher->removePath(path);
|
||||
m_watcher->removePath(path.data());
|
||||
|
||||
m_watchedByTimeoutFolders.insert(path);
|
||||
if (!m_watchTimer->isActive())
|
||||
|
@ -680,7 +664,7 @@ void TorrentFilesWatcher::Worker::updateWatchedFolder(const QString &path, const
|
|||
if (m_watchedByTimeoutFolders.isEmpty())
|
||||
m_watchTimer->stop();
|
||||
|
||||
m_watcher->addPath(path);
|
||||
m_watcher->addPath(path.data());
|
||||
scheduleWatchedFolderProcessing(path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <QHash>
|
||||
|
||||
#include "base/bittorrent/addtorrentparams.h"
|
||||
#include "base/path.h"
|
||||
|
||||
class QThread;
|
||||
|
||||
|
@ -61,15 +62,13 @@ public:
|
|||
static void freeInstance();
|
||||
static TorrentFilesWatcher *instance();
|
||||
|
||||
static QString makeCleanPath(const QString &path);
|
||||
|
||||
QHash<QString, WatchedFolderOptions> folders() const;
|
||||
void setWatchedFolder(const QString &path, const WatchedFolderOptions &options);
|
||||
void removeWatchedFolder(const QString &path);
|
||||
QHash<Path, WatchedFolderOptions> folders() const;
|
||||
void setWatchedFolder(const Path &path, const WatchedFolderOptions &options);
|
||||
void removeWatchedFolder(const Path &path);
|
||||
|
||||
signals:
|
||||
void watchedFolderSet(const QString &path, const WatchedFolderOptions &options);
|
||||
void watchedFolderRemoved(const QString &path);
|
||||
void watchedFolderSet(const Path &path, const WatchedFolderOptions &options);
|
||||
void watchedFolderRemoved(const Path &path);
|
||||
|
||||
private slots:
|
||||
void onMagnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
|
||||
|
@ -83,11 +82,11 @@ private:
|
|||
void loadLegacy();
|
||||
void store() const;
|
||||
|
||||
void doSetWatchedFolder(const QString &path, const WatchedFolderOptions &options);
|
||||
void doSetWatchedFolder(const Path &path, const WatchedFolderOptions &options);
|
||||
|
||||
static TorrentFilesWatcher *m_instance;
|
||||
|
||||
QHash<QString, WatchedFolderOptions> m_watchedFolders;
|
||||
QHash<Path, WatchedFolderOptions> m_watchedFolders;
|
||||
|
||||
QThread *m_ioThread = nullptr;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -50,57 +51,17 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMimeDatabase>
|
||||
#include <QStorageInfo>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "base/global.h"
|
||||
|
||||
QString Utils::Fs::toNativePath(const QString &path)
|
||||
{
|
||||
return QDir::toNativeSeparators(path);
|
||||
}
|
||||
|
||||
QString Utils::Fs::toUniformPath(const QString &path)
|
||||
{
|
||||
return QDir::fromNativeSeparators(path);
|
||||
}
|
||||
|
||||
QString Utils::Fs::resolvePath(const QString &relativePath, const QString &basePath)
|
||||
{
|
||||
Q_ASSERT(QDir::isRelativePath(relativePath));
|
||||
Q_ASSERT(QDir::isAbsolutePath(basePath));
|
||||
|
||||
return (relativePath.isEmpty() ? basePath : QDir(basePath).absoluteFilePath(relativePath));
|
||||
}
|
||||
|
||||
QString Utils::Fs::fileExtension(const QString &filename)
|
||||
{
|
||||
return QMimeDatabase().suffixForFileName(filename);
|
||||
}
|
||||
|
||||
QString Utils::Fs::fileName(const QString &filePath)
|
||||
{
|
||||
const QString path = toUniformPath(filePath);
|
||||
const int slashIndex = path.lastIndexOf('/');
|
||||
if (slashIndex == -1)
|
||||
return path;
|
||||
return path.mid(slashIndex + 1);
|
||||
}
|
||||
|
||||
QString Utils::Fs::folderName(const QString &filePath)
|
||||
{
|
||||
const QString path = toUniformPath(filePath);
|
||||
const int slashIndex = path.lastIndexOf('/');
|
||||
if (slashIndex == -1)
|
||||
return {};
|
||||
return path.left(slashIndex);
|
||||
}
|
||||
#include "base/path.h"
|
||||
|
||||
/**
|
||||
* This function will first check if there are only system cache files, e.g. `Thumbs.db`,
|
||||
|
@ -111,9 +72,9 @@ QString Utils::Fs::folderName(const QString &filePath)
|
|||
* that only the above mentioned "useless" files exist but before the whole folder is removed.
|
||||
* In this case, the folder will not be removed but the "useless" files will be deleted.
|
||||
*/
|
||||
bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
|
||||
bool Utils::Fs::smartRemoveEmptyFolderTree(const Path &path)
|
||||
{
|
||||
if (path.isEmpty() || !QDir(path).exists())
|
||||
if (!path.exists())
|
||||
return true;
|
||||
|
||||
const QStringList deleteFilesList =
|
||||
|
@ -128,8 +89,8 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
|
|||
};
|
||||
|
||||
// travel from the deepest folder and remove anything unwanted on the way out.
|
||||
QStringList dirList(path + '/'); // get all sub directories paths
|
||||
QDirIterator iter(path, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
|
||||
QStringList dirList(path.data() + '/'); // get all sub directories paths
|
||||
QDirIterator iter {path.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
while (iter.hasNext())
|
||||
dirList << iter.next() + '/';
|
||||
// sort descending by directory depth
|
||||
|
@ -138,7 +99,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
|
|||
|
||||
for (const QString &p : asConst(dirList))
|
||||
{
|
||||
const QDir dir(p);
|
||||
const QDir dir {p};
|
||||
// A deeper folder may have not been removed in the previous iteration
|
||||
// so don't remove anything from this folder either.
|
||||
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||
|
@ -156,38 +117,22 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
|
|||
continue;
|
||||
|
||||
for (const QString &f : tmpFileList)
|
||||
forceRemove(p + f);
|
||||
removeFile(Path(p + f));
|
||||
|
||||
// remove directory if empty
|
||||
dir.rmdir(p);
|
||||
}
|
||||
|
||||
return QDir(path).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the file with the given filePath.
|
||||
*
|
||||
* This function will try to fix the file permissions before removing it.
|
||||
*/
|
||||
bool Utils::Fs::forceRemove(const QString &filePath)
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.exists())
|
||||
return true;
|
||||
// Make sure we have read/write permissions
|
||||
f.setPermissions(f.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
|
||||
// Remove the file
|
||||
return f.remove();
|
||||
return path.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes directory and its content recursively.
|
||||
*/
|
||||
void Utils::Fs::removeDirRecursive(const QString &path)
|
||||
void Utils::Fs::removeDirRecursively(const Path &path)
|
||||
{
|
||||
if (!path.isEmpty())
|
||||
QDir(path).removeRecursively();
|
||||
QDir(path.data()).removeRecursively();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,16 +141,16 @@ void Utils::Fs::removeDirRecursive(const QString &path)
|
|||
*
|
||||
* Returns -1 in case of error.
|
||||
*/
|
||||
qint64 Utils::Fs::computePathSize(const QString &path)
|
||||
qint64 Utils::Fs::computePathSize(const Path &path)
|
||||
{
|
||||
// Check if it is a file
|
||||
const QFileInfo fi(path);
|
||||
const QFileInfo fi {path.data()};
|
||||
if (!fi.exists()) return -1;
|
||||
if (fi.isFile()) return fi.size();
|
||||
|
||||
// Compute folder size based on its content
|
||||
qint64 size = 0;
|
||||
QDirIterator iter(path, QDir::Files | QDir::Hidden | QDir::NoSymLinks, QDirIterator::Subdirectories);
|
||||
QDirIterator iter {path.data(), QDir::Files | QDir::Hidden | QDir::NoSymLinks, QDirIterator::Subdirectories};
|
||||
while (iter.hasNext())
|
||||
{
|
||||
iter.next();
|
||||
|
@ -217,9 +162,10 @@ qint64 Utils::Fs::computePathSize(const QString &path)
|
|||
/**
|
||||
* Makes deep comparison of two files to make sure they are identical.
|
||||
*/
|
||||
bool Utils::Fs::sameFiles(const QString &path1, const QString &path2)
|
||||
bool Utils::Fs::sameFiles(const Path &path1, const Path &path2)
|
||||
{
|
||||
QFile f1(path1), f2(path2);
|
||||
QFile f1 {path1.data()};
|
||||
QFile f2 {path2.data()};
|
||||
if (!f1.exists() || !f2.exists()) return false;
|
||||
if (f1.size() != f2.size()) return false;
|
||||
if (!f1.open(QIODevice::ReadOnly)) return false;
|
||||
|
@ -234,120 +180,65 @@ bool Utils::Fs::sameFiles(const QString &path1, const QString &path2)
|
|||
return true;
|
||||
}
|
||||
|
||||
QString Utils::Fs::toValidFileSystemName(const QString &name, const bool allowSeparators, const QString &pad)
|
||||
QString Utils::Fs::toValidFileName(const QString &name, const QString &pad)
|
||||
{
|
||||
const QRegularExpression regex(allowSeparators ? "[:?\"*<>|]+" : "[\\\\/:?\"*<>|]+");
|
||||
const QRegularExpression regex {QLatin1String("[\\\\/:?\"*<>|]+")};
|
||||
|
||||
QString validName = name.trimmed();
|
||||
validName.replace(regex, pad);
|
||||
qDebug() << "toValidFileSystemName:" << name << "=>" << validName;
|
||||
|
||||
return validName;
|
||||
}
|
||||
|
||||
bool Utils::Fs::isValidFileSystemName(const QString &name, const bool allowSeparators)
|
||||
Path Utils::Fs::toValidPath(const QString &name, const QString &pad)
|
||||
{
|
||||
if (name.isEmpty()) return false;
|
||||
const QRegularExpression regex {QLatin1String("[:?\"*<>|]+")};
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const QRegularExpression regex
|
||||
{allowSeparators
|
||||
? QLatin1String("[:?\"*<>|]")
|
||||
: QLatin1String("[\\\\/:?\"*<>|]")};
|
||||
#elif defined(Q_OS_MACOS)
|
||||
const QRegularExpression regex
|
||||
{allowSeparators
|
||||
? QLatin1String("[\\0:]")
|
||||
: QLatin1String("[\\0/:]")};
|
||||
#else
|
||||
const QRegularExpression regex
|
||||
{allowSeparators
|
||||
? QLatin1String("[\\0]")
|
||||
: QLatin1String("[\\0/]")};
|
||||
#endif
|
||||
return !name.contains(regex);
|
||||
QString validPathStr = name;
|
||||
validPathStr.replace(regex, pad);
|
||||
|
||||
return Path(validPathStr);
|
||||
}
|
||||
|
||||
qint64 Utils::Fs::freeDiskSpaceOnPath(const QString &path)
|
||||
qint64 Utils::Fs::freeDiskSpaceOnPath(const Path &path)
|
||||
{
|
||||
return QStorageInfo(path).bytesAvailable();
|
||||
return QStorageInfo(path.data()).bytesAvailable();
|
||||
}
|
||||
|
||||
QString Utils::Fs::branchPath(const QString &filePath, QString *removed)
|
||||
Path Utils::Fs::tempPath()
|
||||
{
|
||||
QString ret = toUniformPath(filePath);
|
||||
if (ret.endsWith('/'))
|
||||
ret.chop(1);
|
||||
const int slashIndex = ret.lastIndexOf('/');
|
||||
if (slashIndex >= 0)
|
||||
{
|
||||
if (removed)
|
||||
*removed = ret.mid(slashIndex + 1);
|
||||
ret = ret.left(slashIndex);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Utils::Fs::sameFileNames(const QString &first, const QString &second)
|
||||
{
|
||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||
return QString::compare(first, second, Qt::CaseSensitive) == 0;
|
||||
#else
|
||||
return QString::compare(first, second, Qt::CaseInsensitive) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString Utils::Fs::expandPath(const QString &path)
|
||||
{
|
||||
const QString ret = path.trimmed();
|
||||
if (ret.isEmpty())
|
||||
return ret;
|
||||
|
||||
return QDir::cleanPath(ret);
|
||||
}
|
||||
|
||||
QString Utils::Fs::expandPathAbs(const QString &path)
|
||||
{
|
||||
return QDir(expandPath(path)).absolutePath();
|
||||
}
|
||||
|
||||
QString Utils::Fs::tempPath()
|
||||
{
|
||||
static const QString path = QDir::tempPath() + "/.qBittorrent/";
|
||||
QDir().mkdir(path);
|
||||
static const Path path = Path(QDir::tempPath()) / Path(".qBittorrent");
|
||||
mkdir(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
bool Utils::Fs::isRegularFile(const QString &path)
|
||||
bool Utils::Fs::isRegularFile(const Path &path)
|
||||
{
|
||||
struct ::stat st;
|
||||
if (::stat(path.toUtf8().constData(), &st) != 0)
|
||||
if (::stat(path.toString().toUtf8().constData(), &st) != 0)
|
||||
{
|
||||
// analyse erno and log the error
|
||||
const auto err = errno;
|
||||
qDebug("Could not get file stats for path '%s'. Error: %s"
|
||||
, qUtf8Printable(path), qUtf8Printable(strerror(err)));
|
||||
, qUtf8Printable(path.toString()), qUtf8Printable(strerror(err)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return (st.st_mode & S_IFMT) == S_IFREG;
|
||||
}
|
||||
|
||||
#if !defined Q_OS_HAIKU
|
||||
bool Utils::Fs::isNetworkFileSystem(const QString &path)
|
||||
bool Utils::Fs::isNetworkFileSystem(const Path &path)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
const std::wstring pathW {path.toStdWString()};
|
||||
auto volumePath = std::make_unique<wchar_t[]>(path.length() + 1);
|
||||
if (!::GetVolumePathNameW(pathW.c_str(), volumePath.get(), (path.length() + 1)))
|
||||
#if defined Q_OS_HAIKU
|
||||
return false;
|
||||
#elif defined(Q_OS_WIN)
|
||||
const std::wstring pathW = path.toString().toStdWString();
|
||||
auto volumePath = std::make_unique<wchar_t[]>(pathW.length() + 1);
|
||||
if (!::GetVolumePathNameW(pathW.c_str(), volumePath.get(), static_cast<DWORD>(pathW.length() + 1)))
|
||||
return false;
|
||||
return (::GetDriveTypeW(volumePath.get()) == DRIVE_REMOTE);
|
||||
#else
|
||||
QString file = path;
|
||||
if (!file.endsWith('/'))
|
||||
file += '/';
|
||||
file += '.';
|
||||
|
||||
const QString file = path.toString() + QLatin1String("/.");
|
||||
struct statfs buf {};
|
||||
if (statfs(file.toLocal8Bit().constData(), &buf) != 0)
|
||||
return false;
|
||||
|
@ -398,41 +289,77 @@ bool Utils::Fs::isNetworkFileSystem(const QString &path)
|
|||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif // Q_OS_HAIKU
|
||||
|
||||
QString Utils::Fs::findRootFolder(const QStringList &filePaths)
|
||||
bool Utils::Fs::copyFile(const Path &from, const Path &to)
|
||||
{
|
||||
QString rootFolder;
|
||||
for (const QString &filePath : filePaths)
|
||||
{
|
||||
const auto filePathElements = QStringView(filePath).split(u'/');
|
||||
// if at least one file has no root folder, no common root folder exists
|
||||
if (filePathElements.count() <= 1)
|
||||
return {};
|
||||
|
||||
if (rootFolder.isEmpty())
|
||||
rootFolder = filePathElements.at(0).toString();
|
||||
else if (rootFolder != filePathElements.at(0))
|
||||
return {};
|
||||
}
|
||||
|
||||
return rootFolder;
|
||||
return QFile::copy(from.data(), to.data());
|
||||
}
|
||||
|
||||
void Utils::Fs::stripRootFolder(QStringList &filePaths)
|
||||
bool Utils::Fs::renameFile(const Path &from, const Path &to)
|
||||
{
|
||||
const QString commonRootFolder = findRootFolder(filePaths);
|
||||
if (commonRootFolder.isEmpty())
|
||||
return;
|
||||
|
||||
for (QString &filePath : filePaths)
|
||||
filePath = filePath.mid(commonRootFolder.size() + 1);
|
||||
return QFile::rename(from.data(), to.data());
|
||||
}
|
||||
|
||||
void Utils::Fs::addRootFolder(QStringList &filePaths, const QString &rootFolder)
|
||||
/**
|
||||
* Removes the file with the given filePath.
|
||||
*
|
||||
* This function will try to fix the file permissions before removing it.
|
||||
*/
|
||||
bool Utils::Fs::removeFile(const Path &path)
|
||||
{
|
||||
Q_ASSERT(!rootFolder.isEmpty());
|
||||
if (QFile::remove(path.data()))
|
||||
return true;
|
||||
|
||||
for (QString &filePath : filePaths)
|
||||
filePath = rootFolder + QLatin1Char('/') + filePath;
|
||||
QFile file {path.data()};
|
||||
if (!file.exists())
|
||||
return true;
|
||||
|
||||
// Make sure we have read/write permissions
|
||||
file.setPermissions(file.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
|
||||
return file.remove();
|
||||
}
|
||||
|
||||
bool Utils::Fs::isReadable(const Path &path)
|
||||
{
|
||||
return QFileInfo(path.data()).isReadable();
|
||||
}
|
||||
|
||||
bool Utils::Fs::isWritable(const Path &path)
|
||||
{
|
||||
return QFileInfo(path.data()).isWritable();
|
||||
}
|
||||
|
||||
QDateTime Utils::Fs::lastModified(const Path &path)
|
||||
{
|
||||
return QFileInfo(path.data()).lastModified();
|
||||
}
|
||||
|
||||
bool Utils::Fs::isDir(const Path &path)
|
||||
{
|
||||
return QFileInfo(path.data()).isDir();
|
||||
}
|
||||
|
||||
Path Utils::Fs::toCanonicalPath(const Path &path)
|
||||
{
|
||||
return Path(QFileInfo(path.data()).canonicalFilePath());
|
||||
}
|
||||
|
||||
Path Utils::Fs::homePath()
|
||||
{
|
||||
return Path(QDir::homePath());
|
||||
}
|
||||
|
||||
bool Utils::Fs::mkdir(const Path &dirPath)
|
||||
{
|
||||
return QDir().mkdir(dirPath.data());
|
||||
}
|
||||
|
||||
bool Utils::Fs::mkpath(const Path &dirPath)
|
||||
{
|
||||
return QDir().mkpath(dirPath.data());
|
||||
}
|
||||
|
||||
bool Utils::Fs::rmdir(const Path &dirPath)
|
||||
{
|
||||
return QDir().rmdir(dirPath.data());
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -34,58 +35,36 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QDateTime;
|
||||
|
||||
namespace Utils::Fs
|
||||
{
|
||||
/**
|
||||
* Converts a path to a string suitable for display.
|
||||
* This function makes sure the directory separator used is consistent
|
||||
* with the OS being run.
|
||||
*/
|
||||
QString toNativePath(const QString &path);
|
||||
qint64 computePathSize(const Path &path);
|
||||
qint64 freeDiskSpaceOnPath(const Path &path);
|
||||
|
||||
/**
|
||||
* Converts a path to a string suitable for processing.
|
||||
* This function makes sure the directory separator used is independent
|
||||
* from the OS being run so it is the same on all supported platforms.
|
||||
* Slash ('/') is used as "uniform" directory separator.
|
||||
*/
|
||||
QString toUniformPath(const QString &path);
|
||||
bool isRegularFile(const Path &path);
|
||||
bool isDir(const Path &path);
|
||||
bool isReadable(const Path &path);
|
||||
bool isWritable(const Path &path);
|
||||
bool isNetworkFileSystem(const Path &path);
|
||||
QDateTime lastModified(const Path &path);
|
||||
bool sameFiles(const Path &path1, const Path &path2);
|
||||
|
||||
/**
|
||||
* If `path is relative then resolves it against `basePath`, otherwise returns the `path` itself
|
||||
*/
|
||||
QString resolvePath(const QString &relativePath, const QString &basePath);
|
||||
QString toValidFileName(const QString &name, const QString &pad = QLatin1String(" "));
|
||||
Path toValidPath(const QString &name, const QString &pad = QLatin1String(" "));
|
||||
Path toCanonicalPath(const Path &path);
|
||||
|
||||
/**
|
||||
* Returns the file extension part of a file name.
|
||||
*/
|
||||
QString fileExtension(const QString &filename);
|
||||
bool copyFile(const Path &from, const Path &to);
|
||||
bool renameFile(const Path &from, const Path &to);
|
||||
bool removeFile(const Path &path);
|
||||
bool mkdir(const Path &dirPath);
|
||||
bool mkpath(const Path &dirPath);
|
||||
bool rmdir(const Path &dirPath);
|
||||
void removeDirRecursively(const Path &path);
|
||||
bool smartRemoveEmptyFolderTree(const Path &path);
|
||||
|
||||
QString fileName(const QString &filePath);
|
||||
QString folderName(const QString &filePath);
|
||||
qint64 computePathSize(const QString &path);
|
||||
bool sameFiles(const QString &path1, const QString &path2);
|
||||
QString toValidFileSystemName(const QString &name, bool allowSeparators = false
|
||||
, const QString &pad = QLatin1String(" "));
|
||||
bool isValidFileSystemName(const QString &name, bool allowSeparators = false);
|
||||
qint64 freeDiskSpaceOnPath(const QString &path);
|
||||
QString branchPath(const QString &filePath, QString *removed = nullptr);
|
||||
bool sameFileNames(const QString &first, const QString &second);
|
||||
QString expandPath(const QString &path);
|
||||
QString expandPathAbs(const QString &path);
|
||||
bool isRegularFile(const QString &path);
|
||||
|
||||
bool smartRemoveEmptyFolderTree(const QString &path);
|
||||
bool forceRemove(const QString &filePath);
|
||||
void removeDirRecursive(const QString &path);
|
||||
|
||||
QString tempPath();
|
||||
|
||||
QString findRootFolder(const QStringList &filePaths);
|
||||
void stripRootFolder(QStringList &filePaths);
|
||||
void addRootFolder(QStringList &filePaths, const QString &name);
|
||||
|
||||
#if !defined Q_OS_HAIKU
|
||||
bool isNetworkFileSystem(const QString &path);
|
||||
#endif
|
||||
Path homePath();
|
||||
Path tempPath();
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include <QSaveFile>
|
||||
#include <QString>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
Utils::IO::FileDeviceOutputIterator::FileDeviceOutputIterator(QFileDevice &device, const int bufferSize)
|
||||
: m_device {&device}
|
||||
, m_buffer {std::make_shared<QByteArray>()}
|
||||
|
@ -66,17 +68,17 @@ Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operat
|
|||
return *this;
|
||||
}
|
||||
|
||||
nonstd::expected<void, QString> Utils::IO::saveToFile(const QString &path, const QByteArray &data)
|
||||
nonstd::expected<void, QString> Utils::IO::saveToFile(const Path &path, const QByteArray &data)
|
||||
{
|
||||
QSaveFile file {path};
|
||||
QSaveFile file {path.data()};
|
||||
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.flush() || !file.commit())
|
||||
return nonstd::make_unexpected(file.errorString());
|
||||
return {};
|
||||
}
|
||||
|
||||
nonstd::expected<void, QString> Utils::IO::saveToFile(const QString &path, const lt::entry &data)
|
||||
nonstd::expected<void, QString> Utils::IO::saveToFile(const Path &path, const lt::entry &data)
|
||||
{
|
||||
QSaveFile file {path};
|
||||
QSaveFile file {path.data()};
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return nonstd::make_unexpected(file.errorString());
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <libtorrent/fwd.hpp>
|
||||
|
||||
#include "base/3rdparty/expected.hpp"
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QByteArray;
|
||||
class QFileDevice;
|
||||
|
@ -80,6 +81,6 @@ namespace Utils::IO
|
|||
int m_bufferSize;
|
||||
};
|
||||
|
||||
nonstd::expected<void, QString> saveToFile(const QString &path, const QByteArray &data);
|
||||
nonstd::expected<void, QString> saveToFile(const QString &path, const lt::entry &data);
|
||||
nonstd::expected<void, QString> saveToFile(const Path &path, const QByteArray &data);
|
||||
nonstd::expected<void, QString> saveToFile(const Path &path, const lt::entry &data);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include <QDBusInterface>
|
||||
#endif
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/types.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "base/utils/fs.h"
|
||||
|
@ -292,9 +293,9 @@ qlonglong Utils::Misc::sizeInBytes(qreal size, const Utils::Misc::SizeUnit unit)
|
|||
return size;
|
||||
}
|
||||
|
||||
bool Utils::Misc::isPreviewable(const QString &filename)
|
||||
bool Utils::Misc::isPreviewable(const Path &filePath)
|
||||
{
|
||||
const QString mime = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension).name();
|
||||
const QString mime = QMimeDatabase().mimeTypeForFile(filePath.data(), QMimeDatabase::MatchExtension).name();
|
||||
|
||||
if (mime.startsWith(QLatin1String("audio"), Qt::CaseInsensitive)
|
||||
|| mime.startsWith(QLatin1String("video"), Qt::CaseInsensitive))
|
||||
|
@ -304,50 +305,50 @@ bool Utils::Misc::isPreviewable(const QString &filename)
|
|||
|
||||
const QSet<QString> multimediaExtensions =
|
||||
{
|
||||
"3GP",
|
||||
"AAC",
|
||||
"AC3",
|
||||
"AIF",
|
||||
"AIFC",
|
||||
"AIFF",
|
||||
"ASF",
|
||||
"AU",
|
||||
"AVI",
|
||||
"FLAC",
|
||||
"FLV",
|
||||
"M3U",
|
||||
"M4A",
|
||||
"M4P",
|
||||
"M4V",
|
||||
"MID",
|
||||
"MKV",
|
||||
"MOV",
|
||||
"MP2",
|
||||
"MP3",
|
||||
"MP4",
|
||||
"MPC",
|
||||
"MPE",
|
||||
"MPEG",
|
||||
"MPG",
|
||||
"MPP",
|
||||
"OGG",
|
||||
"OGM",
|
||||
"OGV",
|
||||
"QT",
|
||||
"RA",
|
||||
"RAM",
|
||||
"RM",
|
||||
"RMV",
|
||||
"RMVB",
|
||||
"SWA",
|
||||
"SWF",
|
||||
"TS",
|
||||
"VOB",
|
||||
"WAV",
|
||||
"WMA",
|
||||
"WMV"
|
||||
".3GP",
|
||||
".AAC",
|
||||
".AC3",
|
||||
".AIF",
|
||||
".AIFC",
|
||||
".AIFF",
|
||||
".ASF",
|
||||
".AU",
|
||||
".AVI",
|
||||
".FLAC",
|
||||
".FLV",
|
||||
".M3U",
|
||||
".M4A",
|
||||
".M4P",
|
||||
".M4V",
|
||||
".MID",
|
||||
".MKV",
|
||||
".MOV",
|
||||
".MP2",
|
||||
".MP3",
|
||||
".MP4",
|
||||
".MPC",
|
||||
".MPE",
|
||||
".MPEG",
|
||||
".MPG",
|
||||
".MPP",
|
||||
".OGG",
|
||||
".OGM",
|
||||
".OGV",
|
||||
".QT",
|
||||
".RA",
|
||||
".RAM",
|
||||
".RM",
|
||||
".RMV",
|
||||
".RMVB",
|
||||
".SWA",
|
||||
".SWF",
|
||||
".TS",
|
||||
".VOB",
|
||||
".WAV",
|
||||
".WMA",
|
||||
".WMV"
|
||||
};
|
||||
return multimediaExtensions.contains(Utils::Fs::fileExtension(filename).toUpper());
|
||||
return multimediaExtensions.contains(filePath.extension().toUpper());
|
||||
}
|
||||
|
||||
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap)
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
enum class ShutdownDialogAction;
|
||||
|
||||
/* Miscellaneous functions that can be useful */
|
||||
|
@ -77,7 +79,7 @@ namespace Utils::Misc
|
|||
int friendlyUnitPrecision(SizeUnit unit);
|
||||
qint64 sizeInBytes(qreal size, SizeUnit unit);
|
||||
|
||||
bool isPreviewable(const QString &filename);
|
||||
bool isPreviewable(const Path &filePath);
|
||||
|
||||
// Take a number of seconds and return a user-friendly
|
||||
// time duration like "1d 2h 10m".
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <QFile>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "base/version.h"
|
||||
|
@ -70,7 +71,7 @@ AboutDialog::AboutDialog(QWidget *parent)
|
|||
, tr("Bug Tracker:"));
|
||||
m_ui->labelAbout->setText(aboutText);
|
||||
|
||||
m_ui->labelMascot->setPixmap(Utils::Gui::scaledPixmap(":/icons/mascot.png", this));
|
||||
m_ui->labelMascot->setPixmap(Utils::Gui::scaledPixmap(Path(":/icons/mascot.png"), this));
|
||||
|
||||
// Thanks
|
||||
QFile thanksfile(":/thanks.html");
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace
|
|||
class FileStorageAdaptor final : public BitTorrent::AbstractFileStorage
|
||||
{
|
||||
public:
|
||||
FileStorageAdaptor(const BitTorrent::TorrentInfo &torrentInfo, QStringList &filePaths)
|
||||
FileStorageAdaptor(const BitTorrent::TorrentInfo &torrentInfo, PathList &filePaths)
|
||||
: m_torrentInfo {torrentInfo}
|
||||
, m_filePaths {filePaths}
|
||||
{
|
||||
|
@ -99,16 +99,16 @@ namespace
|
|||
return m_torrentInfo.fileSize(index);
|
||||
}
|
||||
|
||||
QString filePath(const int index) const override
|
||||
Path filePath(const int index) const override
|
||||
{
|
||||
Q_ASSERT((index >= 0) && (index < filesCount()));
|
||||
return (m_filePaths.isEmpty() ? m_torrentInfo.filePath(index) : m_filePaths.at(index));
|
||||
}
|
||||
|
||||
void renameFile(const int index, const QString &newFilePath) override
|
||||
void renameFile(const int index, const Path &newFilePath) override
|
||||
{
|
||||
Q_ASSERT((index >= 0) && (index < filesCount()));
|
||||
const QString currentFilePath = filePath(index);
|
||||
const Path currentFilePath = filePath(index);
|
||||
if (currentFilePath == newFilePath)
|
||||
return;
|
||||
|
||||
|
@ -120,22 +120,21 @@ namespace
|
|||
|
||||
private:
|
||||
const BitTorrent::TorrentInfo &m_torrentInfo;
|
||||
QStringList &m_filePaths;
|
||||
PathList &m_filePaths;
|
||||
};
|
||||
|
||||
// savePath is a folder, not an absolute file path
|
||||
int indexOfPath(const FileSystemPathComboEdit *fsPathEdit, const QString &savePath)
|
||||
int indexOfPath(const FileSystemPathComboEdit *fsPathEdit, const Path &savePath)
|
||||
{
|
||||
const QDir saveDir {savePath};
|
||||
for (int i = 0; i < fsPathEdit->count(); ++i)
|
||||
{
|
||||
if (QDir(fsPathEdit->item(i)) == saveDir)
|
||||
if (fsPathEdit->item(i) == savePath)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void setPath(FileSystemPathComboEdit *fsPathEdit, const QString &newPath)
|
||||
void setPath(FileSystemPathComboEdit *fsPathEdit, const Path &newPath)
|
||||
{
|
||||
int existingIndex = indexOfPath(fsPathEdit, newPath);
|
||||
if (existingIndex < 0)
|
||||
|
@ -148,16 +147,18 @@ namespace
|
|||
fsPathEdit->setCurrentIndex(existingIndex);
|
||||
}
|
||||
|
||||
void updatePathHistory(const QString &settingsKey, const QString &path, const int maxLength)
|
||||
void updatePathHistory(const QString &settingsKey, const Path &path, const int maxLength)
|
||||
{
|
||||
// Add last used save path to the front of history
|
||||
|
||||
auto pathList = settings()->loadValue<QStringList>(settingsKey);
|
||||
const int selectedSavePathIndex = pathList.indexOf(path);
|
||||
|
||||
const int selectedSavePathIndex = pathList.indexOf(path.toString());
|
||||
if (selectedSavePathIndex > -1)
|
||||
pathList.move(selectedSavePathIndex, 0);
|
||||
else
|
||||
pathList.prepend(path);
|
||||
pathList.prepend(path.toString());
|
||||
|
||||
settings()->storeValue(settingsKey, QStringList(pathList.mid(0, maxLength)));
|
||||
}
|
||||
}
|
||||
|
@ -325,7 +326,7 @@ void AddNewTorrentDialog::show(const QString &source, const BitTorrent::AddTorre
|
|||
return;
|
||||
}
|
||||
|
||||
const BitTorrent::MagnetUri magnetUri(source);
|
||||
const BitTorrent::MagnetUri magnetUri {source};
|
||||
const bool isLoaded = magnetUri.isValid()
|
||||
? dlg->loadMagnet(magnetUri)
|
||||
: dlg->loadTorrentFile(source);
|
||||
|
@ -341,18 +342,18 @@ void AddNewTorrentDialog::show(const QString &source, QWidget *parent)
|
|||
show(source, BitTorrent::AddTorrentParams(), parent);
|
||||
}
|
||||
|
||||
bool AddNewTorrentDialog::loadTorrentFile(const QString &torrentPath)
|
||||
bool AddNewTorrentDialog::loadTorrentFile(const QString &source)
|
||||
{
|
||||
const QString decodedPath = torrentPath.startsWith("file://", Qt::CaseInsensitive)
|
||||
? QUrl::fromEncoded(torrentPath.toLocal8Bit()).toLocalFile()
|
||||
: torrentPath;
|
||||
const Path decodedPath {source.startsWith("file://", Qt::CaseInsensitive)
|
||||
? QUrl::fromEncoded(source.toLocal8Bit()).toLocalFile()
|
||||
: source};
|
||||
|
||||
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(decodedPath);
|
||||
if (!result)
|
||||
{
|
||||
RaisedMessageBox::critical(this, tr("Invalid torrent")
|
||||
, tr("Failed to load the torrent: %1.\nError: %2", "Don't remove the '\n' characters. They insert a newline.")
|
||||
.arg(Utils::Fs::toNativePath(decodedPath), result.error()));
|
||||
.arg(decodedPath.toString(), result.error()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -489,7 +490,7 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
|
|||
m_ui->labelSizeData->setText(sizeString);
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
|
||||
void AddNewTorrentDialog::onSavePathChanged(const Path &newPath)
|
||||
{
|
||||
Q_UNUSED(newPath);
|
||||
// Remember index
|
||||
|
@ -497,7 +498,7 @@ void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
|
|||
updateDiskSpaceLabel();
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::onDownloadPathChanged(const QString &newPath)
|
||||
void AddNewTorrentDialog::onDownloadPathChanged(const Path &newPath)
|
||||
{
|
||||
Q_UNUSED(newPath);
|
||||
// Remember index
|
||||
|
@ -521,11 +522,11 @@ void AddNewTorrentDialog::categoryChanged(int index)
|
|||
const auto *btSession = BitTorrent::Session::instance();
|
||||
const QString categoryName = m_ui->categoryComboBox->currentText();
|
||||
|
||||
const QString savePath = btSession->categorySavePath(categoryName);
|
||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
||||
const Path savePath = btSession->categorySavePath(categoryName);
|
||||
m_ui->savePath->setSelectedPath(savePath);
|
||||
|
||||
const QString downloadPath = btSession->categoryDownloadPath(categoryName);
|
||||
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
|
||||
const Path downloadPath = btSession->categoryDownloadPath(categoryName);
|
||||
m_ui->downloadPath->setSelectedPath(downloadPath);
|
||||
|
||||
m_ui->groupBoxDownloadPath->setChecked(!m_ui->downloadPath->selectedPath().isEmpty());
|
||||
|
||||
|
@ -545,7 +546,7 @@ void AddNewTorrentDialog::contentLayoutChanged(const int index)
|
|||
const auto contentLayout = ((index == 0)
|
||||
? BitTorrent::detectContentLayout(m_torrentInfo.filePaths())
|
||||
: static_cast<BitTorrent::TorrentContentLayout>(index));
|
||||
BitTorrent::applyContentLayout(m_torrentParams.filePaths, contentLayout, Utils::Fs::findRootFolder(m_torrentInfo.filePaths()));
|
||||
BitTorrent::applyContentLayout(m_torrentParams.filePaths, contentLayout, Path::findRootFolder(m_torrentInfo.filePaths()));
|
||||
m_contentModel->model()->setupModelData(FileStorageAdaptor(m_torrentInfo, m_torrentParams.filePaths));
|
||||
m_contentModel->model()->updateFilesPriorities(filePriorities);
|
||||
|
||||
|
@ -574,7 +575,7 @@ void AddNewTorrentDialog::saveTorrentFile()
|
|||
if (!path.endsWith(torrentFileExtension, Qt::CaseInsensitive))
|
||||
path += torrentFileExtension;
|
||||
|
||||
const nonstd::expected<void, QString> result = m_torrentInfo.saveToFile(path);
|
||||
const nonstd::expected<void, QString> result = m_torrentInfo.saveToFile(Path(path));
|
||||
if (!result)
|
||||
{
|
||||
QMessageBox::critical(this, tr("I/O Error")
|
||||
|
@ -597,7 +598,7 @@ void AddNewTorrentDialog::populateSavePaths()
|
|||
if (savePathHistory.size() > 0)
|
||||
{
|
||||
for (const QString &path : savePathHistory)
|
||||
m_ui->savePath->addItem(path);
|
||||
m_ui->savePath->addItem(Path(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -628,7 +629,7 @@ void AddNewTorrentDialog::populateSavePaths()
|
|||
if (downloadPathHistory.size() > 0)
|
||||
{
|
||||
for (const QString &path : downloadPathHistory)
|
||||
m_ui->downloadPath->addItem(path);
|
||||
m_ui->downloadPath->addItem(Path(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -806,14 +807,14 @@ void AddNewTorrentDialog::accept()
|
|||
m_torrentParams.useAutoTMM = useAutoTMM;
|
||||
if (!useAutoTMM)
|
||||
{
|
||||
const QString savePath = m_ui->savePath->selectedPath();
|
||||
const Path savePath = m_ui->savePath->selectedPath();
|
||||
m_torrentParams.savePath = savePath;
|
||||
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength());
|
||||
|
||||
m_torrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
|
||||
if (m_torrentParams.useDownloadPath)
|
||||
{
|
||||
const QString downloadPath = m_ui->downloadPath->selectedPath();
|
||||
const Path downloadPath = m_ui->downloadPath->selectedPath();
|
||||
m_torrentParams.downloadPath = downloadPath;
|
||||
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength());
|
||||
}
|
||||
|
@ -907,7 +908,7 @@ void AddNewTorrentDialog::setupTreeview()
|
|||
: static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
|
||||
if (m_torrentParams.filePaths.isEmpty())
|
||||
m_torrentParams.filePaths = m_torrentInfo.filePaths();
|
||||
BitTorrent::applyContentLayout(m_torrentParams.filePaths, contentLayout, Utils::Fs::findRootFolder(m_torrentInfo.filePaths()));
|
||||
BitTorrent::applyContentLayout(m_torrentParams.filePaths, contentLayout, Path::findRootFolder(m_torrentInfo.filePaths()));
|
||||
// List files in torrent
|
||||
m_contentModel->model()->setupModelData(FileStorageAdaptor(m_torrentInfo, m_torrentParams.filePaths));
|
||||
if (const QByteArray state = m_storeTreeHeaderState; !state.isEmpty())
|
||||
|
@ -981,12 +982,12 @@ void AddNewTorrentDialog::TMMChanged(int index)
|
|||
|
||||
m_ui->savePath->blockSignals(true);
|
||||
m_ui->savePath->clear();
|
||||
const QString savePath = session->categorySavePath(m_ui->categoryComboBox->currentText());
|
||||
const Path savePath = session->categorySavePath(m_ui->categoryComboBox->currentText());
|
||||
m_ui->savePath->addItem(savePath);
|
||||
|
||||
m_ui->downloadPath->blockSignals(true);
|
||||
m_ui->downloadPath->clear();
|
||||
const QString downloadPath = session->categoryDownloadPath(m_ui->categoryComboBox->currentText());
|
||||
const Path downloadPath = session->categoryDownloadPath(m_ui->categoryComboBox->currentText());
|
||||
m_ui->downloadPath->addItem(downloadPath);
|
||||
|
||||
m_ui->groupBoxDownloadPath->blockSignals(true);
|
||||
|
|
|
@ -81,8 +81,8 @@ private slots:
|
|||
void displayContentTreeMenu();
|
||||
void displayColumnHeaderMenu();
|
||||
void updateDiskSpaceLabel();
|
||||
void onSavePathChanged(const QString &newPath);
|
||||
void onDownloadPathChanged(const QString &newPath);
|
||||
void onSavePathChanged(const Path &newPath);
|
||||
void onDownloadPathChanged(const Path &newPath);
|
||||
void onUseDownloadPathChanged(bool checked);
|
||||
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
|
||||
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
|
||||
|
@ -97,7 +97,7 @@ private slots:
|
|||
|
||||
private:
|
||||
explicit AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
|
||||
bool loadTorrentFile(const QString &torrentPath);
|
||||
bool loadTorrentFile(const QString &source);
|
||||
bool loadTorrentImpl();
|
||||
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
|
||||
void populateSavePaths();
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "autoexpandabledialog.h"
|
||||
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "ui_autoexpandabledialog.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -58,9 +58,9 @@ QString AutoExpandableDialog::getText(QWidget *parent, const QString &title, con
|
|||
d.m_ui->textEdit->selectAll();
|
||||
if (excludeExtension)
|
||||
{
|
||||
const QString extension = Utils::Fs::fileExtension(text);
|
||||
const QString extension = Path(text).extension();
|
||||
if (!extension.isEmpty())
|
||||
d.m_ui->textEdit->setSelection(0, (text.length() - extension.length() - 1));
|
||||
d.m_ui->textEdit->setSelection(0, (text.length() - extension.length()));
|
||||
}
|
||||
|
||||
bool res = d.exec();
|
||||
|
|
|
@ -82,7 +82,7 @@ class FileSystemPathEdit::FileSystemPathEditPrivate
|
|||
QToolButton *m_browseBtn;
|
||||
QString m_fileNameFilter;
|
||||
Mode m_mode;
|
||||
QString m_lastSignaledPath;
|
||||
Path m_lastSignaledPath;
|
||||
QString m_dialogCaption;
|
||||
Private::FileSystemPathValidator *m_validator;
|
||||
};
|
||||
|
@ -112,31 +112,32 @@ void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
|
|||
{
|
||||
Q_Q(FileSystemPathEdit);
|
||||
|
||||
const QFileInfo fileInfo {q->selectedPath()};
|
||||
const QString directory = (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave)
|
||||
? fileInfo.absoluteFilePath()
|
||||
: fileInfo.absolutePath();
|
||||
QString filter = q->fileNameFilter();
|
||||
const Path currentDirectory = (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave)
|
||||
? q->selectedPath()
|
||||
: q->selectedPath().parentPath();
|
||||
const Path initialDirectory = currentDirectory.isAbsolute() ? currentDirectory : (Utils::Fs::homePath() / currentDirectory);
|
||||
|
||||
QString selectedPath;
|
||||
QString filter = q->fileNameFilter();
|
||||
QString newPath;
|
||||
switch (m_mode)
|
||||
{
|
||||
case FileSystemPathEdit::Mode::FileOpen:
|
||||
selectedPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), directory, filter);
|
||||
newPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), initialDirectory.data(), filter);
|
||||
break;
|
||||
case FileSystemPathEdit::Mode::FileSave:
|
||||
selectedPath = QFileDialog::getSaveFileName(q, dialogCaptionOrDefault(), directory, filter, &filter);
|
||||
newPath = QFileDialog::getSaveFileName(q, dialogCaptionOrDefault(), initialDirectory.data(), filter, &filter);
|
||||
break;
|
||||
case FileSystemPathEdit::Mode::DirectoryOpen:
|
||||
case FileSystemPathEdit::Mode::DirectorySave:
|
||||
selectedPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(),
|
||||
directory, QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly);
|
||||
newPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(),
|
||||
initialDirectory.data(), QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly);
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unknown FileSystemPathEdit mode");
|
||||
}
|
||||
if (!selectedPath.isEmpty())
|
||||
q->setEditWidgetText(Utils::Fs::toNativePath(selectedPath));
|
||||
|
||||
if (!newPath.isEmpty())
|
||||
q->setSelectedPath(Path(newPath));
|
||||
}
|
||||
|
||||
QString FileSystemPathEdit::FileSystemPathEditPrivate::dialogCaptionOrDefault() const
|
||||
|
@ -202,16 +203,18 @@ FileSystemPathEdit::~FileSystemPathEdit()
|
|||
delete d_ptr;
|
||||
}
|
||||
|
||||
QString FileSystemPathEdit::selectedPath() const
|
||||
Path FileSystemPathEdit::selectedPath() const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(editWidgetText());
|
||||
return Path(editWidgetText());
|
||||
}
|
||||
|
||||
void FileSystemPathEdit::setSelectedPath(const QString &val)
|
||||
void FileSystemPathEdit::setSelectedPath(const Path &val)
|
||||
{
|
||||
Q_D(FileSystemPathEdit);
|
||||
setEditWidgetText(Utils::Fs::toNativePath(val));
|
||||
d->m_editor->widget()->setToolTip(val);
|
||||
|
||||
const QString nativePath = val.toString();
|
||||
setEditWidgetText(nativePath);
|
||||
d->m_editor->widget()->setToolTip(nativePath);
|
||||
}
|
||||
|
||||
QString FileSystemPathEdit::fileNameFilter() const
|
||||
|
@ -251,13 +254,13 @@ void FileSystemPathEdit::setFileNameFilter(const QString &val)
|
|||
#endif
|
||||
}
|
||||
|
||||
QString FileSystemPathEdit::placeholder() const
|
||||
Path FileSystemPathEdit::placeholder() const
|
||||
{
|
||||
Q_D(const FileSystemPathEdit);
|
||||
return d->m_editor->placeholder();
|
||||
}
|
||||
|
||||
void FileSystemPathEdit::setPlaceholder(const QString &val)
|
||||
void FileSystemPathEdit::setPlaceholder(const Path &val)
|
||||
{
|
||||
Q_D(FileSystemPathEdit);
|
||||
d->m_editor->setPlaceholder(val);
|
||||
|
@ -278,7 +281,8 @@ void FileSystemPathEdit::setBriefBrowseButtonCaption(bool brief)
|
|||
void FileSystemPathEdit::onPathEdited()
|
||||
{
|
||||
Q_D(FileSystemPathEdit);
|
||||
QString newPath = selectedPath();
|
||||
|
||||
const Path newPath = selectedPath();
|
||||
if (newPath != d->m_lastSignaledPath)
|
||||
{
|
||||
emit selectedPathChanged(newPath);
|
||||
|
@ -360,19 +364,19 @@ int FileSystemPathComboEdit::count() const
|
|||
return editWidget<WidgetType>()->count();
|
||||
}
|
||||
|
||||
QString FileSystemPathComboEdit::item(int index) const
|
||||
Path FileSystemPathComboEdit::item(int index) const
|
||||
{
|
||||
return Utils::Fs::toUniformPath(editWidget<WidgetType>()->itemText(index));
|
||||
return Path(editWidget<WidgetType>()->itemText(index));
|
||||
}
|
||||
|
||||
void FileSystemPathComboEdit::addItem(const QString &text)
|
||||
void FileSystemPathComboEdit::addItem(const Path &path)
|
||||
{
|
||||
editWidget<WidgetType>()->addItem(Utils::Fs::toNativePath(text));
|
||||
editWidget<WidgetType>()->addItem(path.toString());
|
||||
}
|
||||
|
||||
void FileSystemPathComboEdit::insertItem(int index, const QString &text)
|
||||
void FileSystemPathComboEdit::insertItem(int index, const Path &path)
|
||||
{
|
||||
editWidget<WidgetType>()->insertItem(index, Utils::Fs::toNativePath(text));
|
||||
editWidget<WidgetType>()->insertItem(index, path.toString());
|
||||
}
|
||||
|
||||
int FileSystemPathComboEdit::currentIndex() const
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <QWidget>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace Private
|
||||
{
|
||||
class FileComboEdit;
|
||||
|
@ -45,7 +47,7 @@ class FileSystemPathEdit : public QWidget
|
|||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Mode mode READ mode WRITE setMode)
|
||||
Q_PROPERTY(QString selectedPath READ selectedPath WRITE setSelectedPath NOTIFY selectedPathChanged)
|
||||
Q_PROPERTY(Path selectedPath READ selectedPath WRITE setSelectedPath NOTIFY selectedPathChanged)
|
||||
Q_PROPERTY(QString fileNameFilter READ fileNameFilter WRITE setFileNameFilter)
|
||||
Q_PROPERTY(QString dialogCaption READ dialogCaption WRITE setDialogCaption)
|
||||
|
||||
|
@ -64,14 +66,14 @@ public:
|
|||
Mode mode() const;
|
||||
void setMode(Mode mode);
|
||||
|
||||
QString selectedPath() const;
|
||||
void setSelectedPath(const QString &val);
|
||||
Path selectedPath() const;
|
||||
void setSelectedPath(const Path &val);
|
||||
|
||||
QString fileNameFilter() const;
|
||||
void setFileNameFilter(const QString &val);
|
||||
|
||||
QString placeholder() const;
|
||||
void setPlaceholder(const QString &val);
|
||||
Path placeholder() const;
|
||||
void setPlaceholder(const Path &val);
|
||||
|
||||
/// The browse button caption is "..." if true, and "Browse" otherwise
|
||||
bool briefBrowseButtonCaption() const;
|
||||
|
@ -83,7 +85,7 @@ public:
|
|||
virtual void clear() = 0;
|
||||
|
||||
signals:
|
||||
void selectedPathChanged(const QString &path);
|
||||
void selectedPathChanged(const Path &path);
|
||||
|
||||
protected:
|
||||
explicit FileSystemPathEdit(Private::FileEditorWithCompletion *editor, QWidget *parent);
|
||||
|
@ -136,9 +138,9 @@ public:
|
|||
void clear() override;
|
||||
|
||||
int count() const;
|
||||
QString item(int index) const;
|
||||
void addItem(const QString &text);
|
||||
void insertItem(int index, const QString &text);
|
||||
Path item(int index) const;
|
||||
void addItem(const Path &path);
|
||||
void insertItem(int index, const Path &path);
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int index);
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include <QStringList>
|
||||
#include <QStyle>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
// -------------------- FileSystemPathValidator ----------------------------------------
|
||||
Private::FileSystemPathValidator::FileSystemPathValidator(QObject *parent)
|
||||
: QValidator(parent)
|
||||
|
@ -149,7 +151,7 @@ QValidator::State Private::FileSystemPathValidator::validate(const QList<QString
|
|||
const QStringView componentPath = pathComponents[i];
|
||||
if (componentPath.isEmpty()) continue;
|
||||
|
||||
m_lastTestResult = testPath(pathComponents[i], isFinalPath);
|
||||
m_lastTestResult = testPath(Path(pathComponents[i].toString()), isFinalPath);
|
||||
if (m_lastTestResult != TestResult::OK)
|
||||
{
|
||||
m_lastTestedPath = componentPath.toString();
|
||||
|
@ -161,9 +163,9 @@ QValidator::State Private::FileSystemPathValidator::validate(const QList<QString
|
|||
}
|
||||
|
||||
Private::FileSystemPathValidator::TestResult
|
||||
Private::FileSystemPathValidator::testPath(const QStringView path, bool pathIsComplete) const
|
||||
Private::FileSystemPathValidator::testPath(const Path &path, bool pathIsComplete) const
|
||||
{
|
||||
QFileInfo fi(path.toString());
|
||||
QFileInfo fi {path.data()};
|
||||
if (m_existingOnly && !fi.exists())
|
||||
return TestResult::DoesNotExist;
|
||||
|
||||
|
@ -240,14 +242,14 @@ void Private::FileLineEdit::setValidator(QValidator *validator)
|
|||
QLineEdit::setValidator(validator);
|
||||
}
|
||||
|
||||
QString Private::FileLineEdit::placeholder() const
|
||||
Path Private::FileLineEdit::placeholder() const
|
||||
{
|
||||
return placeholderText();
|
||||
return Path(placeholderText());
|
||||
}
|
||||
|
||||
void Private::FileLineEdit::setPlaceholder(const QString &val)
|
||||
void Private::FileLineEdit::setPlaceholder(const Path &val)
|
||||
{
|
||||
setPlaceholderText(val);
|
||||
setPlaceholderText(val.toString());
|
||||
}
|
||||
|
||||
QWidget *Private::FileLineEdit::widget()
|
||||
|
@ -356,14 +358,14 @@ void Private::FileComboEdit::setValidator(QValidator *validator)
|
|||
lineEdit()->setValidator(validator);
|
||||
}
|
||||
|
||||
QString Private::FileComboEdit::placeholder() const
|
||||
Path Private::FileComboEdit::placeholder() const
|
||||
{
|
||||
return lineEdit()->placeholderText();
|
||||
return Path(lineEdit()->placeholderText());
|
||||
}
|
||||
|
||||
void Private::FileComboEdit::setPlaceholder(const QString &val)
|
||||
void Private::FileComboEdit::setPlaceholder(const Path &val)
|
||||
{
|
||||
lineEdit()->setPlaceholderText(val);
|
||||
lineEdit()->setPlaceholderText(val.toString());
|
||||
}
|
||||
|
||||
void Private::FileComboEdit::setFilenameFilters(const QStringList &filters)
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#include <QtContainerFwd>
|
||||
#include <QValidator>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QAction;
|
||||
class QCompleter;
|
||||
class QContextMenuEvent;
|
||||
|
@ -84,7 +86,7 @@ namespace Private
|
|||
QValidator::State validate(const QList<QStringView> &pathComponents, bool strict,
|
||||
int firstComponentToTest, int lastComponentToTest) const;
|
||||
|
||||
TestResult testPath(QStringView path, bool pathIsComplete) const;
|
||||
TestResult testPath(const Path &path, bool pathIsComplete) const;
|
||||
|
||||
bool m_strictMode;
|
||||
bool m_existingOnly;
|
||||
|
@ -105,8 +107,8 @@ namespace Private
|
|||
virtual void setFilenameFilters(const QStringList &filters) = 0;
|
||||
virtual void setBrowseAction(QAction *action) = 0;
|
||||
virtual void setValidator(QValidator *validator) = 0;
|
||||
virtual QString placeholder() const = 0;
|
||||
virtual void setPlaceholder(const QString &val) = 0;
|
||||
virtual Path placeholder() const = 0;
|
||||
virtual void setPlaceholder(const Path &val) = 0;
|
||||
virtual QWidget *widget() = 0;
|
||||
};
|
||||
|
||||
|
@ -123,8 +125,8 @@ namespace Private
|
|||
void setFilenameFilters(const QStringList &filters) override;
|
||||
void setBrowseAction(QAction *action) override;
|
||||
void setValidator(QValidator *validator) override;
|
||||
QString placeholder() const override;
|
||||
void setPlaceholder(const QString &val) override;
|
||||
Path placeholder() const override;
|
||||
void setPlaceholder(const Path &val) override;
|
||||
QWidget *widget() override;
|
||||
|
||||
protected:
|
||||
|
@ -153,8 +155,8 @@ namespace Private
|
|||
void setFilenameFilters(const QStringList &filters) override;
|
||||
void setBrowseAction(QAction *action) override;
|
||||
void setValidator(QValidator *validator) override;
|
||||
QString placeholder() const override;
|
||||
void setPlaceholder(const QString &val) override;
|
||||
Path placeholder() const override;
|
||||
void setPlaceholder(const Path &val) override;
|
||||
QWidget *widget() override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include <objc/objc.h>
|
||||
|
||||
#include <QSet>
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QPixmap;
|
||||
class QSize;
|
||||
|
@ -41,7 +41,7 @@ namespace MacUtils
|
|||
QPixmap pixmapForExtension(const QString &ext, const QSize &size);
|
||||
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...));
|
||||
void displayNotification(const QString &title, const QString &message);
|
||||
void openFiles(const QSet<QString> &pathsList);
|
||||
void openFiles(const PathList &pathList);
|
||||
|
||||
QString badgeLabelText();
|
||||
void setBadgeLabelText(const QString &text);
|
||||
|
|
|
@ -32,9 +32,11 @@
|
|||
#include <objc/message.h>
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QSet>
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/path.h"
|
||||
|
||||
QImage qt_mac_toQImage(CGImageRef image);
|
||||
|
||||
|
@ -95,14 +97,14 @@ namespace MacUtils
|
|||
}
|
||||
}
|
||||
|
||||
void openFiles(const QSet<QString> &pathsList)
|
||||
void openFiles(const PathList &pathList)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSMutableArray *pathURLs = [NSMutableArray arrayWithCapacity:pathsList.size()];
|
||||
NSMutableArray *pathURLs = [NSMutableArray arrayWithCapacity:pathList.size()];
|
||||
|
||||
for (const auto &path : pathsList)
|
||||
[pathURLs addObject:[NSURL fileURLWithPath:path.toNSString()]];
|
||||
for (const auto &path : pathList)
|
||||
[pathURLs addObject:[NSURL fileURLWithPath:path.toString().toNSString()]];
|
||||
|
||||
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:pathURLs];
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "base/bittorrent/sessionstatus.h"
|
||||
#include "base/global.h"
|
||||
#include "base/net/downloadmanager.h"
|
||||
#include "base/path.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/rss/rss_folder.h"
|
||||
#include "base/rss/rss_session.h"
|
||||
|
@ -1250,10 +1251,10 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
|||
// Display window to create a torrent
|
||||
void MainWindow::on_actionCreateTorrent_triggered()
|
||||
{
|
||||
createTorrentTriggered();
|
||||
createTorrentTriggered({});
|
||||
}
|
||||
|
||||
void MainWindow::createTorrentTriggered(const QString &path)
|
||||
void MainWindow::createTorrentTriggered(const Path &path)
|
||||
{
|
||||
if (m_createTorrentDlg)
|
||||
{
|
||||
|
@ -1261,7 +1262,9 @@ void MainWindow::createTorrentTriggered(const QString &path)
|
|||
m_createTorrentDlg->activateWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_createTorrentDlg = new TorrentCreatorDialog(this, path);
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::event(QEvent *e)
|
||||
|
@ -1367,7 +1370,7 @@ void MainWindow::dropEvent(QDropEvent *event)
|
|||
// Create torrent
|
||||
for (const QString &file : asConst(otherFiles))
|
||||
{
|
||||
createTorrentTriggered(file);
|
||||
createTorrentTriggered(Path(file));
|
||||
|
||||
// currently only handle the first entry
|
||||
// this is a stub that can be expanded later to create many torrents at once
|
||||
|
@ -1423,7 +1426,7 @@ void MainWindow::on_actionOpen_triggered()
|
|||
// Open File Open Dialog
|
||||
// Note: it is possible to select more than one file
|
||||
const QStringList pathsList =
|
||||
QFileDialog::getOpenFileNames(this, tr("Open Torrent Files"), pref->getMainLastDir(),
|
||||
QFileDialog::getOpenFileNames(this, tr("Open Torrent Files"), pref->getMainLastDir().data(),
|
||||
tr("Torrent Files") + " (*" + C_TORRENT_FILE_EXTENSION + ')');
|
||||
|
||||
if (pathsList.isEmpty())
|
||||
|
@ -1440,9 +1443,9 @@ void MainWindow::on_actionOpen_triggered()
|
|||
}
|
||||
|
||||
// Save last dir to remember it
|
||||
QString topDir = Utils::Fs::toUniformPath(pathsList.at(0));
|
||||
topDir = topDir.left(topDir.lastIndexOf('/'));
|
||||
pref->setMainLastDir(topDir);
|
||||
const Path topDir {pathsList.at(0)};
|
||||
const Path parentDir = topDir.parentPath();
|
||||
pref->setMainLastDir(parentDir.isEmpty() ? topDir : parentDir);
|
||||
}
|
||||
|
||||
void MainWindow::activate()
|
||||
|
@ -2110,9 +2113,9 @@ void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
|||
QProcess installer;
|
||||
qDebug("Launching Python installer in passive mode...");
|
||||
|
||||
const QString exePath = result.filePath + QLatin1String(".exe");
|
||||
QFile::rename(result.filePath, exePath);
|
||||
installer.start(Utils::Fs::toNativePath(exePath), {"/passive"});
|
||||
const Path exePath = result.filePath + ".exe";
|
||||
Utils::Fs::renameFile(result.filePath, exePath);
|
||||
installer.start(exePath.toString(), {"/passive"});
|
||||
|
||||
// Wait for setup to complete
|
||||
installer.waitForFinished(10 * 60 * 1000);
|
||||
|
@ -2122,7 +2125,7 @@ void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
|||
qDebug("Setup should be complete!");
|
||||
|
||||
// Delete temp file
|
||||
Utils::Fs::forceRemove(exePath);
|
||||
Utils::Fs::removeFile(exePath);
|
||||
|
||||
// Reload search engine
|
||||
if (Utils::ForeignApps::pythonInfo().isSupportedVersion())
|
||||
|
|
|
@ -212,7 +212,7 @@ private:
|
|||
bool event(QEvent *e) override;
|
||||
void displayRSSTab(bool enable);
|
||||
void displaySearchTab(bool enable);
|
||||
void createTorrentTriggered(const QString &path = {});
|
||||
void createTorrentTriggered(const Path &path);
|
||||
void showStatusBar(bool show);
|
||||
|
||||
Ui::MainWindow *m_ui;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "base/net/dnsupdater.h"
|
||||
#include "base/net/portforwarder.h"
|
||||
#include "base/net/proxyconfigurationmanager.h"
|
||||
#include "base/path.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/rss/rss_autodownloader.h"
|
||||
#include "base/rss/rss_session.h"
|
||||
|
@ -492,9 +493,9 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
|||
connect(m_ui->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const QString &s) { webUIHttpsCertChanged(s, ShowError::Show); });
|
||||
connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const Path &path) { webUIHttpsCertChanged(path, ShowError::Show); });
|
||||
connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const QString &s) { webUIHttpsKeyChanged(s, ShowError::Show); });
|
||||
connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const Path &path) { webUIHttpsKeyChanged(path, ShowError::Show); });
|
||||
connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
|
@ -732,7 +733,7 @@ void OptionsDialog::saveOptions()
|
|||
auto session = BitTorrent::Session::instance();
|
||||
|
||||
// Downloads preferences
|
||||
session->setSavePath(Utils::Fs::expandPathAbs(m_ui->textSavePath->selectedPath()));
|
||||
session->setSavePath(Path(m_ui->textSavePath->selectedPath()));
|
||||
session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
|
||||
session->setUseCategoryPathsInManualMode(m_ui->checkUseCategoryPaths->isChecked());
|
||||
session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
|
||||
|
@ -740,7 +741,7 @@ void OptionsDialog::saveOptions()
|
|||
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
|
||||
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
|
||||
session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked());
|
||||
session->setDownloadPath(Utils::Fs::expandPathAbs(m_ui->textDownloadPath->selectedPath()));
|
||||
session->setDownloadPath(m_ui->textDownloadPath->selectedPath());
|
||||
session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
|
||||
session->setPreallocationEnabled(preAllocateAllFiles());
|
||||
pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
|
||||
|
@ -1008,14 +1009,12 @@ void OptionsDialog::loadOptions()
|
|||
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
|
||||
m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled());
|
||||
m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
|
||||
m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
|
||||
m_ui->textDownloadPath->setSelectedPath(Utils::Fs::toNativePath(session->downloadPath()));
|
||||
m_ui->textDownloadPath->setSelectedPath(session->downloadPath());
|
||||
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
|
||||
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
|
||||
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
|
||||
|
||||
strValue = session->torrentExportDirectory();
|
||||
if (strValue.isEmpty())
|
||||
if (session->torrentExportDirectory().isEmpty())
|
||||
{
|
||||
// Disable
|
||||
m_ui->checkExportDir->setChecked(false);
|
||||
|
@ -1026,11 +1025,10 @@ void OptionsDialog::loadOptions()
|
|||
// Enable
|
||||
m_ui->checkExportDir->setChecked(true);
|
||||
m_ui->textExportDir->setEnabled(true);
|
||||
m_ui->textExportDir->setSelectedPath(strValue);
|
||||
m_ui->textExportDir->setSelectedPath(session->torrentExportDirectory());
|
||||
}
|
||||
|
||||
strValue = session->finishedTorrentExportDirectory();
|
||||
if (strValue.isEmpty())
|
||||
if (session->finishedTorrentExportDirectory().isEmpty())
|
||||
{
|
||||
// Disable
|
||||
m_ui->checkExportDirFin->setChecked(false);
|
||||
|
@ -1041,7 +1039,7 @@ void OptionsDialog::loadOptions()
|
|||
// Enable
|
||||
m_ui->checkExportDirFin->setChecked(true);
|
||||
m_ui->textExportDirFin->setEnabled(true);
|
||||
m_ui->textExportDirFin->setSelectedPath(strValue);
|
||||
m_ui->textExportDirFin->setSelectedPath(session->finishedTorrentExportDirectory());
|
||||
}
|
||||
|
||||
m_ui->groupMailNotification->setChecked(pref->isMailNotificationEnabled());
|
||||
|
@ -1644,25 +1642,25 @@ void OptionsDialog::setLocale(const QString &localeStr)
|
|||
m_ui->comboI18n->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
QString OptionsDialog::getTorrentExportDir() const
|
||||
Path OptionsDialog::getTorrentExportDir() const
|
||||
{
|
||||
if (m_ui->checkExportDir->isChecked())
|
||||
return Utils::Fs::expandPathAbs(m_ui->textExportDir->selectedPath());
|
||||
return m_ui->textExportDir->selectedPath();
|
||||
return {};
|
||||
}
|
||||
|
||||
QString OptionsDialog::getFinishedTorrentExportDir() const
|
||||
Path OptionsDialog::getFinishedTorrentExportDir() const
|
||||
{
|
||||
if (m_ui->checkExportDirFin->isChecked())
|
||||
return Utils::Fs::expandPathAbs(m_ui->textExportDirFin->selectedPath());
|
||||
return m_ui->textExportDirFin->selectedPath();
|
||||
return {};
|
||||
}
|
||||
|
||||
void OptionsDialog::on_addWatchedFolderButton_clicked()
|
||||
{
|
||||
Preferences *const pref = Preferences::instance();
|
||||
const QString dir = QFileDialog::getExistingDirectory(this, tr("Select folder to monitor"),
|
||||
Utils::Fs::toNativePath(Utils::Fs::folderName(pref->getScanDirsLastPath())));
|
||||
const Path dir {QFileDialog::getExistingDirectory(
|
||||
this, tr("Select folder to monitor"), pref->getScanDirsLastPath().parentPath().toString())};
|
||||
if (dir.isEmpty())
|
||||
return;
|
||||
|
||||
|
@ -1739,19 +1737,8 @@ void OptionsDialog::editWatchedFolderOptions(const QModelIndex &index)
|
|||
dialog->open();
|
||||
}
|
||||
|
||||
QString OptionsDialog::askForExportDir(const QString ¤tExportPath)
|
||||
{
|
||||
QDir currentExportDir(Utils::Fs::expandPathAbs(currentExportPath));
|
||||
QString dir;
|
||||
if (!currentExportPath.isEmpty() && currentExportDir.exists())
|
||||
dir = QFileDialog::getExistingDirectory(this, tr("Choose export directory"), currentExportDir.absolutePath());
|
||||
else
|
||||
dir = QFileDialog::getExistingDirectory(this, tr("Choose export directory"), QDir::homePath());
|
||||
return dir;
|
||||
}
|
||||
|
||||
// Return Filter object to apply to BT session
|
||||
QString OptionsDialog::getFilter() const
|
||||
Path OptionsDialog::getFilter() const
|
||||
{
|
||||
return m_ui->textFilterPath->selectedPath();
|
||||
}
|
||||
|
@ -1773,7 +1760,7 @@ QString OptionsDialog::webUiPassword() const
|
|||
return m_ui->textWebUiPassword->text();
|
||||
}
|
||||
|
||||
void OptionsDialog::webUIHttpsCertChanged(const QString &path, const ShowError showError)
|
||||
void OptionsDialog::webUIHttpsCertChanged(const Path &path, const ShowError showError)
|
||||
{
|
||||
m_ui->textWebUIHttpsCert->setSelectedPath(path);
|
||||
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-low")), this, 24));
|
||||
|
@ -1781,7 +1768,7 @@ void OptionsDialog::webUIHttpsCertChanged(const QString &path, const ShowError s
|
|||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
QFile file(path);
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (showError == ShowError::Show)
|
||||
|
@ -1799,7 +1786,7 @@ void OptionsDialog::webUIHttpsCertChanged(const QString &path, const ShowError s
|
|||
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-high")), this, 24));
|
||||
}
|
||||
|
||||
void OptionsDialog::webUIHttpsKeyChanged(const QString &path, const ShowError showError)
|
||||
void OptionsDialog::webUIHttpsKeyChanged(const Path &path, const ShowError showError)
|
||||
{
|
||||
m_ui->textWebUIHttpsKey->setSelectedPath(path);
|
||||
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-low")), this, 24));
|
||||
|
@ -1807,7 +1794,7 @@ void OptionsDialog::webUIHttpsKeyChanged(const QString &path, const ShowError sh
|
|||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
QFile file(path);
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (showError == ShowError::Show)
|
||||
|
@ -1843,7 +1830,7 @@ void OptionsDialog::on_IpFilterRefreshBtn_clicked()
|
|||
// Updating program preferences
|
||||
BitTorrent::Session *const session = BitTorrent::Session::instance();
|
||||
session->setIPFilteringEnabled(true);
|
||||
session->setIPFilterFile(""); // forcing Session reload filter file
|
||||
session->setIPFilterFile({}); // forcing Session reload filter file
|
||||
session->setIPFilterFile(getFilter());
|
||||
connect(session, &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
|
||||
setCursor(QCursor(Qt::WaitCursor));
|
||||
|
@ -1887,7 +1874,7 @@ bool OptionsDialog::webUIAuthenticationOk()
|
|||
|
||||
bool OptionsDialog::isAlternativeWebUIPathValid()
|
||||
{
|
||||
if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().trimmed().isEmpty())
|
||||
if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Location Error"), tr("The alternative Web UI files location cannot be blank."));
|
||||
return false;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <QDialog>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/settingvalue.h"
|
||||
|
||||
class QCloseEvent;
|
||||
|
@ -112,8 +113,8 @@ private slots:
|
|||
void on_removeWatchedFolderButton_clicked();
|
||||
void on_registerDNSBtn_clicked();
|
||||
void setLocale(const QString &localeStr);
|
||||
void webUIHttpsCertChanged(const QString &path, ShowError showError);
|
||||
void webUIHttpsKeyChanged(const QString &path, ShowError showError);
|
||||
void webUIHttpsCertChanged(const Path &path, ShowError showError);
|
||||
void webUIHttpsKeyChanged(const Path &path, ShowError showError);
|
||||
|
||||
private:
|
||||
// Methods
|
||||
|
@ -136,9 +137,8 @@ private:
|
|||
bool preAllocateAllFiles() const;
|
||||
bool useAdditionDialog() const;
|
||||
bool addTorrentsInPause() const;
|
||||
QString getTorrentExportDir() const;
|
||||
QString getFinishedTorrentExportDir() const;
|
||||
QString askForExportDir(const QString ¤tExportPath);
|
||||
Path getTorrentExportDir() const;
|
||||
Path getFinishedTorrentExportDir() const;
|
||||
// Connection options
|
||||
int getPort() const;
|
||||
bool isUPnPEnabled() const;
|
||||
|
@ -162,7 +162,7 @@ private:
|
|||
Net::ProxyType getProxyType() const;
|
||||
// IP Filter
|
||||
bool isIPFilteringEnabled() const;
|
||||
QString getFilter() const;
|
||||
Path getFilter() const;
|
||||
// Queueing system
|
||||
bool isQueueingSystemEnabled() const;
|
||||
int getMaxActiveDownloads() const;
|
||||
|
|
|
@ -93,12 +93,12 @@ PreviewSelectDialog::PreviewSelectDialog(QWidget *parent, const BitTorrent::Torr
|
|||
const QVector<qreal> fp = torrent->filesProgress();
|
||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
||||
{
|
||||
const QString fileName = Utils::Fs::fileName(torrent->filePath(i));
|
||||
if (Utils::Misc::isPreviewable(fileName))
|
||||
const Path filePath = torrent->filePath(i);
|
||||
if (Utils::Misc::isPreviewable(filePath))
|
||||
{
|
||||
int row = m_previewListModel->rowCount();
|
||||
m_previewListModel->insertRow(row);
|
||||
m_previewListModel->setData(m_previewListModel->index(row, NAME), fileName);
|
||||
m_previewListModel->setData(m_previewListModel->index(row, NAME), filePath.filename());
|
||||
m_previewListModel->setData(m_previewListModel->index(row, SIZE), torrent->fileSize(i));
|
||||
m_previewListModel->setData(m_previewListModel->index(row, PROGRESS), fp[i]);
|
||||
m_previewListModel->setData(m_previewListModel->index(row, FILE_INDEX), i);
|
||||
|
@ -133,14 +133,14 @@ void PreviewSelectDialog::previewButtonClicked()
|
|||
|
||||
// Only one file should be selected
|
||||
const int fileIndex = selectedIndexes.at(0).data().toInt();
|
||||
const QString path = QDir(m_torrent->actualStorageLocation()).absoluteFilePath(m_torrent->actualFilePath(fileIndex));
|
||||
const Path path = m_torrent->actualStorageLocation() / m_torrent->actualFilePath(fileIndex);
|
||||
// File
|
||||
if (!QFile::exists(path))
|
||||
if (!path.exists())
|
||||
{
|
||||
const bool isSingleFile = (m_previewListModel->rowCount() == 1);
|
||||
QWidget *parent = isSingleFile ? this->parentWidget() : this;
|
||||
QMessageBox::critical(parent, tr("Preview impossible")
|
||||
, tr("Sorry, we can't preview this file: \"%1\".").arg(Utils::Fs::toNativePath(path)));
|
||||
, tr("Sorry, we can't preview this file: \"%1\".").arg(path.toString()));
|
||||
if (isSingleFile)
|
||||
reject();
|
||||
return;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <QDialog>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
|
||||
class QStandardItemModel;
|
||||
|
@ -38,6 +39,7 @@ namespace BitTorrent
|
|||
{
|
||||
class Torrent;
|
||||
}
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PreviewSelectDialog;
|
||||
|
@ -64,7 +66,7 @@ public:
|
|||
~PreviewSelectDialog();
|
||||
|
||||
signals:
|
||||
void readyToPreviewFile(QString) const;
|
||||
void readyToPreviewFile(const Path &filePath) const;
|
||||
|
||||
private slots:
|
||||
void previewButtonClicked();
|
||||
|
|
|
@ -470,9 +470,11 @@ void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTor
|
|||
setModelData(row, PeerListColumns::TOT_UP, totalUp, peer.totalUpload(), intDataTextAlignment);
|
||||
setModelData(row, PeerListColumns::RELEVANCE, (Utils::String::fromDouble(peer.relevance() * 100, 1) + '%'), peer.relevance(), intDataTextAlignment);
|
||||
|
||||
const QStringList downloadingFiles {torrent->hasMetadata()
|
||||
? torrent->info().filesForPiece(peer.downloadingPieceIndex())
|
||||
: QStringList()};
|
||||
const PathList filePaths = torrent->info().filesForPiece(peer.downloadingPieceIndex());
|
||||
QStringList downloadingFiles;
|
||||
downloadingFiles.reserve(filePaths.size());
|
||||
for (const Path &filePath : filePaths)
|
||||
downloadingFiles.append(filePath.toString());
|
||||
const QString downloadingFilesDisplayValue = downloadingFiles.join(';');
|
||||
setModelData(row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue, downloadingFilesDisplayValue, {}, downloadingFiles.join(QLatin1Char('\n')));
|
||||
|
||||
|
|
|
@ -37,9 +37,10 @@
|
|||
#include <QTextStream>
|
||||
#include <QToolTip>
|
||||
|
||||
#include "base/indexrange.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/bittorrent/torrentinfo.h"
|
||||
#include "base/indexrange.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/misc.h"
|
||||
|
||||
namespace
|
||||
|
@ -53,9 +54,8 @@ namespace
|
|||
{
|
||||
public:
|
||||
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
|
||||
: m_bytesPerPixel
|
||||
{((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
|
||||
? torrentInfo.totalSize() / image.width() : -1}
|
||||
: m_bytesPerPixel {((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
|
||||
? torrentInfo.totalSize() / image.width() : -1}
|
||||
, m_torrentInfo {torrentInfo}
|
||||
{
|
||||
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
|
||||
|
@ -100,9 +100,9 @@ namespace
|
|||
m_stream << "</table>";
|
||||
}
|
||||
|
||||
void operator()(const QString &size, const QString &path)
|
||||
void operator()(const QString &size, const Path &path)
|
||||
{
|
||||
m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path << "</td></tr>";
|
||||
m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path.toString() << "</td></tr>";
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -282,7 +282,7 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
|
|||
|
||||
for (int f : files)
|
||||
{
|
||||
const QString filePath {torrentInfo.filePath(f)};
|
||||
const Path filePath = torrentInfo.filePath(f);
|
||||
renderer(Utils::Misc::friendlyUnit(torrentInfo.fileSize(f)), filePath);
|
||||
}
|
||||
stream << "</body></html>";
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <QClipboard>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QHeaderView>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMenu>
|
||||
|
@ -45,6 +44,7 @@
|
|||
#include "base/bittorrent/infohash.h"
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/path.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "base/utils/fs.h"
|
||||
|
@ -333,7 +333,7 @@ QTreeView *PropertiesWidget::getFilesList() const
|
|||
void PropertiesWidget::updateSavePath(BitTorrent::Torrent *const torrent)
|
||||
{
|
||||
if (torrent == m_torrent)
|
||||
m_ui->labelSavePathVal->setText(Utils::Fs::toNativePath(m_torrent->savePath()));
|
||||
m_ui->labelSavePathVal->setText(m_torrent->savePath().toString());
|
||||
}
|
||||
|
||||
void PropertiesWidget::loadTrackers(BitTorrent::Torrent *const torrent)
|
||||
|
@ -593,25 +593,22 @@ void PropertiesWidget::loadUrlSeeds()
|
|||
}
|
||||
}
|
||||
|
||||
QString PropertiesWidget::getFullPath(const QModelIndex &index) const
|
||||
Path PropertiesWidget::getFullPath(const QModelIndex &index) const
|
||||
{
|
||||
const QDir saveDir {m_torrent->actualStorageLocation()};
|
||||
|
||||
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
|
||||
{
|
||||
const int fileIdx = m_propListModel->getFileIndex(index);
|
||||
const QString filename {m_torrent->actualFilePath(fileIdx)};
|
||||
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(filename))};
|
||||
const Path fullPath = m_torrent->actualStorageLocation() / m_torrent->actualFilePath(fileIdx);
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
// folder type
|
||||
const QModelIndex nameIndex {index.sibling(index.row(), TorrentContentModelItem::COL_NAME)};
|
||||
QString folderPath {nameIndex.data().toString()};
|
||||
Path folderPath {nameIndex.data().toString()};
|
||||
for (QModelIndex modelIdx = m_propListModel->parent(nameIndex); modelIdx.isValid(); modelIdx = modelIdx.parent())
|
||||
folderPath.prepend(modelIdx.data().toString() + '/');
|
||||
folderPath = Path(modelIdx.data().toString()) / folderPath;
|
||||
|
||||
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(folderPath))};
|
||||
const Path fullPath = m_torrent->actualStorageLocation() / folderPath;
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
|
@ -626,7 +623,7 @@ void PropertiesWidget::openItem(const QModelIndex &index) const
|
|||
|
||||
void PropertiesWidget::openParentFolder(const QModelIndex &index) const
|
||||
{
|
||||
const QString path = getFullPath(index);
|
||||
const Path path = getFullPath(index);
|
||||
m_torrent->flushCache(); // Flush data
|
||||
#ifdef Q_OS_MACOS
|
||||
MacUtils::openFiles({path});
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <QList>
|
||||
#include <QWidget>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QPushButton;
|
||||
class QTreeView;
|
||||
|
||||
|
@ -108,7 +110,7 @@ private:
|
|||
QPushButton *getButtonFromIndex(int index);
|
||||
void applyPriorities();
|
||||
void openParentFolder(const QModelIndex &index) const;
|
||||
QString getFullPath(const QModelIndex &index) const;
|
||||
Path getFullPath(const QModelIndex &index) const;
|
||||
|
||||
Ui::PropertiesWidget *m_ui;
|
||||
BitTorrent::Torrent *m_torrent;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/global.h"
|
||||
#include "base/path.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/rss/rss_article.h"
|
||||
#include "base/rss/rss_autodownloader.h"
|
||||
|
@ -47,7 +48,6 @@
|
|||
#include "base/rss/rss_folder.h"
|
||||
#include "base/rss/rss_session.h"
|
||||
#include "base/utils/compare.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/io.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "gui/autoexpandabledialog.h"
|
||||
|
@ -261,7 +261,7 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
|
|||
else
|
||||
m_ui->lineEFilter->clear();
|
||||
m_ui->checkBoxSaveDiffDir->setChecked(!m_currentRule.savePath().isEmpty());
|
||||
m_ui->lineSavePath->setSelectedPath(Utils::Fs::toNativePath(m_currentRule.savePath()));
|
||||
m_ui->lineSavePath->setSelectedPath(m_currentRule.savePath());
|
||||
m_ui->checkRegex->blockSignals(true);
|
||||
m_ui->checkRegex->setChecked(m_currentRule.useRegex());
|
||||
m_ui->checkRegex->blockSignals(false);
|
||||
|
@ -346,7 +346,7 @@ void AutomatedRssDownloader::updateEditedRule()
|
|||
m_currentRule.setMustContain(m_ui->lineContains->text());
|
||||
m_currentRule.setMustNotContain(m_ui->lineNotContains->text());
|
||||
m_currentRule.setEpisodeFilter(m_ui->lineEFilter->text());
|
||||
m_currentRule.setSavePath(m_ui->checkBoxSaveDiffDir->isChecked() ? m_ui->lineSavePath->selectedPath() : "");
|
||||
m_currentRule.setSavePath(m_ui->checkBoxSaveDiffDir->isChecked() ? m_ui->lineSavePath->selectedPath() : Path());
|
||||
m_currentRule.setCategory(m_ui->comboCategory->currentText());
|
||||
std::optional<bool> addPaused;
|
||||
if (m_ui->comboAddPaused->currentIndex() == 1)
|
||||
|
@ -429,9 +429,10 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
|||
}
|
||||
|
||||
QString selectedFilter {m_formatFilterJSON};
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
Path path {QFileDialog::getSaveFileName(
|
||||
this, tr("Export RSS rules"), QDir::homePath()
|
||||
, QString::fromLatin1("%1;;%2").arg(m_formatFilterJSON, m_formatFilterLegacy), &selectedFilter);
|
||||
, QString::fromLatin1("%1;;%2").arg(m_formatFilterJSON, m_formatFilterLegacy), &selectedFilter)};
|
||||
|
||||
if (path.isEmpty()) return;
|
||||
|
||||
const RSS::AutoDownloader::RulesFileFormat format
|
||||
|
@ -443,12 +444,12 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
|||
|
||||
if (format == RSS::AutoDownloader::RulesFileFormat::JSON)
|
||||
{
|
||||
if (!path.endsWith(EXT_JSON, Qt::CaseInsensitive))
|
||||
if (!path.hasExtension(EXT_JSON))
|
||||
path += EXT_JSON;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!path.endsWith(EXT_LEGACY, Qt::CaseInsensitive))
|
||||
if (!path.hasExtension(EXT_LEGACY))
|
||||
path += EXT_LEGACY;
|
||||
}
|
||||
|
||||
|
@ -464,13 +465,13 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
|||
void AutomatedRssDownloader::on_importBtn_clicked()
|
||||
{
|
||||
QString selectedFilter {m_formatFilterJSON};
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
this, tr("Import RSS rules"), QDir::homePath()
|
||||
, QString::fromLatin1("%1;;%2").arg(m_formatFilterJSON, m_formatFilterLegacy), &selectedFilter);
|
||||
if (path.isEmpty() || !QFile::exists(path))
|
||||
const Path path {QFileDialog::getOpenFileName(
|
||||
this, tr("Import RSS rules"), QDir::homePath()
|
||||
, QString::fromLatin1("%1;;%2").arg(m_formatFilterJSON, m_formatFilterLegacy), &selectedFilter)};
|
||||
if (!path.exists())
|
||||
return;
|
||||
|
||||
QFile file {path};
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue