You've already forked qBittorrent
mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-10-09 18:32:15 +02:00
Compare commits
21 Commits
release-4.
...
release-4.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2bbfd317ce | ||
![]() |
f6b58f36e2 | ||
![]() |
79ca2e145f | ||
![]() |
81bc910d68 | ||
![]() |
ff5d02bcf2 | ||
![]() |
2e87e6e0df | ||
![]() |
a5e8af5070 | ||
![]() |
cf415dd7fe | ||
![]() |
83e6afcb71 | ||
![]() |
62d96c068a | ||
![]() |
040c3c7ef8 | ||
![]() |
3ef8726083 | ||
![]() |
dad9157d84 | ||
![]() |
5cea69472f | ||
![]() |
b1492bcd7d | ||
![]() |
d571ab2be1 | ||
![]() |
4550469bb9 | ||
![]() |
160af4feef | ||
![]() |
b27e839405 | ||
![]() |
ecc08dee09 | ||
![]() |
11ac4e7620 |
@@ -37,8 +37,6 @@ install:
|
||||
RMDIR /S /Q "%CACHE_DIR%" & MKDIR "%CACHE_DIR%" &&
|
||||
appveyor DownloadFile "%QBT_LIB_URL%" -FileName "c:\qbt_lib.7z" && 7z x "c:\qbt_lib.7z" -o"%CACHE_DIR%" > nul &&
|
||||
COPY "c:\version_new" "%CACHE_DIR%\version")
|
||||
# Qt stay compressed in cache
|
||||
- 7z x "%CACHE_DIR%\qt5_64.7z" -o"c:\qbt" > nul
|
||||
|
||||
before_build:
|
||||
# setup env
|
||||
@@ -47,6 +45,7 @@ before_build:
|
||||
# setup project
|
||||
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
|
||||
# workarounds
|
||||
- MKDIR "c:\qbt"
|
||||
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
||||
|
||||
build_script:
|
||||
@@ -69,8 +68,11 @@ after_build:
|
||||
- COPY src\release\qbittorrent.exe upload
|
||||
- COPY src\release\qbittorrent.pdb upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libcrypto-1_1-x64.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libcrypto-1_1-x64.pdb" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libssl-1_1-x64.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\lib\torrent-rasterbar.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libssl-1_1-x64.pdb" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\torrent-rasterbar.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\torrent-rasterbar.pdb" upload
|
||||
- COPY "%CACHE_DIR%\base\lib\zlib1.dll" upload
|
||||
- COPY C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll upload
|
||||
- COPY C:\Qt\5.15.2\msvc2019_64\bin\Qt5Gui.dll upload
|
||||
|
13
.github/workflows/ci_macos.yaml
vendored
13
.github/workflows/ci_macos.yaml
vendored
@@ -74,6 +74,7 @@ jobs:
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
@@ -124,7 +125,17 @@ jobs:
|
||||
|
||||
- name: Prepare build artifacts
|
||||
run: |
|
||||
# create .dmg
|
||||
appName="qbittorrent"
|
||||
if [ "${{ matrix.qbt_gui }}" = "GUI=OFF" ]; then
|
||||
appName="qbittorrent-nox"
|
||||
fi
|
||||
pushd build
|
||||
macdeployqt "$appName.app" -dmg -no-strip
|
||||
popd
|
||||
# prepare upload folder
|
||||
mkdir upload
|
||||
cp "build/$appName.dmg" upload
|
||||
mkdir upload/cmake
|
||||
cp build/compile_commands.json upload/cmake
|
||||
mkdir upload/cmake/libtorrent
|
||||
@@ -133,5 +144,5 @@ jobs:
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-info_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
path: upload
|
||||
|
20
configure
vendored
20
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for qbittorrent v4.6.0alpha1.
|
||||
# Generated by GNU Autoconf 2.71 for qbittorrent v4.6.0beta1.
|
||||
#
|
||||
# Report bugs to <bugs.qbittorrent.org>.
|
||||
#
|
||||
@@ -611,8 +611,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='qbittorrent'
|
||||
PACKAGE_TARNAME='qbittorrent'
|
||||
PACKAGE_VERSION='v4.6.0alpha1'
|
||||
PACKAGE_STRING='qbittorrent v4.6.0alpha1'
|
||||
PACKAGE_VERSION='v4.6.0beta1'
|
||||
PACKAGE_STRING='qbittorrent v4.6.0beta1'
|
||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||
|
||||
@@ -1329,7 +1329,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures qbittorrent v4.6.0alpha1 to adapt to many kinds of systems.
|
||||
\`configure' configures qbittorrent v4.6.0beta1 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1400,7 +1400,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.6.0alpha1:";;
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.6.0beta1:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1533,7 +1533,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
qbittorrent configure v4.6.0alpha1
|
||||
qbittorrent configure v4.6.0beta1
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@@ -1648,7 +1648,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by qbittorrent $as_me v4.6.0alpha1, which was
|
||||
It was created by qbittorrent $as_me v4.6.0beta1, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -4779,7 +4779,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='qbittorrent'
|
||||
VERSION='v4.6.0alpha1'
|
||||
VERSION='v4.6.0beta1'
|
||||
|
||||
|
||||
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||
@@ -7237,7 +7237,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by qbittorrent $as_me v4.6.0alpha1, which was
|
||||
This file was extended by qbittorrent $as_me v4.6.0beta1, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -7297,7 +7297,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v4.6.0alpha1
|
||||
qbittorrent config.status v4.6.0beta1
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
AC_INIT([qbittorrent], [v4.6.0alpha1], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_INIT([qbittorrent], [v4.6.0beta1], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
: ${CFLAGS=""}
|
||||
|
@@ -98,8 +98,8 @@ Name[is]=qBittorrent
|
||||
Comment[it]=Scarica e condividi file tramite BitTorrent
|
||||
GenericName[it]=Client BitTorrent
|
||||
Name[it]=qBittorrent
|
||||
Comment[ja]=BitTorrent でファイルをダウンロードおよび共有
|
||||
GenericName[ja]=BitTorrent クライアント
|
||||
Comment[ja]=BitTorrentでファイルのダウンロードと共有
|
||||
GenericName[ja]=BitTorrentクライアント
|
||||
Name[ja]=qBittorrent
|
||||
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
||||
GenericName[ka]=BitTorrent კლიენტი
|
||||
@@ -178,7 +178,7 @@ Comment[zh_HK]=經由BitTorrent下載並分享檔案
|
||||
GenericName[zh_HK]=BitTorrent用戶端
|
||||
Name[zh_HK]=qBittorrent
|
||||
Comment[zh_TW]=經由 BitTorrent 下載並分享檔案
|
||||
GenericName[zh_TW]=BitTorrent 客戶端
|
||||
GenericName[zh_TW]=BitTorrent 用戶端
|
||||
Name[zh_TW]=qBittorrent
|
||||
Comment[eo]=Elŝutu kaj kunhavigu dosierojn per BitTorrent
|
||||
GenericName[eo]=BitTorrent-kliento
|
||||
@@ -208,7 +208,7 @@ Name[ltg]=qBittorrent
|
||||
Comment[hi_IN]=BitTorrent द्वारा फाइल डाउनलोड व सहभाजन
|
||||
GenericName[hi_IN]=Bittorrent साधन
|
||||
Name[hi_IN]=qBittorrent
|
||||
Comment[az@latin]=Faylları BitTorrent vasitəsilə göndərin və paylaşın
|
||||
Comment[az@latin]=Faylları BitTorrent vasitəsilə endirin və paylaşın
|
||||
GenericName[az@latin]=BitTorrent client
|
||||
Name[az@latin]=qBittorrent
|
||||
Comment[lv_LV]=Lejupielādēt un koplietot failus ar BitTorrent
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QProcessEnvironment>
|
||||
@@ -152,7 +153,7 @@ namespace
|
||||
QStringList parts = arg.split(u'=');
|
||||
if (parts.size() == 2)
|
||||
return Utils::String::unquote(parts[1], u"'\""_qs);
|
||||
throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'",
|
||||
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "Parameter '%1' must follow syntax '%1=%2'",
|
||||
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'")
|
||||
.arg(fullParameter(), u"<value>"_qs));
|
||||
}
|
||||
@@ -203,7 +204,7 @@ namespace
|
||||
const int res = val.toInt(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'",
|
||||
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "Parameter '%1' must follow syntax '%1=%2'",
|
||||
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=<value>'")
|
||||
.arg(fullParameter(), u"<integer value>"_qs));
|
||||
}
|
||||
@@ -219,7 +220,7 @@ namespace
|
||||
int res = val.toInt(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
qDebug() << QObject::tr("Expected integer number in environment variable '%1', but got '%2'")
|
||||
qDebug() << QCoreApplication::translate("CMD Options", "Expected integer number in environment variable '%1', but got '%2'")
|
||||
.arg(envVarName(), val);
|
||||
return defaultValue;
|
||||
}
|
||||
@@ -275,7 +276,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'",
|
||||
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "Parameter '%1' must follow syntax '%1=%2'",
|
||||
"e.g. Parameter '--add-paused' must follow syntax "
|
||||
"'--add-paused=<true|false>'")
|
||||
.arg(fullParameter(), u"<true|false>"_qs));
|
||||
@@ -302,7 +303,7 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << QObject::tr("Expected %1 in environment variable '%2', but got '%3'")
|
||||
qDebug() << QCoreApplication::translate("CMD Options", "Expected %1 in environment variable '%2', but got '%3'")
|
||||
.arg(u"true|false"_qs, envVarName(), val);
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -388,7 +389,7 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
||||
{
|
||||
result.webUiPort = WEBUI_PORT_OPTION.value(arg);
|
||||
if ((result.webUiPort < 1) || (result.webUiPort > 65535))
|
||||
throw CommandLineParameterError(QObject::tr("%1 must specify a valid port (1 to 65535).")
|
||||
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "%1 must specify a valid port (1 to 65535).")
|
||||
.arg(u"--webui-port"_qs));
|
||||
}
|
||||
else if (arg == TORRENTING_PORT_OPTION)
|
||||
@@ -396,7 +397,7 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
||||
result.torrentingPort = TORRENTING_PORT_OPTION.value(arg);
|
||||
if ((result.torrentingPort < 1) || (result.torrentingPort > 65535))
|
||||
{
|
||||
throw CommandLineParameterError(QObject::tr("%1 must specify a valid port (1 to 65535).")
|
||||
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "%1 must specify a valid port (1 to 65535).")
|
||||
.arg(u"--torrenting-port"_qs));
|
||||
}
|
||||
}
|
||||
@@ -499,58 +500,58 @@ QString makeUsage(const QString &prgName)
|
||||
{
|
||||
const QString indentation {USAGE_INDENTATION, u' '};
|
||||
|
||||
const QString text = QObject::tr("Usage:") + u'\n'
|
||||
+ indentation + prgName + u' ' + QObject::tr("[options] [(<filename> | <url>)...]") + u'\n'
|
||||
const QString text = QCoreApplication::translate("CMD Options", "Usage:") + u'\n'
|
||||
+ indentation + prgName + u' ' + QCoreApplication::translate("CMD Options", "[options] [(<filename> | <url>)...]") + u'\n'
|
||||
|
||||
+ QObject::tr("Options:") + u'\n'
|
||||
+ QCoreApplication::translate("CMD Options", "Options:") + u'\n'
|
||||
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
||||
+ SHOW_VERSION_OPTION.usage() + wrapText(QObject::tr("Display program version and exit")) + u'\n'
|
||||
+ SHOW_VERSION_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Display program version and exit")) + u'\n'
|
||||
#endif
|
||||
+ SHOW_HELP_OPTION.usage() + wrapText(QObject::tr("Display this help message and exit")) + u'\n'
|
||||
+ WEBUI_PORT_OPTION.usage(QObject::tr("port"))
|
||||
+ wrapText(QObject::tr("Change the Web UI port"))
|
||||
+ SHOW_HELP_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Display this help message and exit")) + u'\n'
|
||||
+ WEBUI_PORT_OPTION.usage(QCoreApplication::translate("CMD Options", "port"))
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Change the Web UI port"))
|
||||
+ u'\n'
|
||||
+ TORRENTING_PORT_OPTION.usage(QObject::tr("port"))
|
||||
+ wrapText(QObject::tr("Change the torrenting port"))
|
||||
+ TORRENTING_PORT_OPTION.usage(QCoreApplication::translate("CMD Options", "port"))
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Change the torrenting port"))
|
||||
+ u'\n'
|
||||
#ifndef DISABLE_GUI
|
||||
+ NO_SPLASH_OPTION.usage() + wrapText(QObject::tr("Disable splash screen")) + u'\n'
|
||||
+ NO_SPLASH_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Disable splash screen")) + u'\n'
|
||||
#elif !defined(Q_OS_WIN)
|
||||
+ DAEMON_OPTION.usage() + wrapText(QObject::tr("Run in daemon-mode (background)")) + u'\n'
|
||||
+ DAEMON_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Run in daemon-mode (background)")) + u'\n'
|
||||
#endif
|
||||
//: Use appropriate short form or abbreviation of "directory"
|
||||
+ PROFILE_OPTION.usage(QObject::tr("dir"))
|
||||
+ wrapText(QObject::tr("Store configuration files in <dir>")) + u'\n'
|
||||
+ CONFIGURATION_OPTION.usage(QObject::tr("name"))
|
||||
+ wrapText(QObject::tr("Store configuration files in directories qBittorrent_<name>")) + u'\n'
|
||||
+ PROFILE_OPTION.usage(QCoreApplication::translate("CMD Options", "dir"))
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Store configuration files in <dir>")) + u'\n'
|
||||
+ CONFIGURATION_OPTION.usage(QCoreApplication::translate("CMD Options", "name"))
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Store configuration files in directories qBittorrent_<name>")) + u'\n'
|
||||
+ RELATIVE_FASTRESUME.usage()
|
||||
+ wrapText(QObject::tr("Hack into libtorrent fastresume files and make file paths relative "
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Hack into libtorrent fastresume files and make file paths relative "
|
||||
"to the profile directory")) + u'\n'
|
||||
+ Option::padUsageText(QObject::tr("files or URLs"))
|
||||
+ wrapText(QObject::tr("Download the torrents passed by the user")) + u'\n'
|
||||
+ Option::padUsageText(QCoreApplication::translate("CMD Options", "files or URLs"))
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Download the torrents passed by the user")) + u'\n'
|
||||
+ u'\n'
|
||||
|
||||
+ wrapText(QObject::tr("Options when adding new torrents:"), 0) + u'\n'
|
||||
+ SAVE_PATH_OPTION.usage(QObject::tr("path")) + wrapText(QObject::tr("Torrent save path")) + u'\n'
|
||||
+ PAUSED_OPTION.usage() + wrapText(QObject::tr("Add torrents as started or paused")) + u'\n'
|
||||
+ SKIP_HASH_CHECK_OPTION.usage() + wrapText(QObject::tr("Skip hash check")) + u'\n'
|
||||
+ CATEGORY_OPTION.usage(QObject::tr("name"))
|
||||
+ wrapText(QObject::tr("Assign torrents to category. If the category doesn't exist, it will be "
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Options when adding new torrents:"), 0) + u'\n'
|
||||
+ SAVE_PATH_OPTION.usage(QCoreApplication::translate("CMD Options", "path")) + wrapText(QCoreApplication::translate("CMD Options", "Torrent save path")) + u'\n'
|
||||
+ PAUSED_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Add torrents as started or paused")) + u'\n'
|
||||
+ SKIP_HASH_CHECK_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Skip hash check")) + u'\n'
|
||||
+ CATEGORY_OPTION.usage(QCoreApplication::translate("CMD Options", "name"))
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Assign torrents to category. If the category doesn't exist, it will be "
|
||||
"created.")) + u'\n'
|
||||
+ SEQUENTIAL_OPTION.usage() + wrapText(QObject::tr("Download files in sequential order")) + u'\n'
|
||||
+ SEQUENTIAL_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Download files in sequential order")) + u'\n'
|
||||
+ FIRST_AND_LAST_OPTION.usage()
|
||||
+ wrapText(QObject::tr("Download first and last pieces first")) + u'\n'
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Download first and last pieces first")) + u'\n'
|
||||
+ SKIP_DIALOG_OPTION.usage()
|
||||
+ wrapText(QObject::tr("Specify whether the \"Add New Torrent\" dialog opens when adding a "
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Specify whether the \"Add New Torrent\" dialog opens when adding a "
|
||||
"torrent.")) + u'\n'
|
||||
+ u'\n'
|
||||
|
||||
+ wrapText(QObject::tr("Option values may be supplied via environment variables. For option named "
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "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) + u'\n'
|
||||
+ u"QBT_NO_SPLASH=1 " + prgName + u'\n'
|
||||
+ wrapText(QObject::tr("Command line parameters take precedence over environment variables"), 0) + u'\n';
|
||||
+ wrapText(QCoreApplication::translate("CMD Options", "Command line parameters take precedence over environment variables"), 0) + u'\n';
|
||||
|
||||
return text;
|
||||
}
|
||||
@@ -558,7 +559,7 @@ QString makeUsage(const QString &prgName)
|
||||
void displayUsage(const QString &prgName)
|
||||
{
|
||||
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
||||
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prgName), QMessageBox::Ok);
|
||||
QMessageBox msgBox(QMessageBox::Information, QCoreApplication::translate("CMD Options", "Help"), makeUsage(prgName), QMessageBox::Ok);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||
msgBox.exec();
|
||||
|
@@ -45,6 +45,7 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
@@ -132,7 +133,7 @@ int main(int argc, char *argv[])
|
||||
const QBtCommandLineParameters params = app->commandLineArgs();
|
||||
if (!params.unknownParameter.isEmpty())
|
||||
{
|
||||
throw CommandLineParameterError(QObject::tr("%1 is an unknown command line parameter.",
|
||||
throw CommandLineParameterError(QCoreApplication::translate("Main", "%1 is an unknown command line parameter.",
|
||||
"--random-parameter is an unknown command line parameter.")
|
||||
.arg(params.unknownParameter));
|
||||
}
|
||||
@@ -144,7 +145,7 @@ int main(int argc, char *argv[])
|
||||
displayVersion();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
|
||||
throw CommandLineParameterError(QCoreApplication::translate("Main", "%1 must be the single command line parameter.")
|
||||
.arg(u"-v (or --version)"_qs));
|
||||
}
|
||||
#endif
|
||||
@@ -155,7 +156,7 @@ int main(int argc, char *argv[])
|
||||
displayUsage(QString::fromLocal8Bit(argv[0]));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
|
||||
throw CommandLineParameterError(QCoreApplication::translate("Main", "%1 must be the single command line parameter.")
|
||||
.arg(u"-h (or --help)"_qs));
|
||||
}
|
||||
|
||||
@@ -187,7 +188,7 @@ int main(int argc, char *argv[])
|
||||
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
||||
if (params.shouldDaemonize)
|
||||
{
|
||||
throw CommandLineParameterError(QObject::tr("You cannot use %1: qBittorrent is already running for this user.")
|
||||
throw CommandLineParameterError(QCoreApplication::translate("Main", "You cannot use %1: qBittorrent is already running for this user.")
|
||||
.arg(u"-d (or --daemon)"_qs));
|
||||
}
|
||||
#endif
|
||||
@@ -295,15 +296,15 @@ void displayVersion()
|
||||
|
||||
void displayBadArgMessage(const QString &message)
|
||||
{
|
||||
const QString help = QObject::tr("Run application with -h option to read about command line parameters.");
|
||||
const QString help = QCoreApplication::translate("Main", "Run application with -h option to read about command line parameters.");
|
||||
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
||||
QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"),
|
||||
QMessageBox msgBox(QMessageBox::Critical, QCoreApplication::translate("Main", "Bad command line"),
|
||||
(message + u'\n' + help), QMessageBox::Ok);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||
msgBox.exec();
|
||||
#else
|
||||
const QString errMsg = QObject::tr("Bad command line: ") + u'\n'
|
||||
const QString errMsg = QCoreApplication::translate("Main", "Bad command line: ") + u'\n'
|
||||
+ message + u'\n'
|
||||
+ help + u'\n';
|
||||
fprintf(stderr, "%s", qUtf8Printable(errMsg));
|
||||
@@ -316,10 +317,10 @@ bool userAgreesWithLegalNotice()
|
||||
Q_ASSERT(!pref->getAcceptedLegal());
|
||||
|
||||
#ifdef DISABLE_GUI
|
||||
const QString eula = u"\n*** %1 ***\n"_qs.arg(QObject::tr("Legal Notice"))
|
||||
+ QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.") + u"\n\n"
|
||||
+ QObject::tr("No further notices will be issued.") + u"\n\n"
|
||||
+ QObject::tr("Press %1 key to accept and continue...").arg(u"'y'"_qs) + u'\n';
|
||||
const QString eula = u"\n*** %1 ***\n"_qs.arg(QCoreApplication::translate("Main", "Legal Notice"))
|
||||
+ QCoreApplication::translate("Main", "qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.") + u"\n\n"
|
||||
+ QCoreApplication::translate("Main", "No further notices will be issued.") + u"\n\n"
|
||||
+ QCoreApplication::translate("Main", "Press %1 key to accept and continue...").arg(u"'y'"_qs) + u'\n';
|
||||
printf("%s", qUtf8Printable(eula));
|
||||
|
||||
const char ret = getchar(); // Read pressed key
|
||||
@@ -331,10 +332,10 @@ bool userAgreesWithLegalNotice()
|
||||
}
|
||||
#else
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
|
||||
msgBox.setWindowTitle(QObject::tr("Legal notice"));
|
||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
||||
const QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
||||
msgBox.setText(QCoreApplication::translate("Main", "qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
|
||||
msgBox.setWindowTitle(QCoreApplication::translate("Main", "Legal notice"));
|
||||
msgBox.addButton(QCoreApplication::translate("Main", "Cancel"), QMessageBox::RejectRole);
|
||||
const QAbstractButton *agreeButton = msgBox.addButton(QCoreApplication::translate("Main", "I Agree"), QMessageBox::AcceptRole);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||
msgBox.exec();
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "upgrade.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "base/bittorrent/torrentcontentlayout.h"
|
||||
@@ -54,7 +55,7 @@ namespace
|
||||
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
||||
const auto oldData {settingsStorage->loadValue<QByteArray>(oldKey)};
|
||||
const auto newData {settingsStorage->loadValue<QString>(newKey)};
|
||||
const QString errorMsgFormat {QObject::tr("Migrate preferences failed: WebUI https, file: \"%1\", error: \"%2\"")};
|
||||
const QString errorMsgFormat {QCoreApplication::translate("Upgrade", "Migrate preferences failed: WebUI https, file: \"%1\", error: \"%2\"")};
|
||||
|
||||
if (!newData.isEmpty() || oldData.isEmpty())
|
||||
return;
|
||||
@@ -69,7 +70,7 @@ namespace
|
||||
settingsStorage->storeValue(newKey, savePath);
|
||||
settingsStorage->removeValue(oldKey);
|
||||
|
||||
LogMsg(QObject::tr("Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath.toString())
|
||||
LogMsg(QCoreApplication::translate("Upgrade", "Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath.toString())
|
||||
, Log::INFO);
|
||||
};
|
||||
|
||||
@@ -161,7 +162,7 @@ namespace
|
||||
settingsStorage->storeValue(key, Scheduler::Days::Sunday);
|
||||
break;
|
||||
default:
|
||||
LogMsg(QObject::tr("Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
LogMsg(QCoreApplication::translate("Upgrade", "Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
.arg(key, QString::number(number)), Log::WARNING);
|
||||
settingsStorage->removeValue(key);
|
||||
break;
|
||||
@@ -192,7 +193,7 @@ namespace
|
||||
settingsStorage->storeValue(key, DNS::Service::NoIP);
|
||||
break;
|
||||
default:
|
||||
LogMsg(QObject::tr("Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
LogMsg(QCoreApplication::translate("Upgrade", "Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
.arg(key, QString::number(number)), Log::WARNING);
|
||||
settingsStorage->removeValue(key);
|
||||
break;
|
||||
@@ -223,7 +224,7 @@ namespace
|
||||
settingsStorage->storeValue(key, TrayIcon::Style::MonoLight);
|
||||
break;
|
||||
default:
|
||||
LogMsg(QObject::tr("Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
LogMsg(QCoreApplication::translate("Upgrade", "Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
.arg(key, QString::number(number)), Log::WARNING);
|
||||
settingsStorage->removeValue(key);
|
||||
break;
|
||||
@@ -361,7 +362,7 @@ namespace
|
||||
settingsStorage->storeValue(key, Net::ProxyType::SOCKS4);
|
||||
break;
|
||||
default:
|
||||
LogMsg(QObject::tr("Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
LogMsg(QCoreApplication::translate("Upgrade", "Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
|
||||
.arg(key, QString::number(number)), Log::WARNING);
|
||||
settingsStorage->removeValue(key);
|
||||
break;
|
||||
|
45
src/base/3rdparty/expected.hpp
vendored
45
src/base/3rdparty/expected.hpp
vendored
@@ -14,7 +14,7 @@
|
||||
|
||||
#define expected_lite_MAJOR 0
|
||||
#define expected_lite_MINOR 6
|
||||
#define expected_lite_PATCH 2
|
||||
#define expected_lite_PATCH 3
|
||||
|
||||
#define expected_lite_VERSION expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH)
|
||||
|
||||
@@ -405,7 +405,7 @@ struct is_nothrow_swappable
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// is [nothow] swappable:
|
||||
// is [nothrow] swappable:
|
||||
|
||||
template< typename T >
|
||||
struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
|
||||
@@ -1002,11 +1002,12 @@ public:
|
||||
|
||||
// x.x.5.2.4 Swap
|
||||
|
||||
template< typename U=E >
|
||||
nsel_REQUIRES_R( void,
|
||||
std17::is_swappable<E>::value
|
||||
std17::is_swappable<U>::value
|
||||
)
|
||||
swap( unexpected_type & other ) noexcept (
|
||||
std17::is_nothrow_swappable<E>::value
|
||||
std17::is_nothrow_swappable<U>::value
|
||||
)
|
||||
{
|
||||
using std::swap;
|
||||
@@ -2164,10 +2165,24 @@ private:
|
||||
|
||||
// x.x.4.6 expected<>: comparison operators
|
||||
|
||||
template< typename T1, typename E1, typename T2, typename E2 >
|
||||
template< typename T1, typename E1, typename T2, typename E2
|
||||
nsel_REQUIRES_T(
|
||||
!std::is_void<T1>::value && !std::is_void<T2>::value
|
||||
)
|
||||
>
|
||||
constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
|
||||
{
|
||||
return bool(x) != bool(y) ? false : bool(x) == false ? x.error() == y.error() : *x == *y;
|
||||
return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error();
|
||||
}
|
||||
|
||||
template< typename T1, typename E1, typename T2, typename E2
|
||||
nsel_REQUIRES_T(
|
||||
std::is_void<T1>::value && std::is_void<T2>::value
|
||||
)
|
||||
>
|
||||
constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
|
||||
{
|
||||
return bool(x) != bool(y) ? false : bool(x) || static_cast<bool>( x.error() == y.error() );
|
||||
}
|
||||
|
||||
template< typename T1, typename E1, typename T2, typename E2 >
|
||||
@@ -2176,12 +2191,6 @@ constexpr bool operator!=( expected<T1,E1> const & x, expected<T2,E2> const & y
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template< typename E1, typename E2 >
|
||||
constexpr bool operator==( expected<void,E1> const & x, expected<void,E1> const & y )
|
||||
{
|
||||
return bool(x) != bool(y) ? false : bool(x) == false ? x.error() == y.error() : true;
|
||||
}
|
||||
|
||||
#if nsel_P0323R <= 2
|
||||
|
||||
template< typename T, typename E >
|
||||
@@ -2212,13 +2221,21 @@ constexpr bool operator>=( expected<T,E> const & x, expected<T,E> const & y )
|
||||
|
||||
// x.x.4.7 expected: comparison with T
|
||||
|
||||
template< typename T1, typename E1, typename T2 >
|
||||
template< typename T1, typename E1, typename T2
|
||||
nsel_REQUIRES_T(
|
||||
!std::is_void<T1>::value
|
||||
)
|
||||
>
|
||||
constexpr bool operator==( expected<T1,E1> const & x, T2 const & v )
|
||||
{
|
||||
return bool(x) ? *x == v : false;
|
||||
}
|
||||
|
||||
template< typename T1, typename E1, typename T2 >
|
||||
template< typename T1, typename E1, typename T2
|
||||
nsel_REQUIRES_T(
|
||||
!std::is_void<T1>::value
|
||||
)
|
||||
>
|
||||
constexpr bool operator==(T2 const & v, expected<T1,E1> const & x )
|
||||
{
|
||||
return bool(x) ? v == *x : false;
|
||||
|
@@ -36,6 +36,7 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QThread>
|
||||
|
||||
@@ -133,17 +134,19 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::load(cons
|
||||
const Path fastresumePath = path() / Path(idString + u".fastresume");
|
||||
const Path torrentFilePath = path() / Path(idString + u".torrent");
|
||||
|
||||
QFile resumeDataFile {fastresumePath.data()};
|
||||
if (!resumeDataFile.open(QIODevice::ReadOnly))
|
||||
return nonstd::make_unexpected(tr("Cannot read file %1: %2").arg(fastresumePath.toString(), resumeDataFile.errorString()));
|
||||
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, MAX_TORRENT_SIZE);
|
||||
if (!resumeDataReadResult)
|
||||
return nonstd::make_unexpected(resumeDataReadResult.error().message);
|
||||
|
||||
QFile metadataFile {torrentFilePath.data()};
|
||||
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
|
||||
return nonstd::make_unexpected(tr("Cannot read file %1: %2").arg(torrentFilePath.toString(), metadataFile.errorString()));
|
||||
|
||||
const QByteArray data = resumeDataFile.readAll();
|
||||
const QByteArray metadata = (metadataFile.isOpen() ? metadataFile.readAll() : "");
|
||||
const auto metadataReadResult = Utils::IO::readFile(torrentFilePath, MAX_TORRENT_SIZE);
|
||||
if (!metadataReadResult)
|
||||
{
|
||||
if (metadataReadResult.error().status != Utils::IO::ReadError::NotExist)
|
||||
return nonstd::make_unexpected(metadataReadResult.error().message);
|
||||
}
|
||||
|
||||
const QByteArray data = resumeDataReadResult.value();
|
||||
const QByteArray metadata = metadataReadResult.value_or(QByteArray());
|
||||
return loadTorrentResumeData(data, metadata);
|
||||
}
|
||||
|
||||
@@ -161,6 +164,8 @@ void BitTorrent::BencodeResumeDataStorage::doLoadAll() const
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||
{
|
||||
const int lineMaxLength = 48;
|
||||
|
||||
QFile queueFile {queueFilename.data()};
|
||||
if (!queueFile.exists())
|
||||
return;
|
||||
@@ -175,7 +180,7 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||
int start = 0;
|
||||
while (true)
|
||||
{
|
||||
const auto line = QString::fromLatin1(queueFile.readLine().trimmed());
|
||||
const auto line = QString::fromLatin1(queueFile.readLine(lineMaxLength).trimmed());
|
||||
if (line.isEmpty())
|
||||
break;
|
||||
|
||||
|
@@ -41,7 +41,6 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QSet>
|
||||
#include <QSqlDatabase>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2023 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
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "base/bittorrent/ltqbitarray.h"
|
||||
#include "base/net/geoipmanager.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "base/utils/bytearray.h"
|
||||
#include "peeraddress.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
@@ -168,6 +169,9 @@ bool PeerInfo::isPlaintextEncrypted() const
|
||||
|
||||
PeerAddress PeerInfo::address() const
|
||||
{
|
||||
if (useI2PSocket())
|
||||
return {};
|
||||
|
||||
// fast path for platforms which boost.asio internal struct maps to `sockaddr`
|
||||
return {QHostAddress(m_nativeInfo.ip.data()), m_nativeInfo.ip.port()};
|
||||
// slow path for the others
|
||||
@@ -175,6 +179,23 @@ PeerAddress PeerInfo::address() const
|
||||
// , m_nativeInfo.ip.port()};
|
||||
}
|
||||
|
||||
QString PeerInfo::I2PAddress() const
|
||||
{
|
||||
if (!useI2PSocket())
|
||||
return {};
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
if (m_I2PAddress.isEmpty())
|
||||
{
|
||||
const lt::sha256_hash destHash = m_nativeInfo.i2p_destination();
|
||||
const QByteArray base32Dest = Utils::ByteArray::toBase32({destHash.data(), destHash.size()}).replace('=', "").toLower();
|
||||
m_I2PAddress = QString::fromLatin1(base32Dest) + u".b32.i2p";
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_I2PAddress;
|
||||
}
|
||||
|
||||
QString PeerInfo::client() const
|
||||
{
|
||||
return QString::fromStdString(m_nativeInfo.client);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2023 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
|
||||
@@ -76,6 +76,7 @@ namespace BitTorrent
|
||||
bool isPlaintextEncrypted() const;
|
||||
|
||||
PeerAddress address() const;
|
||||
QString I2PAddress() const;
|
||||
QString client() const;
|
||||
QString peerIdClient() const;
|
||||
qreal progress() const;
|
||||
@@ -101,5 +102,6 @@ namespace BitTorrent
|
||||
QString m_flagsDescription;
|
||||
|
||||
mutable QString m_country;
|
||||
mutable QString m_I2PAddress;
|
||||
};
|
||||
}
|
||||
|
@@ -271,6 +271,14 @@ namespace BitTorrent
|
||||
virtual void setI2PPort(int port) = 0;
|
||||
virtual bool I2PMixedMode() const = 0;
|
||||
virtual void setI2PMixedMode(bool enabled) = 0;
|
||||
virtual int I2PInboundQuantity() const = 0;
|
||||
virtual void setI2PInboundQuantity(int value) = 0;
|
||||
virtual int I2POutboundQuantity() const = 0;
|
||||
virtual void setI2POutboundQuantity(int value) = 0;
|
||||
virtual int I2PInboundLength() const = 0;
|
||||
virtual void setI2PInboundLength(int value) = 0;
|
||||
virtual int I2POutboundLength() const = 0;
|
||||
virtual void setI2POutboundLength(int value) = 0;
|
||||
virtual bool isProxyPeerConnectionsEnabled() const = 0;
|
||||
virtual void setProxyPeerConnectionsEnabled(bool enabled) = 0;
|
||||
virtual ChokingAlgorithm chokingAlgorithm() const = 0;
|
||||
|
@@ -60,7 +60,6 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -528,6 +527,10 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
, m_I2PAddress {BITTORRENT_SESSION_KEY(u"I2P/Address"_qs), u"127.0.0.1"_qs}
|
||||
, m_I2PPort {BITTORRENT_SESSION_KEY(u"I2P/Port"_qs), 7656}
|
||||
, m_I2PMixedMode {BITTORRENT_SESSION_KEY(u"I2P/MixedMode"_qs), false}
|
||||
, m_I2PInboundQuantity {BITTORRENT_SESSION_KEY(u"I2P/InboundQuantity"_qs), 3}
|
||||
, m_I2POutboundQuantity {BITTORRENT_SESSION_KEY(u"I2P/OutboundQuantity"_qs), 3}
|
||||
, m_I2PInboundLength {BITTORRENT_SESSION_KEY(u"I2P/InboundLength"_qs), 3}
|
||||
, m_I2POutboundLength {BITTORRENT_SESSION_KEY(u"I2P/OutboundLength"_qs), 3}
|
||||
, m_seedingLimitTimer {new QTimer(this)}
|
||||
, m_resumeDataTimer {new QTimer(this)}
|
||||
, m_ioThread {new QThread}
|
||||
@@ -1678,6 +1681,14 @@ lt::settings_pack SessionImpl::loadLTSettings() const
|
||||
settingsPack.set_bool(lt::settings_pack::allow_i2p_mixed, false);
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
// I2P session options
|
||||
settingsPack.set_int(lt::settings_pack::i2p_inbound_quantity, I2PInboundQuantity());
|
||||
settingsPack.set_int(lt::settings_pack::i2p_outbound_quantity, I2POutboundQuantity());
|
||||
settingsPack.set_int(lt::settings_pack::i2p_inbound_length, I2PInboundLength());
|
||||
settingsPack.set_int(lt::settings_pack::i2p_outbound_length, I2POutboundLength());
|
||||
#endif
|
||||
|
||||
// proxy
|
||||
settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::none);
|
||||
if (Preferences::instance()->useProxyForBT())
|
||||
@@ -3640,6 +3651,62 @@ void SessionImpl::setI2PMixedMode(const bool enabled)
|
||||
}
|
||||
}
|
||||
|
||||
int SessionImpl::I2PInboundQuantity() const
|
||||
{
|
||||
return m_I2PInboundQuantity;
|
||||
}
|
||||
|
||||
void SessionImpl::setI2PInboundQuantity(const int value)
|
||||
{
|
||||
if (value == m_I2PInboundQuantity)
|
||||
return;
|
||||
|
||||
m_I2PInboundQuantity = value;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
int SessionImpl::I2POutboundQuantity() const
|
||||
{
|
||||
return m_I2POutboundQuantity;
|
||||
}
|
||||
|
||||
void SessionImpl::setI2POutboundQuantity(const int value)
|
||||
{
|
||||
if (value == m_I2POutboundQuantity)
|
||||
return;
|
||||
|
||||
m_I2POutboundQuantity = value;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
int SessionImpl::I2PInboundLength() const
|
||||
{
|
||||
return m_I2PInboundLength;
|
||||
}
|
||||
|
||||
void SessionImpl::setI2PInboundLength(const int value)
|
||||
{
|
||||
if (value == m_I2PInboundLength)
|
||||
return;
|
||||
|
||||
m_I2PInboundLength = value;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
int SessionImpl::I2POutboundLength() const
|
||||
{
|
||||
return m_I2POutboundLength;
|
||||
}
|
||||
|
||||
void SessionImpl::setI2POutboundLength(const int value)
|
||||
{
|
||||
if (value == m_I2POutboundLength)
|
||||
return;
|
||||
|
||||
m_I2POutboundLength = value;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
bool SessionImpl::isProxyPeerConnectionsEnabled() const
|
||||
{
|
||||
return m_isProxyPeerConnectionsEnabled;
|
||||
@@ -5033,8 +5100,8 @@ void SessionImpl::loadCategories()
|
||||
{
|
||||
m_categories.clear();
|
||||
|
||||
QFile confFile {(specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME).data()};
|
||||
if (!confFile.exists())
|
||||
const Path path = specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME;
|
||||
if (!path.exists())
|
||||
{
|
||||
// TODO: Remove the following upgrade code in v4.5
|
||||
// == BEGIN UPGRADE CODE ==
|
||||
@@ -5045,26 +5112,27 @@ void SessionImpl::loadCategories()
|
||||
// return;
|
||||
}
|
||||
|
||||
if (!confFile.open(QFile::ReadOnly))
|
||||
const int fileMaxSize = 1024 * 1024;
|
||||
const auto readResult = Utils::IO::readFile(path, fileMaxSize);
|
||||
if (!readResult)
|
||||
{
|
||||
LogMsg(tr("Failed to load Categories. File: \"%1\". Error: \"%2\"")
|
||||
.arg(confFile.fileName(), confFile.errorString()), Log::CRITICAL);
|
||||
LogMsg(tr("Failed to load Categories. %1").arg(readResult.error().message), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(confFile.readAll(), &jsonError);
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(readResult.value(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("Failed to parse Categories configuration. File: \"%1\". Error: \"%2\"")
|
||||
.arg(confFile.fileName(), jsonError.errorString()), Log::WARNING);
|
||||
.arg(path.toString(), jsonError.errorString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDoc.isObject())
|
||||
{
|
||||
LogMsg(tr("Failed to load Categories configuration. File: \"%1\". Reason: invalid data format")
|
||||
.arg(confFile.fileName()), Log::WARNING);
|
||||
LogMsg(tr("Failed to load Categories configuration. File: \"%1\". Error: \"Invalid data format\"")
|
||||
.arg(path.toString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -250,6 +250,14 @@ namespace BitTorrent
|
||||
void setI2PPort(int port) override;
|
||||
bool I2PMixedMode() const override;
|
||||
void setI2PMixedMode(bool enabled) override;
|
||||
int I2PInboundQuantity() const override;
|
||||
void setI2PInboundQuantity(int value) override;
|
||||
int I2POutboundQuantity() const override;
|
||||
void setI2POutboundQuantity(int value) override;
|
||||
int I2PInboundLength() const override;
|
||||
void setI2PInboundLength(int value) override;
|
||||
int I2POutboundLength() const override;
|
||||
void setI2POutboundLength(int value) override;
|
||||
bool isProxyPeerConnectionsEnabled() const override;
|
||||
void setProxyPeerConnectionsEnabled(bool enabled) override;
|
||||
ChokingAlgorithm chokingAlgorithm() const override;
|
||||
@@ -699,6 +707,10 @@ namespace BitTorrent
|
||||
CachedSettingValue<QString> m_I2PAddress;
|
||||
CachedSettingValue<int> m_I2PPort;
|
||||
CachedSettingValue<bool> m_I2PMixedMode;
|
||||
CachedSettingValue<int> m_I2PInboundQuantity;
|
||||
CachedSettingValue<int> m_I2POutboundQuantity;
|
||||
CachedSettingValue<int> m_I2PInboundLength;
|
||||
CachedSettingValue<int> m_I2POutboundLength;
|
||||
|
||||
bool m_isRestored = false;
|
||||
|
||||
|
@@ -46,7 +46,6 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
|
@@ -103,28 +103,20 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)
|
||||
|
||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const Path &path) noexcept
|
||||
{
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return nonstd::make_unexpected(file.errorString());
|
||||
|
||||
if (file.size() > MAX_TORRENT_SIZE)
|
||||
return nonstd::make_unexpected(tr("File size exceeds max limit %1").arg(Utils::Misc::friendlyUnit(MAX_TORRENT_SIZE)));
|
||||
|
||||
QByteArray data;
|
||||
try
|
||||
{
|
||||
data = file.readAll();
|
||||
const auto readResult = Utils::IO::readFile(path, MAX_TORRENT_SIZE);
|
||||
if (!readResult)
|
||||
return nonstd::make_unexpected(readResult.error().message);
|
||||
data = readResult.value();
|
||||
}
|
||||
catch (const std::bad_alloc &e)
|
||||
{
|
||||
return nonstd::make_unexpected(tr("Torrent file read error: %1").arg(QString::fromLocal8Bit(e.what())));
|
||||
return nonstd::make_unexpected(tr("Failed to allocate memory when reading file. File: \"%1\". Error: \"%2\"")
|
||||
.arg(path.toString(), QString::fromLocal8Bit(e.what())));
|
||||
}
|
||||
|
||||
if (data.size() != file.size())
|
||||
return nonstd::make_unexpected(tr("Torrent file read error: size mismatch"));
|
||||
|
||||
file.close();
|
||||
|
||||
return load(data);
|
||||
}
|
||||
|
||||
|
@@ -47,6 +47,7 @@ namespace Http
|
||||
inline const QString HEADER_CONTENT_LENGTH = u"content-length"_qs;
|
||||
inline const QString HEADER_CONTENT_SECURITY_POLICY = u"content-security-policy"_qs;
|
||||
inline const QString HEADER_CONTENT_TYPE = u"content-type"_qs;
|
||||
inline const QString HEADER_CROSS_ORIGIN_OPENER_POLICY = u"cross-origin-opener-policy"_qs;
|
||||
inline const QString HEADER_DATE = u"date"_qs;
|
||||
inline const QString HEADER_HOST = u"host"_qs;
|
||||
inline const QString HEADER_ORIGIN = u"origin"_qs;
|
||||
|
@@ -1224,6 +1224,16 @@ void Preferences::setConfirmRemoveAllTags(const bool enabled)
|
||||
setValue(u"Preferences/Advanced/confirmRemoveAllTags"_qs, enabled);
|
||||
}
|
||||
|
||||
bool Preferences::confirmPauseAndResumeAll() const
|
||||
{
|
||||
return value(u"GUI/ConfirmActions/PauseAndResumeAllTorrents"_qs, true);
|
||||
}
|
||||
|
||||
void Preferences::setConfirmPauseAndResumeAll(const bool enabled)
|
||||
{
|
||||
setValue(u"GUI/ConfirmActions/PauseAndResumeAllTorrents"_qs, enabled);
|
||||
}
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
TrayIcon::Style Preferences::trayIconStyle() const
|
||||
{
|
||||
|
@@ -315,6 +315,8 @@ public:
|
||||
void setConfirmTorrentRecheck(bool enabled);
|
||||
bool confirmRemoveAllTags() const;
|
||||
void setConfirmRemoveAllTags(bool enabled);
|
||||
bool confirmPauseAndResumeAll() const;
|
||||
void setConfirmPauseAndResumeAll(bool enabled);
|
||||
#ifndef Q_OS_MACOS
|
||||
bool systemTrayEnabled() const;
|
||||
void setSystemTrayEnabled(bool enabled);
|
||||
|
@@ -31,7 +31,6 @@
|
||||
|
||||
#include "feed_serializer.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
@@ -46,23 +45,21 @@ const int ARTICLEDATALIST_TYPEID = qRegisterMetaType<QVector<QVariantHash>>();
|
||||
|
||||
void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
|
||||
{
|
||||
QFile file {dataFileName.data()};
|
||||
const int fileMaxSize = 10 * 1024 * 1024;
|
||||
const auto readResult = Utils::IO::readFile(dataFileName, fileMaxSize);
|
||||
if (!readResult)
|
||||
{
|
||||
if (readResult.error().status == Utils::IO::ReadError::NotExist)
|
||||
{
|
||||
emit loadingFinished({});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.exists())
|
||||
{
|
||||
emit loadingFinished({});
|
||||
}
|
||||
else if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
emit loadingFinished(loadArticles(file.readAll(), url));
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
|
||||
.arg(dataFileName.toString(), file.errorString())
|
||||
, Log::WARNING);
|
||||
LogMsg(tr("Failed to read RSS session data. %1").arg(readResult.error().message), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
emit loadingFinished(loadArticles(readResult.value(), url));
|
||||
}
|
||||
|
||||
void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QVector<QVariantHash> &articlesData)
|
||||
|
@@ -28,6 +28,8 @@
|
||||
|
||||
#include "rss_autodownloader.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
@@ -46,6 +48,7 @@
|
||||
#include "../logger.h"
|
||||
#include "../profile.h"
|
||||
#include "../utils/fs.h"
|
||||
#include "../utils/io.h"
|
||||
#include "rss_article.h"
|
||||
#include "rss_autodownloadrule.h"
|
||||
#include "rss_feed.h"
|
||||
@@ -166,25 +169,27 @@ AutoDownloader *AutoDownloader::instance()
|
||||
|
||||
bool AutoDownloader::hasRule(const QString &ruleName) const
|
||||
{
|
||||
return m_rules.contains(ruleName);
|
||||
return m_rulesByName.contains(ruleName);
|
||||
}
|
||||
|
||||
AutoDownloadRule AutoDownloader::ruleByName(const QString &ruleName) const
|
||||
{
|
||||
return m_rules.value(ruleName, AutoDownloadRule(u"Unknown Rule"_qs));
|
||||
const auto index = m_rulesByName.value(ruleName, -1);
|
||||
return m_rules.value(index, AutoDownloadRule(u"Unknown Rule"_qs));
|
||||
}
|
||||
|
||||
QList<AutoDownloadRule> AutoDownloader::rules() const
|
||||
{
|
||||
return m_rules.values();
|
||||
return m_rules;
|
||||
}
|
||||
|
||||
void AutoDownloader::insertRule(const AutoDownloadRule &rule)
|
||||
void AutoDownloader::setRule(const AutoDownloadRule &rule)
|
||||
{
|
||||
if (!hasRule(rule.name()))
|
||||
{
|
||||
// Insert new rule
|
||||
setRule_impl(rule);
|
||||
sortRules();
|
||||
m_dirty = true;
|
||||
store();
|
||||
emit ruleAdded(rule.name());
|
||||
@@ -194,6 +199,7 @@ void AutoDownloader::insertRule(const AutoDownloadRule &rule)
|
||||
{
|
||||
// Update existing rule
|
||||
setRule_impl(rule);
|
||||
sortRules();
|
||||
m_dirty = true;
|
||||
storeDeferred();
|
||||
emit ruleChanged(rule.name());
|
||||
@@ -203,12 +209,12 @@ void AutoDownloader::insertRule(const AutoDownloadRule &rule)
|
||||
|
||||
bool AutoDownloader::renameRule(const QString &ruleName, const QString &newRuleName)
|
||||
{
|
||||
if (!hasRule(ruleName)) return false;
|
||||
if (hasRule(newRuleName)) return false;
|
||||
if (!hasRule(ruleName) || hasRule(newRuleName))
|
||||
return false;
|
||||
|
||||
AutoDownloadRule rule = m_rules.take(ruleName);
|
||||
rule.setName(newRuleName);
|
||||
m_rules.insert(newRuleName, rule);
|
||||
const auto index = m_rulesByName.take(ruleName);
|
||||
m_rules[index].setName(newRuleName);
|
||||
m_rulesByName.insert(newRuleName, index);
|
||||
m_dirty = true;
|
||||
store();
|
||||
emit ruleRenamed(newRuleName, ruleName);
|
||||
@@ -217,13 +223,21 @@ bool AutoDownloader::renameRule(const QString &ruleName, const QString &newRuleN
|
||||
|
||||
void AutoDownloader::removeRule(const QString &ruleName)
|
||||
{
|
||||
if (m_rules.contains(ruleName))
|
||||
if (!hasRule(ruleName))
|
||||
return;
|
||||
|
||||
emit ruleAboutToBeRemoved(ruleName);
|
||||
|
||||
const auto index = m_rulesByName.take(ruleName);
|
||||
m_rules.removeAt(index);
|
||||
for (qsizetype i = index; i < m_rules.size(); ++i)
|
||||
{
|
||||
emit ruleAboutToBeRemoved(ruleName);
|
||||
m_rules.remove(ruleName);
|
||||
m_dirty = true;
|
||||
store();
|
||||
const AutoDownloadRule &rule = m_rules[i];
|
||||
m_rulesByName[rule.name()] = i;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
store();
|
||||
}
|
||||
|
||||
QByteArray AutoDownloader::exportRules(AutoDownloader::RulesFileFormat format) const
|
||||
@@ -261,7 +275,7 @@ QByteArray AutoDownloader::exportRulesToJSONFormat() const
|
||||
void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data)
|
||||
{
|
||||
for (const auto &rule : asConst(rulesFromJSON(data)))
|
||||
insertRule(rule);
|
||||
setRule(rule);
|
||||
}
|
||||
|
||||
QByteArray AutoDownloader::exportRulesToLegacyFormat() const
|
||||
@@ -288,7 +302,7 @@ void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
|
||||
throw ParsingError(tr("Invalid data format"));
|
||||
|
||||
for (const QVariant &val : asConst(dict))
|
||||
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
|
||||
setRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
|
||||
}
|
||||
|
||||
QStringList AutoDownloader::smartEpisodeFilters() const
|
||||
@@ -399,7 +413,31 @@ void AutoDownloader::handleFeedURLChanged(Feed *feed, const QString &oldURL)
|
||||
|
||||
void AutoDownloader::setRule_impl(const AutoDownloadRule &rule)
|
||||
{
|
||||
m_rules.insert(rule.name(), rule);
|
||||
const QString ruleName = rule.name();
|
||||
const auto index = m_rulesByName.value(ruleName, -1);
|
||||
if (index < 0)
|
||||
{
|
||||
m_rules.append(rule);
|
||||
m_rulesByName[ruleName] = m_rules.size() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rules[index] = rule;
|
||||
}
|
||||
}
|
||||
|
||||
void AutoDownloader::sortRules()
|
||||
{
|
||||
std::sort(m_rules.begin(), m_rules.end(), [](const AutoDownloadRule &lhs, const AutoDownloadRule &rhs)
|
||||
{
|
||||
return (lhs.priority() < rhs.priority());
|
||||
});
|
||||
|
||||
for (qsizetype i = 0; i < m_rules.size(); ++i)
|
||||
{
|
||||
const AutoDownloadRule &rule = m_rules[i];
|
||||
m_rulesByName[rule.name()] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void AutoDownloader::addJobForArticle(const Article *article)
|
||||
@@ -430,6 +468,9 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
|
||||
m_dirty = true;
|
||||
storeDeferred();
|
||||
|
||||
LogMsg(tr("RSS article '%1' is accepted by rule '%2'. Trying to add torrent...")
|
||||
.arg(job->articleData.value(Article::KeyTitle).toString(), rule.name()));
|
||||
|
||||
const auto torrentURL = job->articleData.value(Article::KeyTorrentURL).toString();
|
||||
BitTorrent::Session::instance()->addTorrent(torrentURL, rule.addTorrentParams());
|
||||
|
||||
@@ -453,21 +494,21 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
|
||||
|
||||
void AutoDownloader::load()
|
||||
{
|
||||
QFile rulesFile {(m_fileStorage->storageDir() / Path(RULES_FILE_NAME)).data()};
|
||||
const qint64 maxFileSize = 10 * 1024 * 1024;
|
||||
const auto readResult = Utils::IO::readFile((m_fileStorage->storageDir() / Path(RULES_FILE_NAME)), maxFileSize);
|
||||
if (!readResult)
|
||||
{
|
||||
if (readResult.error().status == Utils::IO::ReadError::NotExist)
|
||||
{
|
||||
loadRulesLegacy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rulesFile.exists())
|
||||
{
|
||||
loadRulesLegacy();
|
||||
}
|
||||
else if (rulesFile.open(QFile::ReadOnly))
|
||||
{
|
||||
loadRules(rulesFile.readAll());
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
|
||||
.arg(rulesFile.fileName(), rulesFile.errorString()), Log::CRITICAL);
|
||||
LogMsg((tr("Failed to read RSS AutoDownloader rules. %1").arg(readResult.error().message)), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
loadRules(readResult.value());
|
||||
}
|
||||
|
||||
void AutoDownloader::loadRules(const QByteArray &data)
|
||||
@@ -477,6 +518,7 @@ void AutoDownloader::loadRules(const QByteArray &data)
|
||||
const auto rules = rulesFromJSON(data);
|
||||
for (const auto &rule : rules)
|
||||
setRule_impl(rule);
|
||||
sortRules();
|
||||
}
|
||||
catch (const ParsingError &error)
|
||||
{
|
||||
@@ -493,7 +535,7 @@ void AutoDownloader::loadRulesLegacy()
|
||||
{
|
||||
const auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
|
||||
if (!rule.name().isEmpty())
|
||||
insertRule(rule);
|
||||
setRule(rule);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -94,7 +94,7 @@ namespace RSS
|
||||
AutoDownloadRule ruleByName(const QString &ruleName) const;
|
||||
QList<AutoDownloadRule> rules() const;
|
||||
|
||||
void insertRule(const AutoDownloadRule &rule);
|
||||
void setRule(const AutoDownloadRule &rule);
|
||||
bool renameRule(const QString &ruleName, const QString &newRuleName);
|
||||
void removeRule(const QString &ruleName);
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace RSS
|
||||
private:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
void setRule_impl(const AutoDownloadRule &rule);
|
||||
void sortRules();
|
||||
void resetProcessingQueue();
|
||||
void startProcessing();
|
||||
void addJobForArticle(const Article *article);
|
||||
@@ -141,7 +142,8 @@ namespace RSS
|
||||
QTimer *m_processingTimer = nullptr;
|
||||
Utils::Thread::UniquePtr m_ioThread;
|
||||
AsyncFileStorage *m_fileStorage = nullptr;
|
||||
QHash<QString, AutoDownloadRule> m_rules;
|
||||
QList<AutoDownloadRule> m_rules;
|
||||
QHash<QString, qsizetype> m_rulesByName;
|
||||
QList<QSharedPointer<ProcessingJob>> m_processingQueue;
|
||||
QHash<QString, QSharedPointer<ProcessingJob>> m_waitingJobs;
|
||||
bool m_dirty = false;
|
||||
|
@@ -103,6 +103,7 @@ namespace
|
||||
|
||||
const QString S_NAME = u"name"_qs;
|
||||
const QString S_ENABLED = u"enabled"_qs;
|
||||
const QString S_PRIORITY = u"priority"_qs;
|
||||
const QString S_USE_REGEX = u"useRegex"_qs;
|
||||
const QString S_MUST_CONTAIN = u"mustContain"_qs;
|
||||
const QString S_MUST_NOT_CONTAIN = u"mustNotContain"_qs;
|
||||
@@ -126,6 +127,7 @@ namespace RSS
|
||||
{
|
||||
QString name;
|
||||
bool enabled = true;
|
||||
int priority = 0;
|
||||
|
||||
QStringList mustContain;
|
||||
QStringList mustNotContain;
|
||||
@@ -147,6 +149,7 @@ namespace RSS
|
||||
{
|
||||
return (left.name == right.name)
|
||||
&& (left.enabled == right.enabled)
|
||||
&& (left.priority == right.priority)
|
||||
&& (left.mustContain == right.mustContain)
|
||||
&& (left.mustNotContain == right.mustNotContain)
|
||||
&& (left.episodeFilter == right.episodeFilter)
|
||||
@@ -457,6 +460,7 @@ QJsonObject AutoDownloadRule::toJsonObject() const
|
||||
const BitTorrent::AddTorrentParams &addTorrentParams = m_dataPtr->addTorrentParams;
|
||||
|
||||
return {{S_ENABLED, isEnabled()}
|
||||
, {S_PRIORITY, priority()}
|
||||
, {S_USE_REGEX, useRegex()}
|
||||
, {S_MUST_CONTAIN, mustContain()}
|
||||
, {S_MUST_NOT_CONTAIN, mustNotContain()}
|
||||
@@ -483,11 +487,13 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
|
||||
{
|
||||
AutoDownloadRule rule {(name.isEmpty() ? jsonObj.value(S_NAME).toString() : name)};
|
||||
|
||||
rule.setEnabled(jsonObj.value(S_ENABLED).toBool(true));
|
||||
rule.setPriority(jsonObj.value(S_PRIORITY).toInt(0));
|
||||
|
||||
rule.setUseRegex(jsonObj.value(S_USE_REGEX).toBool(false));
|
||||
rule.setMustContain(jsonObj.value(S_MUST_CONTAIN).toString());
|
||||
rule.setMustNotContain(jsonObj.value(S_MUST_NOT_CONTAIN).toString());
|
||||
rule.setEpisodeFilter(jsonObj.value(S_EPISODE_FILTER).toString());
|
||||
rule.setEnabled(jsonObj.value(S_ENABLED).toBool(true));
|
||||
rule.setLastMatch(QDateTime::fromString(jsonObj.value(S_LAST_MATCH).toString(), Qt::RFC2822Date));
|
||||
rule.setIgnoreDays(jsonObj.value(S_IGNORE_DAYS).toInt());
|
||||
rule.setUseSmartFilter(jsonObj.value(S_SMART_FILTER).toBool(false));
|
||||
@@ -665,6 +671,16 @@ void AutoDownloadRule::setEnabled(const bool enable)
|
||||
m_dataPtr->enabled = enable;
|
||||
}
|
||||
|
||||
int AutoDownloadRule::priority() const
|
||||
{
|
||||
return m_dataPtr->priority;
|
||||
}
|
||||
|
||||
void AutoDownloadRule::setPriority(const int value)
|
||||
{
|
||||
m_dataPtr->priority = value;
|
||||
}
|
||||
|
||||
QDateTime AutoDownloadRule::lastMatch() const
|
||||
{
|
||||
return m_dataPtr->lastMatch;
|
||||
|
@@ -61,6 +61,9 @@ namespace RSS
|
||||
bool isEnabled() const;
|
||||
void setEnabled(bool enable);
|
||||
|
||||
int priority() const;
|
||||
void setPriority(int value);
|
||||
|
||||
QString mustContain() const;
|
||||
void setMustContain(const QString &tokens);
|
||||
QString mustNotContain() const;
|
||||
|
@@ -45,6 +45,7 @@
|
||||
#include "../profile.h"
|
||||
#include "../settingsstorage.h"
|
||||
#include "../utils/fs.h"
|
||||
#include "../utils/io.h"
|
||||
#include "rss_article.h"
|
||||
#include "rss_feed.h"
|
||||
#include "rss_folder.h"
|
||||
@@ -261,33 +262,35 @@ Item *Session::itemByPath(const QString &path) const
|
||||
|
||||
void Session::load()
|
||||
{
|
||||
QFile itemsFile {(m_confFileStorage->storageDir() / Path(FEEDS_FILE_NAME)).data()};
|
||||
if (!itemsFile.exists())
|
||||
{
|
||||
loadLegacy();
|
||||
return;
|
||||
}
|
||||
const int fileMaxSize = 10 * 1024 * 1024;
|
||||
const Path path = m_confFileStorage->storageDir() / Path(FEEDS_FILE_NAME);
|
||||
|
||||
if (!itemsFile.open(QFile::ReadOnly))
|
||||
const auto readResult = Utils::IO::readFile(path, fileMaxSize);
|
||||
if (!readResult)
|
||||
{
|
||||
LogMsg(tr("Couldn't read RSS session data. File: \"%1\". Error: \"%2\"")
|
||||
.arg(itemsFile.fileName(), itemsFile.errorString()), Log::WARNING);
|
||||
if (readResult.error().status == Utils::IO::ReadError::NotExist)
|
||||
{
|
||||
loadLegacy();
|
||||
return;
|
||||
}
|
||||
|
||||
LogMsg(tr("Failed to read RSS session data. %1").arg(readResult.error().message), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(itemsFile.readAll(), &jsonError);
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(readResult.value(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("Couldn't parse RSS session data. File: \"%1\". Error: \"%2\"")
|
||||
.arg(itemsFile.fileName(), jsonError.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Failed to parse RSS session data. File: \"%1\". Error: \"%2\"")
|
||||
.arg(path.toString(), jsonError.errorString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDoc.isObject())
|
||||
{
|
||||
LogMsg(tr("Couldn't load RSS session data. File: \"%1\". Error: Invalid data format.")
|
||||
.arg(itemsFile.fileName()), Log::WARNING);
|
||||
LogMsg(tr("Failed to load RSS session data. File: \"%1\". Error: \"Invalid data format.\"")
|
||||
.arg(path.toString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QDomNode>
|
||||
#include <QFile>
|
||||
#include <QPointer>
|
||||
#include <QProcess>
|
||||
#include <QUrl>
|
||||
@@ -500,7 +501,6 @@ void SearchPluginManager::updateNova()
|
||||
updateFile(Path(u"nova2.py"_qs), true);
|
||||
updateFile(Path(u"nova2dl.py"_qs), true);
|
||||
updateFile(Path(u"novaprinter.py"_qs), true);
|
||||
updateFile(Path(u"sgmllib3.py"_qs), false);
|
||||
updateFile(Path(u"socks.py"_qs), false);
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ void SearchPluginManager::update()
|
||||
nova.start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly);
|
||||
nova.waitForFinished();
|
||||
|
||||
const auto capabilities = QString::fromUtf8(nova.readAll());
|
||||
const auto capabilities = QString::fromUtf8(nova.readAllStandardOutput());
|
||||
QDomDocument xmlDoc;
|
||||
if (!xmlDoc.setContent(capabilities))
|
||||
{
|
||||
@@ -630,13 +630,15 @@ Path SearchPluginManager::pluginPath(const QString &name)
|
||||
|
||||
PluginVersion SearchPluginManager::getPluginVersion(const Path &filePath)
|
||||
{
|
||||
const int lineMaxLength = 16;
|
||||
|
||||
QFile pluginFile {filePath.data()};
|
||||
if (!pluginFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return {};
|
||||
|
||||
while (!pluginFile.atEnd())
|
||||
{
|
||||
const auto line = QString::fromUtf8(pluginFile.readLine()).remove(u' ');
|
||||
const auto line = QString::fromUtf8(pluginFile.readLine(lineMaxLength)).remove(u' ');
|
||||
if (!line.startsWith(u"#VERSION:", Qt::CaseInsensitive)) continue;
|
||||
|
||||
const QString versionStr = line.mid(9);
|
||||
|
@@ -177,33 +177,35 @@ void TorrentFilesWatcher::initWorker()
|
||||
|
||||
void TorrentFilesWatcher::load()
|
||||
{
|
||||
QFile confFile {(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FILE_NAME)).data()};
|
||||
if (!confFile.exists())
|
||||
{
|
||||
loadLegacy();
|
||||
return;
|
||||
}
|
||||
const int fileMaxSize = 10 * 1024 * 1024;
|
||||
const Path path = specialFolderLocation(SpecialFolder::Config) / Path(CONF_FILE_NAME);
|
||||
|
||||
if (!confFile.open(QFile::ReadOnly))
|
||||
const auto readResult = Utils::IO::readFile(path, fileMaxSize);
|
||||
if (!readResult)
|
||||
{
|
||||
LogMsg(tr("Couldn't load Watched Folders configuration from %1. Error: %2")
|
||||
.arg(confFile.fileName(), confFile.errorString()), Log::WARNING);
|
||||
if (readResult.error().status == Utils::IO::ReadError::NotExist)
|
||||
{
|
||||
loadLegacy();
|
||||
return;
|
||||
}
|
||||
|
||||
LogMsg(tr("Failed to load Watched Folders configuration. %1").arg(readResult.error().message), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(confFile.readAll(), &jsonError);
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(readResult.value(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("Couldn't parse Watched Folders configuration from %1. Error: %2")
|
||||
.arg(confFile.fileName(), jsonError.errorString()), Log::WARNING);
|
||||
LogMsg(tr("Failed to parse Watched Folders configuration from %1. Error: \"%2\"")
|
||||
.arg(path.toString(), jsonError.errorString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDoc.isObject())
|
||||
{
|
||||
LogMsg(tr("Couldn't load Watched Folders configuration from %1. Invalid data format.")
|
||||
.arg(confFile.fileName()), Log::WARNING);
|
||||
LogMsg(tr("Failed to load Watched Folders configuration from %1. Error: \"Invalid data format.\"")
|
||||
.arg(path.toString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -426,17 +428,26 @@ void TorrentFilesWatcher::Worker::processFolder(const Path &path, const Path &wa
|
||||
|
||||
if (filePath.hasExtension(u".magnet"_qs))
|
||||
{
|
||||
const int fileMaxSize = 100 * 1024 * 1024;
|
||||
|
||||
QFile file {filePath.data()};
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
while (!file.atEnd())
|
||||
if (file.size() <= fileMaxSize)
|
||||
{
|
||||
const auto line = QString::fromLatin1(file.readLine()).trimmed();
|
||||
emit magnetFound(BitTorrent::MagnetUri(line), addTorrentParams);
|
||||
}
|
||||
while (!file.atEnd())
|
||||
{
|
||||
const auto line = QString::fromLatin1(file.readLine()).trimmed();
|
||||
emit magnetFound(BitTorrent::MagnetUri(line), addTorrentParams);
|
||||
}
|
||||
|
||||
file.close();
|
||||
Utils::Fs::removeFile(filePath);
|
||||
file.close();
|
||||
Utils::Fs::removeFile(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Magnet file too big. File: %1").arg(file.errorString()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -68,3 +69,45 @@ const QByteArray Utils::ByteArray::midView(const QByteArray &in, const int pos,
|
||||
: len;
|
||||
return QByteArray::fromRawData(in.constData() + pos, validLen);
|
||||
}
|
||||
|
||||
QByteArray Utils::ByteArray::toBase32(const QByteArray &in)
|
||||
{
|
||||
const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
const char padchar = '=';
|
||||
|
||||
const qsizetype inSize = in.size();
|
||||
|
||||
auto tmp = QByteArray((inSize + 4) / 5 * 8, Qt::Uninitialized);
|
||||
qsizetype inIndex = 0;
|
||||
char *out = tmp.data();
|
||||
while (inIndex < inSize)
|
||||
{
|
||||
// encode 5 bytes at a time
|
||||
qsizetype inPadLen = 5;
|
||||
int64_t chunk = 0;
|
||||
while (inPadLen > 0)
|
||||
{
|
||||
chunk |= static_cast<int64_t>(static_cast<uchar>(in.data()[inIndex++])) << (--inPadLen * 8);
|
||||
if (inIndex == inSize)
|
||||
break;
|
||||
}
|
||||
|
||||
const int outCharCounts[] = {8, 7, 5, 4, 2};
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
if (i >= (8 - outCharCounts[inPadLen]))
|
||||
{
|
||||
const int shift = (i * 5);
|
||||
const int64_t mask = static_cast<int64_t>(0x1f) << shift;
|
||||
const int charIndex = (chunk & mask) >> shift;
|
||||
*out++ = alphabet[charIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = padchar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -41,4 +42,6 @@ namespace Utils::ByteArray
|
||||
// Mimic QByteArray::mid(pos, len) but instead of returning a full-copy,
|
||||
// we only return a partial view
|
||||
const QByteArray midView(const QByteArray &in, int pos, int len = -1);
|
||||
|
||||
QByteArray toBase32(const QByteArray &in);
|
||||
}
|
||||
|
@@ -31,7 +31,9 @@
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <QFileDevice>
|
||||
#include <QSaveFile>
|
||||
#include <QString>
|
||||
@@ -69,6 +71,36 @@ Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operat
|
||||
return *this;
|
||||
}
|
||||
|
||||
nonstd::expected<QByteArray, Utils::IO::ReadError> Utils::IO::readFile(const Path &path, const qint64 maxSize, const QIODevice::OpenMode additionalMode)
|
||||
{
|
||||
QFile file {path.data()};
|
||||
if (!file.open(QIODevice::ReadOnly | additionalMode))
|
||||
{
|
||||
const QString message = QCoreApplication::translate("Utils::IO", "File open error. File: \"%1\". Error: \"%2\"")
|
||||
.arg(file.fileName(), file.errorString());
|
||||
return nonstd::make_unexpected(ReadError {ReadError::NotExist, message});
|
||||
}
|
||||
|
||||
const qint64 fileSize = file.size();
|
||||
if ((maxSize >= 0) && (fileSize > maxSize))
|
||||
{
|
||||
const QString message = QCoreApplication::translate("Utils::IO", "File size exceeds limit. File: \"%1\". File size: %2. Size limit: %3")
|
||||
.arg(file.fileName(), QString::number(fileSize), QString::number(maxSize));
|
||||
return nonstd::make_unexpected(ReadError {ReadError::ExceedSize, message});
|
||||
}
|
||||
|
||||
// Do not use `QIODevice::readAll()` it won't stop when reading `/dev/zero`
|
||||
const QByteArray data = file.read(fileSize);
|
||||
if (const qint64 dataSize = data.size(); dataSize != fileSize)
|
||||
{
|
||||
const QString message = QCoreApplication::translate("Utils::IO", "Read size mismatch. File: \"%1\". Expected: %2. Actual: %3")
|
||||
.arg(file.fileName(), QString::number(fileSize), QString::number(dataSize));
|
||||
return nonstd::make_unexpected(ReadError {ReadError::SizeMismatch, message});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
nonstd::expected<void, QString> Utils::IO::saveToFile(const Path &path, const QByteArray &data)
|
||||
{
|
||||
if (const Path parentPath = path.parentPath(); !parentPath.isEmpty())
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user