Browse Source

Added command line arguments for specifying options when adding torrents

Arguments include adding torrents as paused/started, skiping hash check, setting category, downloading in sequential order, downloading first and last pieces first, and skipping the 'add new torrent' dialog. Added TriStateBoolOption class for specifying options that don't have a default value when unused (e.g. add-paused). Also improved command line usage text to include more information, have better organization, and not exceed 80 columns in width. Also also added firstLastPiecePriority field to BitTorrent::AddTorrentData, and modified TorrentHandle so that if first/last piece priority should be on, it will be toggled on after the torrent's metadata has loaded.
adaptive-webui-19844
Brian Kendall 8 years ago
parent
commit
eba41978b0
  1. 54
      src/app/application.cpp
  2. 4
      src/app/main.cpp
  3. 249
      src/app/options.cpp
  4. 12
      src/app/options.h
  5. 1
      src/base/bittorrent/addtorrentparams.h
  6. 2
      src/base/bittorrent/session.cpp
  7. 42
      src/base/bittorrent/torrenthandle.cpp
  8. 2
      src/base/bittorrent/torrenthandle.h
  9. 81
      src/gui/addnewtorrentdialog.cpp
  10. 8
      src/gui/addnewtorrentdialog.h
  11. 10
      src/gui/addnewtorrentdialog.ui
  12. 2
      src/gui/rss/rsswidget.cpp

54
src/app/application.cpp

@ -407,15 +407,63 @@ void Application::processParams(const QStringList &params) @@ -407,15 +407,63 @@ void Application::processParams(const QStringList &params)
return;
}
#endif
BitTorrent::AddTorrentParams torrentParams;
TriStateBool skipTorrentDialog;
foreach (QString param, params) {
param = param.trimmed();
// Process strings indicating options specified by the user.
if (param.startsWith(QLatin1String("@savePath="))) {
torrentParams.savePath = param.mid(10);
continue;
}
if (param.startsWith(QLatin1String("@addPaused="))) {
torrentParams.addPaused = param.mid(11).toInt() ? TriStateBool::True : TriStateBool::False;
continue;
}
if (param == QLatin1String("@skipChecking")) {
torrentParams.skipChecking = true;
continue;
}
if (param.startsWith(QLatin1String("@category="))) {
torrentParams.category = param.mid(10);
continue;
}
if (param == QLatin1String("@sequential")) {
torrentParams.sequential = true;
continue;
}
if (param == QLatin1String("@firstLastPiecePriority")) {
torrentParams.firstLastPiecePriority = true;
continue;
}
if (param.startsWith(QLatin1String("@skipDialog="))) {
skipTorrentDialog = param.mid(12).toInt() ? TriStateBool::True : TriStateBool::False;
continue;
}
#ifndef DISABLE_GUI
if (AddNewTorrentDialog::isEnabled())
AddNewTorrentDialog::show(param, m_window);
// There are two circumstances in which we want to show the torrent
// dialog. One is when the application settings specify that it should
// be shown and skipTorrentDialog is undefined. The other is when
// skipTorrentDialog is false, meaning that the application setting
// should be overridden.
const bool showDialogForThisTorrent =
((AddNewTorrentDialog::isEnabled() && skipTorrentDialog == TriStateBool::Undefined)
|| skipTorrentDialog == TriStateBool::False);
if (showDialogForThisTorrent)
AddNewTorrentDialog::show(param, torrentParams, m_window);
else
#endif
BitTorrent::Session::instance()->addTorrent(param);
BitTorrent::Session::instance()->addTorrent(param, torrentParams);
}
}

4
src/app/main.cpp

@ -170,7 +170,7 @@ int main(int argc, char *argv[]) @@ -170,7 +170,7 @@ int main(int argc, char *argv[])
qDebug("qBittorrent is already running for this user.");
QThread::msleep(300);
app->sendParams(params.torrents);
app->sendParams(params.paramList());
return EXIT_SUCCESS;
}
@ -235,7 +235,7 @@ int main(int argc, char *argv[]) @@ -235,7 +235,7 @@ int main(int argc, char *argv[])
signal(SIGSEGV, sigAbnormalHandler);
#endif
return app->exec(params.torrents);
return app->exec(params.paramList());
}
catch (CommandLineParameterError &er) {
displayBadArgMessage(er.messageForUser());

249
src/app/options.cpp

@ -47,6 +47,10 @@ @@ -47,6 +47,10 @@
namespace
{
const int USAGE_INDENTATION = 4;
const int USAGE_TEXT_COLUMN = 31;
const int WRAP_AT_COLUMN = 80;
// Base option class. Encapsulates name operations.
class Option
{
@ -78,14 +82,15 @@ namespace @@ -78,14 +82,15 @@ namespace
+ QString(QLatin1String(m_name)).toUpper().replace(QLatin1Char('-'), QLatin1Char('_'));
}
public:
static QString padUsageText(const QString &usage)
{
const int TAB_WIDTH = 8;
QString res = QLatin1String("\t") + usage;
if (usage.size() < 2 * TAB_WIDTH)
return res + QLatin1String("\t\t");
QString res = QString(USAGE_INDENTATION, ' ') + usage;
if ((USAGE_TEXT_COLUMN - usage.length() - 4) > 0)
return res + QString(USAGE_TEXT_COLUMN - usage.length() - 4, ' ');
else
return res + QLatin1String("\t");
return res;
}
private:
@ -125,7 +130,7 @@ namespace @@ -125,7 +130,7 @@ namespace
}
};
inline bool operator==(const QString &s, const BoolOption &o)
bool operator==(const QString &s, const BoolOption &o)
{
return o == s;
}
@ -185,7 +190,7 @@ namespace @@ -185,7 +190,7 @@ namespace
}
};
inline bool operator==(const QString &s, const StringOption &o)
bool operator==(const QString &s, const StringOption &o)
{
return o == s;
}
@ -230,7 +235,69 @@ namespace @@ -230,7 +235,69 @@ namespace
}
};
inline bool operator==(const QString &s, const IntOption &o)
bool operator==(const QString &s, const IntOption &o)
{
return o == s;
}
// Option that is explicitly set to true or false, and whose value is undefined when unspecified.
// May not have a shortcut.
class TriStateBoolOption: protected StringOption
{
public:
constexpr TriStateBoolOption(const char *name)
: StringOption {name}
{
}
using StringOption::operator==;
QString usage() const
{
return StringOption::usage(QLatin1String("true|false"));
}
TriStateBool value(const QString &arg) const
{
QString val = StringOption::value(arg);
if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
return TriStateBool::True;
}
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
return TriStateBool::False;
}
else {
throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'",
"e.g. Parameter '--add-paused' must follow syntax "
"'--add-paused=<true|false>'")
.arg(fullParameter())
.arg(QLatin1String("<true|false>")));
}
}
TriStateBool value(const QProcessEnvironment &env) const
{
QString val = env.value(envVarName());
if (val.isEmpty()) {
return TriStateBool::Undefined;
}
else if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
return TriStateBool::True;
}
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
return TriStateBool::False;
}
else {
qDebug() << QObject::tr("Expected %1 in environment variable '%2', but got '%3'")
.arg(QLatin1String("true|false")).arg(envVarName()).arg(val);
return TriStateBool::Undefined;
}
}
};
bool operator==(const QString &s, const TriStateBoolOption &o)
{
return o == s;
}
@ -247,10 +314,22 @@ namespace @@ -247,10 +314,22 @@ namespace
constexpr const StringOption CONFIGURATION_OPTION = {"configuration"};
constexpr const BoolOption PORTABLE_OPTION = {"portable"};
constexpr const BoolOption RELATIVE_FASTRESUME = {"relative-fastresume"};
constexpr const StringOption SAVE_PATH_OPTION = {"save-path"};
constexpr const TriStateBoolOption PAUSED_OPTION = {"add-paused"};
constexpr const BoolOption SKIP_HASH_CHECK_OPTION = {"skip-hash-check"};
constexpr const StringOption CATEGORY_OPTION = {"category"};
constexpr const BoolOption SEQUENTIAL_OPTION = {"sequential"};
constexpr const BoolOption FIRST_AND_LAST_OPTION = {"first-and-last"};
constexpr const TriStateBoolOption SKIP_DIALOG_OPTION = {"skip-dialog"};
}
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
: showHelp(false)
, relativeFastresumePaths(RELATIVE_FASTRESUME.value(env))
, portableMode(PORTABLE_OPTION.value(env))
, skipChecking(SKIP_HASH_CHECK_OPTION.value(env))
, sequential(SEQUENTIAL_OPTION.value(env))
, firstLastPiecePriority(FIRST_AND_LAST_OPTION.value(env))
#ifndef Q_OS_WIN
, showVersion(false)
#endif
@ -260,13 +339,59 @@ QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &en @@ -260,13 +339,59 @@ QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &en
, shouldDaemonize(DAEMON_OPTION.value(env))
#endif
, webUiPort(WEBUI_PORT_OPTION.value(env, -1))
, addPaused(PAUSED_OPTION.value(env))
, skipDialog(SKIP_DIALOG_OPTION.value(env))
, profileDir(PROFILE_OPTION.value(env))
, relativeFastresumePaths(RELATIVE_FASTRESUME.value(env))
, portableMode(PORTABLE_OPTION.value(env))
, configurationName(CONFIGURATION_OPTION.value(env))
, savePath(SAVE_PATH_OPTION.value(env))
, category(CATEGORY_OPTION.value(env))
{
}
QStringList QBtCommandLineParameters::paramList() const
{
QStringList result;
// Because we're passing a string list to the currently running
// qBittorrent process, we need some way of passing along the options
// the user has specified. Here we place special strings that are
// almost certainly not going to collide with a file path or URL
// specified by the user, and placing them at the beginning of the
// string listr so that they will be processed before the list of
// torrent paths or URLs.
if (!savePath.isEmpty())
result.append(QString("@savePath=%1").arg(savePath));
if (addPaused == TriStateBool::True) {
result.append(QLatin1String("@addPaused=1"));
}
else if (addPaused == TriStateBool::False) {
result.append(QLatin1String("@addPaused=0"));
}
if (skipChecking)
result.append(QLatin1String("@skipChecking"));
if (!category.isEmpty())
result.append(QString("@category=%1").arg(category));
if (sequential)
result.append(QLatin1String("@sequential"));
if (firstLastPiecePriority)
result.append(QLatin1String("@firstLastPiecePriority"));
if (skipDialog == TriStateBool::True) {
result.append(QLatin1String("@skipDialog=1"));
}
else if (skipDialog == TriStateBool::False) {
result.append(QLatin1String("@skipDialog=0"));
}
result += torrents;
return result;
}
QBtCommandLineParameters parseCommandLine(const QStringList &args)
{
QBtCommandLineParameters result {QProcessEnvironment::systemEnvironment()};
@ -312,6 +437,27 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args) @@ -312,6 +437,27 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
else if (arg == CONFIGURATION_OPTION) {
result.configurationName = CONFIGURATION_OPTION.value(arg);
}
else if (arg == SAVE_PATH_OPTION) {
result.savePath = SAVE_PATH_OPTION.value(arg);
}
else if (arg == PAUSED_OPTION) {
result.addPaused = PAUSED_OPTION.value(arg);
}
else if (arg == SKIP_HASH_CHECK_OPTION) {
result.skipChecking = true;
}
else if (arg == CATEGORY_OPTION) {
result.category = CATEGORY_OPTION.value(arg);
}
else if (arg == SEQUENTIAL_OPTION) {
result.sequential = true;
}
else if (arg == FIRST_AND_LAST_OPTION) {
result.firstLastPiecePriority = true;
}
else if (arg == SKIP_DIALOG_OPTION) {
result.skipDialog = SKIP_DIALOG_OPTION.value(arg);
}
else {
// Unknown argument
result.unknownParameter = arg;
@ -343,43 +489,82 @@ const QString& CommandLineParameterError::messageForUser() const @@ -343,43 +489,82 @@ const QString& CommandLineParameterError::messageForUser() const
return m_messageForUser;
}
QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN, int wrapAtColumn = WRAP_AT_COLUMN)
{
QStringList words = text.split(' ');
QStringList lines = {words.first()};
int currentLineMaxLength = wrapAtColumn - initialIndentation;
foreach (const QString &word, words.mid(1)) {
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
lines.last().append(" " + word);
}
else {
lines.append(QString(initialIndentation, ' ') + word);
currentLineMaxLength = wrapAtColumn;
}
}
return lines.join("\n");
}
QString makeUsage(const QString &prgName)
{
QString text;
QTextStream stream(&text, QIODevice::WriteOnly);
QString indentation = QString(USAGE_INDENTATION, ' ');
stream << QObject::tr("Usage:") << '\n';
#ifndef Q_OS_WIN
stream << '\t' << prgName << " [options] [(<filename> | <url>)...]" << '\n';
#endif
stream << indentation << prgName << QLatin1String(" [options] [(<filename> | <url>)...]") << '\n';
stream << QObject::tr("Options:") << '\n';
#ifndef Q_OS_WIN
stream << SHOW_VERSION_OPTION.usage() << QObject::tr("Displays program version and exit") << '\n';
stream << SHOW_VERSION_OPTION.usage() << wrapText(QObject::tr("Displays program version and exit")) << '\n';
#endif
stream << SHOW_HELP_OPTION.usage() << QObject::tr("Displays this help message and exit") << '\n';
stream << WEBUI_PORT_OPTION.usage(QLatin1String("port"))
<< QObject::tr("Changes the Web UI port")
stream << SHOW_HELP_OPTION.usage() << wrapText(QObject::tr("Displays this help message and exit")) << '\n';
stream << WEBUI_PORT_OPTION.usage(QObject::tr("port"))
<< wrapText(QObject::tr("Changes the Web UI port"))
<< '\n';
#ifndef DISABLE_GUI
stream << NO_SPLASH_OPTION.usage() << QObject::tr("Disable splash screen") << '\n';
stream << NO_SPLASH_OPTION.usage() << wrapText(QObject::tr("Disable splash screen")) << '\n';
#else
stream << DAEMON_OPTION.usage() << QObject::tr("Run in daemon-mode (background)") << '\n';
stream << DAEMON_OPTION.usage() << wrapText(QObject::tr("Run in daemon-mode (background)")) << '\n';
#endif
stream << PROFILE_OPTION.usage(QLatin1String("dir")) << QObject::tr("Store configuration files in <dir>") << '\n';
stream << CONFIGURATION_OPTION.usage(QLatin1String("name")) << QObject::tr("Store configuration files in directories qBittorrent_<name>") << '\n';
stream << RELATIVE_FASTRESUME.usage() << QObject::tr("Hack into libtorrent fastresume files and make file paths relative to the profile directory") << '\n';
stream << PORTABLE_OPTION.usage() << QObject::tr("Shortcut for --profile=<exe dir>/profile --relative-fastresume") << '\n';
stream << "\tfiles or urls\t\t" << QObject::tr("Downloads the torrents passed by the user") << '\n'
//: Use appropriate short form or abbreviation of "directory"
stream << PROFILE_OPTION.usage(QObject::tr("dir"))
<< wrapText(QObject::tr("Store configuration files in <dir>")) << '\n';
stream << CONFIGURATION_OPTION.usage(QObject::tr("name"))
<< wrapText(QObject::tr("Store configuration files in directories qBittorrent_<name>")) << '\n';
stream << RELATIVE_FASTRESUME.usage()
<< wrapText(QObject::tr("Hack into libtorrent fastresume files and make file paths relative "
"to the profile directory")) << '\n';
stream << PORTABLE_OPTION.usage()
<< wrapText(QObject::tr("Shortcut for --profile=<exe dir>/profile --relative-fastresume")) << '\n';
stream << Option::padUsageText(QObject::tr("files or urls"))
<< wrapText(QObject::tr("Downloads the torrents passed by the user")) << '\n'
<< '\n';
stream << QObject::tr("Option values may be supplied via environment variables.") << '\n'
<< QObject::tr("For option named 'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper case, '-' replaced with '_')") << '\n'
<< QObject::tr("To pass flag values, set the variable to '1' or 'TRUE'.") << '\n'
<< QObject::tr("For example, to disable the splash screen: ")
<< "QBT_NO_SPLASH=1 " << prgName << '\n'
<< '\n'
<< QObject::tr("Command line parameters take precedence over environment variables") << '\n';
stream << wrapText(QObject::tr("Options when adding new torrents:"), 0) << '\n';
stream << SAVE_PATH_OPTION.usage(QObject::tr("path")) << wrapText(QObject::tr("Torrent save path")) << '\n';
stream << PAUSED_OPTION.usage() << wrapText(QObject::tr("Add torrents as started or paused")) << '\n';
stream << SKIP_HASH_CHECK_OPTION.usage() << wrapText(QObject::tr("Skip hash check")) << '\n';
stream << CATEGORY_OPTION.usage(QObject::tr("name"))
<< wrapText(QObject::tr("Assign torrents to category. If the category doesn't exist, it will be "
"created.")) << '\n';
stream << SEQUENTIAL_OPTION.usage() << wrapText(QObject::tr("Download files in sequential order")) << '\n';
stream << FIRST_AND_LAST_OPTION.usage()
<< wrapText(QObject::tr("Download first and last pieces first")) << '\n';
stream << SKIP_DIALOG_OPTION.usage()
<< wrapText(QObject::tr("Specifies whether the \"Add New Torrent\" dialog opens when adding a "
"torrent.")) << '\n';
stream << '\n';
stream << wrapText(QObject::tr("Option values may be supplied via environment variables. For option named "
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
"'TRUE'. For example, to disable the splash screen: "), 0) << "\n"
<< QLatin1String("QBT_NO_SPLASH=1 ") << prgName << '\n'
<< wrapText(QObject::tr("Command line parameters take precedence over environment variables"), 0) << '\n';
stream << flush;
return text;

12
src/app/options.h

@ -38,11 +38,13 @@ @@ -38,11 +38,13 @@
#include <QString>
#include <QStringList>
#include "base/tristatebool.h"
class QProcessEnvironment;
struct QBtCommandLineParameters
{
bool showHelp;
bool showHelp, relativeFastresumePaths, portableMode, skipChecking, sequential, firstLastPiecePriority;
#ifndef Q_OS_WIN
bool showVersion;
#endif
@ -52,14 +54,12 @@ struct QBtCommandLineParameters @@ -52,14 +54,12 @@ struct QBtCommandLineParameters
bool shouldDaemonize;
#endif
int webUiPort;
QString profileDir;
bool relativeFastresumePaths;
bool portableMode;
QString configurationName;
TriStateBool addPaused, skipDialog;
QStringList torrents;
QString unknownParameter;
QString profileDir, configurationName, savePath, category, unknownParameter;
QBtCommandLineParameters(const QProcessEnvironment&);
QStringList paramList() const;
};
class CommandLineParameterError: public std::runtime_error

1
src/base/bittorrent/addtorrentparams.h

@ -42,6 +42,7 @@ namespace BitTorrent @@ -42,6 +42,7 @@ namespace BitTorrent
QString savePath;
bool disableTempPath = false; // e.g. for imported torrents
bool sequential = false;
bool firstLastPiecePriority = false;
TriStateBool addForced;
TriStateBool addPaused;
QVector<int> filePriorities; // used if TorrentInfo is set

2
src/base/bittorrent/session.cpp

@ -3666,6 +3666,8 @@ namespace @@ -3666,6 +3666,8 @@ namespace
magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
torrentData.addPaused = fast.dict_find_int_value("qBt-paused");
torrentData.addForced = fast.dict_find_int_value("qBt-forced");
torrentData.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority");
torrentData.sequential = fast.dict_find_int_value("qBt-sequential");
prio = fast.dict_find_int_value("qBt-queuePosition");

42
src/base/bittorrent/torrenthandle.cpp

@ -90,6 +90,7 @@ AddTorrentData::AddTorrentData(const AddTorrentParams &params) @@ -90,6 +90,7 @@ AddTorrentData::AddTorrentData(const AddTorrentParams &params)
, savePath(params.savePath)
, disableTempPath(params.disableTempPath)
, sequential(params.sequential)
, firstLastPiecePriority(params.firstLastPiecePriority)
, hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping
, skipChecking(params.skipChecking)
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
@ -208,6 +209,7 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle @@ -208,6 +209,7 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_tempPathDisabled(data.disableTempPath)
, m_hasMissingFiles(false)
, m_hasRootFolder(data.hasRootFolder)
, m_needsToSetFirstLastPiecePriority(false)
, m_pauseAfterRecheck(false)
, m_needSaveResumeData(false)
{
@ -217,12 +219,22 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle @@ -217,12 +219,22 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
updateStatus();
m_hash = InfoHash(m_nativeStatus.info_hash);
if (!data.resumed) {
// NB: the following two if statements are present because we don't want
// to set either sequential download or first/last piece priority to false
// if their respective flags in data are false when a torrent is being
// resumed. This is because, in that circumstance, this constructor is
// called with those flags set to false, even if the torrent was set to
// download sequentially or have first/last piece priority enabled when
// its resume data was saved. These two settings are restored later. But
// if we set them to false now, both will erroneously not be restored.
if (!data.resumed || data.sequential)
setSequentialDownload(data.sequential);
if (hasMetadata()) {
if (filesCount() == 1)
m_hasRootFolder = false;
}
if (!data.resumed || data.firstLastPiecePriority)
setFirstLastPiecePriority(data.firstLastPiecePriority);
if (!data.resumed && hasMetadata()) {
if (filesCount() == 1)
m_hasRootFolder = false;
}
}
@ -729,7 +741,8 @@ bool TorrentHandle::isSequentialDownload() const @@ -729,7 +741,8 @@ bool TorrentHandle::isSequentialDownload() const
bool TorrentHandle::hasFirstLastPiecePriority() const
{
if (!hasMetadata()) return false;
if (!hasMetadata())
return m_needsToSetFirstLastPiecePriority;
// Get int first media file
std::vector<int> fp;
@ -1229,7 +1242,10 @@ void TorrentHandle::toggleSequentialDownload() @@ -1229,7 +1242,10 @@ void TorrentHandle::toggleSequentialDownload()
void TorrentHandle::setFirstLastPiecePriority(bool b)
{
if (!hasMetadata()) return;
if (!hasMetadata()) {
m_needsToSetFirstLastPiecePriority = b;
return;
}
std::vector<int> fp = m_nativeHandle.file_priorities();
std::vector<int> pp = m_nativeHandle.piece_priorities();
@ -1508,6 +1524,11 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert @@ -1508,6 +1524,11 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
resumeData["qBt-magnetUri"] = toMagnetUri().toStdString();
resumeData["qBt-paused"] = isPaused();
resumeData["qBt-forced"] = isForced();
// Both firstLastPiecePriority and sequential need to be stored in the
// resume data if there is no metadata, otherwise they won't be
// restored if qBittorrent quits before the metadata are retrieved:
resumeData["qBt-firstLastPiecePriority"] = hasFirstLastPiecePriority();
resumeData["qBt-sequential"] = isSequentialDownload();
}
else {
auto savePath = resumeData.find_key("save_path")->string();
@ -1634,6 +1655,13 @@ void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p @@ -1634,6 +1655,13 @@ void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p
m_speedMonitor.reset();
m_session->handleTorrentPaused(this);
}
// If first/last piece priority was specified when adding this torrent, we can set it
// now that we have metadata:
if (m_needsToSetFirstLastPiecePriority) {
setFirstLastPiecePriority(true);
m_needsToSetFirstLastPiecePriority = false;
}
}
void TorrentHandle::handleTempPathChanged()

2
src/base/bittorrent/torrenthandle.h

@ -96,6 +96,7 @@ namespace BitTorrent @@ -96,6 +96,7 @@ namespace BitTorrent
QString savePath;
bool disableTempPath;
bool sequential;
bool firstLastPiecePriority;
bool hasSeedStatus;
bool skipChecking;
bool hasRootFolder;
@ -429,6 +430,7 @@ namespace BitTorrent @@ -429,6 +430,7 @@ namespace BitTorrent
bool m_tempPathDisabled;
bool m_hasMissingFiles;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority;
bool m_pauseAfterRecheck;
bool m_needSaveResumeData;

81
src/gui/addnewtorrentdialog.cpp

@ -76,14 +76,16 @@ namespace @@ -76,14 +76,16 @@ namespace
}
}
AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
: QDialog(parent)
, ui(new Ui::AddNewTorrentDialog)
, m_contentModel(0)
, m_contentDelegate(0)
, m_hasMetadata(false)
, m_oldIndex(0)
, m_torrentParams(inParams)
{
// TODO: set dialog file properties using m_torrentParams.filePriorities
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
ui->lblMetaLoading->setVisible(false);
@ -91,7 +93,13 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) @@ -91,7 +93,13 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
auto session = BitTorrent::Session::instance();
ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused());
if (m_torrentParams.addPaused == TriStateBool::True)
ui->startTorrentCheckBox->setChecked(false);
else if (m_torrentParams.addPaused == TriStateBool::False)
ui->startTorrentCheckBox->setChecked(true);
else
ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused());
ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does it job at this point
ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
ui->comboTTM->blockSignals(false);
@ -99,8 +107,15 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) @@ -99,8 +107,15 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
connect(ui->browseButton, SIGNAL(clicked()), SLOT(browseButton_clicked()));
ui->defaultSavePathCheckBox->setVisible(false); // Default path is selected by default
ui->createSubfolderCheckBox->setChecked(session->isCreateTorrentSubfolder());
if (m_torrentParams.createSubfolder == TriStateBool::True)
ui->createSubfolderCheckBox->setChecked(true);
else if (m_torrentParams.createSubfolder == TriStateBool::False)
ui->createSubfolderCheckBox->setChecked(false);
else
ui->createSubfolderCheckBox->setChecked(session->isCreateTorrentSubfolder());
ui->skipCheckingCheckBox->setChecked(m_torrentParams.skipChecking);
ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
// Load categories
@ -108,12 +123,14 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) @@ -108,12 +123,14 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString();
if (!m_torrentParams.category.isEmpty())
ui->categoryComboBox->addItem(m_torrentParams.category);
if (!defaultCategory.isEmpty())
ui->categoryComboBox->addItem(defaultCategory);
ui->categoryComboBox->addItem("");
foreach (const QString &category, categories)
if (category != defaultCategory)
if (category != defaultCategory && category != m_torrentParams.category)
ui->categoryComboBox->addItem(category);
ui->categoryComboBox->model()->sort(0);
@ -179,9 +196,9 @@ void AddNewTorrentDialog::saveState() @@ -179,9 +196,9 @@ void AddNewTorrentDialog::saveState()
settings()->storeValue(KEY_EXPANDED, ui->adv_button->isChecked());
}
void AddNewTorrentDialog::show(QString source, QWidget *parent)
void AddNewTorrentDialog::show(QString source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
{
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(inParams, parent);
if (Utils::Misc::isUrl(source)) {
// Launch downloader
@ -205,6 +222,11 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent) @@ -205,6 +222,11 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent)
}
}
void AddNewTorrentDialog::show(QString source, QWidget *parent)
{
show(source, BitTorrent::AddTorrentParams(), parent);
}
bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
{
if (torrentPath.startsWith("file://", Qt::CaseInsensitive))
@ -416,6 +438,18 @@ void AddNewTorrentDialog::categoryChanged(int index) @@ -416,6 +438,18 @@ void AddNewTorrentDialog::categoryChanged(int index)
}
}
void AddNewTorrentDialog::setSavePath(const QString &newPath)
{
int existingIndex = indexOfSavePath(newPath);
if (existingIndex < 0) {
// New path, prepend to combo box
ui->savePathComboBox->insertItem(0, Utils::Fs::toNativePath(newPath), newPath);
existingIndex = 0;
}
ui->savePathComboBox->setCurrentIndex(existingIndex);
onSavePathChanged(existingIndex);
}
void AddNewTorrentDialog::browseButton_clicked()
{
disconnect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int)));
@ -430,17 +464,7 @@ void AddNewTorrentDialog::browseButton_clicked() @@ -430,17 +464,7 @@ void AddNewTorrentDialog::browseButton_clicked()
newPath = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
if (!newPath.isEmpty()) {
const int existingIndex = indexOfSavePath(newPath);
if (existingIndex >= 0) {
ui->savePathComboBox->setCurrentIndex(existingIndex);
}
else {
// New path, prepend to combo box
ui->savePathComboBox->insertItem(0, Utils::Fs::toNativePath(newPath), newPath);
ui->savePathComboBox->setCurrentIndex(0);
}
onSavePathChanged(0);
setSavePath(newPath);
}
else {
// Restore index
@ -579,6 +603,9 @@ void AddNewTorrentDialog::populateSavePathComboBox() @@ -579,6 +603,9 @@ void AddNewTorrentDialog::populateSavePathComboBox()
foreach (const QString &savePath, settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList())
if (QDir(savePath) != defaultSaveDir)
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath);
if (!m_torrentParams.savePath.isEmpty())
setSavePath(m_torrentParams.savePath);
}
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
@ -626,27 +653,25 @@ void AddNewTorrentDialog::accept() @@ -626,27 +653,25 @@ void AddNewTorrentDialog::accept()
if (!m_hasMetadata)
disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo&)));
BitTorrent::AddTorrentParams params;
// TODO: Check if destination actually exists
params.skipChecking = ui->skipCheckingCheckBox->isChecked();
m_torrentParams.skipChecking = ui->skipCheckingCheckBox->isChecked();
// Category
params.category = ui->categoryComboBox->currentText();
m_torrentParams.category = ui->categoryComboBox->currentText();
if (ui->defaultCategoryCheckbox->isChecked())
settings()->storeValue(KEY_DEFAULTCATEGORY, params.category);
settings()->storeValue(KEY_DEFAULTCATEGORY, m_torrentParams.category);
// Save file priorities
if (m_contentModel)
params.filePriorities = m_contentModel->model()->getFilePriorities();
m_torrentParams.filePriorities = m_contentModel->model()->getFilePriorities();
params.addPaused = TriStateBool(!ui->startTorrentCheckBox->isChecked());
params.createSubfolder = TriStateBool(ui->createSubfolderCheckBox->isChecked());
m_torrentParams.addPaused = TriStateBool(!ui->startTorrentCheckBox->isChecked());
m_torrentParams.createSubfolder = TriStateBool(ui->createSubfolderCheckBox->isChecked());
QString savePath = ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString();
if (ui->comboTTM->currentIndex() != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
params.savePath = savePath;
m_torrentParams.savePath = savePath;
saveSavePathHistory();
if (ui->defaultSavePathCheckBox->isChecked())
BitTorrent::Session::instance()->setDefaultSavePath(savePath);
@ -656,9 +681,9 @@ void AddNewTorrentDialog::accept() @@ -656,9 +681,9 @@ void AddNewTorrentDialog::accept()
// Add torrent
if (!m_hasMetadata)
BitTorrent::Session::instance()->addTorrent(m_hash, params);
BitTorrent::Session::instance()->addTorrent(m_hash, m_torrentParams);
else
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, params);
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, m_torrentParams);
m_torrentGuard->markAsAddedToSession();
QDialog::accept();

8
src/gui/addnewtorrentdialog.h

@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/bittorrent/addtorrentparams.h"
namespace BitTorrent
{
@ -65,7 +66,8 @@ public: @@ -65,7 +66,8 @@ public:
static bool isTopLevel();
static void setTopLevel(bool value);
static void show(QString source, QWidget *parent = 0);
static void show(QString source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
static void show(QString source, QWidget *parent);
private slots:
void showAdvancedSettings(bool show);
@ -87,7 +89,7 @@ private slots: @@ -87,7 +89,7 @@ private slots:
void reject() override;
private:
explicit AddNewTorrentDialog(QWidget *parent = 0);
explicit AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
bool loadTorrent(const QString &torrentPath);
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
void populateSavePathComboBox();
@ -98,6 +100,7 @@ private: @@ -98,6 +100,7 @@ private:
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
void setupTreeview();
void setCommentText(const QString &str) const;
void setSavePath(const QString &newPath);
void showEvent(QShowEvent *event) override;
@ -112,6 +115,7 @@ private: @@ -112,6 +115,7 @@ private:
QByteArray m_headerState;
int m_oldIndex;
QScopedPointer<TorrentFileGuard> m_torrentGuard;
BitTorrent::AddTorrentParams m_torrentParams;
};
#endif // ADDNEWTORRENTDIALOG_H

10
src/gui/addnewtorrentdialog.ui

@ -192,16 +192,6 @@ @@ -192,16 +192,6 @@
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="startTorrentCheckBox_2">
<property name="text">
<string>Start torrent</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="createSubfolderCheckBox">
<property name="text">

2
src/gui/rss/rsswidget.cpp

@ -343,7 +343,7 @@ void RSSWidget::downloadSelectedTorrents() @@ -343,7 +343,7 @@ void RSSWidget::downloadSelectedTorrents()
if (!article->torrentUrl().isEmpty()) {
if (AddNewTorrentDialog::isEnabled())
AddNewTorrentDialog::show(article->torrentUrl());
AddNewTorrentDialog::show(article->torrentUrl(), window());
else
BitTorrent::Session::instance()->addTorrent(article->torrentUrl());
}

Loading…
Cancel
Save