parent
facfa26eed
commit
dd1bd8ad10
|
@ -45,7 +45,6 @@
|
||||||
|
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInt>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
|
@ -80,6 +79,7 @@
|
||||||
#include "base/torrentfileswatcher.h"
|
#include "base/torrentfileswatcher.h"
|
||||||
#include "base/utils/compare.h"
|
#include "base/utils/compare.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/version.h"
|
#include "base/version.h"
|
||||||
#include "applicationinstancemanager.h"
|
#include "applicationinstancemanager.h"
|
||||||
|
@ -105,7 +105,7 @@ namespace
|
||||||
const QString LOG_FOLDER = QStringLiteral("logs");
|
const QString LOG_FOLDER = QStringLiteral("logs");
|
||||||
const QChar PARAMS_SEPARATOR = QLatin1Char('|');
|
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 MIN_FILELOG_SIZE = 1024; // 1KiB
|
||||||
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
||||||
|
@ -143,16 +143,16 @@ Application::Application(int &argc, char **argv)
|
||||||
QPixmapCache::setCacheLimit(PIXMAP_CACHE_SIZE);
|
QPixmapCache::setCacheLimit(PIXMAP_CACHE_SIZE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty()
|
const auto portableProfilePath = Path(QCoreApplication::applicationDirPath()) / DEFAULT_PORTABLE_MODE_PROFILE_DIR;
|
||||||
&& QDir(QCoreApplication::applicationDirPath()).exists(DEFAULT_PORTABLE_MODE_PROFILE_DIR);
|
const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty() && portableProfilePath.exists();
|
||||||
|
|
||||||
const QString profileDir = portableModeEnabled
|
const Path profileDir = portableModeEnabled
|
||||||
? QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(DEFAULT_PORTABLE_MODE_PROFILE_DIR)
|
? portableProfilePath
|
||||||
: m_commandLineArgs.profileDir;
|
: m_commandLineArgs.profileDir;
|
||||||
Profile::initInstance(profileDir, m_commandLineArgs.configurationName,
|
Profile::initInstance(profileDir, m_commandLineArgs.configurationName,
|
||||||
(m_commandLineArgs.relativeFastresumePaths || portableModeEnabled));
|
(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();
|
Logger::initInstance();
|
||||||
SettingsStorage::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));
|
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(QBT_VERSION));
|
||||||
if (portableModeEnabled)
|
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)
|
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
|
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
|
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;
|
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)
|
if (m_fileLogger)
|
||||||
m_fileLogger->changePath(path);
|
m_fileLogger->changePath(path);
|
||||||
|
@ -327,16 +327,16 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
|
||||||
break;
|
break;
|
||||||
case u'D':
|
case u'D':
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->savePath())));
|
program.replace(i, 2, chopPathSep(torrent->savePath().toString()));
|
||||||
#else
|
#else
|
||||||
program.replace(i, 2, Utils::Fs::toNativePath(torrent->savePath()));
|
program.replace(i, 2, torrent->savePath().toString());
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case u'F':
|
case u'F':
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->contentPath())));
|
program.replace(i, 2, chopPathSep(torrent->contentPath().toString()));
|
||||||
#else
|
#else
|
||||||
program.replace(i, 2, Utils::Fs::toNativePath(torrent->contentPath()));
|
program.replace(i, 2, torrent->contentPath().toString());
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case u'G':
|
case u'G':
|
||||||
|
@ -359,9 +359,9 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
|
||||||
break;
|
break;
|
||||||
case u'R':
|
case u'R':
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->rootPath())));
|
program.replace(i, 2, chopPathSep(torrent->rootPath().toString()));
|
||||||
#else
|
#else
|
||||||
program.replace(i, 2, Utils::Fs::toNativePath(torrent->rootPath()));
|
program.replace(i, 2, torrent->rootPath().toString());
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case u'T':
|
case u'T':
|
||||||
|
@ -439,7 +439,7 @@ void Application::sendNotificationEmail(const BitTorrent::Torrent *torrent)
|
||||||
// Prepare mail content
|
// Prepare mail content
|
||||||
const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
|
const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
|
||||||
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + '\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")
|
+ 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"
|
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
|
||||||
+ tr("Thank you for using qBittorrent.") + '\n';
|
+ tr("Thank you for using qBittorrent.") + '\n';
|
||||||
|
@ -545,7 +545,7 @@ void Application::processParams(const QStringList ¶ms)
|
||||||
|
|
||||||
if (param.startsWith(QLatin1String("@savePath=")))
|
if (param.startsWith(QLatin1String("@savePath=")))
|
||||||
{
|
{
|
||||||
torrentParams.savePath = param.mid(10);
|
torrentParams.savePath = Path(param.mid(10));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ void Application::cleanup()
|
||||||
Logger::freeInstance();
|
Logger::freeInstance();
|
||||||
IconProvider::freeInstance();
|
IconProvider::freeInstance();
|
||||||
SearchPluginManager::freeInstance();
|
SearchPluginManager::freeInstance();
|
||||||
Utils::Fs::removeDirRecursive(Utils::Fs::tempPath());
|
Utils::Fs::removeDirRecursively(Utils::Fs::tempPath());
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
if (m_window)
|
if (m_window)
|
||||||
|
|
|
@ -47,6 +47,7 @@ class QSessionManager;
|
||||||
using BaseApplication = QCoreApplication;
|
using BaseApplication = QCoreApplication;
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
|
@ -91,8 +92,8 @@ public:
|
||||||
// FileLogger properties
|
// FileLogger properties
|
||||||
bool isFileLoggerEnabled() const;
|
bool isFileLoggerEnabled() const;
|
||||||
void setFileLoggerEnabled(bool value);
|
void setFileLoggerEnabled(bool value);
|
||||||
QString fileLoggerPath() const;
|
Path fileLoggerPath() const;
|
||||||
void setFileLoggerPath(const QString &path);
|
void setFileLoggerPath(const Path &path);
|
||||||
bool isFileLoggerBackup() const;
|
bool isFileLoggerBackup() const;
|
||||||
void setFileLoggerBackup(bool value);
|
void setFileLoggerBackup(bool value);
|
||||||
bool isFileLoggerDeleteOld() const;
|
bool isFileLoggerDeleteOld() const;
|
||||||
|
@ -152,5 +153,5 @@ private:
|
||||||
SettingValue<int> m_storeFileLoggerMaxSize;
|
SettingValue<int> m_storeFileLoggerMaxSize;
|
||||||
SettingValue<int> m_storeFileLoggerAge;
|
SettingValue<int> m_storeFileLoggerAge;
|
||||||
SettingValue<int> m_storeFileLoggerAgeType;
|
SettingValue<int> m_storeFileLoggerAgeType;
|
||||||
SettingValue<QString> m_storeFileLoggerPath;
|
SettingValue<Path> m_storeFileLoggerPath;
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,18 +37,19 @@
|
||||||
#include <QSharedMemory>
|
#include <QSharedMemory>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "qtlocalpeer/qtlocalpeer.h"
|
#include "qtlocalpeer/qtlocalpeer.h"
|
||||||
|
|
||||||
ApplicationInstanceManager::ApplicationInstanceManager(const QString &instancePath, QObject *parent)
|
ApplicationInstanceManager::ApplicationInstanceManager(const Path &instancePath, QObject *parent)
|
||||||
: QObject {parent}
|
: QObject {parent}
|
||||||
, m_peer {new QtLocalPeer {instancePath, this}}
|
, m_peer {new QtLocalPeer(instancePath.data(), this)}
|
||||||
, m_isFirstInstance {!m_peer->isClient()}
|
, m_isFirstInstance {!m_peer->isClient()}
|
||||||
{
|
{
|
||||||
connect(m_peer, &QtLocalPeer::messageReceived, this, &ApplicationInstanceManager::messageReceived);
|
connect(m_peer, &QtLocalPeer::messageReceived, this, &ApplicationInstanceManager::messageReceived);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
const QString sharedMemoryKey = instancePath + QLatin1String {"/shared-memory"};
|
const QString sharedMemoryKey = instancePath.data() + QLatin1String("/shared-memory");
|
||||||
auto sharedMem = new QSharedMemory {sharedMemoryKey, this};
|
auto sharedMem = new QSharedMemory(sharedMemoryKey, this);
|
||||||
if (m_isFirstInstance)
|
if (m_isFirstInstance)
|
||||||
{
|
{
|
||||||
// First instance creates shared memory and store PID
|
// First instance creates shared memory and store PID
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QtLocalPeer;
|
class QtLocalPeer;
|
||||||
|
|
||||||
class ApplicationInstanceManager final : public QObject
|
class ApplicationInstanceManager final : public QObject
|
||||||
|
@ -38,7 +40,7 @@ class ApplicationInstanceManager final : public QObject
|
||||||
Q_DISABLE_COPY_MOVE(ApplicationInstanceManager)
|
Q_DISABLE_COPY_MOVE(ApplicationInstanceManager)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ApplicationInstanceManager(const QString &instancePath, QObject *parent = nullptr);
|
explicit ApplicationInstanceManager(const Path &instancePath, QObject *parent = nullptr);
|
||||||
|
|
||||||
bool isFirstInstance() const;
|
bool isFirstInstance() const;
|
||||||
|
|
||||||
|
|
|
@ -372,7 +372,7 @@ QStringList QBtCommandLineParameters::paramList() const
|
||||||
// torrent paths or URLs.
|
// torrent paths or URLs.
|
||||||
|
|
||||||
if (!savePath.isEmpty())
|
if (!savePath.isEmpty())
|
||||||
result.append(QLatin1String("@savePath=") + savePath);
|
result.append(QLatin1String("@savePath=") + savePath.data());
|
||||||
|
|
||||||
if (addPaused.has_value())
|
if (addPaused.has_value())
|
||||||
result.append(*addPaused ? QLatin1String {"@addPaused=1"} : QLatin1String {"@addPaused=0"});
|
result.append(*addPaused ? QLatin1String {"@addPaused=1"} : QLatin1String {"@addPaused=0"});
|
||||||
|
@ -438,7 +438,7 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
||||||
#endif
|
#endif
|
||||||
else if (arg == PROFILE_OPTION)
|
else if (arg == PROFILE_OPTION)
|
||||||
{
|
{
|
||||||
result.profileDir = PROFILE_OPTION.value(arg);
|
result.profileDir = Path(PROFILE_OPTION.value(arg));
|
||||||
}
|
}
|
||||||
else if (arg == RELATIVE_FASTRESUME)
|
else if (arg == RELATIVE_FASTRESUME)
|
||||||
{
|
{
|
||||||
|
@ -450,7 +450,7 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
||||||
}
|
}
|
||||||
else if (arg == SAVE_PATH_OPTION)
|
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)
|
else if (arg == PAUSED_OPTION)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class QProcessEnvironment;
|
class QProcessEnvironment;
|
||||||
|
|
||||||
|
@ -58,9 +59,9 @@ struct QBtCommandLineParameters
|
||||||
std::optional<bool> addPaused;
|
std::optional<bool> addPaused;
|
||||||
std::optional<bool> skipDialog;
|
std::optional<bool> skipDialog;
|
||||||
QStringList torrents;
|
QStringList torrents;
|
||||||
QString profileDir;
|
Path profileDir;
|
||||||
QString configurationName;
|
QString configurationName;
|
||||||
QString savePath;
|
Path savePath;
|
||||||
QString category;
|
QString category;
|
||||||
QString unknownParameter;
|
QString unknownParameter;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,9 @@ namespace
|
||||||
const std::chrono::seconds FLUSH_INTERVAL {2};
|
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_backup(backup)
|
||||||
, m_maxSize(maxSize)
|
, m_maxSize(maxSize)
|
||||||
{
|
{
|
||||||
|
@ -68,26 +70,25 @@ FileLogger::~FileLogger()
|
||||||
closeLogFile();
|
closeLogFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLogger::changePath(const QString &newPath)
|
void FileLogger::changePath(const Path &newPath)
|
||||||
{
|
{
|
||||||
const QDir dir(newPath);
|
// compare paths as strings to perform case sensitive comparison on all the platforms
|
||||||
dir.mkpath(newPath);
|
if (newPath.data() == m_path.parentPath().data())
|
||||||
const QString tmpPath = dir.absoluteFilePath("qbittorrent.log");
|
return;
|
||||||
|
|
||||||
if (tmpPath != m_path)
|
closeLogFile();
|
||||||
{
|
|
||||||
m_path = tmpPath;
|
|
||||||
|
|
||||||
closeLogFile();
|
m_path = newPath / Path("qbittorrent.log");
|
||||||
m_logFile.setFileName(m_path);
|
m_logFile.setFileName(m_path.data());
|
||||||
openLogFile();
|
|
||||||
}
|
Utils::Fs::mkpath(newPath);
|
||||||
|
openLogFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
||||||
{
|
{
|
||||||
const QDateTime date = QDateTime::currentDateTime();
|
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*")
|
const QFileInfoList fileList = dir.entryInfoList(QStringList("qbittorrent.log.bak*")
|
||||||
, (QDir::Files | QDir::Writable), (QDir::Time | QDir::Reversed));
|
, (QDir::Files | QDir::Writable), (QDir::Time | QDir::Reversed));
|
||||||
|
|
||||||
|
@ -107,7 +108,7 @@ void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
||||||
}
|
}
|
||||||
if (modificationDate > date)
|
if (modificationDate > date)
|
||||||
break;
|
break;
|
||||||
Utils::Fs::forceRemove(file.absoluteFilePath());
|
Utils::Fs::removeFile(Path(file.absoluteFilePath()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,15 +152,15 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
|
||||||
{
|
{
|
||||||
closeLogFile();
|
closeLogFile();
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
QString backupLogFilename = m_path + ".bak";
|
Path backupLogFilename = m_path + ".bak";
|
||||||
|
|
||||||
while (QFile::exists(backupLogFilename))
|
while (backupLogFilename.exists())
|
||||||
{
|
{
|
||||||
++counter;
|
++counter;
|
||||||
backupLogFilename = m_path + ".bak" + QString::number(counter);
|
backupLogFilename = m_path + ".bak" + QString::number(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile::rename(m_path, backupLogFilename);
|
Utils::Fs::renameFile(m_path, backupLogFilename);
|
||||||
openLogFile();
|
openLogFile();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace Log
|
namespace Log
|
||||||
{
|
{
|
||||||
struct Msg;
|
struct Msg;
|
||||||
|
@ -50,10 +52,10 @@ public:
|
||||||
YEARS
|
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();
|
~FileLogger();
|
||||||
|
|
||||||
void changePath(const QString &newPath);
|
void changePath(const Path &newPath);
|
||||||
void deleteOld(int age, FileLogAgeType ageType);
|
void deleteOld(int age, FileLogAgeType ageType);
|
||||||
void setBackup(bool value);
|
void setBackup(bool value);
|
||||||
void setMaxSize(int value);
|
void setMaxSize(int value);
|
||||||
|
@ -66,7 +68,7 @@ private:
|
||||||
void openLogFile();
|
void openLogFile();
|
||||||
void closeLogFile();
|
void closeLogFile();
|
||||||
|
|
||||||
QString m_path;
|
Path m_path;
|
||||||
bool m_backup;
|
bool m_backup;
|
||||||
int m_maxSize;
|
int m_maxSize;
|
||||||
QFile m_logFile;
|
QFile m_logFile;
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace
|
||||||
|
|
||||||
void exportWebUIHttpsFiles()
|
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()};
|
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
||||||
const auto oldData {settingsStorage->loadValue<QByteArray>(oldKey)};
|
const auto oldData {settingsStorage->loadValue<QByteArray>(oldKey)};
|
||||||
|
@ -61,24 +61,24 @@ namespace
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(savePath, oldData);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(savePath, oldData);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(errorMsgFormat.arg(savePath, result.error()) , Log::WARNING);
|
LogMsg(errorMsgFormat.arg(savePath.toString(), result.error()) , Log::WARNING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsStorage->storeValue(newKey, savePath);
|
settingsStorage->storeValue(newKey, savePath);
|
||||||
settingsStorage->removeValue(oldKey);
|
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);
|
, Log::INFO);
|
||||||
};
|
};
|
||||||
|
|
||||||
const QString configPath {specialFolderLocation(SpecialFolder::Config)};
|
const Path configPath = specialFolderLocation(SpecialFolder::Config);
|
||||||
migrate(QLatin1String("Preferences/WebUI/HTTPS/Certificate")
|
migrate(QLatin1String("Preferences/WebUI/HTTPS/Certificate")
|
||||||
, QLatin1String("Preferences/WebUI/HTTPS/CertificatePath")
|
, QLatin1String("Preferences/WebUI/HTTPS/CertificatePath")
|
||||||
, Utils::Fs::toNativePath(configPath + QLatin1String("/WebUICertificate.crt")));
|
, (configPath / Path("WebUICertificate.crt")));
|
||||||
migrate(QLatin1String("Preferences/WebUI/HTTPS/Key")
|
migrate(QLatin1String("Preferences/WebUI/HTTPS/Key")
|
||||||
, QLatin1String("Preferences/WebUI/HTTPS/KeyPath")
|
, QLatin1String("Preferences/WebUI/HTTPS/KeyPath")
|
||||||
, Utils::Fs::toNativePath(configPath + QLatin1String("/WebUIPrivateKey.pem")));
|
, (configPath / Path("WebUIPrivateKey.pem")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void upgradeTorrentContentLayout()
|
void upgradeTorrentContentLayout()
|
||||||
|
|
|
@ -62,6 +62,8 @@ add_library(qbt_base STATIC
|
||||||
net/reverseresolution.h
|
net/reverseresolution.h
|
||||||
net/smtp.h
|
net/smtp.h
|
||||||
orderedset.h
|
orderedset.h
|
||||||
|
path.h
|
||||||
|
pathfwd.h
|
||||||
preferences.h
|
preferences.h
|
||||||
profile.h
|
profile.h
|
||||||
profile_p.h
|
profile_p.h
|
||||||
|
@ -144,6 +146,7 @@ add_library(qbt_base STATIC
|
||||||
net/proxyconfigurationmanager.cpp
|
net/proxyconfigurationmanager.cpp
|
||||||
net/reverseresolution.cpp
|
net/reverseresolution.cpp
|
||||||
net/smtp.cpp
|
net/smtp.cpp
|
||||||
|
path.cpp
|
||||||
preferences.cpp
|
preferences.cpp
|
||||||
profile.cpp
|
profile.cpp
|
||||||
profile_p.cpp
|
profile_p.cpp
|
||||||
|
|
|
@ -31,21 +31,22 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
|
|
||||||
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
|
|
||||||
AsyncFileStorage::AsyncFileStorage(const QString &storageFolderPath, QObject *parent)
|
AsyncFileStorage::AsyncFileStorage(const Path &storageFolderPath, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_storageDir(storageFolderPath)
|
, 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()))
|
Q_ASSERT(m_storageDir.isAbsolute());
|
||||||
throw AsyncFileStorageError
|
|
||||||
{tr("Could not create directory '%1'.")
|
if (!Utils::Fs::mkpath(m_storageDir))
|
||||||
.arg(m_storageDir.absolutePath())};
|
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.
|
// TODO: This folder locking approach does not work for UNIX systems. Implement it.
|
||||||
if (!m_lockFile.open(QFile::WriteOnly))
|
if (!m_lockFile.open(QFile::WriteOnly))
|
||||||
throw AsyncFileStorageError {m_lockFile.errorString()};
|
throw AsyncFileStorageError(m_lockFile.errorString());
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncFileStorage::~AsyncFileStorage()
|
AsyncFileStorage::~AsyncFileStorage()
|
||||||
|
@ -54,21 +55,21 @@ AsyncFileStorage::~AsyncFileStorage()
|
||||||
m_lockFile.remove();
|
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);
|
, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir AsyncFileStorage::storageDir() const
|
Path AsyncFileStorage::storageDir() const
|
||||||
{
|
{
|
||||||
return m_storageDir;
|
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);
|
const Path filePath = m_storageDir / fileName;
|
||||||
qDebug() << "AsyncFileStorage: Saving data to" << filePath;
|
qDebug() << "AsyncFileStorage: Saving data to" << filePath.toString();
|
||||||
|
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filePath, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filePath, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
|
|
|
@ -28,11 +28,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class AsyncFileStorageError : public RuntimeError
|
class AsyncFileStorageError : public RuntimeError
|
||||||
{
|
{
|
||||||
|
@ -46,19 +46,19 @@ class AsyncFileStorage : public QObject
|
||||||
Q_DISABLE_COPY_MOVE(AsyncFileStorage)
|
Q_DISABLE_COPY_MOVE(AsyncFileStorage)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AsyncFileStorage(const QString &storageFolderPath, QObject *parent = nullptr);
|
explicit AsyncFileStorage(const Path &storageFolderPath, QObject *parent = nullptr);
|
||||||
~AsyncFileStorage() override;
|
~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:
|
signals:
|
||||||
void failed(const QString &fileName, const QString &errorString);
|
void failed(const Path &filePath, const QString &errorString);
|
||||||
|
|
||||||
private:
|
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;
|
QFile m_lockFile;
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,6 +61,8 @@ HEADERS += \
|
||||||
$$PWD/net/reverseresolution.h \
|
$$PWD/net/reverseresolution.h \
|
||||||
$$PWD/net/smtp.h \
|
$$PWD/net/smtp.h \
|
||||||
$$PWD/orderedset.h \
|
$$PWD/orderedset.h \
|
||||||
|
$$PWD/path.h \
|
||||||
|
$$PWD/pathfwd.h \
|
||||||
$$PWD/preferences.h \
|
$$PWD/preferences.h \
|
||||||
$$PWD/profile.h \
|
$$PWD/profile.h \
|
||||||
$$PWD/profile_p.h \
|
$$PWD/profile_p.h \
|
||||||
|
@ -144,6 +146,7 @@ SOURCES += \
|
||||||
$$PWD/net/proxyconfigurationmanager.cpp \
|
$$PWD/net/proxyconfigurationmanager.cpp \
|
||||||
$$PWD/net/reverseresolution.cpp \
|
$$PWD/net/reverseresolution.cpp \
|
||||||
$$PWD/net/smtp.cpp \
|
$$PWD/net/smtp.cpp \
|
||||||
|
$$PWD/path.cpp \
|
||||||
$$PWD/preferences.cpp \
|
$$PWD/preferences.cpp \
|
||||||
$$PWD/profile.cpp \
|
$$PWD/profile.cpp \
|
||||||
$$PWD/profile_p.cpp \
|
$$PWD/profile_p.cpp \
|
||||||
|
|
|
@ -33,93 +33,63 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
void BitTorrent::AbstractFileStorage::renameFile(const Path &oldPath, const Path &newPath)
|
||||||
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseInsensitive};
|
|
||||||
#else
|
|
||||||
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseSensitive};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
{
|
||||||
bool areSameFileNames(QString first, QString second)
|
if (!oldPath.isValid())
|
||||||
{
|
throw RuntimeError(tr("The old path is invalid: '%1'.").arg(oldPath.toString()));
|
||||||
return QString::compare(first, second, CASE_SENSITIVITY) == 0;
|
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()));
|
||||||
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)};
|
|
||||||
|
|
||||||
int renamingFileIndex = -1;
|
int renamingFileIndex = -1;
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
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;
|
renamingFileIndex = i;
|
||||||
else if (areSameFileNames(path, newFilePath))
|
else if (path == newPath)
|
||||||
throw RuntimeError {tr("The file already exists: '%1'.").arg(newFilePath)};
|
throw RuntimeError(tr("The file already exists: '%1'.").arg(newPath.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renamingFileIndex < 0)
|
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))
|
if (!oldFolderPath.isValid())
|
||||||
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
throw RuntimeError(tr("The old path is invalid: '%1'.").arg(oldFolderPath.toString()));
|
||||||
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
if (!newFolderPath.isValid())
|
||||||
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
throw RuntimeError(tr("The new path is invalid: '%1'.").arg(newFolderPath.toString()));
|
||||||
|
if (newFolderPath.isAbsolute())
|
||||||
const auto cleanFolderPath = [](const QString &path) -> QString
|
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath.toString()));
|
||||||
{
|
|
||||||
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)};
|
|
||||||
|
|
||||||
QVector<int> renamingFileIndexes;
|
QVector<int> renamingFileIndexes;
|
||||||
renamingFileIndexes.reserve(filesCount());
|
renamingFileIndexes.reserve(filesCount());
|
||||||
|
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
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);
|
renamingFileIndexes.append(i);
|
||||||
else if (path.startsWith(newFolderPath, CASE_SENSITIVITY))
|
else if (path.hasAncestor(newFolderPath))
|
||||||
throw RuntimeError {tr("The folder already exists: '%1'.").arg(newFolderPath)};
|
throw RuntimeError(tr("The folder already exists: '%1'.").arg(newFolderPath.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renamingFileIndexes.isEmpty())
|
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)
|
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);
|
renameFile(index, newFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
|
@ -43,12 +45,12 @@ namespace BitTorrent
|
||||||
virtual ~AbstractFileStorage() = default;
|
virtual ~AbstractFileStorage() = default;
|
||||||
|
|
||||||
virtual int filesCount() const = 0;
|
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 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 renameFile(const Path &oldPath, const Path &newPath);
|
||||||
void renameFolder(const QString &oldPath, const QString &newPath);
|
void renameFolder(const Path &oldFolderPath, const Path &newFolderPath);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
#include "torrentcontentlayout.h"
|
#include "torrentcontentlayout.h"
|
||||||
|
@ -47,14 +48,14 @@ namespace BitTorrent
|
||||||
QString name;
|
QString name;
|
||||||
QString category;
|
QString category;
|
||||||
TagSet tags;
|
TagSet tags;
|
||||||
QString savePath;
|
Path savePath;
|
||||||
std::optional<bool> useDownloadPath;
|
std::optional<bool> useDownloadPath;
|
||||||
QString downloadPath;
|
Path downloadPath;
|
||||||
bool sequential = false;
|
bool sequential = false;
|
||||||
bool firstLastPiecePriority = false;
|
bool firstLastPiecePriority = false;
|
||||||
bool addForced = false;
|
bool addForced = false;
|
||||||
std::optional<bool> addPaused;
|
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
|
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
|
||||||
bool skipChecking = false;
|
bool skipChecking = false;
|
||||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||||
|
|
|
@ -58,14 +58,14 @@ namespace BitTorrent
|
||||||
Q_DISABLE_COPY_MOVE(Worker)
|
Q_DISABLE_COPY_MOVE(Worker)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Worker(const QDir &resumeDataDir);
|
explicit Worker(const Path &resumeDataDir);
|
||||||
|
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
||||||
void remove(const TorrentID &id) const;
|
void remove(const TorrentID &id) const;
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||||
|
|
||||||
private:
|
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}
|
: ResumeDataStorage {parent}
|
||||||
, m_resumeDataDir {path}
|
, m_resumeDataPath {path}
|
||||||
, m_ioThread {new QThread {this}}
|
, 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\"")
|
throw RuntimeError(tr("Cannot create torrent resume folder: \"%1\"")
|
||||||
.arg(Utils::Fs::toNativePath(m_resumeDataDir.absolutePath()))};
|
.arg(m_resumeDataPath.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRegularExpression filenamePattern {QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$")};
|
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());
|
m_registeredTorrents.reserve(filenames.size());
|
||||||
for (const QString &filename : filenames)
|
for (const QString &filename : filenames)
|
||||||
|
@ -112,7 +114,7 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &pa
|
||||||
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
|
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();
|
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
|
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::load(const TorrentID &id) const
|
||||||
{
|
{
|
||||||
const QString idString = id.toString();
|
const QString idString = id.toString();
|
||||||
const QString fastresumePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(idString));
|
const Path fastresumePath = m_resumeDataPath / Path(idString + QLatin1String(".fastresume"));
|
||||||
const QString torrentFilePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(idString));
|
const Path torrentFilePath = m_resumeDataPath / Path(idString + QLatin1String(".torrent"));
|
||||||
|
|
||||||
QFile resumeDataFile {fastresumePath};
|
QFile resumeDataFile {fastresumePath.data()};
|
||||||
if (!resumeDataFile.open(QIODevice::ReadOnly))
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile metadataFile {torrentFilePath};
|
QFile metadataFile {torrentFilePath.data()};
|
||||||
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
|
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;
|
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.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||||
|
|
||||||
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
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();
|
torrentParams.useAutoTMM = torrentParams.savePath.isEmpty();
|
||||||
if (!torrentParams.useAutoTMM)
|
if (!torrentParams.useAutoTMM)
|
||||||
{
|
{
|
||||||
torrentParams.downloadPath = Profile::instance()->fromPortablePath(
|
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.
|
// 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;
|
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
|
||||||
|
|
||||||
p = lt::read_resume_data(root, ec);
|
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)
|
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())
|
if (!queueFile.exists())
|
||||||
return;
|
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}
|
: m_resumeDataDir {resumeDataDir}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -315,7 +318,8 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||||
{
|
{
|
||||||
// We need to adjust native libtorrent resume data
|
// We need to adjust native libtorrent resume data
|
||||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
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)
|
if (resumeData.stopped)
|
||||||
{
|
{
|
||||||
p.flags |= lt::torrent_flags::paused;
|
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("created by"));
|
||||||
metadataDict.insert(dataDict.extract("comment"));
|
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);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(torrentFilepath, metadata);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,26 +374,26 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||||
|
|
||||||
if (!resumeData.useAutoTMM)
|
if (!resumeData.useAutoTMM)
|
||||||
{
|
{
|
||||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
|
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).data().toStdString();
|
||||||
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).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);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
|
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
|
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
|
||||||
{
|
{
|
||||||
const QString resumeFilename = QString::fromLatin1("%1.fastresume").arg(id.toString());
|
const Path resumeFilename {QString::fromLatin1("%1.fastresume").arg(id.toString())};
|
||||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(resumeFilename));
|
Utils::Fs::removeFile(m_resumeDataDir / resumeFilename);
|
||||||
|
|
||||||
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(id.toString());
|
const Path torrentFilename {QString::fromLatin1("%1.torrent").arg(id.toString())};
|
||||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(torrentFilename));
|
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
|
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)
|
for (const BitTorrent::TorrentID &torrentID : queue)
|
||||||
data += (torrentID.toString().toLatin1() + '\n');
|
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);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filepath, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
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 <QDir>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
|
@ -44,7 +45,7 @@ namespace BitTorrent
|
||||||
Q_DISABLE_COPY_MOVE(BencodeResumeDataStorage)
|
Q_DISABLE_COPY_MOVE(BencodeResumeDataStorage)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BencodeResumeDataStorage(const QString &path, QObject *parent = nullptr);
|
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
||||||
~BencodeResumeDataStorage() override;
|
~BencodeResumeDataStorage() override;
|
||||||
|
|
||||||
QVector<TorrentID> registeredTorrents() const override;
|
QVector<TorrentID> registeredTorrents() const override;
|
||||||
|
@ -54,10 +55,10 @@ namespace BitTorrent
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadQueue(const QString &queueFilename);
|
void loadQueue(const Path &queueFilename);
|
||||||
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
||||||
|
|
||||||
const QDir m_resumeDataDir;
|
const Path m_resumeDataPath;
|
||||||
QVector<TorrentID> m_registeredTorrents;
|
QVector<TorrentID> m_registeredTorrents;
|
||||||
QThread *m_ioThread = nullptr;
|
QThread *m_ioThread = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -37,13 +37,13 @@ const QString OPTION_DOWNLOADPATH {QStringLiteral("download_path")};
|
||||||
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
||||||
{
|
{
|
||||||
CategoryOptions options;
|
CategoryOptions options;
|
||||||
options.savePath = jsonObj.value(OPTION_SAVEPATH).toString();
|
options.savePath = Path(jsonObj.value(OPTION_SAVEPATH).toString());
|
||||||
|
|
||||||
const QJsonValue downloadPathValue = jsonObj.value(OPTION_DOWNLOADPATH);
|
const QJsonValue downloadPathValue = jsonObj.value(OPTION_DOWNLOADPATH);
|
||||||
if (downloadPathValue.isBool())
|
if (downloadPathValue.isBool())
|
||||||
options.downloadPath = {downloadPathValue.toBool(), {}};
|
options.downloadPath = {downloadPathValue.toBool(), {}};
|
||||||
else if (downloadPathValue.isString())
|
else if (downloadPathValue.isString())
|
||||||
options.downloadPath = {true, downloadPathValue.toString()};
|
options.downloadPath = {true, Path(downloadPathValue.toString())};
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -54,13 +54,13 @@ QJsonObject BitTorrent::CategoryOptions::toJSON() const
|
||||||
if (downloadPath)
|
if (downloadPath)
|
||||||
{
|
{
|
||||||
if (downloadPath->enabled)
|
if (downloadPath->enabled)
|
||||||
downloadPathValue = downloadPath->path;
|
downloadPathValue = downloadPath->path.data();
|
||||||
else
|
else
|
||||||
downloadPathValue = false;
|
downloadPathValue = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{OPTION_SAVEPATH, savePath},
|
{OPTION_SAVEPATH, savePath.data()},
|
||||||
{OPTION_DOWNLOADPATH, downloadPathValue}
|
{OPTION_DOWNLOADPATH, downloadPathValue}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class QJsonObject;
|
class QJsonObject;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
|
@ -41,10 +43,10 @@ namespace BitTorrent
|
||||||
struct DownloadPathOption
|
struct DownloadPathOption
|
||||||
{
|
{
|
||||||
bool enabled;
|
bool enabled;
|
||||||
QString path;
|
Path path;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString savePath;
|
Path savePath;
|
||||||
std::optional<DownloadPathOption> downloadPath;
|
std::optional<DownloadPathOption> downloadPath;
|
||||||
|
|
||||||
static CategoryOptions fromJSON(const QJsonObject &jsonObj);
|
static CategoryOptions fromJSON(const QJsonObject &jsonObj);
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
|
|
||||||
#include <libtorrent/download_priority.hpp>
|
#include <libtorrent/download_priority.hpp>
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "common.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);
|
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] =
|
m_storageData[storageHolder] =
|
||||||
{
|
{
|
||||||
savePath
|
savePath,
|
||||||
, storageParams.mapped_files ? *storageParams.mapped_files : storageParams.files
|
storageParams.mapped_files ? *storageParams.mapped_files : storageParams.files,
|
||||||
, storageParams.priorities};
|
storageParams.priorities
|
||||||
|
};
|
||||||
|
|
||||||
return storageHolder;
|
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
|
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)
|
, 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)
|
if (flags == lt::move_flags_t::dont_replace)
|
||||||
handleCompleteFiles(storage, newSavePath);
|
handleCompleteFiles(storage, newSavePath);
|
||||||
|
@ -192,9 +191,8 @@ void CustomDiskIOThread::settings_updated()
|
||||||
m_nativeDiskIO->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 StorageData storageData = m_storageData[storage];
|
||||||
const lt::file_storage &fileStorage = storageData.files;
|
const lt::file_storage &fileStorage = storageData.files;
|
||||||
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
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
|
// ignore pad files
|
||||||
if (fileStorage.pad_file_at(fileIndex)) continue;
|
if (fileStorage.pad_file_at(fileIndex)) continue;
|
||||||
|
|
||||||
const QString filePath = QString::fromStdString(fileStorage.file_path(fileIndex));
|
const Path filePath {fileStorage.file_path(fileIndex)};
|
||||||
if (filePath.endsWith(QB_EXT))
|
if (filePath.hasExtension(QB_EXT))
|
||||||
{
|
{
|
||||||
const QString completeFilePath = filePath.left(filePath.size() - QB_EXT.size());
|
const Path incompleteFilePath = savePath / filePath;
|
||||||
QFile completeFile {saveDir.absoluteFilePath(completeFilePath)};
|
Path completeFilePath = incompleteFilePath;
|
||||||
if (completeFile.exists())
|
completeFilePath.removeExtension();
|
||||||
|
if (completeFilePath.exists())
|
||||||
{
|
{
|
||||||
QFile incompleteFile {saveDir.absoluteFilePath(filePath)};
|
Utils::Fs::removeFile(incompleteFilePath);
|
||||||
incompleteFile.remove();
|
Utils::Fs::renameFile(completeFilePath, incompleteFilePath);
|
||||||
completeFile.rename(incompleteFile.fileName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +228,7 @@ lt::storage_interface *customStorageConstructor(const lt::storage_params ¶ms
|
||||||
|
|
||||||
CustomStorage::CustomStorage(const lt::storage_params ¶ms, lt::file_pool &filePool)
|
CustomStorage::CustomStorage(const lt::storage_params ¶ms, lt::file_pool &filePool)
|
||||||
: lt::default_storage {params, 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)
|
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)
|
if (flags == lt::move_flags_t::dont_replace)
|
||||||
handleCompleteFiles(newSavePath);
|
handleCompleteFiles(newSavePath);
|
||||||
|
@ -260,10 +258,8 @@ lt::status_t CustomStorage::move_storage(const std::string &savePath, lt::move_f
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomStorage::handleCompleteFiles(const QString &savePath)
|
void CustomStorage::handleCompleteFiles(const Path &savePath)
|
||||||
{
|
{
|
||||||
const QDir saveDir {savePath};
|
|
||||||
|
|
||||||
const lt::file_storage &fileStorage = files();
|
const lt::file_storage &fileStorage = files();
|
||||||
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
for (const lt::file_index_t fileIndex : fileStorage.file_range())
|
||||||
{
|
{
|
||||||
|
@ -274,16 +270,16 @@ void CustomStorage::handleCompleteFiles(const QString &savePath)
|
||||||
// ignore pad files
|
// ignore pad files
|
||||||
if (fileStorage.pad_file_at(fileIndex)) continue;
|
if (fileStorage.pad_file_at(fileIndex)) continue;
|
||||||
|
|
||||||
const QString filePath = QString::fromStdString(fileStorage.file_path(fileIndex));
|
const Path filePath {fileStorage.file_path(fileIndex)};
|
||||||
if (filePath.endsWith(QB_EXT))
|
if (filePath.hasExtension(QB_EXT))
|
||||||
{
|
{
|
||||||
const QString completeFilePath = filePath.left(filePath.size() - QB_EXT.size());
|
const Path incompleteFilePath = savePath / filePath;
|
||||||
QFile completeFile {saveDir.absoluteFilePath(completeFilePath)};
|
Path completeFilePath = incompleteFilePath;
|
||||||
if (completeFile.exists())
|
completeFilePath.removeExtension();
|
||||||
|
if (completeFilePath.exists())
|
||||||
{
|
{
|
||||||
QFile incompleteFile {saveDir.absoluteFilePath(filePath)};
|
Utils::Fs::removeFile(incompleteFilePath);
|
||||||
incompleteFile.remove();
|
Utils::Fs::renameFile(completeFilePath, incompleteFilePath);
|
||||||
completeFile.rename(incompleteFile.fileName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
#include <libtorrent/disk_interface.hpp>
|
#include <libtorrent/disk_interface.hpp>
|
||||||
#include <libtorrent/file_storage.hpp>
|
#include <libtorrent/file_storage.hpp>
|
||||||
|
@ -86,13 +88,13 @@ public:
|
||||||
void settings_updated() override;
|
void settings_updated() override;
|
||||||
|
|
||||||
private:
|
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;
|
std::unique_ptr<lt::disk_interface> m_nativeDiskIO;
|
||||||
|
|
||||||
struct StorageData
|
struct StorageData
|
||||||
{
|
{
|
||||||
QString savePath;
|
Path savePath;
|
||||||
lt::file_storage files;
|
lt::file_storage files;
|
||||||
lt::aux::vector<lt::download_priority_t, lt::file_index_t> filePriorities;
|
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;
|
lt::status_t move_storage(const std::string &savePath, lt::move_flags_t flags, lt::storage_error &ec) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleCompleteFiles(const QString &savePath);
|
void handleCompleteFiles(const Path &savePath);
|
||||||
|
|
||||||
lt::aux::vector<lt::download_priority_t, lt::file_index_t> m_filePriorities;
|
lt::aux::vector<lt::download_priority_t, lt::file_index_t> m_filePriorities;
|
||||||
QString m_savePath;
|
Path m_savePath;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
|
@ -173,7 +174,7 @@ namespace BitTorrent
|
||||||
Q_DISABLE_COPY_MOVE(Worker)
|
Q_DISABLE_COPY_MOVE(Worker)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Worker(const QString &dbPath, const QString &dbConnectionName);
|
Worker(const Path &dbPath, const QString &dbConnectionName);
|
||||||
|
|
||||||
void openDatabase() const;
|
void openDatabase() const;
|
||||||
void closeDatabase() const;
|
void closeDatabase() const;
|
||||||
|
@ -183,19 +184,19 @@ namespace BitTorrent
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString m_path;
|
const Path m_path;
|
||||||
const QString m_connectionName;
|
const QString m_connectionName;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const QString &dbPath, QObject *parent)
|
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
||||||
: ResumeDataStorage {parent}
|
: ResumeDataStorage {parent}
|
||||||
, m_ioThread {new QThread(this)}
|
, 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);
|
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), DB_CONNECTION_NAME);
|
||||||
db.setDatabaseName(dbPath);
|
db.setDatabaseName(dbPath.data());
|
||||||
if (!db.open())
|
if (!db.open())
|
||||||
throw RuntimeError(db.lastError().text());
|
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.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||||
|
|
||||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
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();
|
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
||||||
if (!resumeData.useAutoTMM)
|
if (!resumeData.useAutoTMM)
|
||||||
{
|
{
|
||||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
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();
|
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;
|
lt::error_code ec;
|
||||||
const lt::bdecode_node root = lt::bdecode(allData, 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;
|
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||||
|
|
||||||
p = lt::read_resume_data(root, ec);
|
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;
|
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_path {dbPath}
|
||||||
, m_connectionName {dbConnectionName}
|
, m_connectionName {dbConnectionName}
|
||||||
{
|
{
|
||||||
|
@ -494,7 +493,7 @@ BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QSt
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
|
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
|
||||||
{
|
{
|
||||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
|
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
|
||||||
db.setDatabaseName(m_path);
|
db.setDatabaseName(m_path.data());
|
||||||
if (!db.open())
|
if (!db.open())
|
||||||
throw RuntimeError(db.lastError().text());
|
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
|
// We need to adjust native libtorrent resume data
|
||||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
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)
|
if (resumeData.stopped)
|
||||||
{
|
{
|
||||||
p.flags |= lt::torrent_flags::paused;
|
p.flags |= lt::torrent_flags::paused;
|
||||||
|
@ -603,8 +603,8 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
||||||
|
|
||||||
if (!resumeData.useAutoTMM)
|
if (!resumeData.useAutoTMM)
|
||||||
{
|
{
|
||||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
|
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));
|
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath).data());
|
||||||
}
|
}
|
||||||
|
|
||||||
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
|
@ -40,7 +41,7 @@ namespace BitTorrent
|
||||||
Q_DISABLE_COPY_MOVE(DBResumeDataStorage)
|
Q_DISABLE_COPY_MOVE(DBResumeDataStorage)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DBResumeDataStorage(const QString &dbPath, QObject *parent = nullptr);
|
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
||||||
~DBResumeDataStorage() override;
|
~DBResumeDataStorage() override;
|
||||||
|
|
||||||
QVector<TorrentID> registeredTorrents() const override;
|
QVector<TorrentID> registeredTorrents() const override;
|
||||||
|
|
|
@ -27,37 +27,33 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "filesearcher.h"
|
#include "filesearcher.h"
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#include "base/bittorrent/common.h"
|
#include "base/bittorrent/common.h"
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
|
|
||||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||||
, const QString &savePath, const QString &downloadPath)
|
, 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;
|
bool found = false;
|
||||||
for (QString &fileName : fileNames)
|
for (Path &fileName : fileNames)
|
||||||
{
|
{
|
||||||
if (dir.exists(fileName))
|
if ((dirPath / fileName).exists())
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
else if (dir.exists(fileName + QB_EXT))
|
else if ((dirPath / fileName + QB_EXT).exists())
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
fileName += QB_EXT;
|
fileName = fileName + QB_EXT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString usedPath = savePath;
|
Path usedPath = savePath;
|
||||||
QStringList adjustedFileNames = originalFileNames;
|
PathList adjustedFileNames = originalFileNames;
|
||||||
const bool found = findInDir(usedPath, adjustedFileNames);
|
const bool found = findInDir(usedPath, adjustedFileNames);
|
||||||
if (!found && !downloadPath.isEmpty())
|
if (!found && !downloadPath.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
class TorrentID;
|
class TorrentID;
|
||||||
|
@ -44,9 +46,9 @@ public:
|
||||||
FileSearcher() = default;
|
FileSearcher() = default;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||||
, const QString &savePath, const QString &downloadPath);
|
, const Path &savePath, const Path &downloadPath);
|
||||||
|
|
||||||
signals:
|
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 FilterParserThread::parseDATFilterFile()
|
||||||
{
|
{
|
||||||
int ruleCount = 0;
|
int ruleCount = 0;
|
||||||
QFile file(m_filePath);
|
QFile file {m_filePath.data()};
|
||||||
if (!file.exists()) return ruleCount;
|
if (!file.exists()) return ruleCount;
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
@ -288,7 +288,7 @@ int FilterParserThread::parseDATFilterFile()
|
||||||
int FilterParserThread::parseP2PFilterFile()
|
int FilterParserThread::parseP2PFilterFile()
|
||||||
{
|
{
|
||||||
int ruleCount = 0;
|
int ruleCount = 0;
|
||||||
QFile file(m_filePath);
|
QFile file {m_filePath.data()};
|
||||||
if (!file.exists()) return ruleCount;
|
if (!file.exists()) return ruleCount;
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
@ -469,7 +469,7 @@ int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name,
|
||||||
int FilterParserThread::parseP2BFilterFile()
|
int FilterParserThread::parseP2BFilterFile()
|
||||||
{
|
{
|
||||||
int ruleCount = 0;
|
int ruleCount = 0;
|
||||||
QFile file(m_filePath);
|
QFile file {m_filePath.data()};
|
||||||
if (!file.exists()) return ruleCount;
|
if (!file.exists()) return ruleCount;
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
@ -592,7 +592,7 @@ int FilterParserThread::parseP2BFilterFile()
|
||||||
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
||||||
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
||||||
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_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())
|
if (isRunning())
|
||||||
{
|
{
|
||||||
|
@ -617,17 +617,17 @@ void FilterParserThread::run()
|
||||||
{
|
{
|
||||||
qDebug("Processing filter file");
|
qDebug("Processing filter file");
|
||||||
int ruleCount = 0;
|
int ruleCount = 0;
|
||||||
if (m_filePath.endsWith(".p2p", Qt::CaseInsensitive))
|
if (m_filePath.hasExtension(QLatin1String(".p2p")))
|
||||||
{
|
{
|
||||||
// PeerGuardian p2p file
|
// PeerGuardian p2p file
|
||||||
ruleCount = parseP2PFilterFile();
|
ruleCount = parseP2PFilterFile();
|
||||||
}
|
}
|
||||||
else if (m_filePath.endsWith(".p2b", Qt::CaseInsensitive))
|
else if (m_filePath.hasExtension(QLatin1String(".p2b")))
|
||||||
{
|
{
|
||||||
// PeerGuardian p2b file
|
// PeerGuardian p2b file
|
||||||
ruleCount = parseP2BFilterFile();
|
ruleCount = parseP2BFilterFile();
|
||||||
}
|
}
|
||||||
else if (m_filePath.endsWith(".dat", Qt::CaseInsensitive))
|
else if (m_filePath.hasExtension(QLatin1String(".dat")))
|
||||||
{
|
{
|
||||||
// eMule DAT format
|
// eMule DAT format
|
||||||
ruleCount = parseDATFilterFile();
|
ruleCount = parseDATFilterFile();
|
||||||
|
|
|
@ -32,16 +32,19 @@
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class QDataStream;
|
class QDataStream;
|
||||||
|
|
||||||
class FilterParserThread final : public QThread
|
class FilterParserThread final : public QThread
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(FilterParserThread)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FilterParserThread(QObject *parent = nullptr);
|
FilterParserThread(QObject *parent = nullptr);
|
||||||
~FilterParserThread();
|
~FilterParserThread();
|
||||||
void processFilterFile(const QString &filePath);
|
void processFilterFile(const Path &filePath);
|
||||||
lt::ip_filter IPfilter();
|
lt::ip_filter IPfilter();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -60,6 +63,6 @@ private:
|
||||||
int parseP2BFilterFile();
|
int parseP2BFilterFile();
|
||||||
|
|
||||||
bool m_abort;
|
bool m_abort;
|
||||||
QString m_filePath;
|
Path m_filePath;
|
||||||
lt::ip_filter m_filter;
|
lt::ip_filter m_filter;
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
#include "torrentcontentlayout.h"
|
#include "torrentcontentlayout.h"
|
||||||
|
@ -45,8 +46,8 @@ namespace BitTorrent
|
||||||
QString name;
|
QString name;
|
||||||
QString category;
|
QString category;
|
||||||
TagSet tags;
|
TagSet tags;
|
||||||
QString savePath;
|
Path savePath;
|
||||||
QString downloadPath;
|
Path downloadPath;
|
||||||
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
||||||
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
||||||
bool useAutoTMM = false;
|
bool useAutoTMM = false;
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
const QString CATEGORIES_FILE_NAME {QStringLiteral("categories.json")};
|
const Path CATEGORIES_FILE_NAME {"categories.json"};
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -394,8 +394,8 @@ Session::Session(QObject *parent)
|
||||||
, clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
|
, clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
|
||||||
, m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
|
, m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
|
||||||
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
|
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
|
||||||
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), Utils::Fs::toUniformPath)
|
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads))
|
||||||
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp"), Utils::Fs::toUniformPath)
|
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), (savePath() / Path("temp")))
|
||||||
, m_isDownloadPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
|
, m_isDownloadPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
|
||||||
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
|
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
|
||||||
, m_useCategoryPathsInManualMode(BITTORRENT_SESSION_KEY("UseCategoryPathsInManualMode"), false)
|
, m_useCategoryPathsInManualMode(BITTORRENT_SESSION_KEY("UseCategoryPathsInManualMode"), false)
|
||||||
|
@ -594,36 +594,34 @@ void Session::setPreallocationEnabled(const bool enabled)
|
||||||
m_isPreallocationEnabled = 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())
|
if (path != torrentExportDirectory())
|
||||||
m_torrentExportDirectory = path;
|
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())
|
if (path != finishedTorrentExportDirectory())
|
||||||
m_finishedTorrentExportDirectory = path;
|
m_finishedTorrentExportDirectory = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Session::savePath() const
|
Path Session::savePath() const
|
||||||
{
|
{
|
||||||
return m_savePath;
|
return m_savePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Session::downloadPath() const
|
Path Session::downloadPath() const
|
||||||
{
|
{
|
||||||
return m_downloadPath;
|
return m_downloadPath;
|
||||||
}
|
}
|
||||||
|
@ -667,20 +665,20 @@ CategoryOptions Session::categoryOptions(const QString &categoryName) const
|
||||||
return m_categories.value(categoryName);
|
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())
|
if (categoryName.isEmpty())
|
||||||
return basePath;
|
return basePath;
|
||||||
|
|
||||||
QString path = m_categories.value(categoryName).savePath;
|
Path path = m_categories.value(categoryName).savePath;
|
||||||
if (path.isEmpty()) // use implicit save path
|
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 categoryOptions = m_categories.value(categoryName);
|
||||||
const CategoryOptions::DownloadPathOption downloadPathOption =
|
const CategoryOptions::DownloadPathOption downloadPathOption =
|
||||||
|
@ -688,15 +686,15 @@ QString Session::categoryDownloadPath(const QString &categoryName) const
|
||||||
if (!downloadPathOption.enabled)
|
if (!downloadPathOption.enabled)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const QString basePath = downloadPath();
|
const Path basePath = downloadPath();
|
||||||
if (categoryName.isEmpty())
|
if (categoryName.isEmpty())
|
||||||
return basePath;
|
return basePath;
|
||||||
|
|
||||||
const QString path = (!downloadPathOption.path.isEmpty()
|
const Path path = (!downloadPathOption.path.isEmpty()
|
||||||
? downloadPathOption.path
|
? 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)
|
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);
|
TorrentImpl *torrent = m_torrents.value(id);
|
||||||
if (torrent)
|
if (torrent)
|
||||||
|
@ -1739,11 +1737,11 @@ void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, c
|
||||||
|
|
||||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
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 TorrentInfo torrentInfo {*p.ti};
|
||||||
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||||
for (int i = 0; i < fileNames.size(); ++i)
|
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);
|
loadTorrent(params);
|
||||||
}
|
}
|
||||||
|
@ -1811,7 +1809,7 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
|
||||||
// Remove it from session
|
// Remove it from session
|
||||||
if (deleteOption == DeleteTorrent)
|
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 lt::torrent_handle nativeHandle {torrent->nativeHandle()};
|
||||||
const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
|
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())
|
if (magnetUri.isValid())
|
||||||
return addTorrent(magnetUri, params);
|
return addTorrent(magnetUri, params);
|
||||||
|
|
||||||
TorrentFileGuard guard {source};
|
const Path path {source};
|
||||||
const nonstd::expected<TorrentInfo, QString> loadResult = TorrentInfo::loadFromFile(source);
|
TorrentFileGuard guard {path};
|
||||||
|
const nonstd::expected<TorrentInfo, QString> loadResult = TorrentInfo::loadFromFile(path);
|
||||||
if (!loadResult)
|
if (!loadResult)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't load torrent: %1").arg(loadResult.error()), Log::WARNING);
|
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 (!loadTorrentParams.useAutoTMM)
|
||||||
{
|
{
|
||||||
if (QDir::isAbsolutePath(addTorrentParams.savePath))
|
if (addTorrentParams.savePath.isAbsolute())
|
||||||
{
|
{
|
||||||
loadTorrentParams.savePath = addTorrentParams.savePath;
|
loadTorrentParams.savePath = addTorrentParams.savePath;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
const Path basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
||||||
loadTorrentParams.savePath = Utils::Fs::resolvePath(addTorrentParams.savePath, basePath);
|
loadTorrentParams.savePath = basePath / addTorrentParams.savePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(isDownloadPathEnabled());
|
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(isDownloadPathEnabled());
|
||||||
if (useDownloadPath)
|
if (useDownloadPath)
|
||||||
{
|
{
|
||||||
if (QDir::isAbsolutePath(addTorrentParams.downloadPath))
|
if (addTorrentParams.downloadPath.isAbsolute())
|
||||||
{
|
{
|
||||||
loadTorrentParams.downloadPath = addTorrentParams.downloadPath;
|
loadTorrentParams.downloadPath = addTorrentParams.downloadPath;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
const Path basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
||||||
loadTorrentParams.downloadPath = Utils::Fs::resolvePath(addTorrentParams.downloadPath, basePath);
|
loadTorrentParams.downloadPath = basePath / addTorrentParams.downloadPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2165,7 +2164,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||||
bool isFindingIncompleteFiles = false;
|
bool isFindingIncompleteFiles = false;
|
||||||
|
|
||||||
const bool useAutoTMM = loadTorrentParams.useAutoTMM;
|
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)
|
if (hasMetadata)
|
||||||
{
|
{
|
||||||
|
@ -2175,16 +2174,16 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||||
|
|
||||||
const TorrentContentLayout contentLayout = ((loadTorrentParams.contentLayout == TorrentContentLayout::Original)
|
const TorrentContentLayout contentLayout = ((loadTorrentParams.contentLayout == TorrentContentLayout::Original)
|
||||||
? detectContentLayout(torrentInfo.filePaths()) : loadTorrentParams.contentLayout);
|
? detectContentLayout(torrentInfo.filePaths()) : loadTorrentParams.contentLayout);
|
||||||
QStringList filePaths = (!addTorrentParams.filePaths.isEmpty() ? addTorrentParams.filePaths : torrentInfo.filePaths());
|
PathList filePaths = (!addTorrentParams.filePaths.isEmpty() ? addTorrentParams.filePaths : torrentInfo.filePaths());
|
||||||
applyContentLayout(filePaths, contentLayout, Utils::Fs::findRootFolder(torrentInfo.filePaths()));
|
applyContentLayout(filePaths, contentLayout, Path::findRootFolder(torrentInfo.filePaths()));
|
||||||
|
|
||||||
// if torrent name wasn't explicitly set we handle the case of
|
// if torrent name wasn't explicitly set we handle the case of
|
||||||
// initial renaming of torrent content and rename torrent accordingly
|
// initial renaming of torrent content and rename torrent accordingly
|
||||||
if (loadTorrentParams.name.isEmpty())
|
if (loadTorrentParams.name.isEmpty())
|
||||||
{
|
{
|
||||||
QString contentName = Utils::Fs::findRootFolder(filePaths);
|
QString contentName = Path::findRootFolder(filePaths).toString();
|
||||||
if (contentName.isEmpty() && (filePaths.size() == 1))
|
if (contentName.isEmpty() && (filePaths.size() == 1))
|
||||||
contentName = Utils::Fs::fileName(filePaths.at(0));
|
contentName = filePaths.at(0).filename();
|
||||||
|
|
||||||
if (!contentName.isEmpty() && (contentName != torrentInfo.name()))
|
if (!contentName.isEmpty() && (contentName != torrentInfo.name()))
|
||||||
loadTorrentParams.name = contentName;
|
loadTorrentParams.name = contentName;
|
||||||
|
@ -2192,7 +2191,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||||
|
|
||||||
if (!loadTorrentParams.hasSeedStatus)
|
if (!loadTorrentParams.hasSeedStatus)
|
||||||
{
|
{
|
||||||
const QString actualDownloadPath = useAutoTMM
|
const Path actualDownloadPath = useAutoTMM
|
||||||
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||||
findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths);
|
findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths);
|
||||||
isFindingIncompleteFiles = true;
|
isFindingIncompleteFiles = true;
|
||||||
|
@ -2202,7 +2201,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||||
if (!filePaths.isEmpty())
|
if (!filePaths.isEmpty())
|
||||||
{
|
{
|
||||||
for (int index = 0; index < addTorrentParams.filePaths.size(); ++index)
|
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());
|
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);
|
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.upload_limit = addTorrentParams.uploadLimit;
|
||||||
p.download_limit = addTorrentParams.downloadLimit;
|
p.download_limit = addTorrentParams.downloadLimit;
|
||||||
|
@ -2288,13 +2287,13 @@ bool Session::loadTorrent(LoadTorrentParams params)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||||
, const QString &downloadPath, const QStringList &filePaths) const
|
, const Path &downloadPath, const PathList &filePaths) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
||||||
|
|
||||||
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
|
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, [=]()
|
QMetaObject::invokeMethod(m_fileSearcher, [=]()
|
||||||
{
|
{
|
||||||
m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath);
|
m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath);
|
||||||
|
@ -2333,8 +2332,8 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
|
||||||
p.max_connections = maxConnectionsPerTorrent();
|
p.max_connections = maxConnectionsPerTorrent();
|
||||||
p.max_uploads = maxUploadsPerTorrent();
|
p.max_uploads = maxUploadsPerTorrent();
|
||||||
|
|
||||||
const QString savePath = Utils::Fs::tempPath() + id.toString();
|
const Path savePath = Utils::Fs::tempPath() / Path(id.toString());
|
||||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
p.save_path = savePath.toString().toStdString();
|
||||||
|
|
||||||
// Forced start
|
// Forced start
|
||||||
p.flags &= ~lt::torrent_flags::paused;
|
p.flags &= ~lt::torrent_flags::paused;
|
||||||
|
@ -2360,28 +2359,27 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
|
||||||
return true;
|
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);
|
if (!folderPath.exists() && !Utils::Fs::mkpath(folderPath))
|
||||||
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
return;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nonstd::expected<void, QString> result = torrentInfo.saveToFile(newTorrentPath);
|
const QString validName = Utils::Fs::toValidFileName(baseName);
|
||||||
if (!result)
|
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
||||||
{
|
Path newTorrentPath = folderPath / Path(torrentExportFilename);
|
||||||
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
|
int counter = 0;
|
||||||
.arg(newTorrentPath, result.error()), Log::WARNING);
|
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({});
|
m_resumeDataStorage->storeQueue({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setSavePath(const QString &path)
|
void Session::setSavePath(const Path &path)
|
||||||
{
|
{
|
||||||
const QString baseSavePath = specialFolderLocation(SpecialFolder::Downloads);
|
const auto newPath = (path.isAbsolute() ? path : (specialFolderLocation(SpecialFolder::Downloads) / path));
|
||||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseSavePath));
|
if (newPath == m_savePath)
|
||||||
if (resolvedPath == m_savePath) return;
|
return;
|
||||||
|
|
||||||
m_savePath = resolvedPath;
|
m_savePath = newPath;
|
||||||
|
|
||||||
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
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 Path newPath = (path.isAbsolute() ? path : (savePath() / Path("temp") / path));
|
||||||
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseDownloadPath));
|
if (newPath != m_downloadPath)
|
||||||
if (resolvedPath != m_downloadPath)
|
|
||||||
{
|
{
|
||||||
m_downloadPath = resolvedPath;
|
m_downloadPath = newPath;
|
||||||
|
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
torrent->handleDownloadPathChanged();
|
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())
|
if (path != IPFilterFile())
|
||||||
{
|
{
|
||||||
m_IPFilterFile = path;
|
m_IPFilterFile = path;
|
||||||
|
@ -3994,13 +3990,13 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
||||||
|
|
||||||
qDebug("Checking if the torrent contains torrent files to download");
|
qDebug("Checking if the torrent contains torrent files to download");
|
||||||
// Check if there are torrent files inside
|
// 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.");
|
qDebug("Found possible recursive torrent download.");
|
||||||
const QString torrentFullpath = torrent->actualStorageLocation() + '/' + torrentRelpath;
|
const Path torrentFullpath = torrent->actualStorageLocation() / torrentRelpath;
|
||||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath.toString()));
|
||||||
if (TorrentInfo::loadFromFile(torrentFullpath))
|
if (TorrentInfo::loadFromFile(torrentFullpath))
|
||||||
{
|
{
|
||||||
qDebug("emitting recursiveTorrentDownloadPossible()");
|
qDebug("emitting recursiveTorrentDownloadPossible()");
|
||||||
|
@ -4010,7 +4006,7 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qDebug("Caught error loading torrent");
|
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);
|
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);
|
Q_ASSERT(torrent);
|
||||||
|
|
||||||
const lt::torrent_handle torrentHandle = torrent->nativeHandle();
|
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)
|
if (m_moveStorageQueue.size() > 1)
|
||||||
{
|
{
|
||||||
|
@ -4065,7 +4061,7 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
|
||||||
if (iter != m_moveStorageQueue.end())
|
if (iter != m_moveStorageQueue.end())
|
||||||
{
|
{
|
||||||
// remove existing inactive job
|
// 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 = m_moveStorageQueue.erase(iter);
|
||||||
|
|
||||||
iter = std::find_if(iter, m_moveStorageQueue.end(), [&torrentHandle](const MoveStorageJob &job)
|
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
|
// if there is active job for this torrent prevent creating meaningless
|
||||||
// job that will move torrent to the same location as current one
|
// 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.")
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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.")
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode};
|
const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode};
|
||||||
m_moveStorageQueue << moveStorageJob;
|
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)
|
if (m_moveStorageQueue.size() == 1)
|
||||||
moveTorrentStorage(moveStorageJob);
|
moveTorrentStorage(moveStorageJob);
|
||||||
|
@ -4118,9 +4114,9 @@ void Session::moveTorrentStorage(const MoveStorageJob &job) const
|
||||||
#endif
|
#endif
|
||||||
const TorrentImpl *torrent = m_torrents.value(id);
|
const TorrentImpl *torrent = m_torrents.value(id);
|
||||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
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)
|
, ((job.mode == MoveStorageMode::Overwrite)
|
||||||
? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace));
|
? 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();
|
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 QByteArray data = QJsonDocument(jsonObj).toJson();
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't store Categories configuration to %1. Error: %2")
|
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();
|
const QString categoryName = it.key();
|
||||||
CategoryOptions categoryOptions;
|
CategoryOptions categoryOptions;
|
||||||
categoryOptions.savePath = it.value().toString();
|
categoryOptions.savePath = Path(it.value().toString());
|
||||||
m_categories[categoryName] = categoryOptions;
|
m_categories[categoryName] = categoryOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4192,7 +4188,7 @@ void Session::loadCategories()
|
||||||
{
|
{
|
||||||
m_categories.clear();
|
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())
|
if (!confFile.exists())
|
||||||
{
|
{
|
||||||
// TODO: Remove the following upgrade code in v4.5
|
// 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);
|
TorrentImpl *const torrent = m_torrents.value(id);
|
||||||
if (!torrent) return;
|
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'"
|
LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"
|
||||||
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
|
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
|
||||||
.arg(Utils::Fs::toNativePath(torrentRelpath), torrent->name()));
|
.arg(torrentRelpath.toString(), torrent->name()));
|
||||||
const QString torrentFullpath = torrent->savePath() + '/' + torrentRelpath;
|
const Path torrentFullpath = torrent->savePath() / torrentRelpath;
|
||||||
|
|
||||||
AddTorrentParams params;
|
AddTorrentParams params;
|
||||||
// Passing the save path along to the sub torrent file
|
// Passing the save path along to the sub torrent file
|
||||||
|
@ -4343,9 +4339,8 @@ void Session::startUpTorrents()
|
||||||
{
|
{
|
||||||
qDebug("Initializing torrents resume data storage...");
|
qDebug("Initializing torrents resume data storage...");
|
||||||
|
|
||||||
const QString dbPath = Utils::Fs::expandPathAbs(
|
const Path dbPath = specialFolderLocation(SpecialFolder::Data) / Path("torrents.db");
|
||||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/torrents.db"));
|
const bool dbStorageExists = dbPath.exists();
|
||||||
const bool dbStorageExists = QFile::exists(dbPath);
|
|
||||||
|
|
||||||
ResumeDataStorage *startupStorage = nullptr;
|
ResumeDataStorage *startupStorage = nullptr;
|
||||||
if (resumeDataStorageType() == ResumeDataStorageType::SQLite)
|
if (resumeDataStorageType() == ResumeDataStorageType::SQLite)
|
||||||
|
@ -4354,15 +4349,13 @@ void Session::startUpTorrents()
|
||||||
|
|
||||||
if (!dbStorageExists)
|
if (!dbStorageExists)
|
||||||
{
|
{
|
||||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
const Path dataPath = specialFolderLocation(SpecialFolder::Data) / Path("BT_backup");
|
||||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/BT_backup"));
|
|
||||||
startupStorage = new BencodeResumeDataStorage(dataPath, this);
|
startupStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
const Path dataPath = specialFolderLocation(SpecialFolder::Data) / Path("BT_backup");
|
||||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("/BT_backup"));
|
|
||||||
m_resumeDataStorage = new BencodeResumeDataStorage(dataPath, this);
|
m_resumeDataStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||||
|
|
||||||
if (dbStorageExists)
|
if (dbStorageExists)
|
||||||
|
@ -4491,7 +4484,7 @@ void Session::startUpTorrents()
|
||||||
{
|
{
|
||||||
delete startupStorage;
|
delete startupStorage;
|
||||||
if (resumeDataStorageType() == ResumeDataStorageType::Legacy)
|
if (resumeDataStorageType() == ResumeDataStorageType::Legacy)
|
||||||
Utils::Fs::forceRemove(dbPath);
|
Utils::Fs::removeFile(dbPath);
|
||||||
|
|
||||||
if (isQueueingSystemEnabled())
|
if (isQueueingSystemEnabled())
|
||||||
m_resumeDataStorage->storeQueue(queue);
|
m_resumeDataStorage->storeQueue(queue);
|
||||||
|
@ -5064,7 +5057,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
||||||
const MoveStorageJob ¤tJob = m_moveStorageQueue.first();
|
const MoveStorageJob ¤tJob = m_moveStorageQueue.first();
|
||||||
Q_ASSERT(currentJob.torrentHandle == p->handle);
|
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);
|
Q_ASSERT(newPath == currentJob.path);
|
||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
@ -5075,7 +5068,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
||||||
|
|
||||||
TorrentImpl *torrent = m_torrents.value(id);
|
TorrentImpl *torrent = m_torrents.value(id);
|
||||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
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();
|
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 currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path);
|
||||||
const QString errorMessage = QString::fromStdString(p->message());
|
const QString errorMessage = QString::fromStdString(p->message());
|
||||||
LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.")
|
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();
|
handleMoveTorrentStorageJobFinished();
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "addtorrentparams.h"
|
#include "addtorrentparams.h"
|
||||||
|
@ -202,7 +203,7 @@ namespace BitTorrent
|
||||||
} disk;
|
} disk;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Session : public QObject
|
class Session final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(Session)
|
Q_DISABLE_COPY_MOVE(Session)
|
||||||
|
@ -212,10 +213,10 @@ namespace BitTorrent
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static Session *instance();
|
static Session *instance();
|
||||||
|
|
||||||
QString savePath() const;
|
Path savePath() const;
|
||||||
void setSavePath(const QString &path);
|
void setSavePath(const Path &path);
|
||||||
QString downloadPath() const;
|
Path downloadPath() const;
|
||||||
void setDownloadPath(const QString &path);
|
void setDownloadPath(const Path &path);
|
||||||
bool isDownloadPathEnabled() const;
|
bool isDownloadPathEnabled() const;
|
||||||
void setDownloadPathEnabled(bool enabled);
|
void setDownloadPathEnabled(bool enabled);
|
||||||
|
|
||||||
|
@ -225,8 +226,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
QStringList categories() const;
|
QStringList categories() const;
|
||||||
CategoryOptions categoryOptions(const QString &categoryName) const;
|
CategoryOptions categoryOptions(const QString &categoryName) const;
|
||||||
QString categorySavePath(const QString &categoryName) const;
|
Path categorySavePath(const QString &categoryName) const;
|
||||||
QString categoryDownloadPath(const QString &categoryName) const;
|
Path categoryDownloadPath(const QString &categoryName) const;
|
||||||
bool addCategory(const QString &name, const CategoryOptions &options = {});
|
bool addCategory(const QString &name, const CategoryOptions &options = {});
|
||||||
bool editCategory(const QString &name, const CategoryOptions &options);
|
bool editCategory(const QString &name, const CategoryOptions &options);
|
||||||
bool removeCategory(const QString &name);
|
bool removeCategory(const QString &name);
|
||||||
|
@ -283,10 +284,10 @@ namespace BitTorrent
|
||||||
void setRefreshInterval(int value);
|
void setRefreshInterval(int value);
|
||||||
bool isPreallocationEnabled() const;
|
bool isPreallocationEnabled() const;
|
||||||
void setPreallocationEnabled(bool enabled);
|
void setPreallocationEnabled(bool enabled);
|
||||||
QString torrentExportDirectory() const;
|
Path torrentExportDirectory() const;
|
||||||
void setTorrentExportDirectory(QString path);
|
void setTorrentExportDirectory(const Path &path);
|
||||||
QString finishedTorrentExportDirectory() const;
|
Path finishedTorrentExportDirectory() const;
|
||||||
void setFinishedTorrentExportDirectory(QString path);
|
void setFinishedTorrentExportDirectory(const Path &path);
|
||||||
|
|
||||||
int globalDownloadSpeedLimit() const;
|
int globalDownloadSpeedLimit() const;
|
||||||
void setGlobalDownloadSpeedLimit(int limit);
|
void setGlobalDownloadSpeedLimit(int limit);
|
||||||
|
@ -329,8 +330,8 @@ namespace BitTorrent
|
||||||
void setAdditionalTrackers(const QString &trackers);
|
void setAdditionalTrackers(const QString &trackers);
|
||||||
bool isIPFilteringEnabled() const;
|
bool isIPFilteringEnabled() const;
|
||||||
void setIPFilteringEnabled(bool enabled);
|
void setIPFilteringEnabled(bool enabled);
|
||||||
QString IPFilterFile() const;
|
Path IPFilterFile() const;
|
||||||
void setIPFilterFile(QString path);
|
void setIPFilterFile(const Path &path);
|
||||||
bool announceToAllTrackers() const;
|
bool announceToAllTrackers() const;
|
||||||
void setAnnounceToAllTrackers(bool val);
|
void setAnnounceToAllTrackers(bool val);
|
||||||
bool announceToAllTiers() const;
|
bool announceToAllTiers() const;
|
||||||
|
@ -501,10 +502,10 @@ namespace BitTorrent
|
||||||
void handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl);
|
void handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl);
|
||||||
void handleTorrentTrackerError(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
|
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||||
, const QString &downloadPath, const QStringList &filePaths = {}) const;
|
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void allTorrentsFinished();
|
void allTorrentsFinished();
|
||||||
|
@ -553,7 +554,7 @@ namespace BitTorrent
|
||||||
void handleIPFilterParsed(int ruleCount);
|
void handleIPFilterParsed(int ruleCount);
|
||||||
void handleIPFilterError();
|
void handleIPFilterError();
|
||||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
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))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
// Session reconfiguration triggers
|
// Session reconfiguration triggers
|
||||||
|
@ -565,14 +566,14 @@ namespace BitTorrent
|
||||||
struct MoveStorageJob
|
struct MoveStorageJob
|
||||||
{
|
{
|
||||||
lt::torrent_handle torrentHandle;
|
lt::torrent_handle torrentHandle;
|
||||||
QString path;
|
Path path;
|
||||||
MoveStorageMode mode;
|
MoveStorageMode mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RemovingTorrentData
|
struct RemovingTorrentData
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
QString pathToRemove;
|
Path pathToRemove;
|
||||||
DeleteOption deleteOption;
|
DeleteOption deleteOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -611,7 +612,7 @@ namespace BitTorrent
|
||||||
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
|
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
|
||||||
|
|
||||||
void updateSeedingLimitTimer();
|
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 handleAlert(const lt::alert *a);
|
||||||
void dispatchTorrentAlert(const lt::alert *a);
|
void dispatchTorrentAlert(const lt::alert *a);
|
||||||
|
@ -663,7 +664,7 @@ namespace BitTorrent
|
||||||
CachedSettingValue<bool> m_isPeXEnabled;
|
CachedSettingValue<bool> m_isPeXEnabled;
|
||||||
CachedSettingValue<bool> m_isIPFilteringEnabled;
|
CachedSettingValue<bool> m_isIPFilteringEnabled;
|
||||||
CachedSettingValue<bool> m_isTrackerFilteringEnabled;
|
CachedSettingValue<bool> m_isTrackerFilteringEnabled;
|
||||||
CachedSettingValue<QString> m_IPFilterFile;
|
CachedSettingValue<Path> m_IPFilterFile;
|
||||||
CachedSettingValue<bool> m_announceToAllTrackers;
|
CachedSettingValue<bool> m_announceToAllTrackers;
|
||||||
CachedSettingValue<bool> m_announceToAllTiers;
|
CachedSettingValue<bool> m_announceToAllTiers;
|
||||||
CachedSettingValue<int> m_asyncIOThreads;
|
CachedSettingValue<int> m_asyncIOThreads;
|
||||||
|
@ -721,8 +722,8 @@ namespace BitTorrent
|
||||||
CachedSettingValue<bool> m_isAppendExtensionEnabled;
|
CachedSettingValue<bool> m_isAppendExtensionEnabled;
|
||||||
CachedSettingValue<int> m_refreshInterval;
|
CachedSettingValue<int> m_refreshInterval;
|
||||||
CachedSettingValue<bool> m_isPreallocationEnabled;
|
CachedSettingValue<bool> m_isPreallocationEnabled;
|
||||||
CachedSettingValue<QString> m_torrentExportDirectory;
|
CachedSettingValue<Path> m_torrentExportDirectory;
|
||||||
CachedSettingValue<QString> m_finishedTorrentExportDirectory;
|
CachedSettingValue<Path> m_finishedTorrentExportDirectory;
|
||||||
CachedSettingValue<int> m_globalDownloadSpeedLimit;
|
CachedSettingValue<int> m_globalDownloadSpeedLimit;
|
||||||
CachedSettingValue<int> m_globalUploadSpeedLimit;
|
CachedSettingValue<int> m_globalUploadSpeedLimit;
|
||||||
CachedSettingValue<int> m_altGlobalDownloadSpeedLimit;
|
CachedSettingValue<int> m_altGlobalDownloadSpeedLimit;
|
||||||
|
@ -740,8 +741,8 @@ namespace BitTorrent
|
||||||
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
||||||
CachedSettingValue<QStringList> m_storedTags;
|
CachedSettingValue<QStringList> m_storedTags;
|
||||||
CachedSettingValue<int> m_maxRatioAction;
|
CachedSettingValue<int> m_maxRatioAction;
|
||||||
CachedSettingValue<QString> m_savePath;
|
CachedSettingValue<Path> m_savePath;
|
||||||
CachedSettingValue<QString> m_downloadPath;
|
CachedSettingValue<Path> m_downloadPath;
|
||||||
CachedSettingValue<bool> m_isDownloadPathEnabled;
|
CachedSettingValue<bool> m_isDownloadPathEnabled;
|
||||||
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
||||||
CachedSettingValue<bool> m_useCategoryPathsInManualMode;
|
CachedSettingValue<bool> m_useCategoryPathsInManualMode;
|
||||||
|
|
|
@ -29,10 +29,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtContainerFwd>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtContainerFwd>
|
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "abstractfilestorage.h"
|
#include "abstractfilestorage.h"
|
||||||
|
|
||||||
|
@ -169,13 +170,13 @@ namespace BitTorrent
|
||||||
|
|
||||||
virtual bool isAutoTMMEnabled() const = 0;
|
virtual bool isAutoTMMEnabled() const = 0;
|
||||||
virtual void setAutoTMMEnabled(bool enabled) = 0;
|
virtual void setAutoTMMEnabled(bool enabled) = 0;
|
||||||
virtual QString savePath() const = 0;
|
virtual Path savePath() const = 0;
|
||||||
virtual void setSavePath(const QString &savePath) = 0;
|
virtual void setSavePath(const Path &savePath) = 0;
|
||||||
virtual QString downloadPath() const = 0;
|
virtual Path downloadPath() const = 0;
|
||||||
virtual void setDownloadPath(const QString &downloadPath) = 0;
|
virtual void setDownloadPath(const Path &downloadPath) = 0;
|
||||||
virtual QString actualStorageLocation() const = 0;
|
virtual Path actualStorageLocation() const = 0;
|
||||||
virtual QString rootPath() const = 0;
|
virtual Path rootPath() const = 0;
|
||||||
virtual QString contentPath() const = 0;
|
virtual Path contentPath() const = 0;
|
||||||
virtual QString category() const = 0;
|
virtual QString category() const = 0;
|
||||||
virtual bool belongsToCategory(const QString &category) const = 0;
|
virtual bool belongsToCategory(const QString &category) const = 0;
|
||||||
virtual bool setCategory(const QString &category) = 0;
|
virtual bool setCategory(const QString &category) = 0;
|
||||||
|
@ -193,8 +194,8 @@ namespace BitTorrent
|
||||||
virtual qreal ratioLimit() const = 0;
|
virtual qreal ratioLimit() const = 0;
|
||||||
virtual int seedingTimeLimit() const = 0;
|
virtual int seedingTimeLimit() const = 0;
|
||||||
|
|
||||||
virtual QString actualFilePath(int index) const = 0;
|
virtual Path actualFilePath(int index) const = 0;
|
||||||
virtual QStringList filePaths() const = 0;
|
virtual PathList filePaths() const = 0;
|
||||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||||
|
|
||||||
virtual TorrentInfo info() const = 0;
|
virtual TorrentInfo info() const = 0;
|
||||||
|
|
|
@ -32,36 +32,35 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
QString removeExtension(const QString &fileName)
|
Path removeExtension(const Path &fileName)
|
||||||
{
|
{
|
||||||
const QString extension = Utils::Fs::fileExtension(fileName);
|
Path result = fileName;
|
||||||
return extension.isEmpty()
|
result.removeExtension();
|
||||||
? fileName
|
return result;
|
||||||
: fileName.chopped(extension.size() + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
return (rootFolder.isEmpty()
|
||||||
? TorrentContentLayout::NoSubfolder
|
? TorrentContentLayout::NoSubfolder
|
||||||
: TorrentContentLayout::Subfolder);
|
: 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());
|
Q_ASSERT(!filePaths.isEmpty());
|
||||||
|
|
||||||
switch (contentLayout)
|
switch (contentLayout)
|
||||||
{
|
{
|
||||||
case TorrentContentLayout::Subfolder:
|
case TorrentContentLayout::Subfolder:
|
||||||
if (Utils::Fs::findRootFolder(filePaths).isEmpty())
|
if (Path::findRootFolder(filePaths).isEmpty())
|
||||||
Utils::Fs::addRootFolder(filePaths, !rootFolder.isEmpty() ? rootFolder : removeExtension(filePaths.at(0)));
|
Path::addRootFolder(filePaths, !rootFolder.isEmpty() ? rootFolder : removeExtension(filePaths.at(0)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TorrentContentLayout::NoSubfolder:
|
case TorrentContentLayout::NoSubfolder:
|
||||||
Utils::Fs::stripRootFolder(filePaths);
|
Path::stripRootFolder(filePaths);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -28,8 +28,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtContainerFwd>
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
||||||
|
@ -49,6 +52,6 @@ namespace BitTorrent
|
||||||
Q_ENUM_NS(TorrentContentLayout)
|
Q_ENUM_NS(TorrentContentLayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentContentLayout detectContentLayout(const QStringList &filePaths);
|
TorrentContentLayout detectContentLayout(const PathList &filePaths);
|
||||||
void applyContentLayout(QStringList &filePaths, TorrentContentLayout contentLayout, const QString &rootFolder = {});
|
void applyContentLayout(PathList &filePaths, TorrentContentLayout contentLayout, const Path &rootFolder = {});
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace
|
||||||
// name starts with a .
|
// name starts with a .
|
||||||
bool fileFilter(const std::string &f)
|
bool fileFilter(const std::string &f)
|
||||||
{
|
{
|
||||||
return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
|
return !Path(f).filename().startsWith('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
@ -108,50 +108,50 @@ void TorrentCreatorThread::run()
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
|
const Path parentPath = m_params.inputPath.parentPath();
|
||||||
const Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> naturalLessThan {};
|
const Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> naturalLessThan {};
|
||||||
|
|
||||||
// Adding files to the torrent
|
// Adding files to the torrent
|
||||||
lt::file_storage fs;
|
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
|
else
|
||||||
{
|
{
|
||||||
// need to sort the file names by natural sort order
|
// 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())
|
while (dirIter.hasNext())
|
||||||
{
|
{
|
||||||
dirIter.next();
|
dirIter.next();
|
||||||
dirs += dirIter.filePath();
|
dirs.append(dirIter.filePath());
|
||||||
}
|
}
|
||||||
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
||||||
|
|
||||||
QStringList fileNames;
|
QStringList fileNames;
|
||||||
QHash<QString, qint64> fileSizeMap;
|
QHash<QString, qint64> fileSizeMap;
|
||||||
|
|
||||||
for (const auto &dir : asConst(dirs))
|
for (const QString &dir : asConst(dirs))
|
||||||
{
|
{
|
||||||
QStringList tmpNames; // natural sort files within each dir
|
QStringList tmpNames; // natural sort files within each dir
|
||||||
|
|
||||||
QDirIterator fileIter(dir, QDir::Files);
|
QDirIterator fileIter {dir, QDir::Files};
|
||||||
while (fileIter.hasNext())
|
while (fileIter.hasNext())
|
||||||
{
|
{
|
||||||
fileIter.next();
|
fileIter.next();
|
||||||
|
|
||||||
const QString relFilePath = fileIter.filePath().mid(parentPath.length());
|
const auto relFilePath = parentPath.relativePathOf(Path(fileIter.filePath()));
|
||||||
tmpNames += relFilePath;
|
tmpNames.append(relFilePath.toString());
|
||||||
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
|
fileSizeMap[tmpNames.last()] = fileIter.fileInfo().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
||||||
fileNames += tmpNames;
|
fileNames += tmpNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &fileName : asConst(fileNames))
|
for (const QString &fileName : asConst(fileNames))
|
||||||
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
|
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ void TorrentCreatorThread::run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the hash for all pieces
|
// 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)
|
, [this, &newTorrent](const lt::piece_index_t n)
|
||||||
{
|
{
|
||||||
checkInterruptionRequested();
|
checkInterruptionRequested();
|
||||||
|
@ -225,16 +225,16 @@ void TorrentCreatorThread::run()
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#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
|
#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
|
#endif
|
||||||
{
|
{
|
||||||
if (inputPath.isEmpty())
|
if (inputPath.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
lt::file_storage fs;
|
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
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
return lt::create_torrent {fs, pieceSize, toNativeTorrentFormatFlag(torrentFormat)}.num_pieces();
|
return lt::create_torrent {fs, pieceSize, toNativeTorrentFormatFlag(torrentFormat)}.num_pieces();
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
@ -52,8 +54,8 @@ namespace BitTorrent
|
||||||
int paddedFileSizeLimit;
|
int paddedFileSizeLimit;
|
||||||
#endif
|
#endif
|
||||||
int pieceSize;
|
int pieceSize;
|
||||||
QString inputPath;
|
Path inputPath;
|
||||||
QString savePath;
|
Path savePath;
|
||||||
QString comment;
|
QString comment;
|
||||||
QString source;
|
QString source;
|
||||||
QStringList trackers;
|
QStringList trackers;
|
||||||
|
@ -72,15 +74,15 @@ namespace BitTorrent
|
||||||
void create(const TorrentCreatorParams ¶ms);
|
void create(const TorrentCreatorParams ¶ms);
|
||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#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
|
#else
|
||||||
static int calculateTotalPieces(const QString &inputPath
|
static int calculateTotalPieces(const Path &inputPath
|
||||||
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void creationFailure(const QString &msg);
|
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);
|
void updateProgress(int progress);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
@ -282,8 +281,10 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||||
{
|
{
|
||||||
const lt::file_index_t nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
const lt::file_index_t nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||||
m_indexMap[nativeIndex] = i;
|
m_indexMap[nativeIndex] = i;
|
||||||
const QString filePath = Utils::Fs::toUniformPath(QString::fromStdString(fileStorage.file_path(nativeIndex)));
|
Path filePath {fileStorage.file_path(nativeIndex)};
|
||||||
m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
|
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
|
// TODO: Remove the following upgrade code in v4.4
|
||||||
// == BEGIN UPGRADE CODE ==
|
// == BEGIN UPGRADE CODE ==
|
||||||
const QString spath = actualStorageLocation();
|
const Path spath = actualStorageLocation();
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
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
|
// Move "unwanted" files back to their original folder
|
||||||
const QString parentRelPath = Utils::Fs::branchPath(filepath);
|
const Path parentRelPath = filepath.parentPath();
|
||||||
if (QDir(parentRelPath).dirName() == ".unwanted")
|
if (parentRelPath.filename() == QLatin1String(".unwanted"))
|
||||||
{
|
{
|
||||||
const QString oldName = Utils::Fs::fileName(filepath);
|
const QString oldName = filepath.filename();
|
||||||
const QString newRelPath = Utils::Fs::branchPath(parentRelPath);
|
const Path newRelPath = parentRelPath.parentPath();
|
||||||
if (newRelPath.isEmpty())
|
renameFile(i, (newRelPath / Path(oldName)));
|
||||||
renameFile(i, oldName);
|
|
||||||
else
|
|
||||||
renameFile(i, QDir(newRelPath).filePath(oldName));
|
|
||||||
|
|
||||||
// Remove .unwanted directory if empty
|
// Remove .unwanted directory if empty
|
||||||
qDebug() << "Attempting to remove \".unwanted\" folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
|
const Path newPath = spath / newRelPath;
|
||||||
QDir(spath + '/' + newRelPath).rmdir(".unwanted");
|
qDebug() << "Attempting to remove \".unwanted\" folder at " << (newPath / Path(".unwanted")).toString();
|
||||||
|
Utils::Fs::rmdir(newPath / Path(".unwanted"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// == END UPGRADE CODE ==
|
// == END UPGRADE CODE ==
|
||||||
|
@ -396,18 +395,18 @@ QString TorrentImpl::currentTracker() const
|
||||||
return QString::fromStdString(m_nativeStatus.current_tracker);
|
return QString::fromStdString(m_nativeStatus.current_tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::savePath() const
|
Path TorrentImpl::savePath() const
|
||||||
{
|
{
|
||||||
return isAutoTMMEnabled() ? m_session->categorySavePath(category()) : m_savePath;
|
return isAutoTMMEnabled() ? m_session->categorySavePath(category()) : m_savePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::setSavePath(const QString &path)
|
void TorrentImpl::setSavePath(const Path &path)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!isAutoTMMEnabled());
|
Q_ASSERT(!isAutoTMMEnabled());
|
||||||
|
|
||||||
const QString basePath = m_session->useCategoryPathsInManualMode()
|
const Path basePath = m_session->useCategoryPathsInManualMode()
|
||||||
? m_session->categorySavePath(category()) : m_session->savePath();
|
? 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())
|
if (resolvedPath == savePath())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -420,18 +419,18 @@ void TorrentImpl::setSavePath(const QString &path)
|
||||||
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::downloadPath() const
|
Path TorrentImpl::downloadPath() const
|
||||||
{
|
{
|
||||||
return isAutoTMMEnabled() ? m_session->categoryDownloadPath(category()) : m_downloadPath;
|
return isAutoTMMEnabled() ? m_session->categoryDownloadPath(category()) : m_downloadPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::setDownloadPath(const QString &path)
|
void TorrentImpl::setDownloadPath(const Path &path)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!isAutoTMMEnabled());
|
Q_ASSERT(!isAutoTMMEnabled());
|
||||||
|
|
||||||
const QString basePath = m_session->useCategoryPathsInManualMode()
|
const Path basePath = m_session->useCategoryPathsInManualMode()
|
||||||
? m_session->categoryDownloadPath(category()) : m_session->downloadPath();
|
? 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)
|
if (resolvedPath == m_downloadPath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -444,27 +443,27 @@ void TorrentImpl::setDownloadPath(const QString &path)
|
||||||
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::rootPath() const
|
Path TorrentImpl::rootPath() const
|
||||||
{
|
{
|
||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const QString relativeRootPath = Utils::Fs::findRootFolder(filePaths());
|
const Path relativeRootPath = Path::findRootFolder(filePaths());
|
||||||
if (relativeRootPath.isEmpty())
|
if (relativeRootPath.isEmpty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return QDir(actualStorageLocation()).absoluteFilePath(relativeRootPath);
|
return (actualStorageLocation() / relativeRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::contentPath() const
|
Path TorrentImpl::contentPath() const
|
||||||
{
|
{
|
||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (filesCount() == 1)
|
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);
|
return (rootPath.isEmpty() ? actualStorageLocation() : rootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,9 +490,9 @@ void TorrentImpl::setAutoTMMEnabled(bool enabled)
|
||||||
adjustStorageLocation();
|
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)
|
void TorrentImpl::setAutoManaged(const bool enable)
|
||||||
|
@ -799,16 +798,15 @@ int TorrentImpl::seedingTimeLimit() const
|
||||||
return m_seedingTimeLimit;
|
return m_seedingTimeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::filePath(const int index) const
|
Path TorrentImpl::filePath(const int index) const
|
||||||
{
|
{
|
||||||
return m_filePaths.at(index);
|
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 auto nativeIndex = m_torrentInfo.nativeIndexes().at(index);
|
||||||
const std::string filePath = m_nativeHandle.torrent_file()->files().file_path(nativeIndex);
|
return Path(m_nativeHandle.torrent_file()->files().file_path(nativeIndex));
|
||||||
return Utils::Fs::toUniformPath(QString::fromStdString(filePath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qlonglong TorrentImpl::fileSize(const int index) const
|
qlonglong TorrentImpl::fileSize(const int index) const
|
||||||
|
@ -816,7 +814,7 @@ qlonglong TorrentImpl::fileSize(const int index) const
|
||||||
return m_torrentInfo.fileSize(index);
|
return m_torrentInfo.fileSize(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList TorrentImpl::filePaths() const
|
PathList TorrentImpl::filePaths() const
|
||||||
{
|
{
|
||||||
return m_filePaths;
|
return m_filePaths;
|
||||||
}
|
}
|
||||||
|
@ -1481,12 +1479,12 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled, const QVector<
|
||||||
m_nativeHandle.prioritize_pieces(piecePriorities);
|
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);
|
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_filePaths.isEmpty());
|
||||||
Q_ASSERT(m_indexMap.isEmpty());
|
Q_ASSERT(m_indexMap.isEmpty());
|
||||||
|
@ -1502,11 +1500,14 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
|
||||||
const auto nativeIndex = nativeIndexes.at(i);
|
const auto nativeIndex = nativeIndexes.at(i);
|
||||||
m_indexMap[nativeIndex] = i;
|
m_indexMap[nativeIndex] = i;
|
||||||
|
|
||||||
const QString filePath = fileNames.at(i);
|
Path filePath = fileNames.at(i);
|
||||||
m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
|
p.renamed_files[nativeIndex] = filePath.toString().toStdString();
|
||||||
p.renamed_files[nativeIndex] = filePath.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;
|
p.ti = metadata;
|
||||||
|
|
||||||
const int internalFilesCount = p.ti->files().num_files(); // including .pad files
|
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;
|
m_storageIsMoving = true;
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::renameFile(const int index, const QString &path)
|
void TorrentImpl::renameFile(const int index, const Path &path)
|
||||||
{
|
{
|
||||||
++m_renameCount;
|
++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)
|
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());
|
TorrentInfo metadata = TorrentInfo(*m_nativeHandle.torrent_file());
|
||||||
|
|
||||||
QStringList filePaths = metadata.filePaths();
|
PathList filePaths = metadata.filePaths();
|
||||||
applyContentLayout(filePaths, m_contentLayout);
|
applyContentLayout(filePaths, m_contentLayout);
|
||||||
m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths);
|
m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths);
|
||||||
}
|
}
|
||||||
|
@ -1876,37 +1878,23 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
|
||||||
// Remove empty leftover folders
|
// Remove empty leftover folders
|
||||||
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
|
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
|
||||||
// be removed if they are empty
|
// be removed if they are empty
|
||||||
const QString oldFilePath = m_filePaths.at(fileIndex);
|
const Path oldFilePath = m_filePaths.at(fileIndex);
|
||||||
const QString newFilePath = Utils::Fs::toUniformPath(p->new_name());
|
const Path newFilePath {QString(p->new_name())};
|
||||||
|
|
||||||
// Check if ".!qB" extension was just added or removed
|
// 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;
|
m_filePaths[fileIndex] = newFilePath;
|
||||||
|
|
||||||
QList<QStringView> oldPathParts = QStringView(oldFilePath).split('/', Qt::SkipEmptyParts);
|
Path oldParentPath = oldFilePath.parentPath();
|
||||||
oldPathParts.removeLast(); // drop file name part
|
const Path commonBasePath = Path::commonPath(oldParentPath, newFilePath.parentPath());
|
||||||
QList<QStringView> newPathParts = QStringView(newFilePath).split('/', Qt::SkipEmptyParts);
|
while (oldParentPath != commonBasePath)
|
||||||
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()))
|
|
||||||
{
|
{
|
||||||
if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
|
Utils::Fs::rmdir(actualStorageLocation() / oldParentPath);
|
||||||
break;
|
oldParentPath = oldParentPath.parentPath();
|
||||||
++pathIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i)
|
|
||||||
{
|
|
||||||
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QString::fromLatin1("/")));
|
|
||||||
oldPathParts.removeLast();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1923,7 +1911,7 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
|
||||||
Q_ASSERT(fileIndex >= 0);
|
Q_ASSERT(fileIndex >= 0);
|
||||||
|
|
||||||
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
|
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;
|
--m_renameCount;
|
||||||
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
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);
|
const int fileIndex = m_indexMap.value(p->index, -1);
|
||||||
Q_ASSERT(fileIndex >= 0);
|
Q_ASSERT(fileIndex >= 0);
|
||||||
|
|
||||||
const QString path = filePath(fileIndex);
|
const Path path = filePath(fileIndex);
|
||||||
const QString actualPath = actualFilePath(fileIndex);
|
const Path actualPath = actualFilePath(fileIndex);
|
||||||
if (actualPath != path)
|
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);
|
renameFile(fileIndex, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2064,18 +2052,17 @@ void TorrentImpl::manageIncompleteFiles()
|
||||||
|
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
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 auto nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||||
const QString actualPath = Utils::Fs::toUniformPath(
|
const Path actualPath {nativeFiles.file_path(nativeIndex)};
|
||||||
QString::fromStdString(nativeFiles.file_path(nativeIndex)));
|
|
||||||
|
|
||||||
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
|
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
|
||||||
{
|
{
|
||||||
const QString wantedPath = path + QB_EXT;
|
const Path wantedPath = path + QB_EXT;
|
||||||
if (actualPath != wantedPath)
|
if (actualPath != wantedPath)
|
||||||
{
|
{
|
||||||
qDebug() << "Renaming" << actualPath << "to" << wantedPath;
|
qDebug() << "Renaming" << actualPath.toString() << "to" << wantedPath.toString();
|
||||||
renameFile(i, wantedPath);
|
renameFile(i, wantedPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2083,7 +2070,7 @@ void TorrentImpl::manageIncompleteFiles()
|
||||||
{
|
{
|
||||||
if (actualPath != path)
|
if (actualPath != path)
|
||||||
{
|
{
|
||||||
qDebug() << "Renaming" << actualPath << "to" << path;
|
qDebug() << "Renaming" << actualPath.toString() << "to" << path.toString();
|
||||||
renameFile(i, path);
|
renameFile(i, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2092,12 +2079,12 @@ void TorrentImpl::manageIncompleteFiles()
|
||||||
|
|
||||||
void TorrentImpl::adjustStorageLocation()
|
void TorrentImpl::adjustStorageLocation()
|
||||||
{
|
{
|
||||||
const QString downloadPath = this->downloadPath();
|
const Path downloadPath = this->downloadPath();
|
||||||
const bool isFinished = isSeed() || m_hasSeedStatus;
|
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())
|
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
||||||
moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
|
moveStorage(targetPath, MoveStorageMode::Overwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
lt::torrent_handle TorrentImpl::nativeHandle() const
|
lt::torrent_handle TorrentImpl::nativeHandle() const
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "speedmonitor.h"
|
#include "speedmonitor.h"
|
||||||
|
@ -102,13 +103,13 @@ namespace BitTorrent
|
||||||
|
|
||||||
bool isAutoTMMEnabled() const override;
|
bool isAutoTMMEnabled() const override;
|
||||||
void setAutoTMMEnabled(bool enabled) override;
|
void setAutoTMMEnabled(bool enabled) override;
|
||||||
QString savePath() const override;
|
Path savePath() const override;
|
||||||
void setSavePath(const QString &path) override;
|
void setSavePath(const Path &path) override;
|
||||||
QString downloadPath() const override;
|
Path downloadPath() const override;
|
||||||
void setDownloadPath(const QString &path) override;
|
void setDownloadPath(const Path &path) override;
|
||||||
QString actualStorageLocation() const override;
|
Path actualStorageLocation() const override;
|
||||||
QString rootPath() const override;
|
Path rootPath() const override;
|
||||||
QString contentPath() const override;
|
Path contentPath() const override;
|
||||||
QString category() const override;
|
QString category() const override;
|
||||||
bool belongsToCategory(const QString &category) const override;
|
bool belongsToCategory(const QString &category) const override;
|
||||||
bool setCategory(const QString &category) override;
|
bool setCategory(const QString &category) override;
|
||||||
|
@ -127,10 +128,10 @@ namespace BitTorrent
|
||||||
qreal ratioLimit() const override;
|
qreal ratioLimit() const override;
|
||||||
int seedingTimeLimit() const override;
|
int seedingTimeLimit() const override;
|
||||||
|
|
||||||
QString filePath(int index) const override;
|
Path filePath(int index) const override;
|
||||||
QString actualFilePath(int index) const override;
|
Path actualFilePath(int index) const override;
|
||||||
qlonglong fileSize(int index) const override;
|
qlonglong fileSize(int index) const override;
|
||||||
QStringList filePaths() const override;
|
PathList filePaths() const override;
|
||||||
QVector<DownloadPriority> filePriorities() const override;
|
QVector<DownloadPriority> filePriorities() const override;
|
||||||
|
|
||||||
TorrentInfo info() const override;
|
TorrentInfo info() const override;
|
||||||
|
@ -205,7 +206,7 @@ namespace BitTorrent
|
||||||
void forceReannounce(int index = -1) override;
|
void forceReannounce(int index = -1) override;
|
||||||
void forceDHTAnnounce() override;
|
void forceDHTAnnounce() override;
|
||||||
void forceRecheck() 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 prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
||||||
void setRatioLimit(qreal limit) override;
|
void setRatioLimit(qreal limit) override;
|
||||||
void setSeedingTimeLimit(int limit) override;
|
void setSeedingTimeLimit(int limit) override;
|
||||||
|
@ -237,7 +238,7 @@ namespace BitTorrent
|
||||||
void handleAppendExtensionToggled();
|
void handleAppendExtensionToggled();
|
||||||
void saveResumeData();
|
void saveResumeData();
|
||||||
void handleMoveStorageJobFinished(bool hasOutstandingJob);
|
void handleMoveStorageJobFinished(bool hasOutstandingJob);
|
||||||
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);
|
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using EventTrigger = std::function<void ()>;
|
using EventTrigger = std::function<void ()>;
|
||||||
|
@ -271,12 +272,12 @@ namespace BitTorrent
|
||||||
void setAutoManaged(bool enable);
|
void setAutoManaged(bool enable);
|
||||||
|
|
||||||
void adjustStorageLocation();
|
void adjustStorageLocation();
|
||||||
void moveStorage(const QString &newPath, MoveStorageMode mode);
|
void moveStorage(const Path &newPath, MoveStorageMode mode);
|
||||||
void manageIncompleteFiles();
|
void manageIncompleteFiles();
|
||||||
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
||||||
|
|
||||||
void prepareResumeData(const lt::add_torrent_params ¶ms);
|
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();
|
void reload();
|
||||||
|
|
||||||
Session *const m_session;
|
Session *const m_session;
|
||||||
|
@ -285,7 +286,7 @@ namespace BitTorrent
|
||||||
lt::torrent_status m_nativeStatus;
|
lt::torrent_status m_nativeStatus;
|
||||||
TorrentState m_state = TorrentState::Unknown;
|
TorrentState m_state = TorrentState::Unknown;
|
||||||
TorrentInfo m_torrentInfo;
|
TorrentInfo m_torrentInfo;
|
||||||
QStringList m_filePaths;
|
PathList m_filePaths;
|
||||||
QHash<lt::file_index_t, int> m_indexMap;
|
QHash<lt::file_index_t, int> m_indexMap;
|
||||||
SpeedMonitor m_speedMonitor;
|
SpeedMonitor m_speedMonitor;
|
||||||
|
|
||||||
|
@ -304,8 +305,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
// Persistent data
|
// Persistent data
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_savePath;
|
Path m_savePath;
|
||||||
QString m_downloadPath;
|
Path m_downloadPath;
|
||||||
QString m_category;
|
QString m_category;
|
||||||
TagSet m_tags;
|
TagSet m_tags;
|
||||||
qreal m_ratioLimit;
|
qreal m_ratioLimit;
|
||||||
|
|
|
@ -99,16 +99,16 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)
|
||||||
if (ec)
|
if (ec)
|
||||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||||
|
|
||||||
lt::torrent_info nativeInfo {node, ec};
|
const lt::torrent_info nativeInfo {node, ec};
|
||||||
if (ec)
|
if (ec)
|
||||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||||
|
|
||||||
return TorrentInfo(nativeInfo);
|
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))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
return nonstd::make_unexpected(file.errorString());
|
return nonstd::make_unexpected(file.errorString());
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const QString &
|
||||||
return load(data);
|
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())
|
if (!isValid())
|
||||||
return nonstd::make_unexpected(tr("Invalid metadata"));
|
return nonstd::make_unexpected(tr("Invalid metadata"));
|
||||||
|
@ -236,17 +236,16 @@ int TorrentInfo::piecesCount() const
|
||||||
return m_nativeInfo->num_pieces();
|
return m_nativeInfo->num_pieces();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentInfo::filePath(const int index) const
|
Path TorrentInfo::filePath(const int index) const
|
||||||
{
|
{
|
||||||
if (!isValid()) return {};
|
if (!isValid()) return {};
|
||||||
|
|
||||||
return Utils::Fs::toUniformPath(
|
return Path(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index]));
|
||||||
QString::fromStdString(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index])));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList TorrentInfo::filePaths() const
|
PathList TorrentInfo::filePaths() const
|
||||||
{
|
{
|
||||||
QStringList list;
|
PathList list;
|
||||||
list.reserve(filesCount());
|
list.reserve(filesCount());
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
list << filePath(i);
|
list << filePath(i);
|
||||||
|
@ -312,15 +311,15 @@ QByteArray TorrentInfo::metadata() const
|
||||||
#endif
|
#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
|
// no checks here because fileIndicesForPiece() will return an empty list
|
||||||
const QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
const QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||||
|
|
||||||
QStringList res;
|
PathList res;
|
||||||
res.reserve(fileIndices.size());
|
res.reserve(fileIndices.size());
|
||||||
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
|
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res)
|
||||||
[this](int i) { return filePath(i); });
|
, [this](int i) { return filePath(i); });
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -359,15 +358,15 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
|
||||||
return hashes;
|
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
|
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const int index = fileIndex(file);
|
const int index = fileIndex(filePath);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
qDebug() << "Filename" << file << "was not found in torrent" << name();
|
qDebug() << "Filename" << filePath.toString() << "was not found in torrent" << name();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return filePieces(index);
|
return filePieces(index);
|
||||||
|
@ -396,13 +395,13 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
|
||||||
return makeInterval(beginIdx, endIdx);
|
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
|
// the check whether the object is valid is not needed here
|
||||||
// because if filesCount() returns -1 the loop exits immediately
|
// because if filesCount() returns -1 the loop exits immediately
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
{
|
{
|
||||||
if (fileName == filePath(i))
|
if (filePath == this->filePath(i))
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "base/3rdparty/expected.hpp"
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
|
#include "base/pathfwd.h"
|
||||||
#include "torrentcontentlayout.h"
|
#include "torrentcontentlayout.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
|
@ -58,8 +59,8 @@ namespace BitTorrent
|
||||||
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
||||||
|
|
||||||
static nonstd::expected<TorrentInfo, QString> load(const QByteArray &data) noexcept;
|
static nonstd::expected<TorrentInfo, QString> load(const QByteArray &data) noexcept;
|
||||||
static nonstd::expected<TorrentInfo, QString> loadFromFile(const QString &path) noexcept;
|
static nonstd::expected<TorrentInfo, QString> loadFromFile(const Path &path) noexcept;
|
||||||
nonstd::expected<void, QString> saveToFile(const QString &path) const;
|
nonstd::expected<void, QString> saveToFile(const Path &path) const;
|
||||||
|
|
||||||
TorrentInfo &operator=(const TorrentInfo &other);
|
TorrentInfo &operator=(const TorrentInfo &other);
|
||||||
|
|
||||||
|
@ -75,21 +76,21 @@ namespace BitTorrent
|
||||||
int pieceLength() const;
|
int pieceLength() const;
|
||||||
int pieceLength(int index) const;
|
int pieceLength(int index) const;
|
||||||
int piecesCount() const;
|
int piecesCount() const;
|
||||||
QString filePath(int index) const;
|
Path filePath(int index) const;
|
||||||
QStringList filePaths() const;
|
PathList filePaths() const;
|
||||||
qlonglong fileSize(int index) const;
|
qlonglong fileSize(int index) const;
|
||||||
qlonglong fileOffset(int index) const;
|
qlonglong fileOffset(int index) const;
|
||||||
QVector<TrackerEntry> trackers() const;
|
QVector<TrackerEntry> trackers() const;
|
||||||
QVector<QUrl> urlSeeds() const;
|
QVector<QUrl> urlSeeds() const;
|
||||||
QByteArray metadata() const;
|
QByteArray metadata() const;
|
||||||
QStringList filesForPiece(int pieceIndex) const;
|
PathList filesForPiece(int pieceIndex) const;
|
||||||
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
||||||
QVector<QByteArray> pieceHashes() const;
|
QVector<QByteArray> pieceHashes() const;
|
||||||
|
|
||||||
using PieceRange = IndexRange<int>;
|
using PieceRange = IndexRange<int>;
|
||||||
// returns pair of the first and the last pieces into which
|
// returns pair of the first and the last pieces into which
|
||||||
// the given file extends (maybe partially).
|
// the given file extends (maybe partially).
|
||||||
PieceRange filePieces(const QString &file) const;
|
PieceRange filePieces(const Path &filePath) const;
|
||||||
PieceRange filePieces(int fileIndex) const;
|
PieceRange filePieces(int fileIndex) const;
|
||||||
|
|
||||||
std::shared_ptr<lt::torrent_info> nativeInfo() const;
|
std::shared_ptr<lt::torrent_info> nativeInfo() const;
|
||||||
|
@ -97,7 +98,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// returns file index or -1 if fileName is not found
|
// 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;
|
TorrentContentLayout contentLayout() const;
|
||||||
|
|
||||||
std::shared_ptr<const lt::torrent_info> m_nativeInfo;
|
std::shared_ptr<const lt::torrent_info> m_nativeInfo;
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
#include "iconprovider.h"
|
#include "iconprovider.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include "base/path.h"
|
||||||
|
|
||||||
IconProvider::IconProvider(QObject *parent)
|
IconProvider::IconProvider(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -55,14 +55,14 @@ IconProvider *IconProvider::instance()
|
||||||
return m_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
|
// there are a few icons not available in svg
|
||||||
const QString pathSvg = ":/icons/" + iconId + ".svg";
|
const Path pathSvg {":/icons/" + iconId + ".svg"};
|
||||||
if (QFileInfo::exists(pathSvg))
|
if (pathSvg.exists())
|
||||||
return pathSvg;
|
return pathSvg;
|
||||||
|
|
||||||
const QString pathPng = ":/icons/" + iconId + ".png";
|
const Path pathPng {":/icons/" + iconId + ".png"};
|
||||||
return pathPng;
|
return pathPng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
class IconProvider : public QObject
|
class IconProvider : public QObject
|
||||||
|
@ -42,7 +44,7 @@ public:
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static IconProvider *instance();
|
static IconProvider *instance();
|
||||||
|
|
||||||
virtual QString getIconPath(const QString &iconId) const;
|
virtual Path getIconPath(const QString &iconId) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit IconProvider(QObject *parent = nullptr);
|
explicit IconProvider(QObject *parent = nullptr);
|
||||||
|
|
|
@ -42,14 +42,14 @@ const int MAX_REDIRECTIONS = 20; // the common value for web browsers
|
||||||
|
|
||||||
namespace
|
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())
|
if (!file.open() || (file.write(data) != data.length()) || !file.flush())
|
||||||
return nonstd::make_unexpected(file.errorString());
|
return nonstd::make_unexpected(file.errorString());
|
||||||
|
|
||||||
file.setAutoRemove(false);
|
file.setAutoRemove(false);
|
||||||
return file.fileName();
|
return Path(file.fileName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,10 +127,10 @@ void DownloadHandlerImpl::processFinishedDownload()
|
||||||
|
|
||||||
if (m_downloadRequest.saveToFile())
|
if (m_downloadRequest.saveToFile())
|
||||||
{
|
{
|
||||||
const QString destinationPath = m_downloadRequest.destFileName();
|
const Path destinationPath = m_downloadRequest.destFileName();
|
||||||
if (destinationPath.isEmpty())
|
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)
|
if (result)
|
||||||
m_result.filePath = result.value();
|
m_result.filePath = result.value();
|
||||||
else
|
else
|
||||||
|
|
|
@ -348,12 +348,12 @@ Net::DownloadRequest &Net::DownloadRequest::saveToFile(const bool value)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Net::DownloadRequest::destFileName() const
|
Path Net::DownloadRequest::destFileName() const
|
||||||
{
|
{
|
||||||
return m_destFileName;
|
return m_destFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
Net::DownloadRequest &Net::DownloadRequest::destFileName(const QString &value)
|
Net::DownloadRequest &Net::DownloadRequest::destFileName(const Path &value)
|
||||||
{
|
{
|
||||||
m_destFileName = value;
|
m_destFileName = value;
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class QNetworkCookie;
|
class QNetworkCookie;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QSslError;
|
class QSslError;
|
||||||
|
@ -81,15 +83,15 @@ namespace Net
|
||||||
// if saveToFile is set, the file is saved in destFileName
|
// if saveToFile is set, the file is saved in destFileName
|
||||||
// (deprecated) if destFileName is not provided, the file will be saved
|
// (deprecated) if destFileName is not provided, the file will be saved
|
||||||
// in a temporary file, the name of file is set in DownloadResult::filePath
|
// in a temporary file, the name of file is set in DownloadResult::filePath
|
||||||
QString destFileName() const;
|
Path destFileName() const;
|
||||||
DownloadRequest &destFileName(const QString &value);
|
DownloadRequest &destFileName(const Path &value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_url;
|
QString m_url;
|
||||||
QString m_userAgent;
|
QString m_userAgent;
|
||||||
qint64 m_limit = 0;
|
qint64 m_limit = 0;
|
||||||
bool m_saveToFile = false;
|
bool m_saveToFile = false;
|
||||||
QString m_destFileName;
|
Path m_destFileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DownloadResult
|
struct DownloadResult
|
||||||
|
@ -98,7 +100,7 @@ namespace Net
|
||||||
DownloadStatus status;
|
DownloadStatus status;
|
||||||
QString errorString;
|
QString errorString;
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
QString filePath;
|
Path filePath;
|
||||||
QString magnet;
|
QString magnet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,15 @@
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "geoipdatabase.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "geoipdatabase.h"
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace
|
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;
|
GeoIPDatabase *db = nullptr;
|
||||||
QFile file(filename);
|
QFile file {filename.data()};
|
||||||
if (file.size() > MAX_FILE_SIZE)
|
if (file.size() > MAX_FILE_SIZE)
|
||||||
{
|
{
|
||||||
error = tr("Unsupported database file size.");
|
error = tr("Unsupported database file size.");
|
||||||
|
|
|
@ -28,11 +28,15 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QDateTime;
|
|
||||||
class QHostAddress;
|
class QHostAddress;
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
|
@ -43,7 +47,7 @@ class GeoIPDatabase
|
||||||
Q_DECLARE_TR_FUNCTIONS(GeoIPDatabase)
|
Q_DECLARE_TR_FUNCTIONS(GeoIPDatabase)
|
||||||
|
|
||||||
public:
|
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);
|
static GeoIPDatabase *load(const QByteArray &data, QString &error);
|
||||||
|
|
||||||
~GeoIPDatabase();
|
~GeoIPDatabase();
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include "geoipmanager.h"
|
#include "geoipmanager.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDir>
|
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
|
|
||||||
|
@ -43,9 +42,9 @@
|
||||||
#include "downloadmanager.h"
|
#include "downloadmanager.h"
|
||||||
#include "geoipdatabase.h"
|
#include "geoipdatabase.h"
|
||||||
|
|
||||||
static const QString DATABASE_URL = QStringLiteral("https://download.db-ip.com/free/dbip-country-lite-%1.mmdb.gz");
|
const QString DATABASE_URL = QStringLiteral("https://download.db-ip.com/free/dbip-country-lite-%1.mmdb.gz");
|
||||||
static const char GEODB_FOLDER[] = "GeoDB";
|
const char GEODB_FOLDER[] = "GeoDB";
|
||||||
static const char GEODB_FILENAME[] = "dbip-country-lite.mmdb";
|
const char GEODB_FILENAME[] = "dbip-country-lite.mmdb";
|
||||||
|
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
|
|
||||||
|
@ -88,17 +87,21 @@ void GeoIPManager::loadDatabase()
|
||||||
delete m_geoIPDatabase;
|
delete m_geoIPDatabase;
|
||||||
m_geoIPDatabase = nullptr;
|
m_geoIPDatabase = nullptr;
|
||||||
|
|
||||||
const QString filepath = Utils::Fs::expandPathAbs(
|
const Path filepath = specialFolderLocation(SpecialFolder::Data)
|
||||||
QString::fromLatin1("%1/%2/%3").arg(specialFolderLocation(SpecialFolder::Data), GEODB_FOLDER, GEODB_FILENAME));
|
/ Path(GEODB_FOLDER) / Path(GEODB_FILENAME);
|
||||||
|
|
||||||
QString error;
|
QString error;
|
||||||
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
|
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
|
||||||
if (m_geoIPDatabase)
|
if (m_geoIPDatabase)
|
||||||
|
{
|
||||||
Logger::instance()->addMessage(tr("IP geolocation database loaded. Type: %1. Build time: %2.")
|
Logger::instance()->addMessage(tr("IP geolocation database loaded. Type: %1. Build time: %2.")
|
||||||
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
||||||
Log::INFO);
|
Log::INFO);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
Logger::instance()->addMessage(tr("Couldn't load IP geolocation database. Reason: %1").arg(error), Log::WARNING);
|
Logger::instance()->addMessage(tr("Couldn't load IP geolocation database. Reason: %1").arg(error), Log::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
manageDatabaseUpdate();
|
manageDatabaseUpdate();
|
||||||
}
|
}
|
||||||
|
@ -445,14 +448,13 @@ void GeoIPManager::downloadFinished(const DownloadResult &result)
|
||||||
delete m_geoIPDatabase;
|
delete m_geoIPDatabase;
|
||||||
m_geoIPDatabase = geoIPDatabase;
|
m_geoIPDatabase = geoIPDatabase;
|
||||||
LogMsg(tr("IP geolocation database loaded. Type: %1. Build time: %2.")
|
LogMsg(tr("IP geolocation database loaded. Type: %1. Build time: %2.")
|
||||||
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString())
|
||||||
Log::INFO);
|
, Log::INFO);
|
||||||
const QString targetPath = Utils::Fs::expandPathAbs(
|
const Path targetPath = specialFolderLocation(SpecialFolder::Data) / Path(GEODB_FOLDER);
|
||||||
QDir(specialFolderLocation(SpecialFolder::Data)).absoluteFilePath(GEODB_FOLDER));
|
if (!targetPath.exists())
|
||||||
if (!QDir(targetPath).exists())
|
Utils::Fs::mkpath(targetPath);
|
||||||
QDir().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);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||||
if (result)
|
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 "algorithm.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "path.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
#include "settingsstorage.h"
|
#include "settingsstorage.h"
|
||||||
#include "utils/fs.h"
|
#include "utils/fs.h"
|
||||||
|
@ -85,11 +86,11 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QString makeProfileID(const QString &profilePath, const QString &profileName)
|
QString makeProfileID(const Path &profilePath, const QString &profileName)
|
||||||
{
|
{
|
||||||
return profilePath.isEmpty()
|
return profilePath.isEmpty()
|
||||||
? profileName
|
? profileName
|
||||||
: profileName + QLatin1Char('@') + Utils::Fs::toValidFileSystemName(profilePath, false, {});
|
: profileName + QLatin1Char('@') + Utils::Fs::toValidFileName(profilePath.data(), {});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -137,12 +138,12 @@ void Preferences::setUseCustomUITheme(const bool use)
|
||||||
setValue("Preferences/General/UseCustomUITheme", 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);
|
setValue("Preferences/General/CustomUIThemePath", path);
|
||||||
}
|
}
|
||||||
|
@ -336,7 +337,7 @@ void Preferences::setPreventFromSuspendWhenSeeding(const bool b)
|
||||||
bool Preferences::WinStartup() const
|
bool Preferences::WinStartup() const
|
||||||
{
|
{
|
||||||
const QString profileName = Profile::instance()->profileName();
|
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 QString profileID = makeProfileID(profilePath, profileName);
|
||||||
const QSettings settings {"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat};
|
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)
|
void Preferences::setWinStartup(const bool b)
|
||||||
{
|
{
|
||||||
const QString profileName = Profile::instance()->profileName();
|
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 QString profileID = makeProfileID(profilePath, profileName);
|
||||||
QSettings settings {"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat};
|
QSettings settings {"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat};
|
||||||
if (b)
|
if (b)
|
||||||
|
@ -354,7 +355,7 @@ void Preferences::setWinStartup(const bool b)
|
||||||
const QString configuration = Profile::instance()->configurationName();
|
const QString configuration = Profile::instance()->configurationName();
|
||||||
|
|
||||||
const auto cmd = QString::fromLatin1(R"("%1" "--profile=%2" "--configuration=%3")")
|
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);
|
settings.setValue(profileID, cmd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -365,24 +366,14 @@ void Preferences::setWinStartup(const bool b)
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
// Downloads
|
// 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));
|
setValue("Preferences/Downloads/ScanDirsLastPath", 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isMailNotificationEnabled() const
|
bool Preferences::isMailNotificationEnabled() const
|
||||||
|
@ -737,22 +728,22 @@ void Preferences::setWebUiHttpsEnabled(const bool enabled)
|
||||||
setValue("Preferences/WebUI/HTTPS/Enabled", 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);
|
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);
|
setValue("Preferences/WebUI/HTTPS/KeyPath", path);
|
||||||
}
|
}
|
||||||
|
@ -767,12 +758,12 @@ void Preferences::setAltWebUiEnabled(const bool enabled)
|
||||||
setValue("Preferences/WebUI/AlternativeUIEnabled", 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);
|
setValue("Preferences/WebUI/RootFolder", path);
|
||||||
}
|
}
|
||||||
|
@ -1050,14 +1041,14 @@ bool Preferences::isMagnetLinkAssocSet()
|
||||||
const QSettings settings("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
|
const QSettings settings("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
|
||||||
|
|
||||||
// Check magnet link assoc
|
// 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);
|
const QRegularExpressionMatch exeRegMatch = QRegularExpression("\"([^\"]+)\".*").match(shellCommand);
|
||||||
if (!exeRegMatch.hasMatch())
|
if (!exeRegMatch.hasMatch())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const QString assocExe = exeRegMatch.captured(1);
|
const Path assocExe {exeRegMatch.captured(1)};
|
||||||
if (assocExe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
if (assocExe != Path(qApp->applicationFilePath()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1090,15 +1081,16 @@ void Preferences::setMagnetLinkAssoc(const bool set)
|
||||||
// Magnet association
|
// Magnet association
|
||||||
if (set)
|
if (set)
|
||||||
{
|
{
|
||||||
const QString commandStr = '"' + qApp->applicationFilePath() + "\" \"%1\"";
|
const QString applicationFilePath = Path(qApp->applicationFilePath()).toString();
|
||||||
const QString iconStr = '"' + qApp->applicationFilePath() + "\",1";
|
const QString commandStr = '"' + applicationFilePath + "\" \"%1\"";
|
||||||
|
const QString iconStr = '"' + applicationFilePath + "\",1";
|
||||||
|
|
||||||
settings.setValue("magnet/Default", "URL:Magnet link");
|
settings.setValue("magnet/Default", "URL:Magnet link");
|
||||||
settings.setValue("magnet/Content Type", "application/x-magnet");
|
settings.setValue("magnet/Content Type", "application/x-magnet");
|
||||||
settings.setValue("magnet/URL Protocol", "");
|
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/Default", "open");
|
||||||
settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(commandStr));
|
settings.setValue("magnet/shell/open/command/Default", commandStr);
|
||||||
}
|
}
|
||||||
else if (isMagnetLinkAssocSet())
|
else if (isMagnetLinkAssocSet())
|
||||||
{
|
{
|
||||||
|
@ -1294,12 +1286,12 @@ void Preferences::setMainVSplitterState(const QByteArray &state)
|
||||||
#endif
|
#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);
|
setValue("MainWindow/LastDir", path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
|
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
|
@ -108,8 +109,8 @@ public:
|
||||||
void setLocale(const QString &locale);
|
void setLocale(const QString &locale);
|
||||||
bool useCustomUITheme() const;
|
bool useCustomUITheme() const;
|
||||||
void setUseCustomUITheme(bool use);
|
void setUseCustomUITheme(bool use);
|
||||||
QString customUIThemePath() const;
|
Path customUIThemePath() const;
|
||||||
void setCustomUIThemePath(const QString &path);
|
void setCustomUIThemePath(const Path &path);
|
||||||
bool deleteTorrentFilesAsDefault() const;
|
bool deleteTorrentFilesAsDefault() const;
|
||||||
void setDeleteTorrentFilesAsDefault(bool del);
|
void setDeleteTorrentFilesAsDefault(bool del);
|
||||||
bool confirmOnExit() const;
|
bool confirmOnExit() const;
|
||||||
|
@ -140,10 +141,8 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Downloads
|
// Downloads
|
||||||
QString lastLocationPath() const;
|
Path getScanDirsLastPath() const;
|
||||||
void setLastLocationPath(const QString &path);
|
void setScanDirsLastPath(const Path &path);
|
||||||
QString getScanDirsLastPath() const;
|
|
||||||
void setScanDirsLastPath(const QString &path);
|
|
||||||
bool isMailNotificationEnabled() const;
|
bool isMailNotificationEnabled() const;
|
||||||
void setMailNotificationEnabled(bool enabled);
|
void setMailNotificationEnabled(bool enabled);
|
||||||
QString getMailNotificationSender() const;
|
QString getMailNotificationSender() const;
|
||||||
|
@ -220,14 +219,14 @@ public:
|
||||||
// HTTPS
|
// HTTPS
|
||||||
bool isWebUiHttpsEnabled() const;
|
bool isWebUiHttpsEnabled() const;
|
||||||
void setWebUiHttpsEnabled(bool enabled);
|
void setWebUiHttpsEnabled(bool enabled);
|
||||||
QString getWebUIHttpsCertificatePath() const;
|
Path getWebUIHttpsCertificatePath() const;
|
||||||
void setWebUIHttpsCertificatePath(const QString &path);
|
void setWebUIHttpsCertificatePath(const Path &path);
|
||||||
QString getWebUIHttpsKeyPath() const;
|
Path getWebUIHttpsKeyPath() const;
|
||||||
void setWebUIHttpsKeyPath(const QString &path);
|
void setWebUIHttpsKeyPath(const Path &path);
|
||||||
bool isAltWebUiEnabled() const;
|
bool isAltWebUiEnabled() const;
|
||||||
void setAltWebUiEnabled(bool enabled);
|
void setAltWebUiEnabled(bool enabled);
|
||||||
QString getWebUiRootFolder() const;
|
Path getWebUiRootFolder() const;
|
||||||
void setWebUiRootFolder(const QString &path);
|
void setWebUiRootFolder(const Path &path);
|
||||||
|
|
||||||
// WebUI custom HTTP headers
|
// WebUI custom HTTP headers
|
||||||
bool isWebUICustomHTTPHeadersEnabled() const;
|
bool isWebUICustomHTTPHeadersEnabled() const;
|
||||||
|
@ -343,8 +342,8 @@ public:
|
||||||
void setMainGeometry(const QByteArray &geometry);
|
void setMainGeometry(const QByteArray &geometry);
|
||||||
QByteArray getMainVSplitterState() const;
|
QByteArray getMainVSplitterState() const;
|
||||||
void setMainVSplitterState(const QByteArray &state);
|
void setMainVSplitterState(const QByteArray &state);
|
||||||
QString getMainLastDir() const;
|
Path getMainLastDir() const;
|
||||||
void setMainLastDir(const QString &path);
|
void setMainLastDir(const Path &path);
|
||||||
QByteArray getPeerListState() const;
|
QByteArray getPeerListState() const;
|
||||||
void setPeerListState(const QByteArray &state);
|
void setPeerListState(const QByteArray &state);
|
||||||
QString getPropSplitterSizes() const;
|
QString getPropSplitterSizes() const;
|
||||||
|
|
|
@ -29,11 +29,13 @@
|
||||||
|
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
#include "profile_p.h"
|
#include "profile_p.h"
|
||||||
|
|
||||||
Profile *Profile::m_instance = nullptr;
|
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())
|
if (rootProfilePath.isEmpty())
|
||||||
m_profileImpl = std::make_unique<Private::DefaultProfile>(configurationName);
|
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>();
|
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)
|
const bool convertPathsToProfileRelative)
|
||||||
{
|
{
|
||||||
if (m_instance)
|
if (m_instance)
|
||||||
|
@ -69,29 +71,29 @@ const Profile *Profile::instance()
|
||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Profile::location(const SpecialFolder folder) const
|
Path Profile::location(const SpecialFolder folder) const
|
||||||
{
|
{
|
||||||
QString result;
|
|
||||||
switch (folder)
|
switch (folder)
|
||||||
{
|
{
|
||||||
case SpecialFolder::Cache:
|
case SpecialFolder::Cache:
|
||||||
result = m_profileImpl->cacheLocation();
|
return m_profileImpl->cacheLocation();
|
||||||
break;
|
|
||||||
case SpecialFolder::Config:
|
case SpecialFolder::Config:
|
||||||
result = m_profileImpl->configLocation();
|
return m_profileImpl->configLocation();
|
||||||
break;
|
|
||||||
case SpecialFolder::Data:
|
case SpecialFolder::Data:
|
||||||
result = m_profileImpl->dataLocation();
|
return m_profileImpl->dataLocation();
|
||||||
break;
|
|
||||||
case SpecialFolder::Downloads:
|
case SpecialFolder::Downloads:
|
||||||
result = m_profileImpl->downloadLocation();
|
return m_profileImpl->downloadLocation();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
return m_profileImpl->rootPath();
|
||||||
}
|
}
|
||||||
|
@ -113,22 +115,22 @@ SettingsPtr Profile::applicationSettings(const QString &name) const
|
||||||
|
|
||||||
void Profile::ensureDirectoryExists(const SpecialFolder folder) const
|
void Profile::ensureDirectoryExists(const SpecialFolder folder) const
|
||||||
{
|
{
|
||||||
const QString locationPath = location(folder);
|
const Path locationPath = location(folder);
|
||||||
if (!locationPath.isEmpty() && !QDir().mkpath(locationPath))
|
if (!locationPath.isEmpty() && !Utils::Fs::mkpath(locationPath))
|
||||||
qFatal("Could not create required directory '%s'", qUtf8Printable(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);
|
return m_pathConverterImpl->toPortablePath(absolutePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Profile::fromPortablePath(const QString &portablePath) const
|
Path Profile::fromPortablePath(const Path &portablePath) const
|
||||||
{
|
{
|
||||||
return m_pathConverterImpl->fromPortablePath(portablePath);
|
return m_pathConverterImpl->fromPortablePath(portablePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString specialFolderLocation(const SpecialFolder folder)
|
Path specialFolderLocation(const SpecialFolder folder)
|
||||||
{
|
{
|
||||||
return Profile::instance()->location(folder);
|
return Profile::instance()->location(folder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
namespace Private
|
namespace Private
|
||||||
|
@ -54,26 +56,26 @@ enum class SpecialFolder
|
||||||
class Profile
|
class Profile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void initInstance(const QString &rootProfilePath, const QString &configurationName,
|
static void initInstance(const Path &rootProfilePath, const QString &configurationName,
|
||||||
bool convertPathsToProfileRelative);
|
bool convertPathsToProfileRelative);
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static const Profile *instance();
|
static const Profile *instance();
|
||||||
|
|
||||||
QString location(SpecialFolder folder) const;
|
Path location(SpecialFolder folder) const;
|
||||||
SettingsPtr applicationSettings(const QString &name) const;
|
SettingsPtr applicationSettings(const QString &name) const;
|
||||||
|
|
||||||
QString rootPath() const;
|
Path rootPath() const;
|
||||||
QString configurationName() const;
|
QString configurationName() const;
|
||||||
|
|
||||||
/// Returns either default name for configuration file (QCoreApplication::applicationName())
|
/// Returns either default name for configuration file (QCoreApplication::applicationName())
|
||||||
/// or the value, supplied via parameters
|
/// or the value, supplied via parameters
|
||||||
QString profileName() const;
|
QString profileName() const;
|
||||||
|
|
||||||
QString toPortablePath(const QString &absolutePath) const;
|
Path toPortablePath(const Path &absolutePath) const;
|
||||||
QString fromPortablePath(const QString &portablePath) const;
|
Path fromPortablePath(const Path &portablePath) const;
|
||||||
|
|
||||||
private:
|
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()
|
~Profile() = default; // to generate correct call to ProfilePrivate::~ProfileImpl()
|
||||||
|
|
||||||
void ensureDirectoryExists(SpecialFolder folder) const;
|
void ensureDirectoryExists(SpecialFolder folder) const;
|
||||||
|
@ -83,4 +85,4 @@ private:
|
||||||
static Profile *m_instance;
|
static Profile *m_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString specialFolderLocation(SpecialFolder folder);
|
Path specialFolderLocation(SpecialFolder folder);
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
Private::Profile::Profile(const QString &configurationName)
|
Private::Profile::Profile(const QString &configurationName)
|
||||||
: m_configurationName {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 {};
|
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);
|
return locationWithConfigurationName(QStandardPaths::CacheLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Private::DefaultProfile::configLocation() const
|
Path Private::DefaultProfile::configLocation() const
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
// On Windows QSettings stores files in FOLDERID_RoamingAppData\AppName
|
// On Windows QSettings stores files in FOLDERID_RoamingAppData\AppName
|
||||||
|
@ -81,22 +83,22 @@ QString Private::DefaultProfile::configLocation() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Private::DefaultProfile::dataLocation() const
|
Path Private::DefaultProfile::dataLocation() const
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN) || defined (Q_OS_MACOS)
|
#if defined(Q_OS_WIN) || defined (Q_OS_MACOS)
|
||||||
return locationWithConfigurationName(QStandardPaths::AppLocalDataLocation);
|
return locationWithConfigurationName(QStandardPaths::AppLocalDataLocation);
|
||||||
#else
|
#else
|
||||||
// On Linux keep using the legacy directory ~/.local/share/data/ if it exists
|
// On Linux keep using the legacy directory ~/.local/share/data/ if it exists
|
||||||
const QString legacyDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
|
const Path genericDataPath {QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)};
|
||||||
+ QLatin1String("/data/") + profileName();
|
const Path profilePath {profileName()};
|
||||||
|
const Path legacyDir = genericDataPath / Path("data") / profilePath;
|
||||||
|
|
||||||
const QString dataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
|
const Path dataDir = genericDataPath / profilePath;
|
||||||
+ QLatin1Char('/') + profileName();
|
|
||||||
|
|
||||||
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'",
|
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;
|
return legacyDir;
|
||||||
}
|
}
|
||||||
|
@ -105,9 +107,9 @@ QString Private::DefaultProfile::dataLocation() const
|
||||||
#endif
|
#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
|
SettingsPtr Private::DefaultProfile::applicationSettings(const QString &name) const
|
||||||
|
@ -119,48 +121,48 @@ SettingsPtr Private::DefaultProfile::applicationSettings(const QString &name) co
|
||||||
#endif
|
#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}
|
: Profile {configurationName}
|
||||||
, m_rootDir {rootPath}
|
, m_rootPath {rootPath}
|
||||||
, m_baseDir {m_rootDir.absoluteFilePath(profileName())}
|
, m_basePath {m_rootPath / Path(profileName())}
|
||||||
, m_cacheLocation {m_baseDir.absoluteFilePath(QLatin1String("cache"))}
|
, m_cacheLocation {m_basePath / Path("cache")}
|
||||||
, m_configLocation {m_baseDir.absoluteFilePath(QLatin1String("config"))}
|
, m_configLocation {m_basePath / Path("config")}
|
||||||
, m_dataLocation {m_baseDir.absoluteFilePath(QLatin1String("data"))}
|
, m_dataLocation {m_basePath / Path("data")}
|
||||||
, m_downloadLocation {m_baseDir.absoluteFilePath(QLatin1String("downloads"))}
|
, 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;
|
return m_cacheLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Private::CustomProfile::configLocation() const
|
Path Private::CustomProfile::configLocation() const
|
||||||
{
|
{
|
||||||
return m_configLocation;
|
return m_configLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Private::CustomProfile::dataLocation() const
|
Path Private::CustomProfile::dataLocation() const
|
||||||
{
|
{
|
||||||
return m_dataLocation;
|
return m_dataLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Private::CustomProfile::downloadLocation() const
|
Path Private::CustomProfile::downloadLocation() const
|
||||||
{
|
{
|
||||||
return m_downloadLocation;
|
return m_downloadLocation;
|
||||||
}
|
}
|
||||||
|
@ -173,48 +175,48 @@ SettingsPtr Private::CustomProfile::applicationSettings(const QString &name) con
|
||||||
#else
|
#else
|
||||||
const char CONF_FILE_EXTENSION[] = ".conf";
|
const char CONF_FILE_EXTENSION[] = ".conf";
|
||||||
#endif
|
#endif
|
||||||
const QString settingsFileName {QDir(configLocation()).absoluteFilePath(name + QLatin1String(CONF_FILE_EXTENSION))};
|
const Path settingsFilePath = configLocation() / Path(name + QLatin1String(CONF_FILE_EXTENSION));
|
||||||
return SettingsPtr(new QSettings(settingsFileName, QSettings::IniFormat));
|
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;
|
return portablePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Private::NoConvertConverter::toPortablePath(const QString &path) const
|
Path Private::NoConvertConverter::toPortablePath(const Path &path) const
|
||||||
{
|
{
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Private::Converter::Converter(const QString &basePath)
|
Private::Converter::Converter(const Path &basePath)
|
||||||
: m_baseDir {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;
|
return path;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
if (QDir::isAbsolutePath(path))
|
if (path.isAbsolute())
|
||||||
{
|
{
|
||||||
const QChar driveLeter = path[0].toUpper();
|
const QChar driveLetter = path.data()[0].toUpper();
|
||||||
const QChar baseDriveLetter = m_baseDir.path()[0].toUpper();
|
const QChar baseDriveLetter = m_basePath.data()[0].toUpper();
|
||||||
const bool onSameDrive = (driveLeter.category() == QChar::Letter_Uppercase) && (driveLeter == baseDriveLetter);
|
const bool onSameDrive = (driveLetter.category() == QChar::Letter_Uppercase) && (driveLetter == baseDriveLetter);
|
||||||
if (!onSameDrive)
|
if (!onSameDrive)
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
#endif
|
#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 portablePath;
|
||||||
|
|
||||||
return QDir::cleanPath(m_baseDir.absoluteFilePath(portablePath));
|
return m_basePath / portablePath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include "base/profile.h"
|
#include "base/path.h"
|
||||||
|
#include "profile.h"
|
||||||
|
|
||||||
namespace Private
|
namespace Private
|
||||||
{
|
{
|
||||||
|
@ -41,17 +41,17 @@ namespace Private
|
||||||
public:
|
public:
|
||||||
virtual ~Profile() = default;
|
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
|
* @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 Path cacheLocation() const = 0;
|
||||||
virtual QString configLocation() const = 0;
|
virtual Path configLocation() const = 0;
|
||||||
virtual QString dataLocation() const = 0;
|
virtual Path dataLocation() const = 0;
|
||||||
virtual QString downloadLocation() const = 0;
|
virtual Path downloadLocation() const = 0;
|
||||||
|
|
||||||
virtual SettingsPtr applicationSettings(const QString &name) const = 0;
|
virtual SettingsPtr applicationSettings(const QString &name) const = 0;
|
||||||
|
|
||||||
|
@ -77,12 +77,12 @@ namespace Private
|
||||||
public:
|
public:
|
||||||
explicit DefaultProfile(const QString &configurationName);
|
explicit DefaultProfile(const QString &configurationName);
|
||||||
|
|
||||||
QString rootPath() const override;
|
Path rootPath() const override;
|
||||||
QString basePath() const override;
|
Path basePath() const override;
|
||||||
QString cacheLocation() const override;
|
Path cacheLocation() const override;
|
||||||
QString configLocation() const override;
|
Path configLocation() const override;
|
||||||
QString dataLocation() const override;
|
Path dataLocation() const override;
|
||||||
QString downloadLocation() const override;
|
Path downloadLocation() const override;
|
||||||
SettingsPtr applicationSettings(const QString &name) const override;
|
SettingsPtr applicationSettings(const QString &name) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -92,55 +92,55 @@ namespace Private
|
||||||
* @param location location kind
|
* @param location location kind
|
||||||
* @return QStandardPaths::writableLocation(location) / configurationName()
|
* @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
|
/// Custom tree: creates directories under the specified root directory
|
||||||
class CustomProfile final : public Profile
|
class CustomProfile final : public Profile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CustomProfile(const QString &rootPath, const QString &configurationName);
|
CustomProfile(const Path &rootPath, const QString &configurationName);
|
||||||
|
|
||||||
QString rootPath() const override;
|
Path rootPath() const override;
|
||||||
QString basePath() const override;
|
Path basePath() const override;
|
||||||
QString cacheLocation() const override;
|
Path cacheLocation() const override;
|
||||||
QString configLocation() const override;
|
Path configLocation() const override;
|
||||||
QString dataLocation() const override;
|
Path dataLocation() const override;
|
||||||
QString downloadLocation() const override;
|
Path downloadLocation() const override;
|
||||||
SettingsPtr applicationSettings(const QString &name) const override;
|
SettingsPtr applicationSettings(const QString &name) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QDir m_rootDir;
|
const Path m_rootPath;
|
||||||
const QDir m_baseDir;
|
const Path m_basePath;
|
||||||
const QString m_cacheLocation;
|
const Path m_cacheLocation;
|
||||||
const QString m_configLocation;
|
const Path m_configLocation;
|
||||||
const QString m_dataLocation;
|
const Path m_dataLocation;
|
||||||
const QString m_downloadLocation;
|
const Path m_downloadLocation;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PathConverter
|
class PathConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual QString toPortablePath(const QString &path) const = 0;
|
virtual Path toPortablePath(const Path &path) const = 0;
|
||||||
virtual QString fromPortablePath(const QString &portablePath) const = 0;
|
virtual Path fromPortablePath(const Path &portablePath) const = 0;
|
||||||
virtual ~PathConverter() = default;
|
virtual ~PathConverter() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NoConvertConverter final : public PathConverter
|
class NoConvertConverter final : public PathConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString toPortablePath(const QString &path) const override;
|
Path toPortablePath(const Path &path) const override;
|
||||||
QString fromPortablePath(const QString &portablePath) const override;
|
Path fromPortablePath(const Path &portablePath) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Converter final : public PathConverter
|
class Converter final : public PathConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Converter(const QString &basePath);
|
explicit Converter(const Path &basePath);
|
||||||
QString toPortablePath(const QString &path) const override;
|
Path toPortablePath(const Path &path) const override;
|
||||||
QString fromPortablePath(const QString &portablePath) const override;
|
Path fromPortablePath(const Path &portablePath) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir m_baseDir;
|
Path m_basePath;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,8 @@ struct ProcessingJob
|
||||||
QVariantHash articleData;
|
QVariantHash articleData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const QString ConfFolderName(QStringLiteral("rss"));
|
const QString CONF_FOLDER_NAME {QStringLiteral("rss")};
|
||||||
const QString RulesFileName(QStringLiteral("download_rules.json"));
|
const QString RULES_FILE_NAME {QStringLiteral("download_rules.json")};
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -107,17 +107,16 @@ AutoDownloader::AutoDownloader()
|
||||||
Q_ASSERT(!m_instance); // only one instance is allowed
|
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
|
|
||||||
m_fileStorage = new AsyncFileStorage(
|
m_fileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FOLDER_NAME));
|
||||||
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Config) + QLatin1Char('/') + ConfFolderName));
|
|
||||||
if (!m_fileStorage)
|
if (!m_fileStorage)
|
||||||
throw RuntimeError(tr("Directory for RSS AutoDownloader data is unavailable."));
|
throw RuntimeError(tr("Directory for RSS AutoDownloader data is unavailable."));
|
||||||
|
|
||||||
m_fileStorage->moveToThread(m_ioThread);
|
m_fileStorage->moveToThread(m_ioThread);
|
||||||
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
|
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")
|
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();
|
m_ioThread->start();
|
||||||
|
@ -414,7 +413,7 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
|
||||||
|
|
||||||
void AutoDownloader::load()
|
void AutoDownloader::load()
|
||||||
{
|
{
|
||||||
QFile rulesFile(m_fileStorage->storageDir().absoluteFilePath(RulesFileName));
|
QFile rulesFile {(m_fileStorage->storageDir() / Path(RULES_FILE_NAME)).data()};
|
||||||
|
|
||||||
if (!rulesFile.exists())
|
if (!rulesFile.exists())
|
||||||
loadRulesLegacy();
|
loadRulesLegacy();
|
||||||
|
@ -463,7 +462,7 @@ void AutoDownloader::store()
|
||||||
for (const auto &rule : asConst(m_rules))
|
for (const auto &rule : asConst(m_rules))
|
||||||
jsonObj.insert(rule.name(), rule.toJsonObject());
|
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()
|
void AutoDownloader::storeDeferred()
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
|
@ -132,7 +133,7 @@ namespace RSS
|
||||||
int ignoreDays = 0;
|
int ignoreDays = 0;
|
||||||
QDateTime lastMatch;
|
QDateTime lastMatch;
|
||||||
|
|
||||||
QString savePath;
|
Path savePath;
|
||||||
QString category;
|
QString category;
|
||||||
std::optional<bool> addPaused;
|
std::optional<bool> addPaused;
|
||||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||||
|
@ -466,7 +467,7 @@ QJsonObject AutoDownloadRule::toJsonObject() const
|
||||||
, {Str_MustNotContain, mustNotContain()}
|
, {Str_MustNotContain, mustNotContain()}
|
||||||
, {Str_EpisodeFilter, episodeFilter()}
|
, {Str_EpisodeFilter, episodeFilter()}
|
||||||
, {Str_AffectedFeeds, QJsonArray::fromStringList(feedURLs())}
|
, {Str_AffectedFeeds, QJsonArray::fromStringList(feedURLs())}
|
||||||
, {Str_SavePath, savePath()}
|
, {Str_SavePath, savePath().toString()}
|
||||||
, {Str_AssignedCategory, assignedCategory()}
|
, {Str_AssignedCategory, assignedCategory()}
|
||||||
, {Str_LastMatch, lastMatch().toString(Qt::RFC2822Date)}
|
, {Str_LastMatch, lastMatch().toString(Qt::RFC2822Date)}
|
||||||
, {Str_IgnoreDays, ignoreDays()}
|
, {Str_IgnoreDays, ignoreDays()}
|
||||||
|
@ -485,7 +486,7 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
|
||||||
rule.setMustNotContain(jsonObj.value(Str_MustNotContain).toString());
|
rule.setMustNotContain(jsonObj.value(Str_MustNotContain).toString());
|
||||||
rule.setEpisodeFilter(jsonObj.value(Str_EpisodeFilter).toString());
|
rule.setEpisodeFilter(jsonObj.value(Str_EpisodeFilter).toString());
|
||||||
rule.setEnabled(jsonObj.value(Str_Enabled).toBool(true));
|
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.setCategory(jsonObj.value(Str_AssignedCategory).toString());
|
||||||
rule.setAddPaused(toOptionalBool(jsonObj.value(Str_AddPaused)));
|
rule.setAddPaused(toOptionalBool(jsonObj.value(Str_AddPaused)));
|
||||||
|
|
||||||
|
@ -546,7 +547,7 @@ QVariantHash AutoDownloadRule::toLegacyDict() const
|
||||||
return {{"name", name()},
|
return {{"name", name()},
|
||||||
{"must_contain", mustContain()},
|
{"must_contain", mustContain()},
|
||||||
{"must_not_contain", mustNotContain()},
|
{"must_not_contain", mustNotContain()},
|
||||||
{"save_path", savePath()},
|
{"save_path", savePath().toString()},
|
||||||
{"affected_feeds", feedURLs()},
|
{"affected_feeds", feedURLs()},
|
||||||
{"enabled", isEnabled()},
|
{"enabled", isEnabled()},
|
||||||
{"category_assigned", assignedCategory()},
|
{"category_assigned", assignedCategory()},
|
||||||
|
@ -567,7 +568,7 @@ AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
|
||||||
rule.setEpisodeFilter(dict.value("episode_filter").toString());
|
rule.setEpisodeFilter(dict.value("episode_filter").toString());
|
||||||
rule.setFeedURLs(dict.value("affected_feeds").toStringList());
|
rule.setFeedURLs(dict.value("affected_feeds").toStringList());
|
||||||
rule.setEnabled(dict.value("enabled", false).toBool());
|
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.setCategory(dict.value("category_assigned").toString());
|
||||||
rule.setAddPaused(addPausedLegacyToOptionalBool(dict.value("add_paused").toInt()));
|
rule.setAddPaused(addPausedLegacyToOptionalBool(dict.value("add_paused").toInt()));
|
||||||
rule.setLastMatch(dict.value("last_match").toDateTime());
|
rule.setLastMatch(dict.value("last_match").toDateTime());
|
||||||
|
@ -624,14 +625,14 @@ void AutoDownloadRule::setName(const QString &name)
|
||||||
m_dataPtr->name = name;
|
m_dataPtr->name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AutoDownloadRule::savePath() const
|
Path AutoDownloadRule::savePath() const
|
||||||
{
|
{
|
||||||
return m_dataPtr->savePath;
|
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
|
std::optional<bool> AutoDownloadRule::addPaused() const
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "base/bittorrent/torrentcontentlayout.h"
|
#include "base/bittorrent/torrentcontentlayout.h"
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
class QJsonObject;
|
class QJsonObject;
|
||||||
|
@ -77,8 +78,8 @@ namespace RSS
|
||||||
QStringList previouslyMatchedEpisodes() const;
|
QStringList previouslyMatchedEpisodes() const;
|
||||||
void setPreviouslyMatchedEpisodes(const QStringList &previouslyMatchedEpisodes);
|
void setPreviouslyMatchedEpisodes(const QStringList &previouslyMatchedEpisodes);
|
||||||
|
|
||||||
QString savePath() const;
|
Path savePath() const;
|
||||||
void setSavePath(const QString &savePath);
|
void setSavePath(const Path &savePath);
|
||||||
std::optional<bool> addPaused() const;
|
std::optional<bool> addPaused() const;
|
||||||
void setAddPaused(std::optional<bool> addPaused);
|
void setAddPaused(std::optional<bool> addPaused);
|
||||||
std::optional<BitTorrent::TorrentContentLayout> torrentContentLayout() const;
|
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)
|
, m_url(url)
|
||||||
{
|
{
|
||||||
const auto uidHex = QString::fromLatin1(m_uid.toRfc4122().toHex());
|
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)
|
// Move to new file naming scheme (since v4.1.2)
|
||||||
const QString legacyFilename
|
const QString legacyFilename = Utils::Fs::toValidFileName(m_url, QLatin1String("_")) + QLatin1String(".json");
|
||||||
{Utils::Fs::toValidFileSystemName(m_url, false, QLatin1String("_"))
|
const Path storageDir = m_session->dataFileStorage()->storageDir();
|
||||||
+ QLatin1String(".json")};
|
const Path dataFilePath = storageDir / m_dataFileName;
|
||||||
const QDir storageDir {m_session->dataFileStorage()->storageDir()};
|
if (!dataFilePath.exists())
|
||||||
if (!QFile::exists(storageDir.absoluteFilePath(m_dataFileName)))
|
Utils::Fs::renameFile((storageDir / Path(legacyFilename)), dataFilePath);
|
||||||
QFile::rename(storageDir.absoluteFilePath(legacyFilename), storageDir.absoluteFilePath(m_dataFileName));
|
|
||||||
|
|
||||||
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 = new Private::Parser(m_lastBuildDate);
|
||||||
m_parser->moveToThread(m_session->workingThread());
|
m_parser->moveToThread(m_session->workingThread());
|
||||||
|
@ -139,7 +138,7 @@ void Feed::refresh()
|
||||||
m_downloadHandler = Net::DownloadManager::instance()->download(m_url);
|
m_downloadHandler = Net::DownloadManager::instance()->download(m_url);
|
||||||
connect(m_downloadHandler, &Net::DownloadHandler::finished, this, &Feed::handleDownloadFinished);
|
connect(m_downloadHandler, &Net::DownloadHandler::finished, this, &Feed::handleDownloadFinished);
|
||||||
|
|
||||||
if (!QFile::exists(m_iconPath))
|
if (!m_iconPath.exists())
|
||||||
downloadIcon();
|
downloadIcon();
|
||||||
|
|
||||||
m_isLoading = true;
|
m_isLoading = true;
|
||||||
|
@ -262,7 +261,7 @@ void Feed::handleParsingFinished(const RSS::Private::ParsingResult &result)
|
||||||
|
|
||||||
void Feed::load()
|
void Feed::load()
|
||||||
{
|
{
|
||||||
QFile file(m_session->dataFileStorage()->storageDir().absoluteFilePath(m_dataFileName));
|
QFile file {(m_session->dataFileStorage()->storageDir() / m_dataFileName).data()};
|
||||||
|
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
{
|
{
|
||||||
|
@ -278,7 +277,7 @@ void Feed::load()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
|
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);
|
, Log::WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,7 +499,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
|
||||||
return newArticlesCount;
|
return newArticlesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Feed::iconPath() const
|
Path Feed::iconPath() const
|
||||||
{
|
{
|
||||||
return m_iconPath;
|
return m_iconPath;
|
||||||
}
|
}
|
||||||
|
@ -549,8 +548,8 @@ void Feed::handleArticleRead(Article *article)
|
||||||
|
|
||||||
void Feed::cleanup()
|
void Feed::cleanup()
|
||||||
{
|
{
|
||||||
Utils::Fs::forceRemove(m_session->dataFileStorage()->storageDir().absoluteFilePath(m_dataFileName));
|
Utils::Fs::removeFile(m_session->dataFileStorage()->storageDir() / m_dataFileName);
|
||||||
Utils::Fs::forceRemove(m_iconPath);
|
Utils::Fs::removeFile(m_iconPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::timerEvent(QTimerEvent *event)
|
void Feed::timerEvent(QTimerEvent *event)
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "rss_item.h"
|
#include "rss_item.h"
|
||||||
|
|
||||||
class AsyncFileStorage;
|
class AsyncFileStorage;
|
||||||
|
@ -79,7 +80,7 @@ namespace RSS
|
||||||
bool hasError() const;
|
bool hasError() const;
|
||||||
bool isLoading() const;
|
bool isLoading() const;
|
||||||
Article *articleByGUID(const QString &guid) const;
|
Article *articleByGUID(const QString &guid) const;
|
||||||
QString iconPath() const;
|
Path iconPath() const;
|
||||||
|
|
||||||
QJsonValue toJsonValue(bool withData = false) const override;
|
QJsonValue toJsonValue(bool withData = false) const override;
|
||||||
|
|
||||||
|
@ -122,8 +123,8 @@ namespace RSS
|
||||||
QHash<QString, Article *> m_articles;
|
QHash<QString, Article *> m_articles;
|
||||||
QList<Article *> m_articlesByDate;
|
QList<Article *> m_articlesByDate;
|
||||||
int m_unreadCount = 0;
|
int m_unreadCount = 0;
|
||||||
QString m_iconPath;
|
Path m_iconPath;
|
||||||
QString m_dataFileName;
|
Path m_dataFileName;
|
||||||
QBasicTimer m_savingTimer;
|
QBasicTimer m_savingTimer;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
Net::DownloadHandler *m_downloadHandler = nullptr;
|
Net::DownloadHandler *m_downloadHandler = nullptr;
|
||||||
|
|
|
@ -49,9 +49,9 @@
|
||||||
#include "rss_item.h"
|
#include "rss_item.h"
|
||||||
|
|
||||||
const int MsecsPerMin = 60000;
|
const int MsecsPerMin = 60000;
|
||||||
const QString ConfFolderName(QStringLiteral("rss"));
|
const QString CONF_FOLDER_NAME(QStringLiteral("rss"));
|
||||||
const QString DataFolderName(QStringLiteral("rss/articles"));
|
const QString DATA_FOLDER_NAME(QStringLiteral("rss/articles"));
|
||||||
const QString FeedsFileName(QStringLiteral("feeds.json"));
|
const QString FEEDS_FILE_NAME(QStringLiteral("feeds.json"));
|
||||||
|
|
||||||
using namespace RSS;
|
using namespace RSS;
|
||||||
|
|
||||||
|
@ -66,24 +66,22 @@ Session::Session()
|
||||||
Q_ASSERT(!m_instance); // only one instance is allowed
|
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
|
|
||||||
m_confFileStorage = new AsyncFileStorage(
|
m_confFileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FOLDER_NAME));
|
||||||
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Config) + QLatin1Char('/') + ConfFolderName));
|
|
||||||
m_confFileStorage->moveToThread(m_workingThread);
|
m_confFileStorage->moveToThread(m_workingThread);
|
||||||
connect(m_workingThread, &QThread::finished, m_confFileStorage, &AsyncFileStorage::deleteLater);
|
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")
|
LogMsg(tr("Couldn't save RSS Session configuration in %1. Error: %2")
|
||||||
.arg(fileName, errorString), Log::WARNING);
|
.arg(fileName.toString(), errorString), Log::WARNING);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_dataFileStorage = new AsyncFileStorage(
|
m_dataFileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Data) / Path(DATA_FOLDER_NAME));
|
||||||
Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + QLatin1Char('/') + DataFolderName));
|
|
||||||
m_dataFileStorage->moveToThread(m_workingThread);
|
m_dataFileStorage->moveToThread(m_workingThread);
|
||||||
connect(m_workingThread, &QThread::finished, m_dataFileStorage, &AsyncFileStorage::deleteLater);
|
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")
|
LogMsg(tr("Couldn't save RSS Session data in %1. Error: %2")
|
||||||
.arg(fileName, errorString), Log::WARNING);
|
.arg(fileName.toString(), errorString), Log::WARNING);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_itemsByPath.insert("", new Folder); // root folder
|
m_itemsByPath.insert("", new Folder); // root folder
|
||||||
|
@ -233,7 +231,7 @@ Item *Session::itemByPath(const QString &path) const
|
||||||
|
|
||||||
void Session::load()
|
void Session::load()
|
||||||
{
|
{
|
||||||
QFile itemsFile(m_confFileStorage->storageDir().absoluteFilePath(FeedsFileName));
|
QFile itemsFile {(m_confFileStorage->storageDir() / Path(FEEDS_FILE_NAME)).data()};
|
||||||
if (!itemsFile.exists())
|
if (!itemsFile.exists())
|
||||||
{
|
{
|
||||||
loadLegacy();
|
loadLegacy();
|
||||||
|
@ -372,7 +370,8 @@ void Session::loadLegacy()
|
||||||
|
|
||||||
void Session::store()
|
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)
|
nonstd::expected<Folder *, QString> Session::prepareItemDest(const QString &path)
|
||||||
|
|
|
@ -30,8 +30,9 @@
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
#include "../utils/foreignapps.h"
|
#include "base/path.h"
|
||||||
#include "../utils/fs.h"
|
#include "base/utils/foreignapps.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
#include "searchpluginmanager.h"
|
#include "searchpluginmanager.h"
|
||||||
|
|
||||||
SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager)
|
SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager)
|
||||||
|
@ -44,7 +45,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QStri
|
||||||
, this, &SearchDownloadHandler::downloadProcessFinished);
|
, this, &SearchDownloadHandler::downloadProcessFinished);
|
||||||
const QStringList params
|
const QStringList params
|
||||||
{
|
{
|
||||||
Utils::Fs::toNativePath(m_manager->engineLocation() + "/nova2dl.py"),
|
(m_manager->engineLocation() / Path("nova2dl.py")).toString(),
|
||||||
siteUrl,
|
siteUrl,
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/utils/foreignapps.h"
|
#include "base/utils/foreignapps.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "searchpluginmanager.h"
|
#include "searchpluginmanager.h"
|
||||||
|
@ -67,7 +68,7 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co
|
||||||
|
|
||||||
const QStringList params
|
const QStringList params
|
||||||
{
|
{
|
||||||
Utils::Fs::toNativePath(m_manager->engineLocation() + "/nova2.py"),
|
(m_manager->engineLocation() / Path("nova2.py")).toString(),
|
||||||
m_usedPlugins.join(','),
|
m_usedPlugins.join(','),
|
||||||
m_category
|
m_category
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,30 +52,31 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void clearPythonCache(const QString &path)
|
void clearPythonCache(const Path &path)
|
||||||
{
|
{
|
||||||
// remove python cache artifacts in `path` and subdirs
|
// remove python cache artifacts in `path` and subdirs
|
||||||
|
|
||||||
QStringList dirs = {path};
|
PathList dirs = {path};
|
||||||
QDirIterator iter {path, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
QDirIterator iter {path.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||||
while (iter.hasNext())
|
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
|
// python 3: remove "__pycache__" folders
|
||||||
if (dir.endsWith("/__pycache__"))
|
if (dir.filename() == QLatin1String("__pycache__"))
|
||||||
{
|
{
|
||||||
Utils::Fs::removeDirRecursive(dir);
|
Utils::Fs::removeDirRecursively(dir);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// python 2: remove "*.pyc" files
|
// 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)
|
for (const QString &file : files)
|
||||||
{
|
{
|
||||||
if (file.endsWith(".pyc"))
|
const Path path {file};
|
||||||
Utils::Fs::forceRemove(file);
|
if (path.hasExtension(QLatin1String(".pyc")))
|
||||||
|
Utils::Fs::removeFile(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,21 +211,22 @@ void SearchPluginManager::installPlugin(const QString &source)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QString path = source;
|
const Path path {source.startsWith("file:", Qt::CaseInsensitive) ? QUrl(source).toLocalFile() : source};
|
||||||
if (path.startsWith("file:", Qt::CaseInsensitive))
|
|
||||||
path = QUrl(path).toLocalFile();
|
|
||||||
|
|
||||||
QString pluginName = Utils::Fs::fileName(path);
|
QString pluginName = path.filename();
|
||||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf('.'));
|
if (pluginName.endsWith(".py", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
if (!path.endsWith(".py", Qt::CaseInsensitive))
|
pluginName.chop(pluginName.size() - pluginName.lastIndexOf('.'));
|
||||||
emit pluginInstallationFailed(pluginName, tr("Unknown search engine plugin file format."));
|
|
||||||
else
|
|
||||||
installPlugin_impl(pluginName, path);
|
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 PluginVersion newVersion = getPluginVersion(path);
|
||||||
const PluginInfo *plugin = pluginInfo(name);
|
const PluginInfo *plugin = pluginInfo(name);
|
||||||
|
@ -236,30 +238,31 @@ void SearchPluginManager::installPlugin_impl(const QString &name, const QString
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process with install
|
// Process with install
|
||||||
const QString destPath = pluginPath(name);
|
const Path destPath = pluginPath(name);
|
||||||
|
const Path backupPath = destPath + ".bak";
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
if (QFile::exists(destPath))
|
if (destPath.exists())
|
||||||
{
|
{
|
||||||
// Backup in case install fails
|
// Backup in case install fails
|
||||||
QFile::copy(destPath, destPath + ".bak");
|
Utils::Fs::copyFile(destPath, backupPath);
|
||||||
Utils::Fs::forceRemove(destPath);
|
Utils::Fs::removeFile(destPath);
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
// Copy the plugin
|
// Copy the plugin
|
||||||
QFile::copy(path, destPath);
|
Utils::Fs::copyFile(path, destPath);
|
||||||
// Update supported plugins
|
// Update supported plugins
|
||||||
update();
|
update();
|
||||||
// Check if this was correctly installed
|
// Check if this was correctly installed
|
||||||
if (!m_plugins.contains(name))
|
if (!m_plugins.contains(name))
|
||||||
{
|
{
|
||||||
// Remove broken file
|
// Remove broken file
|
||||||
Utils::Fs::forceRemove(destPath);
|
Utils::Fs::removeFile(destPath);
|
||||||
LogMsg(tr("Plugin %1 is not supported.").arg(name), Log::INFO);
|
LogMsg(tr("Plugin %1 is not supported.").arg(name), Log::INFO);
|
||||||
if (updated)
|
if (updated)
|
||||||
{
|
{
|
||||||
// restore backup
|
// restore backup
|
||||||
QFile::copy(destPath + ".bak", destPath);
|
Utils::Fs::copyFile(backupPath, destPath);
|
||||||
Utils::Fs::forceRemove(destPath + ".bak");
|
Utils::Fs::removeFile(backupPath);
|
||||||
// Update supported plugins
|
// Update supported plugins
|
||||||
update();
|
update();
|
||||||
emit pluginUpdateFailed(name, tr("Plugin is not supported."));
|
emit pluginUpdateFailed(name, tr("Plugin is not supported."));
|
||||||
|
@ -275,7 +278,7 @@ void SearchPluginManager::installPlugin_impl(const QString &name, const QString
|
||||||
if (updated)
|
if (updated)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Plugin %1 has been successfully updated.").arg(name), Log::INFO);
|
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());
|
clearPythonCache(engineLocation());
|
||||||
|
|
||||||
// remove it from hard drive
|
// remove it from hard drive
|
||||||
const QDir pluginsFolder(pluginsLocation());
|
const Path pluginsPath = pluginsLocation();
|
||||||
QStringList filters;
|
const QStringList filters {name + QLatin1String(".*")};
|
||||||
filters << name + ".*";
|
const QStringList files = QDir(pluginsPath.data()).entryList(filters, QDir::Files, QDir::Unsorted);
|
||||||
const QStringList files = pluginsFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
|
||||||
for (const QString &file : files)
|
for (const QString &file : files)
|
||||||
Utils::Fs::forceRemove(pluginsFolder.absoluteFilePath(file));
|
Utils::Fs::removeFile(pluginsPath / Path(file));
|
||||||
// Remove it from supported engines
|
// Remove it from supported engines
|
||||||
delete m_plugins.take(name);
|
delete m_plugins.take(name);
|
||||||
|
|
||||||
|
@ -301,15 +303,17 @@ bool SearchPluginManager::uninstallPlugin(const QString &name)
|
||||||
void SearchPluginManager::updateIconPath(PluginInfo *const plugin)
|
void SearchPluginManager::updateIconPath(PluginInfo *const plugin)
|
||||||
{
|
{
|
||||||
if (!plugin) return;
|
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;
|
plugin->iconPath = iconPath;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
iconPath = QString::fromLatin1("%1/%2.ico").arg(pluginsLocation(), plugin->name);
|
iconPath = pluginsPath / Path(plugin->name + QLatin1String(".ico"));
|
||||||
if (QFile::exists(iconPath))
|
if (iconPath.exists())
|
||||||
plugin->iconPath = iconPath;
|
plugin->iconPath = iconPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,20 +361,18 @@ QString SearchPluginManager::pluginFullName(const QString &pluginName)
|
||||||
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
|
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())
|
if (location.isEmpty())
|
||||||
{
|
{
|
||||||
location = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + "/nova3");
|
location = specialFolderLocation(SpecialFolder::Data) / Path("nova3");
|
||||||
|
Utils::Fs::mkpath(location);
|
||||||
const QDir locationDir(location);
|
|
||||||
locationDir.mkpath(locationDir.absolutePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return location;
|
return location;
|
||||||
|
@ -388,12 +390,12 @@ void SearchPluginManager::pluginDownloadFinished(const Net::DownloadResult &resu
|
||||||
{
|
{
|
||||||
if (result.status == Net::DownloadStatus::Success)
|
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);
|
Path pluginPath {QUrl(result.url).path()};
|
||||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf('.')); // Remove extension
|
pluginPath.removeExtension(); // Remove extension
|
||||||
installPlugin_impl(pluginName, filePath);
|
installPlugin_impl(pluginPath.filename(), filePath);
|
||||||
Utils::Fs::forceRemove(filePath);
|
Utils::Fs::removeFile(filePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -412,37 +414,37 @@ void SearchPluginManager::pluginDownloadFinished(const Net::DownloadResult &resu
|
||||||
void SearchPluginManager::updateNova()
|
void SearchPluginManager::updateNova()
|
||||||
{
|
{
|
||||||
// create nova directory if necessary
|
// 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.open(QIODevice::WriteOnly);
|
||||||
packageFile.close();
|
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.open(QIODevice::WriteOnly);
|
||||||
packageFile2.close();
|
packageFile2.close();
|
||||||
|
|
||||||
// Copy search plugin files (if necessary)
|
// 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 Path filePathBundled = Path(":/searchengine/nova3") / filename;
|
||||||
const QString filePathDisk = QDir(engineLocation()).absoluteFilePath(filename);
|
const Path filePathDisk = enginePath / filename;
|
||||||
|
|
||||||
if (compareVersion && (getPluginVersion(filePathBundled) <= getPluginVersion(filePathDisk)))
|
if (compareVersion && (getPluginVersion(filePathBundled) <= getPluginVersion(filePathDisk)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Utils::Fs::forceRemove(filePathDisk);
|
Utils::Fs::removeFile(filePathDisk);
|
||||||
QFile::copy(filePathBundled, filePathDisk);
|
Utils::Fs::copyFile(filePathBundled, filePathDisk);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateFile("helpers.py", true);
|
updateFile(Path("helpers.py"), true);
|
||||||
updateFile("nova2.py", true);
|
updateFile(Path("nova2.py"), true);
|
||||||
updateFile("nova2dl.py", true);
|
updateFile(Path("nova2dl.py"), true);
|
||||||
updateFile("novaprinter.py", true);
|
updateFile(Path("novaprinter.py"), true);
|
||||||
updateFile("sgmllib3.py", false);
|
updateFile(Path("sgmllib3.py"), false);
|
||||||
updateFile("socks.py", false);
|
updateFile(Path("socks.py"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchPluginManager::update()
|
void SearchPluginManager::update()
|
||||||
|
@ -450,7 +452,7 @@ void SearchPluginManager::update()
|
||||||
QProcess nova;
|
QProcess nova;
|
||||||
nova.setProcessEnvironment(QProcessEnvironment::systemEnvironment());
|
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.start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly);
|
||||||
nova.waitForFinished();
|
nova.waitForFinished();
|
||||||
|
|
||||||
|
@ -559,14 +561,14 @@ bool SearchPluginManager::isUpdateNeeded(const QString &pluginName, const Plugin
|
||||||
return (newVersion > oldVersion);
|
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))
|
if (!pluginFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
@ -581,7 +583,7 @@ PluginVersion SearchPluginManager::getPluginVersion(const QString &filePath)
|
||||||
return version;
|
return version;
|
||||||
|
|
||||||
LogMsg(tr("Search plugin '%1' contains invalid version string ('%2')")
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/utils/version.h"
|
#include "base/utils/version.h"
|
||||||
|
|
||||||
using PluginVersion = Utils::Version<unsigned short, 2>;
|
using PluginVersion = Utils::Version<unsigned short, 2>;
|
||||||
|
@ -50,7 +51,7 @@ struct PluginInfo
|
||||||
QString fullName;
|
QString fullName;
|
||||||
QString url;
|
QString url;
|
||||||
QStringList supportedCategories;
|
QStringList supportedCategories;
|
||||||
QString iconPath;
|
Path iconPath;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,11 +86,11 @@ public:
|
||||||
SearchHandler *startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
|
SearchHandler *startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
|
||||||
SearchDownloadHandler *downloadTorrent(const QString &siteUrl, const QString &url);
|
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);
|
static QString categoryFullName(const QString &categoryName);
|
||||||
QString pluginFullName(const QString &pluginName);
|
QString pluginFullName(const QString &pluginName);
|
||||||
static QString pluginsLocation();
|
static Path pluginsLocation();
|
||||||
static QString engineLocation();
|
static Path engineLocation();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pluginEnabled(const QString &name, bool enabled);
|
void pluginEnabled(const QString &name, bool enabled);
|
||||||
|
@ -106,13 +107,13 @@ private:
|
||||||
void update();
|
void update();
|
||||||
void updateNova();
|
void updateNova();
|
||||||
void parseVersionInfo(const QByteArray &info);
|
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;
|
bool isUpdateNeeded(const QString &pluginName, PluginVersion newVersion) const;
|
||||||
|
|
||||||
void versionInfoDownloadFinished(const Net::DownloadResult &result);
|
void versionInfoDownloadFinished(const Net::DownloadResult &result);
|
||||||
void pluginDownloadFinished(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;
|
static QPointer<SearchPluginManager> m_instance;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "path.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
#include "utils/fs.h"
|
#include "utils/fs.h"
|
||||||
|
|
||||||
|
@ -59,8 +60,8 @@ namespace
|
||||||
// there is no other way to get that name except
|
// there is no other way to get that name except
|
||||||
// actually create a QSettings object.
|
// actually create a QSettings object.
|
||||||
// if serialization operation was not successful we return empty string
|
// if serialization operation was not successful we return empty string
|
||||||
QString deserialize(const QString &name, QVariantHash &data) const;
|
Path deserialize(const QString &name, QVariantHash &data) const;
|
||||||
QString serialize(const QString &name, const QVariantHash &data) const;
|
Path serialize(const QString &name, const QVariantHash &data) const;
|
||||||
|
|
||||||
const QString m_name;
|
const QString m_name;
|
||||||
};
|
};
|
||||||
|
@ -156,7 +157,7 @@ QVariantHash TransactionalSettings::read() const
|
||||||
{
|
{
|
||||||
QVariantHash res;
|
QVariantHash res;
|
||||||
|
|
||||||
const QString newPath = deserialize(m_name + QLatin1String("_new"), res);
|
const Path newPath = deserialize(m_name + QLatin1String("_new"), res);
|
||||||
if (!newPath.isEmpty())
|
if (!newPath.isEmpty())
|
||||||
{ // "_new" file is NOT empty
|
{ // "_new" file is NOT empty
|
||||||
// This means that the PC closed either due to power outage
|
// 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
|
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
|
||||||
// contains the most recent settings.
|
// contains the most recent settings.
|
||||||
Logger::instance()->addMessage(QObject::tr("Detected unclean program exit. Using fallback file to restore settings: %1")
|
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);
|
, Log::WARNING);
|
||||||
|
|
||||||
QString finalPath = newPath;
|
QString finalPathStr = newPath.data();
|
||||||
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
const int index = finalPathStr.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||||
finalPath.remove(index, 4);
|
finalPathStr.remove(index, 4);
|
||||||
|
|
||||||
Utils::Fs::forceRemove(finalPath);
|
const Path finalPath {finalPathStr};
|
||||||
QFile::rename(newPath, finalPath);
|
Utils::Fs::removeFile(finalPath);
|
||||||
|
Utils::Fs::renameFile(newPath, finalPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -189,22 +191,23 @@ bool TransactionalSettings::write(const QVariantHash &data) const
|
||||||
// between deleting the file and recreating it. This is a safety measure.
|
// 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
|
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
|
||||||
// replace qBittorrent.ini/qBittorrent.conf with it.
|
// 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())
|
if (newPath.isEmpty())
|
||||||
{
|
{
|
||||||
Utils::Fs::forceRemove(newPath);
|
Utils::Fs::removeFile(newPath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString finalPath = newPath;
|
QString finalPathStr = newPath.data();
|
||||||
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
const int index = finalPathStr.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||||
finalPath.remove(index, 4);
|
finalPathStr.remove(index, 4);
|
||||||
|
|
||||||
Utils::Fs::forceRemove(finalPath);
|
const Path finalPath {finalPathStr};
|
||||||
return QFile::rename(newPath, finalPath);
|
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);
|
SettingsPtr settings = Profile::instance()->applicationSettings(name);
|
||||||
|
|
||||||
|
@ -221,10 +224,10 @@ QString TransactionalSettings::deserialize(const QString &name, QVariantHash &da
|
||||||
data[key] = value;
|
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);
|
SettingsPtr settings = Profile::instance()->applicationSettings(name);
|
||||||
for (auto i = data.begin(); i != data.end(); ++i)
|
for (auto i = data.begin(); i != data.end(); ++i)
|
||||||
|
@ -235,7 +238,7 @@ QString TransactionalSettings::serialize(const QString &name, const QVariantHash
|
||||||
switch (settings->status())
|
switch (settings->status())
|
||||||
{
|
{
|
||||||
case QSettings::NoError:
|
case QSettings::NoError:
|
||||||
return settings->fileName();
|
return Path(settings->fileName());
|
||||||
case QSettings::AccessError:
|
case QSettings::AccessError:
|
||||||
Logger::instance()->addMessage(QObject::tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
|
Logger::instance()->addMessage(QObject::tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVariantHash>
|
#include <QVariantHash>
|
||||||
|
|
||||||
|
#include "path.h"
|
||||||
#include "utils/string.h"
|
#include "utils/string.h"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -68,6 +69,11 @@ public:
|
||||||
const typename T::Int value = loadValue(key, static_cast<typename T::Int>(defaultValue));
|
const typename T::Int value = loadValue(key, static_cast<typename T::Int>(defaultValue));
|
||||||
return T {value};
|
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>)
|
else if constexpr (std::is_same_v<T, QVariant>)
|
||||||
{
|
{
|
||||||
// fast path for loading QVariant
|
// fast path for loading QVariant
|
||||||
|
@ -88,8 +94,10 @@ public:
|
||||||
storeValueImpl(key, Utils::String::fromEnum(value));
|
storeValueImpl(key, Utils::String::fromEnum(value));
|
||||||
else if constexpr (IsQFlags<T>::value)
|
else if constexpr (IsQFlags<T>::value)
|
||||||
storeValueImpl(key, static_cast<typename T::Int>(value));
|
storeValueImpl(key, static_cast<typename T::Int>(value));
|
||||||
|
else if constexpr (std::is_same_v<T, Path>)
|
||||||
|
storeValueImpl(key, value.toString());
|
||||||
else
|
else
|
||||||
storeValueImpl(key, value);
|
storeValueImpl(key, QVariant::fromValue(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeValue(const QString &key);
|
void removeValue(const QString &key);
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include "settingvalue.h"
|
#include "settingvalue.h"
|
||||||
#include "utils/fs.h"
|
#include "utils/fs.h"
|
||||||
|
|
||||||
FileGuard::FileGuard(const QString &path)
|
FileGuard::FileGuard(const Path &path)
|
||||||
: m_path {path}
|
: m_path {path}
|
||||||
, m_remove {true}
|
, m_remove {true}
|
||||||
{
|
{
|
||||||
|
@ -45,17 +45,17 @@ void FileGuard::setAutoRemove(const bool remove) noexcept
|
||||||
FileGuard::~FileGuard()
|
FileGuard::~FileGuard()
|
||||||
{
|
{
|
||||||
if (m_remove && !m_path.isEmpty())
|
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)
|
TorrentFileGuard::TorrentFileGuard(const Path &path, const TorrentFileGuard::AutoDeleteMode mode)
|
||||||
: FileGuard {mode != Never ? path : QString()}
|
: FileGuard {mode != Never ? path : Path()}
|
||||||
, m_mode {mode}
|
, m_mode {mode}
|
||||||
, m_wasAdded {false}
|
, m_wasAdded {false}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentFileGuard::TorrentFileGuard(const QString &path)
|
TorrentFileGuard::TorrentFileGuard(const Path &path)
|
||||||
: TorrentFileGuard {path, autoDeleteMode()}
|
: TorrentFileGuard {path, autoDeleteMode()}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,20 +31,22 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
template <typename T> class SettingValue;
|
template <typename T> class SettingValue;
|
||||||
|
|
||||||
/// Utility class to defer file deletion
|
/// Utility class to defer file deletion
|
||||||
class FileGuard
|
class FileGuard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit FileGuard(const QString &path = {});
|
explicit FileGuard(const Path &path = {});
|
||||||
~FileGuard();
|
~FileGuard();
|
||||||
|
|
||||||
/// Cancels or re-enables deferred file deletion
|
/// Cancels or re-enables deferred file deletion
|
||||||
void setAutoRemove(bool remove) noexcept;
|
void setAutoRemove(bool remove) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_path;
|
Path m_path;
|
||||||
bool m_remove;
|
bool m_remove;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,7 +57,7 @@ class TorrentFileGuard : private FileGuard
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TorrentFileGuard(const QString &path = {});
|
explicit TorrentFileGuard(const Path &path = {});
|
||||||
~TorrentFileGuard();
|
~TorrentFileGuard();
|
||||||
|
|
||||||
/// marks the torrent file as loaded (added) into the BitTorrent::Session
|
/// marks the torrent file as loaded (added) into the BitTorrent::Session
|
||||||
|
@ -74,7 +76,7 @@ public:
|
||||||
static void setAutoDeleteMode(AutoDeleteMode mode);
|
static void setAutoDeleteMode(AutoDeleteMode mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TorrentFileGuard(const QString &path, AutoDeleteMode mode);
|
TorrentFileGuard(const Path &path, AutoDeleteMode mode);
|
||||||
static SettingValue<AutoDeleteMode> &autoDeleteModeSetting();
|
static SettingValue<AutoDeleteMode> &autoDeleteModeSetting();
|
||||||
|
|
||||||
Q_ENUM(AutoDeleteMode)
|
Q_ENUM(AutoDeleteMode)
|
||||||
|
|
|
@ -137,9 +137,9 @@ namespace
|
||||||
BitTorrent::AddTorrentParams params;
|
BitTorrent::AddTorrentParams params;
|
||||||
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
||||||
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
|
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.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.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
||||||
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
||||||
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
||||||
|
@ -158,8 +158,8 @@ namespace
|
||||||
QJsonObject jsonObj {
|
QJsonObject jsonObj {
|
||||||
{PARAM_CATEGORY, params.category},
|
{PARAM_CATEGORY, params.category},
|
||||||
{PARAM_TAGS, serializeTagSet(params.tags)},
|
{PARAM_TAGS, serializeTagSet(params.tags)},
|
||||||
{PARAM_SAVEPATH, params.savePath},
|
{PARAM_SAVEPATH, params.savePath.data()},
|
||||||
{PARAM_DOWNLOADPATH, params.downloadPath},
|
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
||||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||||
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
||||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||||
|
@ -208,8 +208,8 @@ public:
|
||||||
Worker();
|
Worker();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setWatchedFolder(const QString &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
void setWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||||
void removeWatchedFolder(const QString &path);
|
void removeWatchedFolder(const Path &path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void magnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
|
void magnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
|
||||||
|
@ -217,21 +217,21 @@ signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onTimeout();
|
void onTimeout();
|
||||||
void scheduleWatchedFolderProcessing(const QString &path);
|
void scheduleWatchedFolderProcessing(const Path &path);
|
||||||
void processWatchedFolder(const QString &path);
|
void processWatchedFolder(const Path &path);
|
||||||
void processFolder(const QString &path, const QString &watchedFolderPath, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
void processFolder(const Path &path, const Path &watchedFolderPath, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||||
void processFailedTorrents();
|
void processFailedTorrents();
|
||||||
void addWatchedFolder(const QString &watchedFolderID, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
void addWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||||
void updateWatchedFolder(const QString &watchedFolderID, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
void updateWatchedFolder(const Path &path, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||||
|
|
||||||
QFileSystemWatcher *m_watcher = nullptr;
|
QFileSystemWatcher *m_watcher = nullptr;
|
||||||
QTimer *m_watchTimer = nullptr;
|
QTimer *m_watchTimer = nullptr;
|
||||||
QHash<QString, TorrentFilesWatcher::WatchedFolderOptions> m_watchedFolders;
|
QHash<Path, TorrentFilesWatcher::WatchedFolderOptions> m_watchedFolders;
|
||||||
QSet<QString> m_watchedByTimeoutFolders;
|
QSet<Path> m_watchedByTimeoutFolders;
|
||||||
|
|
||||||
// Failed torrents
|
// Failed torrents
|
||||||
QTimer *m_retryTorrentTimer = nullptr;
|
QTimer *m_retryTorrentTimer = nullptr;
|
||||||
QHash<QString, QHash<QString, int>> m_failedTorrents;
|
QHash<Path, QHash<Path, int>> m_failedTorrents;
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentFilesWatcher *TorrentFilesWatcher::m_instance = nullptr;
|
TorrentFilesWatcher *TorrentFilesWatcher::m_instance = nullptr;
|
||||||
|
@ -274,20 +274,9 @@ TorrentFilesWatcher::~TorrentFilesWatcher()
|
||||||
delete m_asyncWorker;
|
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()
|
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())
|
if (!confFile.exists())
|
||||||
{
|
{
|
||||||
loadLegacy();
|
loadLegacy();
|
||||||
|
@ -320,7 +309,7 @@ void TorrentFilesWatcher::load()
|
||||||
const QJsonObject jsonObj = jsonDoc.object();
|
const QJsonObject jsonObj = jsonDoc.object();
|
||||||
for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it)
|
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());
|
const WatchedFolderOptions options = parseWatchedFolderOptions(it.value().toObject());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -337,13 +326,13 @@ void TorrentFilesWatcher::loadLegacy()
|
||||||
{
|
{
|
||||||
const auto dirs = SettingsStorage::instance()->loadValue<QVariantHash>("Preferences/Downloads/ScanDirsV2");
|
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;
|
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.savePath = watchedFolder;
|
||||||
params.useAutoTMM = false;
|
params.useAutoTMM = false;
|
||||||
|
@ -351,7 +340,7 @@ void TorrentFilesWatcher::loadLegacy()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString customSavePath = i.value().toString();
|
const Path customSavePath {it.value().toString()};
|
||||||
params.savePath = customSavePath;
|
params.savePath = customSavePath;
|
||||||
params.useAutoTMM = false;
|
params.useAutoTMM = false;
|
||||||
}
|
}
|
||||||
|
@ -375,56 +364,60 @@ void TorrentFilesWatcher::store() const
|
||||||
QJsonObject jsonObj;
|
QJsonObject jsonObj;
|
||||||
for (auto it = m_watchedFolders.cbegin(); it != m_watchedFolders.cend(); ++it)
|
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();
|
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 QByteArray data = QJsonDocument(jsonObj).toJson();
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't store Watched Folders configuration to %1. Error: %2")
|
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;
|
return m_watchedFolders;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentFilesWatcher::setWatchedFolder(const QString &path, const WatchedFolderOptions &options)
|
void TorrentFilesWatcher::setWatchedFolder(const Path &path, const WatchedFolderOptions &options)
|
||||||
{
|
{
|
||||||
doSetWatchedFolder(path, options);
|
doSetWatchedFolder(path, options);
|
||||||
store();
|
store();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentFilesWatcher::doSetWatchedFolder(const QString &path, const WatchedFolderOptions &options)
|
void TorrentFilesWatcher::doSetWatchedFolder(const Path &path, const WatchedFolderOptions &options)
|
||||||
{
|
{
|
||||||
const QString cleanPath = makeCleanPath(path);
|
if (path.isEmpty())
|
||||||
m_watchedFolders[cleanPath] = options;
|
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]()
|
QMetaObject::invokeMethod(m_asyncWorker, [this, path, options]()
|
||||||
{
|
{
|
||||||
m_asyncWorker->setWatchedFolder(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(path))
|
||||||
if (m_watchedFolders.remove(cleanPath))
|
|
||||||
{
|
{
|
||||||
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();
|
store();
|
||||||
}
|
}
|
||||||
|
@ -447,7 +440,10 @@ TorrentFilesWatcher::Worker::Worker()
|
||||||
, m_watchTimer {new QTimer(this)}
|
, m_watchTimer {new QTimer(this)}
|
||||||
, m_retryTorrentTimer {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_watchTimer, &QTimer::timeout, this, &Worker::onTimeout);
|
||||||
|
|
||||||
connect(m_retryTorrentTimer, &QTimer::timeout, this, &Worker::processFailedTorrents);
|
connect(m_retryTorrentTimer, &QTimer::timeout, this, &Worker::processFailedTorrents);
|
||||||
|
@ -455,11 +451,11 @@ TorrentFilesWatcher::Worker::Worker()
|
||||||
|
|
||||||
void TorrentFilesWatcher::Worker::onTimeout()
|
void TorrentFilesWatcher::Worker::onTimeout()
|
||||||
{
|
{
|
||||||
for (const QString &path : asConst(m_watchedByTimeoutFolders))
|
for (const Path &path : asConst(m_watchedByTimeoutFolders))
|
||||||
processWatchedFolder(path);
|
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))
|
if (m_watchedFolders.contains(path))
|
||||||
updateWatchedFolder(path, options);
|
updateWatchedFolder(path, options);
|
||||||
|
@ -467,11 +463,11 @@ void TorrentFilesWatcher::Worker::setWatchedFolder(const QString &path, const To
|
||||||
addWatchedFolder(path, options);
|
addWatchedFolder(path, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentFilesWatcher::Worker::removeWatchedFolder(const QString &path)
|
void TorrentFilesWatcher::Worker::removeWatchedFolder(const Path &path)
|
||||||
{
|
{
|
||||||
m_watchedFolders.remove(path);
|
m_watchedFolders.remove(path);
|
||||||
|
|
||||||
m_watcher->removePath(path);
|
m_watcher->removePath(path.data());
|
||||||
m_watchedByTimeoutFolders.remove(path);
|
m_watchedByTimeoutFolders.remove(path);
|
||||||
if (m_watchedByTimeoutFolders.isEmpty())
|
if (m_watchedByTimeoutFolders.isEmpty())
|
||||||
m_watchTimer->stop();
|
m_watchTimer->stop();
|
||||||
|
@ -481,7 +477,7 @@ void TorrentFilesWatcher::Worker::removeWatchedFolder(const QString &path)
|
||||||
m_retryTorrentTimer->stop();
|
m_retryTorrentTimer->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const QString &path)
|
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const Path &path)
|
||||||
{
|
{
|
||||||
QTimer::singleShot(2000, this, [this, 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);
|
const TorrentFilesWatcher::WatchedFolderOptions options = m_watchedFolders.value(path);
|
||||||
processFolder(path, path, options);
|
processFolder(path, path, options);
|
||||||
|
@ -498,34 +494,32 @@ void TorrentFilesWatcher::Worker::processWatchedFolder(const QString &path)
|
||||||
m_retryTorrentTimer->start(WATCH_INTERVAL);
|
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 TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||||
{
|
{
|
||||||
const QDir watchedDir {watchedFolderPath};
|
QDirIterator dirIter {path.data(), {"*.torrent", "*.magnet"}, QDir::Files};
|
||||||
|
|
||||||
QDirIterator dirIter {path, {"*.torrent", "*.magnet"}, QDir::Files};
|
|
||||||
while (dirIter.hasNext())
|
while (dirIter.hasNext())
|
||||||
{
|
{
|
||||||
const QString filePath = dirIter.next();
|
const Path filePath {dirIter.next()};
|
||||||
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
||||||
if (path != watchedFolderPath)
|
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());
|
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
|
||||||
if (useAutoTMM)
|
if (useAutoTMM)
|
||||||
{
|
{
|
||||||
addTorrentParams.category = addTorrentParams.category.isEmpty()
|
addTorrentParams.category = addTorrentParams.category.isEmpty()
|
||||||
? subdirPath : (addTorrentParams.category + QLatin1Char('/') + subdirPath);
|
? subdirPath.data() : (addTorrentParams.category + QLatin1Char('/') + subdirPath.data());
|
||||||
}
|
}
|
||||||
else
|
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))
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
{
|
{
|
||||||
QTextStream str {&file};
|
QTextStream str {&file};
|
||||||
|
@ -533,7 +527,7 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
||||||
emit magnetFound(BitTorrent::MagnetUri(str.readLine()), addTorrentParams);
|
emit magnetFound(BitTorrent::MagnetUri(str.readLine()), addTorrentParams);
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
Utils::Fs::forceRemove(filePath);
|
Utils::Fs::removeFile(filePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -546,7 +540,7 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
emit torrentFound(result.value(), addTorrentParams);
|
emit torrentFound(result.value(), addTorrentParams);
|
||||||
Utils::Fs::forceRemove(filePath);
|
Utils::Fs::removeFile(filePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -560,10 +554,10 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
||||||
|
|
||||||
if (options.recursive)
|
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())
|
while (dirIter.hasNext())
|
||||||
{
|
{
|
||||||
const QString folderPath = dirIter.next();
|
const Path folderPath {dirIter.next()};
|
||||||
// Skip processing of subdirectory that is explicitly set as watched folder
|
// Skip processing of subdirectory that is explicitly set as watched folder
|
||||||
if (!m_watchedFolders.contains(folderPath))
|
if (!m_watchedFolders.contains(folderPath))
|
||||||
processFolder(folderPath, watchedFolderPath, options);
|
processFolder(folderPath, watchedFolderPath, options);
|
||||||
|
@ -574,45 +568,43 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
|
||||||
void TorrentFilesWatcher::Worker::processFailedTorrents()
|
void TorrentFilesWatcher::Worker::processFailedTorrents()
|
||||||
{
|
{
|
||||||
// Check which torrents are still partial
|
// 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);
|
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;
|
return true;
|
||||||
|
|
||||||
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(torrentPath);
|
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(torrentPath);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
||||||
const QString exactDirPath = QFileInfo(torrentPath).canonicalPath();
|
if (torrentPath != watchedFolderPath)
|
||||||
if (exactDirPath != dir.path())
|
|
||||||
{
|
{
|
||||||
const QString subdirPath = dir.relativeFilePath(exactDirPath);
|
const Path subdirPath = watchedFolderPath.relativePathOf(torrentPath);
|
||||||
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
|
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
|
||||||
if (useAutoTMM)
|
if (useAutoTMM)
|
||||||
{
|
{
|
||||||
addTorrentParams.category = addTorrentParams.category.isEmpty()
|
addTorrentParams.category = addTorrentParams.category.isEmpty()
|
||||||
? subdirPath : (addTorrentParams.category + QLatin1Char('/') + subdirPath);
|
? subdirPath.data() : (addTorrentParams.category + QLatin1Char('/') + subdirPath.data());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addTorrentParams.savePath = QDir(addTorrentParams.savePath).filePath(subdirPath);
|
addTorrentParams.savePath = addTorrentParams.savePath / subdirPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit torrentFound(result.value(), addTorrentParams);
|
emit torrentFound(result.value(), addTorrentParams);
|
||||||
Utils::Fs::forceRemove(torrentPath);
|
Utils::Fs::removeFile(torrentPath);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value >= MAX_FAILED_RETRIES)
|
if (value >= MAX_FAILED_RETRIES)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Rejecting failed torrent file: %1").arg(torrentPath));
|
LogMsg(tr("Rejecting failed torrent file: %1").arg(torrentPath.toString()));
|
||||||
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
|
Utils::Fs::renameFile(torrentPath, (torrentPath + ".qbt_rejected"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,14 +625,10 @@ void TorrentFilesWatcher::Worker::processFailedTorrents()
|
||||||
m_retryTorrentTimer->start(WATCH_INTERVAL);
|
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)
|
if (Utils::Fs::isNetworkFileSystem(path) || options.recursive)
|
||||||
#else
|
|
||||||
if (options.recursive)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
m_watchedByTimeoutFolders.insert(path);
|
m_watchedByTimeoutFolders.insert(path);
|
||||||
if (!m_watchTimer->isActive())
|
if (!m_watchTimer->isActive())
|
||||||
|
@ -648,27 +636,23 @@ void TorrentFilesWatcher::Worker::addWatchedFolder(const QString &path, const To
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_watcher->addPath(path);
|
m_watcher->addPath(path.data());
|
||||||
scheduleWatchedFolderProcessing(path);
|
scheduleWatchedFolderProcessing(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_watchedFolders[path] = options;
|
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);
|
const bool recursiveModeChanged = (m_watchedFolders[path].recursive != options.recursive);
|
||||||
#if !defined Q_OS_HAIKU
|
|
||||||
if (recursiveModeChanged && !Utils::Fs::isNetworkFileSystem(path))
|
if (recursiveModeChanged && !Utils::Fs::isNetworkFileSystem(path))
|
||||||
#else
|
|
||||||
if (recursiveModeChanged)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
if (options.recursive)
|
if (options.recursive)
|
||||||
{
|
{
|
||||||
m_watcher->removePath(path);
|
m_watcher->removePath(path.data());
|
||||||
|
|
||||||
m_watchedByTimeoutFolders.insert(path);
|
m_watchedByTimeoutFolders.insert(path);
|
||||||
if (!m_watchTimer->isActive())
|
if (!m_watchTimer->isActive())
|
||||||
|
@ -680,7 +664,7 @@ void TorrentFilesWatcher::Worker::updateWatchedFolder(const QString &path, const
|
||||||
if (m_watchedByTimeoutFolders.isEmpty())
|
if (m_watchedByTimeoutFolders.isEmpty())
|
||||||
m_watchTimer->stop();
|
m_watchTimer->stop();
|
||||||
|
|
||||||
m_watcher->addPath(path);
|
m_watcher->addPath(path.data());
|
||||||
scheduleWatchedFolderProcessing(path);
|
scheduleWatchedFolderProcessing(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
#include "base/bittorrent/addtorrentparams.h"
|
#include "base/bittorrent/addtorrentparams.h"
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
|
|
||||||
|
@ -61,15 +62,13 @@ public:
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static TorrentFilesWatcher *instance();
|
static TorrentFilesWatcher *instance();
|
||||||
|
|
||||||
static QString makeCleanPath(const QString &path);
|
QHash<Path, WatchedFolderOptions> folders() const;
|
||||||
|
void setWatchedFolder(const Path &path, const WatchedFolderOptions &options);
|
||||||
QHash<QString, WatchedFolderOptions> folders() const;
|
void removeWatchedFolder(const Path &path);
|
||||||
void setWatchedFolder(const QString &path, const WatchedFolderOptions &options);
|
|
||||||
void removeWatchedFolder(const QString &path);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void watchedFolderSet(const QString &path, const WatchedFolderOptions &options);
|
void watchedFolderSet(const Path &path, const WatchedFolderOptions &options);
|
||||||
void watchedFolderRemoved(const QString &path);
|
void watchedFolderRemoved(const Path &path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onMagnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
|
void onMagnetFound(const BitTorrent::MagnetUri &magnetURI, const BitTorrent::AddTorrentParams &addTorrentParams);
|
||||||
|
@ -83,11 +82,11 @@ private:
|
||||||
void loadLegacy();
|
void loadLegacy();
|
||||||
void store() const;
|
void store() const;
|
||||||
|
|
||||||
void doSetWatchedFolder(const QString &path, const WatchedFolderOptions &options);
|
void doSetWatchedFolder(const Path &path, const WatchedFolderOptions &options);
|
||||||
|
|
||||||
static TorrentFilesWatcher *m_instance;
|
static TorrentFilesWatcher *m_instance;
|
||||||
|
|
||||||
QHash<QString, WatchedFolderOptions> m_watchedFolders;
|
QHash<Path, WatchedFolderOptions> m_watchedFolders;
|
||||||
|
|
||||||
QThread *m_ioThread = nullptr;
|
QThread *m_ioThread = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -50,57 +51,17 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMimeDatabase>
|
|
||||||
#include <QStorageInfo>
|
#include <QStorageInfo>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/path.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will first check if there are only system cache files, e.g. `Thumbs.db`,
|
* 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.
|
* 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.
|
* 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;
|
return true;
|
||||||
|
|
||||||
const QStringList deleteFilesList =
|
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.
|
// travel from the deepest folder and remove anything unwanted on the way out.
|
||||||
QStringList dirList(path + '/'); // get all sub directories paths
|
QStringList dirList(path.data() + '/'); // get all sub directories paths
|
||||||
QDirIterator iter(path, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
|
QDirIterator iter {path.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
dirList << iter.next() + '/';
|
dirList << iter.next() + '/';
|
||||||
// sort descending by directory depth
|
// sort descending by directory depth
|
||||||
|
@ -138,7 +99,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
|
||||||
|
|
||||||
for (const QString &p : asConst(dirList))
|
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
|
// A deeper folder may have not been removed in the previous iteration
|
||||||
// so don't remove anything from this folder either.
|
// so don't remove anything from this folder either.
|
||||||
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
|
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||||
|
@ -156,38 +117,22 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const QString &f : tmpFileList)
|
for (const QString &f : tmpFileList)
|
||||||
forceRemove(p + f);
|
removeFile(Path(p + f));
|
||||||
|
|
||||||
// remove directory if empty
|
// remove directory if empty
|
||||||
dir.rmdir(p);
|
dir.rmdir(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QDir(path).exists();
|
return 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes directory and its content recursively.
|
* Removes directory and its content recursively.
|
||||||
*/
|
*/
|
||||||
void Utils::Fs::removeDirRecursive(const QString &path)
|
void Utils::Fs::removeDirRecursively(const Path &path)
|
||||||
{
|
{
|
||||||
if (!path.isEmpty())
|
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.
|
* 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
|
// Check if it is a file
|
||||||
const QFileInfo fi(path);
|
const QFileInfo fi {path.data()};
|
||||||
if (!fi.exists()) return -1;
|
if (!fi.exists()) return -1;
|
||||||
if (fi.isFile()) return fi.size();
|
if (fi.isFile()) return fi.size();
|
||||||
|
|
||||||
// Compute folder size based on its content
|
// Compute folder size based on its content
|
||||||
qint64 size = 0;
|
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())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
iter.next();
|
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.
|
* 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.exists() || !f2.exists()) return false;
|
||||||
if (f1.size() != f2.size()) return false;
|
if (f1.size() != f2.size()) return false;
|
||||||
if (!f1.open(QIODevice::ReadOnly)) 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;
|
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();
|
QString validName = name.trimmed();
|
||||||
validName.replace(regex, pad);
|
validName.replace(regex, pad);
|
||||||
qDebug() << "toValidFileSystemName:" << name << "=>" << validName;
|
|
||||||
|
|
||||||
return 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)
|
QString validPathStr = name;
|
||||||
const QRegularExpression regex
|
validPathStr.replace(regex, pad);
|
||||||
{allowSeparators
|
|
||||||
? QLatin1String("[:?\"*<>|]")
|
return Path(validPathStr);
|
||||||
: 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
static const Path path = Path(QDir::tempPath()) / Path(".qBittorrent");
|
||||||
if (ret.endsWith('/'))
|
mkdir(path);
|
||||||
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);
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Fs::isRegularFile(const QString &path)
|
bool Utils::Fs::isRegularFile(const Path &path)
|
||||||
{
|
{
|
||||||
struct ::stat st;
|
struct ::stat st;
|
||||||
if (::stat(path.toUtf8().constData(), &st) != 0)
|
if (::stat(path.toString().toUtf8().constData(), &st) != 0)
|
||||||
{
|
{
|
||||||
// analyse erno and log the error
|
// analyse erno and log the error
|
||||||
const auto err = errno;
|
const auto err = errno;
|
||||||
qDebug("Could not get file stats for path '%s'. Error: %s"
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (st.st_mode & S_IFMT) == S_IFREG;
|
return (st.st_mode & S_IFMT) == S_IFREG;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined Q_OS_HAIKU
|
bool Utils::Fs::isNetworkFileSystem(const Path &path)
|
||||||
bool Utils::Fs::isNetworkFileSystem(const QString &path)
|
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN)
|
#if defined Q_OS_HAIKU
|
||||||
const std::wstring pathW {path.toStdWString()};
|
return false;
|
||||||
auto volumePath = std::make_unique<wchar_t[]>(path.length() + 1);
|
#elif defined(Q_OS_WIN)
|
||||||
if (!::GetVolumePathNameW(pathW.c_str(), volumePath.get(), (path.length() + 1)))
|
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 false;
|
||||||
return (::GetDriveTypeW(volumePath.get()) == DRIVE_REMOTE);
|
return (::GetDriveTypeW(volumePath.get()) == DRIVE_REMOTE);
|
||||||
#else
|
#else
|
||||||
QString file = path;
|
const QString file = path.toString() + QLatin1String("/.");
|
||||||
if (!file.endsWith('/'))
|
|
||||||
file += '/';
|
|
||||||
file += '.';
|
|
||||||
|
|
||||||
struct statfs buf {};
|
struct statfs buf {};
|
||||||
if (statfs(file.toLocal8Bit().constData(), &buf) != 0)
|
if (statfs(file.toLocal8Bit().constData(), &buf) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -398,41 +289,77 @@ bool Utils::Fs::isNetworkFileSystem(const QString &path)
|
||||||
#endif
|
#endif
|
||||||
#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;
|
return QFile::copy(from.data(), to.data());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Fs::stripRootFolder(QStringList &filePaths)
|
bool Utils::Fs::renameFile(const Path &from, const Path &to)
|
||||||
{
|
{
|
||||||
const QString commonRootFolder = findRootFolder(filePaths);
|
return QFile::rename(from.data(), to.data());
|
||||||
if (commonRootFolder.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (QString &filePath : filePaths)
|
|
||||||
filePath = filePath.mid(commonRootFolder.size() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
QFile file {path.data()};
|
||||||
filePath = rootFolder + QLatin1Char('/') + filePath;
|
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.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -34,58 +35,36 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
|
class QDateTime;
|
||||||
|
|
||||||
namespace Utils::Fs
|
namespace Utils::Fs
|
||||||
{
|
{
|
||||||
/**
|
qint64 computePathSize(const Path &path);
|
||||||
* Converts a path to a string suitable for display.
|
qint64 freeDiskSpaceOnPath(const Path &path);
|
||||||
* This function makes sure the directory separator used is consistent
|
|
||||||
* with the OS being run.
|
|
||||||
*/
|
|
||||||
QString toNativePath(const QString &path);
|
|
||||||
|
|
||||||
/**
|
bool isRegularFile(const Path &path);
|
||||||
* Converts a path to a string suitable for processing.
|
bool isDir(const Path &path);
|
||||||
* This function makes sure the directory separator used is independent
|
bool isReadable(const Path &path);
|
||||||
* from the OS being run so it is the same on all supported platforms.
|
bool isWritable(const Path &path);
|
||||||
* Slash ('/') is used as "uniform" directory separator.
|
bool isNetworkFileSystem(const Path &path);
|
||||||
*/
|
QDateTime lastModified(const Path &path);
|
||||||
QString toUniformPath(const QString &path);
|
bool sameFiles(const Path &path1, const Path &path2);
|
||||||
|
|
||||||
/**
|
QString toValidFileName(const QString &name, const QString &pad = QLatin1String(" "));
|
||||||
* If `path is relative then resolves it against `basePath`, otherwise returns the `path` itself
|
Path toValidPath(const QString &name, const QString &pad = QLatin1String(" "));
|
||||||
*/
|
Path toCanonicalPath(const Path &path);
|
||||||
QString resolvePath(const QString &relativePath, const QString &basePath);
|
|
||||||
|
|
||||||
/**
|
bool copyFile(const Path &from, const Path &to);
|
||||||
* Returns the file extension part of a file name.
|
bool renameFile(const Path &from, const Path &to);
|
||||||
*/
|
bool removeFile(const Path &path);
|
||||||
QString fileExtension(const QString &filename);
|
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);
|
Path homePath();
|
||||||
QString folderName(const QString &filePath);
|
Path tempPath();
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
Utils::IO::FileDeviceOutputIterator::FileDeviceOutputIterator(QFileDevice &device, const int bufferSize)
|
Utils::IO::FileDeviceOutputIterator::FileDeviceOutputIterator(QFileDevice &device, const int bufferSize)
|
||||||
: m_device {&device}
|
: m_device {&device}
|
||||||
, m_buffer {std::make_shared<QByteArray>()}
|
, m_buffer {std::make_shared<QByteArray>()}
|
||||||
|
@ -66,17 +68,17 @@ Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operat
|
||||||
return *this;
|
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())
|
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.flush() || !file.commit())
|
||||||
return nonstd::make_unexpected(file.errorString());
|
return nonstd::make_unexpected(file.errorString());
|
||||||
return {};
|
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))
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
return nonstd::make_unexpected(file.errorString());
|
return nonstd::make_unexpected(file.errorString());
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <libtorrent/fwd.hpp>
|
#include <libtorrent/fwd.hpp>
|
||||||
|
|
||||||
#include "base/3rdparty/expected.hpp"
|
#include "base/3rdparty/expected.hpp"
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QFileDevice;
|
class QFileDevice;
|
||||||
|
@ -80,6 +81,6 @@ namespace Utils::IO
|
||||||
int m_bufferSize;
|
int m_bufferSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
nonstd::expected<void, QString> saveToFile(const QString &path, const QByteArray &data);
|
nonstd::expected<void, QString> saveToFile(const Path &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 lt::entry &data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
#include <QDBusInterface>
|
#include <QDBusInterface>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
@ -292,9 +293,9 @@ qlonglong Utils::Misc::sizeInBytes(qreal size, const Utils::Misc::SizeUnit unit)
|
||||||
return size;
|
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)
|
if (mime.startsWith(QLatin1String("audio"), Qt::CaseInsensitive)
|
||||||
|| mime.startsWith(QLatin1String("video"), Qt::CaseInsensitive))
|
|| mime.startsWith(QLatin1String("video"), Qt::CaseInsensitive))
|
||||||
|
@ -304,50 +305,50 @@ bool Utils::Misc::isPreviewable(const QString &filename)
|
||||||
|
|
||||||
const QSet<QString> multimediaExtensions =
|
const QSet<QString> multimediaExtensions =
|
||||||
{
|
{
|
||||||
"3GP",
|
".3GP",
|
||||||
"AAC",
|
".AAC",
|
||||||
"AC3",
|
".AC3",
|
||||||
"AIF",
|
".AIF",
|
||||||
"AIFC",
|
".AIFC",
|
||||||
"AIFF",
|
".AIFF",
|
||||||
"ASF",
|
".ASF",
|
||||||
"AU",
|
".AU",
|
||||||
"AVI",
|
".AVI",
|
||||||
"FLAC",
|
".FLAC",
|
||||||
"FLV",
|
".FLV",
|
||||||
"M3U",
|
".M3U",
|
||||||
"M4A",
|
".M4A",
|
||||||
"M4P",
|
".M4P",
|
||||||
"M4V",
|
".M4V",
|
||||||
"MID",
|
".MID",
|
||||||
"MKV",
|
".MKV",
|
||||||
"MOV",
|
".MOV",
|
||||||
"MP2",
|
".MP2",
|
||||||
"MP3",
|
".MP3",
|
||||||
"MP4",
|
".MP4",
|
||||||
"MPC",
|
".MPC",
|
||||||
"MPE",
|
".MPE",
|
||||||
"MPEG",
|
".MPEG",
|
||||||
"MPG",
|
".MPG",
|
||||||
"MPP",
|
".MPP",
|
||||||
"OGG",
|
".OGG",
|
||||||
"OGM",
|
".OGM",
|
||||||
"OGV",
|
".OGV",
|
||||||
"QT",
|
".QT",
|
||||||
"RA",
|
".RA",
|
||||||
"RAM",
|
".RAM",
|
||||||
"RM",
|
".RM",
|
||||||
"RMV",
|
".RMV",
|
||||||
"RMVB",
|
".RMVB",
|
||||||
"SWA",
|
".SWA",
|
||||||
"SWF",
|
".SWF",
|
||||||
"TS",
|
".TS",
|
||||||
"VOB",
|
".VOB",
|
||||||
"WAV",
|
".WAV",
|
||||||
"WMA",
|
".WMA",
|
||||||
"WMV"
|
".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)
|
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap)
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
enum class ShutdownDialogAction;
|
enum class ShutdownDialogAction;
|
||||||
|
|
||||||
/* Miscellaneous functions that can be useful */
|
/* Miscellaneous functions that can be useful */
|
||||||
|
@ -77,7 +79,7 @@ namespace Utils::Misc
|
||||||
int friendlyUnitPrecision(SizeUnit unit);
|
int friendlyUnitPrecision(SizeUnit unit);
|
||||||
qint64 sizeInBytes(qreal size, 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
|
// Take a number of seconds and return a user-friendly
|
||||||
// time duration like "1d 2h 10m".
|
// time duration like "1d 2h 10m".
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/version.h"
|
#include "base/version.h"
|
||||||
|
@ -70,7 +71,7 @@ AboutDialog::AboutDialog(QWidget *parent)
|
||||||
, tr("Bug Tracker:"));
|
, tr("Bug Tracker:"));
|
||||||
m_ui->labelAbout->setText(aboutText);
|
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
|
// Thanks
|
||||||
QFile thanksfile(":/thanks.html");
|
QFile thanksfile(":/thanks.html");
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace
|
||||||
class FileStorageAdaptor final : public BitTorrent::AbstractFileStorage
|
class FileStorageAdaptor final : public BitTorrent::AbstractFileStorage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileStorageAdaptor(const BitTorrent::TorrentInfo &torrentInfo, QStringList &filePaths)
|
FileStorageAdaptor(const BitTorrent::TorrentInfo &torrentInfo, PathList &filePaths)
|
||||||
: m_torrentInfo {torrentInfo}
|
: m_torrentInfo {torrentInfo}
|
||||||
, m_filePaths {filePaths}
|
, m_filePaths {filePaths}
|
||||||
{
|
{
|
||||||
|
@ -99,16 +99,16 @@ namespace
|
||||||
return m_torrentInfo.fileSize(index);
|
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()));
|
Q_ASSERT((index >= 0) && (index < filesCount()));
|
||||||
return (m_filePaths.isEmpty() ? m_torrentInfo.filePath(index) : m_filePaths.at(index));
|
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()));
|
Q_ASSERT((index >= 0) && (index < filesCount()));
|
||||||
const QString currentFilePath = filePath(index);
|
const Path currentFilePath = filePath(index);
|
||||||
if (currentFilePath == newFilePath)
|
if (currentFilePath == newFilePath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -120,22 +120,21 @@ namespace
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const BitTorrent::TorrentInfo &m_torrentInfo;
|
const BitTorrent::TorrentInfo &m_torrentInfo;
|
||||||
QStringList &m_filePaths;
|
PathList &m_filePaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
// savePath is a folder, not an absolute file path
|
// 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)
|
for (int i = 0; i < fsPathEdit->count(); ++i)
|
||||||
{
|
{
|
||||||
if (QDir(fsPathEdit->item(i)) == saveDir)
|
if (fsPathEdit->item(i) == savePath)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPath(FileSystemPathComboEdit *fsPathEdit, const QString &newPath)
|
void setPath(FileSystemPathComboEdit *fsPathEdit, const Path &newPath)
|
||||||
{
|
{
|
||||||
int existingIndex = indexOfPath(fsPathEdit, newPath);
|
int existingIndex = indexOfPath(fsPathEdit, newPath);
|
||||||
if (existingIndex < 0)
|
if (existingIndex < 0)
|
||||||
|
@ -148,16 +147,18 @@ namespace
|
||||||
fsPathEdit->setCurrentIndex(existingIndex);
|
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
|
// Add last used save path to the front of history
|
||||||
|
|
||||||
auto pathList = settings()->loadValue<QStringList>(settingsKey);
|
auto pathList = settings()->loadValue<QStringList>(settingsKey);
|
||||||
const int selectedSavePathIndex = pathList.indexOf(path);
|
|
||||||
|
const int selectedSavePathIndex = pathList.indexOf(path.toString());
|
||||||
if (selectedSavePathIndex > -1)
|
if (selectedSavePathIndex > -1)
|
||||||
pathList.move(selectedSavePathIndex, 0);
|
pathList.move(selectedSavePathIndex, 0);
|
||||||
else
|
else
|
||||||
pathList.prepend(path);
|
pathList.prepend(path.toString());
|
||||||
|
|
||||||
settings()->storeValue(settingsKey, QStringList(pathList.mid(0, maxLength)));
|
settings()->storeValue(settingsKey, QStringList(pathList.mid(0, maxLength)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +326,7 @@ void AddNewTorrentDialog::show(const QString &source, const BitTorrent::AddTorre
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BitTorrent::MagnetUri magnetUri(source);
|
const BitTorrent::MagnetUri magnetUri {source};
|
||||||
const bool isLoaded = magnetUri.isValid()
|
const bool isLoaded = magnetUri.isValid()
|
||||||
? dlg->loadMagnet(magnetUri)
|
? dlg->loadMagnet(magnetUri)
|
||||||
: dlg->loadTorrentFile(source);
|
: dlg->loadTorrentFile(source);
|
||||||
|
@ -341,18 +342,18 @@ void AddNewTorrentDialog::show(const QString &source, QWidget *parent)
|
||||||
show(source, BitTorrent::AddTorrentParams(), 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)
|
const Path decodedPath {source.startsWith("file://", Qt::CaseInsensitive)
|
||||||
? QUrl::fromEncoded(torrentPath.toLocal8Bit()).toLocalFile()
|
? QUrl::fromEncoded(source.toLocal8Bit()).toLocalFile()
|
||||||
: torrentPath;
|
: source};
|
||||||
|
|
||||||
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(decodedPath);
|
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(decodedPath);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
RaisedMessageBox::critical(this, tr("Invalid torrent")
|
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.")
|
, 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +490,7 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
|
||||||
m_ui->labelSizeData->setText(sizeString);
|
m_ui->labelSizeData->setText(sizeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
|
void AddNewTorrentDialog::onSavePathChanged(const Path &newPath)
|
||||||
{
|
{
|
||||||
Q_UNUSED(newPath);
|
Q_UNUSED(newPath);
|
||||||
// Remember index
|
// Remember index
|
||||||
|
@ -497,7 +498,7 @@ void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
|
||||||
updateDiskSpaceLabel();
|
updateDiskSpaceLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::onDownloadPathChanged(const QString &newPath)
|
void AddNewTorrentDialog::onDownloadPathChanged(const Path &newPath)
|
||||||
{
|
{
|
||||||
Q_UNUSED(newPath);
|
Q_UNUSED(newPath);
|
||||||
// Remember index
|
// Remember index
|
||||||
|
@ -521,11 +522,11 @@ void AddNewTorrentDialog::categoryChanged(int index)
|
||||||
const auto *btSession = BitTorrent::Session::instance();
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
const QString categoryName = m_ui->categoryComboBox->currentText();
|
const QString categoryName = m_ui->categoryComboBox->currentText();
|
||||||
|
|
||||||
const QString savePath = btSession->categorySavePath(categoryName);
|
const Path savePath = btSession->categorySavePath(categoryName);
|
||||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
m_ui->savePath->setSelectedPath(savePath);
|
||||||
|
|
||||||
const QString downloadPath = btSession->categoryDownloadPath(categoryName);
|
const Path downloadPath = btSession->categoryDownloadPath(categoryName);
|
||||||
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
|
m_ui->downloadPath->setSelectedPath(downloadPath);
|
||||||
|
|
||||||
m_ui->groupBoxDownloadPath->setChecked(!m_ui->downloadPath->selectedPath().isEmpty());
|
m_ui->groupBoxDownloadPath->setChecked(!m_ui->downloadPath->selectedPath().isEmpty());
|
||||||
|
|
||||||
|
@ -545,7 +546,7 @@ void AddNewTorrentDialog::contentLayoutChanged(const int index)
|
||||||
const auto contentLayout = ((index == 0)
|
const auto contentLayout = ((index == 0)
|
||||||
? BitTorrent::detectContentLayout(m_torrentInfo.filePaths())
|
? BitTorrent::detectContentLayout(m_torrentInfo.filePaths())
|
||||||
: static_cast<BitTorrent::TorrentContentLayout>(index));
|
: 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()->setupModelData(FileStorageAdaptor(m_torrentInfo, m_torrentParams.filePaths));
|
||||||
m_contentModel->model()->updateFilesPriorities(filePriorities);
|
m_contentModel->model()->updateFilesPriorities(filePriorities);
|
||||||
|
|
||||||
|
@ -574,7 +575,7 @@ void AddNewTorrentDialog::saveTorrentFile()
|
||||||
if (!path.endsWith(torrentFileExtension, Qt::CaseInsensitive))
|
if (!path.endsWith(torrentFileExtension, Qt::CaseInsensitive))
|
||||||
path += torrentFileExtension;
|
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)
|
if (!result)
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("I/O Error")
|
QMessageBox::critical(this, tr("I/O Error")
|
||||||
|
@ -597,7 +598,7 @@ void AddNewTorrentDialog::populateSavePaths()
|
||||||
if (savePathHistory.size() > 0)
|
if (savePathHistory.size() > 0)
|
||||||
{
|
{
|
||||||
for (const QString &path : savePathHistory)
|
for (const QString &path : savePathHistory)
|
||||||
m_ui->savePath->addItem(path);
|
m_ui->savePath->addItem(Path(path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -628,7 +629,7 @@ void AddNewTorrentDialog::populateSavePaths()
|
||||||
if (downloadPathHistory.size() > 0)
|
if (downloadPathHistory.size() > 0)
|
||||||
{
|
{
|
||||||
for (const QString &path : downloadPathHistory)
|
for (const QString &path : downloadPathHistory)
|
||||||
m_ui->downloadPath->addItem(path);
|
m_ui->downloadPath->addItem(Path(path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -806,14 +807,14 @@ void AddNewTorrentDialog::accept()
|
||||||
m_torrentParams.useAutoTMM = useAutoTMM;
|
m_torrentParams.useAutoTMM = useAutoTMM;
|
||||||
if (!useAutoTMM)
|
if (!useAutoTMM)
|
||||||
{
|
{
|
||||||
const QString savePath = m_ui->savePath->selectedPath();
|
const Path savePath = m_ui->savePath->selectedPath();
|
||||||
m_torrentParams.savePath = savePath;
|
m_torrentParams.savePath = savePath;
|
||||||
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength());
|
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength());
|
||||||
|
|
||||||
m_torrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
|
m_torrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
|
||||||
if (m_torrentParams.useDownloadPath)
|
if (m_torrentParams.useDownloadPath)
|
||||||
{
|
{
|
||||||
const QString downloadPath = m_ui->downloadPath->selectedPath();
|
const Path downloadPath = m_ui->downloadPath->selectedPath();
|
||||||
m_torrentParams.downloadPath = downloadPath;
|
m_torrentParams.downloadPath = downloadPath;
|
||||||
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength());
|
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength());
|
||||||
}
|
}
|
||||||
|
@ -907,7 +908,7 @@ void AddNewTorrentDialog::setupTreeview()
|
||||||
: static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
|
: static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
|
||||||
if (m_torrentParams.filePaths.isEmpty())
|
if (m_torrentParams.filePaths.isEmpty())
|
||||||
m_torrentParams.filePaths = m_torrentInfo.filePaths();
|
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
|
// List files in torrent
|
||||||
m_contentModel->model()->setupModelData(FileStorageAdaptor(m_torrentInfo, m_torrentParams.filePaths));
|
m_contentModel->model()->setupModelData(FileStorageAdaptor(m_torrentInfo, m_torrentParams.filePaths));
|
||||||
if (const QByteArray state = m_storeTreeHeaderState; !state.isEmpty())
|
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->blockSignals(true);
|
||||||
m_ui->savePath->clear();
|
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->savePath->addItem(savePath);
|
||||||
|
|
||||||
m_ui->downloadPath->blockSignals(true);
|
m_ui->downloadPath->blockSignals(true);
|
||||||
m_ui->downloadPath->clear();
|
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->downloadPath->addItem(downloadPath);
|
||||||
|
|
||||||
m_ui->groupBoxDownloadPath->blockSignals(true);
|
m_ui->groupBoxDownloadPath->blockSignals(true);
|
||||||
|
|
|
@ -81,8 +81,8 @@ private slots:
|
||||||
void displayContentTreeMenu();
|
void displayContentTreeMenu();
|
||||||
void displayColumnHeaderMenu();
|
void displayColumnHeaderMenu();
|
||||||
void updateDiskSpaceLabel();
|
void updateDiskSpaceLabel();
|
||||||
void onSavePathChanged(const QString &newPath);
|
void onSavePathChanged(const Path &newPath);
|
||||||
void onDownloadPathChanged(const QString &newPath);
|
void onDownloadPathChanged(const Path &newPath);
|
||||||
void onUseDownloadPathChanged(bool checked);
|
void onUseDownloadPathChanged(bool checked);
|
||||||
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
|
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
|
||||||
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
|
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
|
||||||
|
@ -97,7 +97,7 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
|
explicit AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
|
||||||
bool loadTorrentFile(const QString &torrentPath);
|
bool loadTorrentFile(const QString &source);
|
||||||
bool loadTorrentImpl();
|
bool loadTorrentImpl();
|
||||||
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
|
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
|
||||||
void populateSavePaths();
|
void populateSavePaths();
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include "autoexpandabledialog.h"
|
#include "autoexpandabledialog.h"
|
||||||
|
|
||||||
#include "base/utils/fs.h"
|
#include "base/path.h"
|
||||||
#include "ui_autoexpandabledialog.h"
|
#include "ui_autoexpandabledialog.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
@ -58,9 +58,9 @@ QString AutoExpandableDialog::getText(QWidget *parent, const QString &title, con
|
||||||
d.m_ui->textEdit->selectAll();
|
d.m_ui->textEdit->selectAll();
|
||||||
if (excludeExtension)
|
if (excludeExtension)
|
||||||
{
|
{
|
||||||
const QString extension = Utils::Fs::fileExtension(text);
|
const QString extension = Path(text).extension();
|
||||||
if (!extension.isEmpty())
|
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();
|
bool res = d.exec();
|
||||||
|
|
|
@ -82,7 +82,7 @@ class FileSystemPathEdit::FileSystemPathEditPrivate
|
||||||
QToolButton *m_browseBtn;
|
QToolButton *m_browseBtn;
|
||||||
QString m_fileNameFilter;
|
QString m_fileNameFilter;
|
||||||
Mode m_mode;
|
Mode m_mode;
|
||||||
QString m_lastSignaledPath;
|
Path m_lastSignaledPath;
|
||||||
QString m_dialogCaption;
|
QString m_dialogCaption;
|
||||||
Private::FileSystemPathValidator *m_validator;
|
Private::FileSystemPathValidator *m_validator;
|
||||||
};
|
};
|
||||||
|
@ -112,31 +112,32 @@ void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
|
||||||
{
|
{
|
||||||
Q_Q(FileSystemPathEdit);
|
Q_Q(FileSystemPathEdit);
|
||||||
|
|
||||||
const QFileInfo fileInfo {q->selectedPath()};
|
const Path currentDirectory = (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave)
|
||||||
const QString directory = (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave)
|
? q->selectedPath()
|
||||||
? fileInfo.absoluteFilePath()
|
: q->selectedPath().parentPath();
|
||||||
: fileInfo.absolutePath();
|
const Path initialDirectory = currentDirectory.isAbsolute() ? currentDirectory : (Utils::Fs::homePath() / currentDirectory);
|
||||||
QString filter = q->fileNameFilter();
|
|
||||||
|
|
||||||
QString selectedPath;
|
QString filter = q->fileNameFilter();
|
||||||
|
QString newPath;
|
||||||
switch (m_mode)
|
switch (m_mode)
|
||||||
{
|
{
|
||||||
case FileSystemPathEdit::Mode::FileOpen:
|
case FileSystemPathEdit::Mode::FileOpen:
|
||||||
selectedPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), directory, filter);
|
newPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), initialDirectory.data(), filter);
|
||||||
break;
|
break;
|
||||||
case FileSystemPathEdit::Mode::FileSave:
|
case FileSystemPathEdit::Mode::FileSave:
|
||||||
selectedPath = QFileDialog::getSaveFileName(q, dialogCaptionOrDefault(), directory, filter, &filter);
|
newPath = QFileDialog::getSaveFileName(q, dialogCaptionOrDefault(), initialDirectory.data(), filter, &filter);
|
||||||
break;
|
break;
|
||||||
case FileSystemPathEdit::Mode::DirectoryOpen:
|
case FileSystemPathEdit::Mode::DirectoryOpen:
|
||||||
case FileSystemPathEdit::Mode::DirectorySave:
|
case FileSystemPathEdit::Mode::DirectorySave:
|
||||||
selectedPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(),
|
newPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(),
|
||||||
directory, QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly);
|
initialDirectory.data(), QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::logic_error("Unknown FileSystemPathEdit mode");
|
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
|
QString FileSystemPathEdit::FileSystemPathEditPrivate::dialogCaptionOrDefault() const
|
||||||
|
@ -202,16 +203,18 @@ FileSystemPathEdit::~FileSystemPathEdit()
|
||||||
delete d_ptr;
|
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);
|
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
|
QString FileSystemPathEdit::fileNameFilter() const
|
||||||
|
@ -251,13 +254,13 @@ void FileSystemPathEdit::setFileNameFilter(const QString &val)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileSystemPathEdit::placeholder() const
|
Path FileSystemPathEdit::placeholder() const
|
||||||
{
|
{
|
||||||
Q_D(const FileSystemPathEdit);
|
Q_D(const FileSystemPathEdit);
|
||||||
return d->m_editor->placeholder();
|
return d->m_editor->placeholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemPathEdit::setPlaceholder(const QString &val)
|
void FileSystemPathEdit::setPlaceholder(const Path &val)
|
||||||
{
|
{
|
||||||
Q_D(FileSystemPathEdit);
|
Q_D(FileSystemPathEdit);
|
||||||
d->m_editor->setPlaceholder(val);
|
d->m_editor->setPlaceholder(val);
|
||||||
|
@ -278,7 +281,8 @@ void FileSystemPathEdit::setBriefBrowseButtonCaption(bool brief)
|
||||||
void FileSystemPathEdit::onPathEdited()
|
void FileSystemPathEdit::onPathEdited()
|
||||||
{
|
{
|
||||||
Q_D(FileSystemPathEdit);
|
Q_D(FileSystemPathEdit);
|
||||||
QString newPath = selectedPath();
|
|
||||||
|
const Path newPath = selectedPath();
|
||||||
if (newPath != d->m_lastSignaledPath)
|
if (newPath != d->m_lastSignaledPath)
|
||||||
{
|
{
|
||||||
emit selectedPathChanged(newPath);
|
emit selectedPathChanged(newPath);
|
||||||
|
@ -360,19 +364,19 @@ int FileSystemPathComboEdit::count() const
|
||||||
return editWidget<WidgetType>()->count();
|
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
|
int FileSystemPathComboEdit::currentIndex() const
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace Private
|
namespace Private
|
||||||
{
|
{
|
||||||
class FileComboEdit;
|
class FileComboEdit;
|
||||||
|
@ -45,7 +47,7 @@ class FileSystemPathEdit : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(Mode mode READ mode WRITE setMode)
|
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 fileNameFilter READ fileNameFilter WRITE setFileNameFilter)
|
||||||
Q_PROPERTY(QString dialogCaption READ dialogCaption WRITE setDialogCaption)
|
Q_PROPERTY(QString dialogCaption READ dialogCaption WRITE setDialogCaption)
|
||||||
|
|
||||||
|
@ -64,14 +66,14 @@ public:
|
||||||
Mode mode() const;
|
Mode mode() const;
|
||||||
void setMode(Mode mode);
|
void setMode(Mode mode);
|
||||||
|
|
||||||
QString selectedPath() const;
|
Path selectedPath() const;
|
||||||
void setSelectedPath(const QString &val);
|
void setSelectedPath(const Path &val);
|
||||||
|
|
||||||
QString fileNameFilter() const;
|
QString fileNameFilter() const;
|
||||||
void setFileNameFilter(const QString &val);
|
void setFileNameFilter(const QString &val);
|
||||||
|
|
||||||
QString placeholder() const;
|
Path placeholder() const;
|
||||||
void setPlaceholder(const QString &val);
|
void setPlaceholder(const Path &val);
|
||||||
|
|
||||||
/// The browse button caption is "..." if true, and "Browse" otherwise
|
/// The browse button caption is "..." if true, and "Browse" otherwise
|
||||||
bool briefBrowseButtonCaption() const;
|
bool briefBrowseButtonCaption() const;
|
||||||
|
@ -83,7 +85,7 @@ public:
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void selectedPathChanged(const QString &path);
|
void selectedPathChanged(const Path &path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit FileSystemPathEdit(Private::FileEditorWithCompletion *editor, QWidget *parent);
|
explicit FileSystemPathEdit(Private::FileEditorWithCompletion *editor, QWidget *parent);
|
||||||
|
@ -136,9 +138,9 @@ public:
|
||||||
void clear() override;
|
void clear() override;
|
||||||
|
|
||||||
int count() const;
|
int count() const;
|
||||||
QString item(int index) const;
|
Path item(int index) const;
|
||||||
void addItem(const QString &text);
|
void addItem(const Path &path);
|
||||||
void insertItem(int index, const QString &text);
|
void insertItem(int index, const Path &path);
|
||||||
|
|
||||||
int currentIndex() const;
|
int currentIndex() const;
|
||||||
void setCurrentIndex(int index);
|
void setCurrentIndex(int index);
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
// -------------------- FileSystemPathValidator ----------------------------------------
|
// -------------------- FileSystemPathValidator ----------------------------------------
|
||||||
Private::FileSystemPathValidator::FileSystemPathValidator(QObject *parent)
|
Private::FileSystemPathValidator::FileSystemPathValidator(QObject *parent)
|
||||||
: QValidator(parent)
|
: QValidator(parent)
|
||||||
|
@ -149,7 +151,7 @@ QValidator::State Private::FileSystemPathValidator::validate(const QList<QString
|
||||||
const QStringView componentPath = pathComponents[i];
|
const QStringView componentPath = pathComponents[i];
|
||||||
if (componentPath.isEmpty()) continue;
|
if (componentPath.isEmpty()) continue;
|
||||||
|
|
||||||
m_lastTestResult = testPath(pathComponents[i], isFinalPath);
|
m_lastTestResult = testPath(Path(pathComponents[i].toString()), isFinalPath);
|
||||||
if (m_lastTestResult != TestResult::OK)
|
if (m_lastTestResult != TestResult::OK)
|
||||||
{
|
{
|
||||||
m_lastTestedPath = componentPath.toString();
|
m_lastTestedPath = componentPath.toString();
|
||||||
|
@ -161,9 +163,9 @@ QValidator::State Private::FileSystemPathValidator::validate(const QList<QString
|
||||||
}
|
}
|
||||||
|
|
||||||
Private::FileSystemPathValidator::TestResult
|
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())
|
if (m_existingOnly && !fi.exists())
|
||||||
return TestResult::DoesNotExist;
|
return TestResult::DoesNotExist;
|
||||||
|
|
||||||
|
@ -240,14 +242,14 @@ void Private::FileLineEdit::setValidator(QValidator *validator)
|
||||||
QLineEdit::setValidator(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()
|
QWidget *Private::FileLineEdit::widget()
|
||||||
|
@ -356,14 +358,14 @@ void Private::FileComboEdit::setValidator(QValidator *validator)
|
||||||
lineEdit()->setValidator(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)
|
void Private::FileComboEdit::setFilenameFilters(const QStringList &filters)
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
#include <QValidator>
|
#include <QValidator>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QAction;
|
class QAction;
|
||||||
class QCompleter;
|
class QCompleter;
|
||||||
class QContextMenuEvent;
|
class QContextMenuEvent;
|
||||||
|
@ -84,7 +86,7 @@ namespace Private
|
||||||
QValidator::State validate(const QList<QStringView> &pathComponents, bool strict,
|
QValidator::State validate(const QList<QStringView> &pathComponents, bool strict,
|
||||||
int firstComponentToTest, int lastComponentToTest) const;
|
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_strictMode;
|
||||||
bool m_existingOnly;
|
bool m_existingOnly;
|
||||||
|
@ -105,8 +107,8 @@ namespace Private
|
||||||
virtual void setFilenameFilters(const QStringList &filters) = 0;
|
virtual void setFilenameFilters(const QStringList &filters) = 0;
|
||||||
virtual void setBrowseAction(QAction *action) = 0;
|
virtual void setBrowseAction(QAction *action) = 0;
|
||||||
virtual void setValidator(QValidator *validator) = 0;
|
virtual void setValidator(QValidator *validator) = 0;
|
||||||
virtual QString placeholder() const = 0;
|
virtual Path placeholder() const = 0;
|
||||||
virtual void setPlaceholder(const QString &val) = 0;
|
virtual void setPlaceholder(const Path &val) = 0;
|
||||||
virtual QWidget *widget() = 0;
|
virtual QWidget *widget() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,8 +125,8 @@ namespace Private
|
||||||
void setFilenameFilters(const QStringList &filters) override;
|
void setFilenameFilters(const QStringList &filters) override;
|
||||||
void setBrowseAction(QAction *action) override;
|
void setBrowseAction(QAction *action) override;
|
||||||
void setValidator(QValidator *validator) override;
|
void setValidator(QValidator *validator) override;
|
||||||
QString placeholder() const override;
|
Path placeholder() const override;
|
||||||
void setPlaceholder(const QString &val) override;
|
void setPlaceholder(const Path &val) override;
|
||||||
QWidget *widget() override;
|
QWidget *widget() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -153,8 +155,8 @@ namespace Private
|
||||||
void setFilenameFilters(const QStringList &filters) override;
|
void setFilenameFilters(const QStringList &filters) override;
|
||||||
void setBrowseAction(QAction *action) override;
|
void setBrowseAction(QAction *action) override;
|
||||||
void setValidator(QValidator *validator) override;
|
void setValidator(QValidator *validator) override;
|
||||||
QString placeholder() const override;
|
Path placeholder() const override;
|
||||||
void setPlaceholder(const QString &val) override;
|
void setPlaceholder(const Path &val) override;
|
||||||
QWidget *widget() override;
|
QWidget *widget() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
#include <objc/objc.h>
|
#include <objc/objc.h>
|
||||||
|
|
||||||
#include <QSet>
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QPixmap;
|
class QPixmap;
|
||||||
class QSize;
|
class QSize;
|
||||||
|
@ -41,7 +41,7 @@ namespace MacUtils
|
||||||
QPixmap pixmapForExtension(const QString &ext, const QSize &size);
|
QPixmap pixmapForExtension(const QString &ext, const QSize &size);
|
||||||
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...));
|
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...));
|
||||||
void displayNotification(const QString &title, const QString &message);
|
void displayNotification(const QString &title, const QString &message);
|
||||||
void openFiles(const QSet<QString> &pathsList);
|
void openFiles(const PathList &pathList);
|
||||||
|
|
||||||
QString badgeLabelText();
|
QString badgeLabelText();
|
||||||
void setBadgeLabelText(const QString &text);
|
void setBadgeLabelText(const QString &text);
|
||||||
|
|
|
@ -32,9 +32,11 @@
|
||||||
#include <objc/message.h>
|
#include <objc/message.h>
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QSet>
|
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
QImage qt_mac_toQImage(CGImageRef image);
|
QImage qt_mac_toQImage(CGImageRef image);
|
||||||
|
|
||||||
|
@ -95,14 +97,14 @@ namespace MacUtils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void openFiles(const QSet<QString> &pathsList)
|
void openFiles(const PathList &pathList)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
@autoreleasepool
|
||||||
{
|
{
|
||||||
NSMutableArray *pathURLs = [NSMutableArray arrayWithCapacity:pathsList.size()];
|
NSMutableArray *pathURLs = [NSMutableArray arrayWithCapacity:pathList.size()];
|
||||||
|
|
||||||
for (const auto &path : pathsList)
|
for (const auto &path : pathList)
|
||||||
[pathURLs addObject:[NSURL fileURLWithPath:path.toNSString()]];
|
[pathURLs addObject:[NSURL fileURLWithPath:path.toString().toNSString()]];
|
||||||
|
|
||||||
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:pathURLs];
|
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:pathURLs];
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
#include "base/bittorrent/sessionstatus.h"
|
#include "base/bittorrent/sessionstatus.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/net/downloadmanager.h"
|
#include "base/net/downloadmanager.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/rss/rss_folder.h"
|
#include "base/rss/rss_folder.h"
|
||||||
#include "base/rss/rss_session.h"
|
#include "base/rss/rss_session.h"
|
||||||
|
@ -1250,10 +1251,10 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
||||||
// Display window to create a torrent
|
// Display window to create a torrent
|
||||||
void MainWindow::on_actionCreateTorrent_triggered()
|
void MainWindow::on_actionCreateTorrent_triggered()
|
||||||
{
|
{
|
||||||
createTorrentTriggered();
|
createTorrentTriggered({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::createTorrentTriggered(const QString &path)
|
void MainWindow::createTorrentTriggered(const Path &path)
|
||||||
{
|
{
|
||||||
if (m_createTorrentDlg)
|
if (m_createTorrentDlg)
|
||||||
{
|
{
|
||||||
|
@ -1261,7 +1262,9 @@ void MainWindow::createTorrentTriggered(const QString &path)
|
||||||
m_createTorrentDlg->activateWindow();
|
m_createTorrentDlg->activateWindow();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
m_createTorrentDlg = new TorrentCreatorDialog(this, path);
|
m_createTorrentDlg = new TorrentCreatorDialog(this, path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::event(QEvent *e)
|
bool MainWindow::event(QEvent *e)
|
||||||
|
@ -1367,7 +1370,7 @@ void MainWindow::dropEvent(QDropEvent *event)
|
||||||
// Create torrent
|
// Create torrent
|
||||||
for (const QString &file : asConst(otherFiles))
|
for (const QString &file : asConst(otherFiles))
|
||||||
{
|
{
|
||||||
createTorrentTriggered(file);
|
createTorrentTriggered(Path(file));
|
||||||
|
|
||||||
// currently only handle the first entry
|
// currently only handle the first entry
|
||||||
// this is a stub that can be expanded later to create many torrents at once
|
// 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
|
// Open File Open Dialog
|
||||||
// Note: it is possible to select more than one file
|
// Note: it is possible to select more than one file
|
||||||
const QStringList pathsList =
|
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 + ')');
|
tr("Torrent Files") + " (*" + C_TORRENT_FILE_EXTENSION + ')');
|
||||||
|
|
||||||
if (pathsList.isEmpty())
|
if (pathsList.isEmpty())
|
||||||
|
@ -1440,9 +1443,9 @@ void MainWindow::on_actionOpen_triggered()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save last dir to remember it
|
// Save last dir to remember it
|
||||||
QString topDir = Utils::Fs::toUniformPath(pathsList.at(0));
|
const Path topDir {pathsList.at(0)};
|
||||||
topDir = topDir.left(topDir.lastIndexOf('/'));
|
const Path parentDir = topDir.parentPath();
|
||||||
pref->setMainLastDir(topDir);
|
pref->setMainLastDir(parentDir.isEmpty() ? topDir : parentDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::activate()
|
void MainWindow::activate()
|
||||||
|
@ -2110,9 +2113,9 @@ void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
||||||
QProcess installer;
|
QProcess installer;
|
||||||
qDebug("Launching Python installer in passive mode...");
|
qDebug("Launching Python installer in passive mode...");
|
||||||
|
|
||||||
const QString exePath = result.filePath + QLatin1String(".exe");
|
const Path exePath = result.filePath + ".exe";
|
||||||
QFile::rename(result.filePath, exePath);
|
Utils::Fs::renameFile(result.filePath, exePath);
|
||||||
installer.start(Utils::Fs::toNativePath(exePath), {"/passive"});
|
installer.start(exePath.toString(), {"/passive"});
|
||||||
|
|
||||||
// Wait for setup to complete
|
// Wait for setup to complete
|
||||||
installer.waitForFinished(10 * 60 * 1000);
|
installer.waitForFinished(10 * 60 * 1000);
|
||||||
|
@ -2122,7 +2125,7 @@ void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
||||||
qDebug("Setup should be complete!");
|
qDebug("Setup should be complete!");
|
||||||
|
|
||||||
// Delete temp file
|
// Delete temp file
|
||||||
Utils::Fs::forceRemove(exePath);
|
Utils::Fs::removeFile(exePath);
|
||||||
|
|
||||||
// Reload search engine
|
// Reload search engine
|
||||||
if (Utils::ForeignApps::pythonInfo().isSupportedVersion())
|
if (Utils::ForeignApps::pythonInfo().isSupportedVersion())
|
||||||
|
|
|
@ -212,7 +212,7 @@ private:
|
||||||
bool event(QEvent *e) override;
|
bool event(QEvent *e) override;
|
||||||
void displayRSSTab(bool enable);
|
void displayRSSTab(bool enable);
|
||||||
void displaySearchTab(bool enable);
|
void displaySearchTab(bool enable);
|
||||||
void createTorrentTriggered(const QString &path = {});
|
void createTorrentTriggered(const Path &path);
|
||||||
void showStatusBar(bool show);
|
void showStatusBar(bool show);
|
||||||
|
|
||||||
Ui::MainWindow *m_ui;
|
Ui::MainWindow *m_ui;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "base/net/dnsupdater.h"
|
#include "base/net/dnsupdater.h"
|
||||||
#include "base/net/portforwarder.h"
|
#include "base/net/portforwarder.h"
|
||||||
#include "base/net/proxyconfigurationmanager.h"
|
#include "base/net/proxyconfigurationmanager.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/rss/rss_autodownloader.h"
|
#include "base/rss/rss_autodownloader.h"
|
||||||
#include "base/rss/rss_session.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->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkWebUiHttps, &QGroupBox::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, &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, &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->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
|
@ -732,7 +733,7 @@ void OptionsDialog::saveOptions()
|
||||||
auto session = BitTorrent::Session::instance();
|
auto session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
// Downloads preferences
|
// 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->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
|
||||||
session->setUseCategoryPathsInManualMode(m_ui->checkUseCategoryPaths->isChecked());
|
session->setUseCategoryPathsInManualMode(m_ui->checkUseCategoryPaths->isChecked());
|
||||||
session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
|
session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
|
||||||
|
@ -740,7 +741,7 @@ void OptionsDialog::saveOptions()
|
||||||
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
|
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
|
||||||
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
|
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
|
||||||
session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked());
|
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->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
|
||||||
session->setPreallocationEnabled(preAllocateAllFiles());
|
session->setPreallocationEnabled(preAllocateAllFiles());
|
||||||
pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
|
pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
|
||||||
|
@ -1008,14 +1009,12 @@ void OptionsDialog::loadOptions()
|
||||||
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
|
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
|
||||||
m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled());
|
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->setEnabled(m_ui->checkUseDownloadPath->isChecked());
|
m_ui->textDownloadPath->setSelectedPath(session->downloadPath());
|
||||||
m_ui->textDownloadPath->setSelectedPath(Utils::Fs::toNativePath(session->downloadPath()));
|
|
||||||
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
|
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
|
||||||
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
|
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
|
||||||
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
|
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
|
||||||
|
|
||||||
strValue = session->torrentExportDirectory();
|
if (session->torrentExportDirectory().isEmpty())
|
||||||
if (strValue.isEmpty())
|
|
||||||
{
|
{
|
||||||
// Disable
|
// Disable
|
||||||
m_ui->checkExportDir->setChecked(false);
|
m_ui->checkExportDir->setChecked(false);
|
||||||
|
@ -1026,11 +1025,10 @@ void OptionsDialog::loadOptions()
|
||||||
// Enable
|
// Enable
|
||||||
m_ui->checkExportDir->setChecked(true);
|
m_ui->checkExportDir->setChecked(true);
|
||||||
m_ui->textExportDir->setEnabled(true);
|
m_ui->textExportDir->setEnabled(true);
|
||||||
m_ui->textExportDir->setSelectedPath(strValue);
|
m_ui->textExportDir->setSelectedPath(session->torrentExportDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
strValue = session->finishedTorrentExportDirectory();
|
if (session->finishedTorrentExportDirectory().isEmpty())
|
||||||
if (strValue.isEmpty())
|
|
||||||
{
|
{
|
||||||
// Disable
|
// Disable
|
||||||
m_ui->checkExportDirFin->setChecked(false);
|
m_ui->checkExportDirFin->setChecked(false);
|
||||||
|
@ -1041,7 +1039,7 @@ void OptionsDialog::loadOptions()
|
||||||
// Enable
|
// Enable
|
||||||
m_ui->checkExportDirFin->setChecked(true);
|
m_ui->checkExportDirFin->setChecked(true);
|
||||||
m_ui->textExportDirFin->setEnabled(true);
|
m_ui->textExportDirFin->setEnabled(true);
|
||||||
m_ui->textExportDirFin->setSelectedPath(strValue);
|
m_ui->textExportDirFin->setSelectedPath(session->finishedTorrentExportDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ui->groupMailNotification->setChecked(pref->isMailNotificationEnabled());
|
m_ui->groupMailNotification->setChecked(pref->isMailNotificationEnabled());
|
||||||
|
@ -1644,25 +1642,25 @@ void OptionsDialog::setLocale(const QString &localeStr)
|
||||||
m_ui->comboI18n->setCurrentIndex(index);
|
m_ui->comboI18n->setCurrentIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OptionsDialog::getTorrentExportDir() const
|
Path OptionsDialog::getTorrentExportDir() const
|
||||||
{
|
{
|
||||||
if (m_ui->checkExportDir->isChecked())
|
if (m_ui->checkExportDir->isChecked())
|
||||||
return Utils::Fs::expandPathAbs(m_ui->textExportDir->selectedPath());
|
return m_ui->textExportDir->selectedPath();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OptionsDialog::getFinishedTorrentExportDir() const
|
Path OptionsDialog::getFinishedTorrentExportDir() const
|
||||||
{
|
{
|
||||||
if (m_ui->checkExportDirFin->isChecked())
|
if (m_ui->checkExportDirFin->isChecked())
|
||||||
return Utils::Fs::expandPathAbs(m_ui->textExportDirFin->selectedPath());
|
return m_ui->textExportDirFin->selectedPath();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptionsDialog::on_addWatchedFolderButton_clicked()
|
void OptionsDialog::on_addWatchedFolderButton_clicked()
|
||||||
{
|
{
|
||||||
Preferences *const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
const QString dir = QFileDialog::getExistingDirectory(this, tr("Select folder to monitor"),
|
const Path dir {QFileDialog::getExistingDirectory(
|
||||||
Utils::Fs::toNativePath(Utils::Fs::folderName(pref->getScanDirsLastPath())));
|
this, tr("Select folder to monitor"), pref->getScanDirsLastPath().parentPath().toString())};
|
||||||
if (dir.isEmpty())
|
if (dir.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1739,19 +1737,8 @@ void OptionsDialog::editWatchedFolderOptions(const QModelIndex &index)
|
||||||
dialog->open();
|
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
|
// Return Filter object to apply to BT session
|
||||||
QString OptionsDialog::getFilter() const
|
Path OptionsDialog::getFilter() const
|
||||||
{
|
{
|
||||||
return m_ui->textFilterPath->selectedPath();
|
return m_ui->textFilterPath->selectedPath();
|
||||||
}
|
}
|
||||||
|
@ -1773,7 +1760,7 @@ QString OptionsDialog::webUiPassword() const
|
||||||
return m_ui->textWebUiPassword->text();
|
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->textWebUIHttpsCert->setSelectedPath(path);
|
||||||
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-low")), this, 24));
|
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())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QFile file(path);
|
QFile file {path.data()};
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
if (showError == ShowError::Show)
|
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));
|
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->textWebUIHttpsKey->setSelectedPath(path);
|
||||||
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(UIThemeManager::instance()->getIconPath(QLatin1String("security-low")), this, 24));
|
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())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QFile file(path);
|
QFile file {path.data()};
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
if (showError == ShowError::Show)
|
if (showError == ShowError::Show)
|
||||||
|
@ -1843,7 +1830,7 @@ void OptionsDialog::on_IpFilterRefreshBtn_clicked()
|
||||||
// Updating program preferences
|
// Updating program preferences
|
||||||
BitTorrent::Session *const session = BitTorrent::Session::instance();
|
BitTorrent::Session *const session = BitTorrent::Session::instance();
|
||||||
session->setIPFilteringEnabled(true);
|
session->setIPFilteringEnabled(true);
|
||||||
session->setIPFilterFile(""); // forcing Session reload filter file
|
session->setIPFilterFile({}); // forcing Session reload filter file
|
||||||
session->setIPFilterFile(getFilter());
|
session->setIPFilterFile(getFilter());
|
||||||
connect(session, &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
|
connect(session, &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
|
||||||
setCursor(QCursor(Qt::WaitCursor));
|
setCursor(QCursor(Qt::WaitCursor));
|
||||||
|
@ -1887,7 +1874,7 @@ bool OptionsDialog::webUIAuthenticationOk()
|
||||||
|
|
||||||
bool OptionsDialog::isAlternativeWebUIPathValid()
|
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."));
|
QMessageBox::warning(this, tr("Location Error"), tr("The alternative Web UI files location cannot be blank."));
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
|
@ -112,8 +113,8 @@ private slots:
|
||||||
void on_removeWatchedFolderButton_clicked();
|
void on_removeWatchedFolderButton_clicked();
|
||||||
void on_registerDNSBtn_clicked();
|
void on_registerDNSBtn_clicked();
|
||||||
void setLocale(const QString &localeStr);
|
void setLocale(const QString &localeStr);
|
||||||
void webUIHttpsCertChanged(const QString &path, ShowError showError);
|
void webUIHttpsCertChanged(const Path &path, ShowError showError);
|
||||||
void webUIHttpsKeyChanged(const QString &path, ShowError showError);
|
void webUIHttpsKeyChanged(const Path &path, ShowError showError);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Methods
|
// Methods
|
||||||
|
@ -136,9 +137,8 @@ private:
|
||||||
bool preAllocateAllFiles() const;
|
bool preAllocateAllFiles() const;
|
||||||
bool useAdditionDialog() const;
|
bool useAdditionDialog() const;
|
||||||
bool addTorrentsInPause() const;
|
bool addTorrentsInPause() const;
|
||||||
QString getTorrentExportDir() const;
|
Path getTorrentExportDir() const;
|
||||||
QString getFinishedTorrentExportDir() const;
|
Path getFinishedTorrentExportDir() const;
|
||||||
QString askForExportDir(const QString ¤tExportPath);
|
|
||||||
// Connection options
|
// Connection options
|
||||||
int getPort() const;
|
int getPort() const;
|
||||||
bool isUPnPEnabled() const;
|
bool isUPnPEnabled() const;
|
||||||
|
@ -162,7 +162,7 @@ private:
|
||||||
Net::ProxyType getProxyType() const;
|
Net::ProxyType getProxyType() const;
|
||||||
// IP Filter
|
// IP Filter
|
||||||
bool isIPFilteringEnabled() const;
|
bool isIPFilteringEnabled() const;
|
||||||
QString getFilter() const;
|
Path getFilter() const;
|
||||||
// Queueing system
|
// Queueing system
|
||||||
bool isQueueingSystemEnabled() const;
|
bool isQueueingSystemEnabled() const;
|
||||||
int getMaxActiveDownloads() const;
|
int getMaxActiveDownloads() const;
|
||||||
|
|
|
@ -93,12 +93,12 @@ PreviewSelectDialog::PreviewSelectDialog(QWidget *parent, const BitTorrent::Torr
|
||||||
const QVector<qreal> fp = torrent->filesProgress();
|
const QVector<qreal> fp = torrent->filesProgress();
|
||||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
for (int i = 0; i < torrent->filesCount(); ++i)
|
||||||
{
|
{
|
||||||
const QString fileName = Utils::Fs::fileName(torrent->filePath(i));
|
const Path filePath = torrent->filePath(i);
|
||||||
if (Utils::Misc::isPreviewable(fileName))
|
if (Utils::Misc::isPreviewable(filePath))
|
||||||
{
|
{
|
||||||
int row = m_previewListModel->rowCount();
|
int row = m_previewListModel->rowCount();
|
||||||
m_previewListModel->insertRow(row);
|
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, SIZE), torrent->fileSize(i));
|
||||||
m_previewListModel->setData(m_previewListModel->index(row, PROGRESS), fp[i]);
|
m_previewListModel->setData(m_previewListModel->index(row, PROGRESS), fp[i]);
|
||||||
m_previewListModel->setData(m_previewListModel->index(row, FILE_INDEX), i);
|
m_previewListModel->setData(m_previewListModel->index(row, FILE_INDEX), i);
|
||||||
|
@ -133,14 +133,14 @@ void PreviewSelectDialog::previewButtonClicked()
|
||||||
|
|
||||||
// Only one file should be selected
|
// Only one file should be selected
|
||||||
const int fileIndex = selectedIndexes.at(0).data().toInt();
|
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
|
// File
|
||||||
if (!QFile::exists(path))
|
if (!path.exists())
|
||||||
{
|
{
|
||||||
const bool isSingleFile = (m_previewListModel->rowCount() == 1);
|
const bool isSingleFile = (m_previewListModel->rowCount() == 1);
|
||||||
QWidget *parent = isSingleFile ? this->parentWidget() : this;
|
QWidget *parent = isSingleFile ? this->parentWidget() : this;
|
||||||
QMessageBox::critical(parent, tr("Preview impossible")
|
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)
|
if (isSingleFile)
|
||||||
reject();
|
reject();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
|
|
||||||
class QStandardItemModel;
|
class QStandardItemModel;
|
||||||
|
@ -38,6 +39,7 @@ namespace BitTorrent
|
||||||
{
|
{
|
||||||
class Torrent;
|
class Torrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
class PreviewSelectDialog;
|
class PreviewSelectDialog;
|
||||||
|
@ -64,7 +66,7 @@ public:
|
||||||
~PreviewSelectDialog();
|
~PreviewSelectDialog();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void readyToPreviewFile(QString) const;
|
void readyToPreviewFile(const Path &filePath) const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void previewButtonClicked();
|
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::TOT_UP, totalUp, peer.totalUpload(), intDataTextAlignment);
|
||||||
setModelData(row, PeerListColumns::RELEVANCE, (Utils::String::fromDouble(peer.relevance() * 100, 1) + '%'), peer.relevance(), intDataTextAlignment);
|
setModelData(row, PeerListColumns::RELEVANCE, (Utils::String::fromDouble(peer.relevance() * 100, 1) + '%'), peer.relevance(), intDataTextAlignment);
|
||||||
|
|
||||||
const QStringList downloadingFiles {torrent->hasMetadata()
|
const PathList filePaths = torrent->info().filesForPiece(peer.downloadingPieceIndex());
|
||||||
? torrent->info().filesForPiece(peer.downloadingPieceIndex())
|
QStringList downloadingFiles;
|
||||||
: QStringList()};
|
downloadingFiles.reserve(filePaths.size());
|
||||||
|
for (const Path &filePath : filePaths)
|
||||||
|
downloadingFiles.append(filePath.toString());
|
||||||
const QString downloadingFilesDisplayValue = downloadingFiles.join(';');
|
const QString downloadingFilesDisplayValue = downloadingFiles.join(';');
|
||||||
setModelData(row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue, downloadingFilesDisplayValue, {}, downloadingFiles.join(QLatin1Char('\n')));
|
setModelData(row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue, downloadingFilesDisplayValue, {}, downloadingFiles.join(QLatin1Char('\n')));
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,10 @@
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QToolTip>
|
#include <QToolTip>
|
||||||
|
|
||||||
#include "base/indexrange.h"
|
|
||||||
#include "base/bittorrent/torrent.h"
|
#include "base/bittorrent/torrent.h"
|
||||||
#include "base/bittorrent/torrentinfo.h"
|
#include "base/bittorrent/torrentinfo.h"
|
||||||
|
#include "base/indexrange.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -53,9 +54,8 @@ namespace
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
|
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
|
||||||
: m_bytesPerPixel
|
: m_bytesPerPixel {((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
|
||||||
{((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
|
? torrentInfo.totalSize() / image.width() : -1}
|
||||||
? torrentInfo.totalSize() / image.width() : -1}
|
|
||||||
, m_torrentInfo {torrentInfo}
|
, m_torrentInfo {torrentInfo}
|
||||||
{
|
{
|
||||||
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
|
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
|
||||||
|
@ -100,9 +100,9 @@ namespace
|
||||||
m_stream << "</table>";
|
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:
|
private:
|
||||||
|
@ -282,7 +282,7 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
|
||||||
|
|
||||||
for (int f : files)
|
for (int f : files)
|
||||||
{
|
{
|
||||||
const QString filePath {torrentInfo.filePath(f)};
|
const Path filePath = torrentInfo.filePath(f);
|
||||||
renderer(Utils::Misc::friendlyUnit(torrentInfo.fileSize(f)), filePath);
|
renderer(Utils::Misc::friendlyUnit(torrentInfo.fileSize(f)), filePath);
|
||||||
}
|
}
|
||||||
stream << "</body></html>";
|
stream << "</body></html>";
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QListWidgetItem>
|
#include <QListWidgetItem>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
@ -45,6 +44,7 @@
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/torrent.h"
|
#include "base/bittorrent/torrent.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
@ -333,7 +333,7 @@ QTreeView *PropertiesWidget::getFilesList() const
|
||||||
void PropertiesWidget::updateSavePath(BitTorrent::Torrent *const torrent)
|
void PropertiesWidget::updateSavePath(BitTorrent::Torrent *const torrent)
|
||||||
{
|
{
|
||||||
if (torrent == m_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)
|
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)
|
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
|
||||||
{
|
{
|
||||||
const int fileIdx = m_propListModel->getFileIndex(index);
|
const int fileIdx = m_propListModel->getFileIndex(index);
|
||||||
const QString filename {m_torrent->actualFilePath(fileIdx)};
|
const Path fullPath = m_torrent->actualStorageLocation() / m_torrent->actualFilePath(fileIdx);
|
||||||
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(filename))};
|
|
||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// folder type
|
// folder type
|
||||||
const QModelIndex nameIndex {index.sibling(index.row(), TorrentContentModelItem::COL_NAME)};
|
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())
|
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;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +623,7 @@ void PropertiesWidget::openItem(const QModelIndex &index) const
|
||||||
|
|
||||||
void PropertiesWidget::openParentFolder(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
|
m_torrent->flushCache(); // Flush data
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
MacUtils::openFiles({path});
|
MacUtils::openFiles({path});
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QTreeView;
|
class QTreeView;
|
||||||
|
|
||||||
|
@ -108,7 +110,7 @@ private:
|
||||||
QPushButton *getButtonFromIndex(int index);
|
QPushButton *getButtonFromIndex(int index);
|
||||||
void applyPriorities();
|
void applyPriorities();
|
||||||
void openParentFolder(const QModelIndex &index) const;
|
void openParentFolder(const QModelIndex &index) const;
|
||||||
QString getFullPath(const QModelIndex &index) const;
|
Path getFullPath(const QModelIndex &index) const;
|
||||||
|
|
||||||
Ui::PropertiesWidget *m_ui;
|
Ui::PropertiesWidget *m_ui;
|
||||||
BitTorrent::Torrent *m_torrent;
|
BitTorrent::Torrent *m_torrent;
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/rss/rss_article.h"
|
#include "base/rss/rss_article.h"
|
||||||
#include "base/rss/rss_autodownloader.h"
|
#include "base/rss/rss_autodownloader.h"
|
||||||
|
@ -47,7 +48,6 @@
|
||||||
#include "base/rss/rss_folder.h"
|
#include "base/rss/rss_folder.h"
|
||||||
#include "base/rss/rss_session.h"
|
#include "base/rss/rss_session.h"
|
||||||
#include "base/utils/compare.h"
|
#include "base/utils/compare.h"
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "gui/autoexpandabledialog.h"
|
#include "gui/autoexpandabledialog.h"
|
||||||
|
@ -261,7 +261,7 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
|
||||||
else
|
else
|
||||||
m_ui->lineEFilter->clear();
|
m_ui->lineEFilter->clear();
|
||||||
m_ui->checkBoxSaveDiffDir->setChecked(!m_currentRule.savePath().isEmpty());
|
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->blockSignals(true);
|
||||||
m_ui->checkRegex->setChecked(m_currentRule.useRegex());
|
m_ui->checkRegex->setChecked(m_currentRule.useRegex());
|
||||||
m_ui->checkRegex->blockSignals(false);
|
m_ui->checkRegex->blockSignals(false);
|
||||||
|
@ -346,7 +346,7 @@ void AutomatedRssDownloader::updateEditedRule()
|
||||||
m_currentRule.setMustContain(m_ui->lineContains->text());
|
m_currentRule.setMustContain(m_ui->lineContains->text());
|
||||||
m_currentRule.setMustNotContain(m_ui->lineNotContains->text());
|
m_currentRule.setMustNotContain(m_ui->lineNotContains->text());
|
||||||
m_currentRule.setEpisodeFilter(m_ui->lineEFilter->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());
|
m_currentRule.setCategory(m_ui->comboCategory->currentText());
|
||||||
std::optional<bool> addPaused;
|
std::optional<bool> addPaused;
|
||||||
if (m_ui->comboAddPaused->currentIndex() == 1)
|
if (m_ui->comboAddPaused->currentIndex() == 1)
|
||||||
|
@ -429,9 +429,10 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
QString selectedFilter {m_formatFilterJSON};
|
QString selectedFilter {m_formatFilterJSON};
|
||||||
QString path = QFileDialog::getSaveFileName(
|
Path path {QFileDialog::getSaveFileName(
|
||||||
this, tr("Export RSS rules"), QDir::homePath()
|
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;
|
if (path.isEmpty()) return;
|
||||||
|
|
||||||
const RSS::AutoDownloader::RulesFileFormat format
|
const RSS::AutoDownloader::RulesFileFormat format
|
||||||
|
@ -443,12 +444,12 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
||||||
|
|
||||||
if (format == RSS::AutoDownloader::RulesFileFormat::JSON)
|
if (format == RSS::AutoDownloader::RulesFileFormat::JSON)
|
||||||
{
|
{
|
||||||
if (!path.endsWith(EXT_JSON, Qt::CaseInsensitive))
|
if (!path.hasExtension(EXT_JSON))
|
||||||
path += EXT_JSON;
|
path += EXT_JSON;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!path.endsWith(EXT_LEGACY, Qt::CaseInsensitive))
|
if (!path.hasExtension(EXT_LEGACY))
|
||||||
path += EXT_LEGACY;
|
path += EXT_LEGACY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,13 +465,13 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
||||||
void AutomatedRssDownloader::on_importBtn_clicked()
|
void AutomatedRssDownloader::on_importBtn_clicked()
|
||||||
{
|
{
|
||||||
QString selectedFilter {m_formatFilterJSON};
|
QString selectedFilter {m_formatFilterJSON};
|
||||||
QString path = QFileDialog::getOpenFileName(
|
const Path path {QFileDialog::getOpenFileName(
|
||||||
this, tr("Import RSS rules"), QDir::homePath()
|
this, tr("Import 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() || !QFile::exists(path))
|
if (!path.exists())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QFile file {path};
|
QFile file {path.data()};
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue