mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-11 01:44:58 -06:00
Compare commits
91 Commits
5dcc14153f
...
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 | ||
|
|
fbe93f0c47 | ||
|
|
11945eef3f | ||
|
|
a35dbc6df7 | ||
|
|
3fb4e4d293 | ||
|
|
f5a4065101 | ||
|
|
ba93d55a6d | ||
|
|
a59301712e | ||
|
|
b406d669b3 | ||
|
|
4ef8f39f23 | ||
|
|
34802362ad | ||
|
|
c10f1f0ad2 | ||
|
|
58ae98026b | ||
|
|
32a55551fe | ||
|
|
7880fe8440 | ||
|
|
bb959bda8c | ||
|
|
d629c77184 | ||
|
|
b953d223e4 | ||
|
|
6fa53b5ed8 | ||
|
|
c777ed3299 | ||
|
|
341b2f345a | ||
|
|
905f141657 | ||
|
|
0a87bb368f | ||
|
|
93a1e58554 | ||
|
|
0cc29f1851 | ||
|
|
81daad92ec | ||
|
|
41be7e9bbe | ||
|
|
179a61d75e | ||
|
|
73134d5f4d | ||
|
|
29c05ed3e8 | ||
|
|
e375f3ee0b | ||
|
|
b185153254 | ||
|
|
e7e5c38384 | ||
|
|
9a00839a75 | ||
|
|
79e85d01fa | ||
|
|
e408973ee6 | ||
|
|
8c9b6e2f2d | ||
|
|
5b43782f58 | ||
|
|
2059825597 | ||
|
|
e1be46820b | ||
|
|
8219b1f695 | ||
|
|
3fbe380582 | ||
|
|
5f00d42a49 | ||
|
|
15de7aed9a | ||
|
|
5c38cc00d9 | ||
|
|
5a1dcbae9c | ||
|
|
7c6a852f85 | ||
|
|
147b22ddd3 | ||
|
|
d83b2a6131 | ||
|
|
821e946bbe | ||
|
|
634eb4a183 | ||
|
|
758ea7edca | ||
|
|
1bd499565e | ||
|
|
be9ec5a329 | ||
|
|
df895cb2a7 | ||
|
|
3b72859980 | ||
|
|
69df85f564 | ||
|
|
1f1da32371 | ||
|
|
cddf8c199c | ||
|
|
bbd5ed1142 | ||
|
|
0f033ec9c8 | ||
|
|
7397c80837 | ||
|
|
51132c817b | ||
|
|
1fe006d16f | ||
|
|
bd31eddb94 | ||
|
|
0defb7d79d | ||
|
|
1e400df324 | ||
|
|
9ea48539b4 | ||
|
|
d63e0ad78f | ||
|
|
eaee38a19e | ||
|
|
b3e9c46eff |
@@ -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
|
||||
|
||||
82
.clang-tidy
Normal file
82
.clang-tidy
Normal file
@@ -0,0 +1,82 @@
|
||||
Checks: >
|
||||
bugprone-*,
|
||||
cert-*,
|
||||
concurrency-*,
|
||||
cppcoreguidelines-*,
|
||||
misc-*,
|
||||
modernize-*,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-# not applicable at all,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-macro-parentheses,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-cppcoreguidelines-virtual-class-destructor,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-unused-parameters,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-use-using,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-function-size,
|
||||
-readability-identifier-length,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-isolate-declaration,
|
||||
-readability-magic-numbers,
|
||||
-readability-named-parameter,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-simplify-boolean-expr,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-# only sometimes useful,
|
||||
-bugprone-narrowing-conversions,
|
||||
-cert-dcl58-cpp,
|
||||
-cert-err33-c,
|
||||
-cert-err58-cpp,
|
||||
-clang-analyzer-core.CallAndMessage,
|
||||
-clang-analyzer-cplusplus.NewDelete,
|
||||
-clang-analyzer-cplusplus.NewDeleteLeaks,
|
||||
-concurrency-mt-unsafe,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-prefer-member-initializer,
|
||||
-cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
-misc-definitions-in-headers,
|
||||
-modernize-concat-nested-namespaces,
|
||||
-modernize-loop-convert,
|
||||
-modernize-raw-string-literal,
|
||||
-modernize-unary-static-assert,
|
||||
-performance-no-automatic-move,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-else-after-return,
|
||||
-readability-redundant-declaration,
|
||||
-# obsoleted,
|
||||
-cert-dcl21-cpp
|
||||
|
||||
CheckOptions:
|
||||
- { key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors, value: true }
|
||||
- { key: modernize-use-override.IgnoreDestructors, value: true }
|
||||
- { key: performance-for-range-copy.AllowedTypes, value: "QJsonValue" }
|
||||
- { key: performance-for-range-copy.WarnOnAllAutoCopies, value: true }
|
||||
- { key: readability-braces-around-statements.ShortStatementLines, value: 3 }
|
||||
|
||||
HeaderFilterRegex: ".+/src/.*\\.h"
|
||||
WarningsAsErrors: "*"
|
||||
23
.github/workflows/ci_macos.yaml
vendored
23
.github/workflows/ci_macos.yaml
vendored
@@ -17,12 +17,12 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
libt_version: ["2.0.8", "1.2.18"]
|
||||
libt_version: ["2.0.9", "1.2.19"]
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
qt_version: ["5.15.2", "6.4.0"]
|
||||
qt_version: ["5.15.2", "6.5.0"]
|
||||
exclude:
|
||||
- libt_version: "1.2.18"
|
||||
qt_version: "6.4.0"
|
||||
- libt_version: "1.2.19"
|
||||
qt_version: "6.5.0"
|
||||
|
||||
env:
|
||||
boost_path: "${{ github.workspace }}/../boost"
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
curl \
|
||||
-L \
|
||||
-o "${{ runner.temp }}/boost.tar.bz2" \
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.bz2"
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.tar.bz2"
|
||||
tar -xf "${{ runner.temp }}/boost.tar.bz2" -C "${{ github.workspace }}/.."
|
||||
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
||||
|
||||
@@ -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
|
||||
|
||||
5
.github/workflows/ci_ubuntu.yaml
vendored
5
.github/workflows/ci_ubuntu.yaml
vendored
@@ -18,11 +18,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
libt_version: ["2.0.8", "1.2.18"]
|
||||
libt_version: ["2.0.9", "1.2.19"]
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
qt_version: ["5.15.2", "6.2.0"]
|
||||
exclude:
|
||||
- libt_version: "1.2.18"
|
||||
- libt_version: "1.2.19"
|
||||
qt_version: "6.2.0"
|
||||
|
||||
steps:
|
||||
@@ -60,6 +60,7 @@ jobs:
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-Ddeprecated-functions=OFF
|
||||
|
||||
6
.github/workflows/ci_windows.yaml
vendored
6
.github/workflows/ci_windows.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
libt_version: ["2.0.8", "1.2.18"]
|
||||
libt_version: ["2.0.9", "1.2.19"]
|
||||
|
||||
env:
|
||||
boost_path: "${{ github.workspace }}/../boost"
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
- name: Install boost
|
||||
run: |
|
||||
aria2c `
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.7z" `
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.7z" `
|
||||
-d "${{ runner.temp }}" `
|
||||
-o "boost.7z"
|
||||
7z x "${{ runner.temp }}/boost.7z" -o"${{ github.workspace }}/.."
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: "6.4.0"
|
||||
version: "6.5.0"
|
||||
archives: qtbase qtsvg qttools
|
||||
|
||||
- name: Install libtorrent
|
||||
|
||||
@@ -26,13 +26,13 @@ jobs:
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: "6.4.0"
|
||||
version: "6.5.0"
|
||||
archives: icu qtbase qtsvg qttools
|
||||
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone \
|
||||
--branch "v2.0.8" \
|
||||
--branch "v2.0.9" \
|
||||
--depth 1 \
|
||||
--recurse-submodules \
|
||||
https://github.com/arvidn/libtorrent.git
|
||||
4
.github/workflows/helper/appimage/export_vars.sh
vendored
Normal file → Executable file
4
.github/workflows/helper/appimage/export_vars.sh
vendored
Normal file → Executable file
@@ -1,10 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
# this file is called from AppRun so 'root_dir' will point to where AppRun is
|
||||
root_dir="$(readlink -f "$(dirname "$0")")"
|
||||
|
||||
# Insert the default values because after the test we prepend our path
|
||||
# and it will create problems with DEs (eg KDE) that don't set the variable
|
||||
# and rely on the default paths
|
||||
if [[ -z ${XDG_DATA_DIRS} ]]; then
|
||||
if [ -z "${XDG_DATA_DIRS}" ]; then
|
||||
XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
|
||||
fi
|
||||
|
||||
|
||||
2
.github/workflows/stale_bot.yaml
vendored
2
.github/workflows/stale_bot.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark and close stale PRs
|
||||
uses: actions/stale@v5
|
||||
uses: actions/stale@v8
|
||||
with:
|
||||
stale-pr-message: "This PR is stale because it has been 60 days with no activity. This PR will be automatically closed within 7 days if there is no further activity."
|
||||
close-pr-message: "This PR was closed because it has been stalled for some time with no activity."
|
||||
|
||||
@@ -3,7 +3,7 @@ repos:
|
||||
hooks:
|
||||
- id: check-translation-tag
|
||||
name: Check newline characters in <translation> tag
|
||||
entry: .github/workflows/check_translation_tag.py
|
||||
entry: .github/workflows/helper/pre-commit/check_translation_tag.py
|
||||
language: script
|
||||
exclude: |
|
||||
(?x)^(
|
||||
@@ -13,7 +13,7 @@ repos:
|
||||
- ts
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
||||
rev: v4.3.0
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-json
|
||||
name: Check JSON files
|
||||
|
||||
@@ -11,8 +11,8 @@ set(minBoostVersion 1.71)
|
||||
set(minQt5Version 5.15.2)
|
||||
set(minQt6Version 6.2)
|
||||
set(minOpenSSLVersion 1.1.1)
|
||||
set(minLibtorrent1Version 1.2.18)
|
||||
set(minLibtorrentVersion 2.0.8)
|
||||
set(minLibtorrent1Version 1.2.19)
|
||||
set(minLibtorrentVersion 2.0.9)
|
||||
set(minZlibVersion 1.2.11)
|
||||
|
||||
include(CheckCXXSourceCompiles) # TODO: migrate to CheckSourceCompiles in CMake >= 3.19
|
||||
|
||||
@@ -200,7 +200,7 @@ Following these guidelines helps maintainers and the community understand your s
|
||||
[coding-guidelines-url]: https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md
|
||||
[coding-guidelines-git-commit-message-url]: https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md#10-git-commit-message
|
||||
[commit-message-fix-issue-example-url]: https://github.com/qbittorrent/qBittorrent/commit/c07cd440cd46345297debb47cb260f8688975f50
|
||||
[forum-url]: http://forum.qbittorrent.org/
|
||||
[forum-url]: https://forum.qbittorrent.org/
|
||||
[howto-report-bugs-url]: https://www.chiark.greenend.org.uk/~sgtatham/bugs.html
|
||||
[how-to-translate-url]: https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent
|
||||
[merging-vs-rebasing-url]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing
|
||||
|
||||
6
INSTALL
6
INSTALL
@@ -5,7 +5,7 @@ qBittorrent - A BitTorrent client in C++ / Qt
|
||||
|
||||
- Boost >= 1.71
|
||||
|
||||
- libtorrent-rasterbar 1.2.18 - 1.2.x || 2.0.8 - 2.0.x
|
||||
- libtorrent-rasterbar 1.2.19 - 1.2.x || 2.0.9 - 2.0.x
|
||||
* By Arvid Norberg, https://www.libtorrent.org/
|
||||
* Be careful: another library (the one used by rTorrent) uses a similar name
|
||||
|
||||
@@ -18,7 +18,7 @@ qBittorrent - A BitTorrent client in C++ / Qt
|
||||
- pkg-config *
|
||||
* Compile-time only on *nix systems
|
||||
|
||||
- Python >= 3.5.0
|
||||
- Python >= 3.7.0
|
||||
* Optional, run-time only
|
||||
* Used by the bundled search engine
|
||||
|
||||
@@ -43,7 +43,7 @@ Please ensure you are building with an officially supported configuration when r
|
||||
will install and execute qBittorrent.
|
||||
|
||||
DOCUMENTATION:
|
||||
Please note that there is a "Compilation" section at http://wiki.qbittorrent.org.
|
||||
Please note that there is a "Compilation" section at https://wiki.qbittorrent.org.
|
||||
|
||||
------------------------------------------
|
||||
sledgehammer999 <sledgehammer999@qbittorrent.org>
|
||||
|
||||
@@ -37,13 +37,13 @@ For more information please visit:
|
||||
https://www.qbittorrent.org
|
||||
|
||||
or our wiki here:
|
||||
http://wiki.qbittorrent.org
|
||||
https://wiki.qbittorrent.org
|
||||
|
||||
Use the forum for troubleshooting before reporting bugs:
|
||||
http://forum.qbittorrent.org
|
||||
https://forum.qbittorrent.org
|
||||
|
||||
Please report any bug (or feature request) to:
|
||||
http://bugs.qbittorrent.org
|
||||
https://bugs.qbittorrent.org
|
||||
|
||||
Official IRC channel:
|
||||
[#qbittorrent on irc.libera.chat](ircs://irc.libera.chat:6697/qbittorrent)
|
||||
|
||||
84
configure
vendored
84
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
|
||||
@@ -6024,19 +6024,19 @@ LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
||||
|
||||
|
||||
pkg_failed=no
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 2.0.8" >&5
|
||||
printf %s "checking for libtorrent-rasterbar >= 2.0.8... " >&6; }
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 2.0.9" >&5
|
||||
printf %s "checking for libtorrent-rasterbar >= 2.0.9... " >&6; }
|
||||
|
||||
if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.8\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.8") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.9\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.9") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 2.0.8" 2>/dev/null`
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 2.0.9" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6048,12 +6048,12 @@ if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.8\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.8") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.9\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.9") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 2.0.8" 2>/dev/null`
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 2.0.9" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6074,28 +6074,28 @@ else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 2.0.8" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 2.0.9" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 2.0.8" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 2.0.9" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
|
||||
pkg_failed=no
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" >&5
|
||||
printf %s "checking for libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2... " >&6; }
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" >&5
|
||||
printf %s "checking for libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2... " >&6; }
|
||||
|
||||
if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6107,12 +6107,12 @@ if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6133,14 +6133,14 @@ else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2) were not met:
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2) were not met:
|
||||
|
||||
$libtorrent_PKG_ERRORS
|
||||
|
||||
@@ -6177,19 +6177,19 @@ elif test $pkg_failed = untried; then
|
||||
printf "%s\n" "no" >&6; }
|
||||
|
||||
pkg_failed=no
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" >&5
|
||||
printf %s "checking for libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2... " >&6; }
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" >&5
|
||||
printf %s "checking for libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2... " >&6; }
|
||||
|
||||
if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6201,12 +6201,12 @@ if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6227,14 +6227,14 @@ else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2) were not met:
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2) were not met:
|
||||
|
||||
$libtorrent_PKG_ERRORS
|
||||
|
||||
@@ -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=""}
|
||||
@@ -188,10 +188,10 @@ m4_define([DETECT_BOOST_VERSION_PROGRAM],
|
||||
[[(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));]])])
|
||||
|
||||
PKG_CHECK_MODULES(libtorrent,
|
||||
[libtorrent-rasterbar >= 2.0.8],
|
||||
[libtorrent-rasterbar >= 2.0.9],
|
||||
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS" QBT_ADD_DEFINES="$QBT_ADD_DEFINES QBT_USES_LIBTORRENT2"],
|
||||
[PKG_CHECK_MODULES(libtorrent,
|
||||
[libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2],
|
||||
[libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2],
|
||||
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS"])])
|
||||
|
||||
PKG_CHECK_MODULES(openssl,
|
||||
|
||||
@@ -68,9 +68,9 @@
|
||||
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
|
||||
<developer_name>The qBittorrent Project</developer_name>
|
||||
<url type="homepage">https://www.qbittorrent.org/</url>
|
||||
<url type="bugtracker">http://bugs.qbittorrent.org/</url>
|
||||
<url type="bugtracker">https://bugs.qbittorrent.org/</url>
|
||||
<url type="donation">https://www.qbittorrent.org/donate</url>
|
||||
<url type="help">http://forum.qbittorrent.org/</url>
|
||||
<url type="help">https://forum.qbittorrent.org/</url>
|
||||
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
|
||||
@@ -98,7 +98,7 @@ Name[is]=qBittorrent
|
||||
Comment[it]=Scarica e condividi file tramite BitTorrent
|
||||
GenericName[it]=Client BitTorrent
|
||||
Name[it]=qBittorrent
|
||||
Comment[ja]=BitTorrentでファイルをダウンロードおよび共有
|
||||
Comment[ja]=BitTorrentでファイルのダウンロードと共有
|
||||
GenericName[ja]=BitTorrentクライアント
|
||||
Name[ja]=qBittorrent
|
||||
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
||||
@@ -160,7 +160,7 @@ Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్
|
||||
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
|
||||
Name[te]=qBittorrent
|
||||
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่าน BitTorrent
|
||||
GenericName[th]=ไคลเอนต์ BitTorrent
|
||||
GenericName[th]=โปรแกรมบิททอเร้นท์
|
||||
Name[th]=qBittorrent
|
||||
Comment[tr]=Dosyaları BitTorrent üzerinden indirin ve paylaşın
|
||||
GenericName[tr]=BitTorrent istemcisi
|
||||
@@ -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
|
||||
|
||||
@@ -42,6 +42,6 @@ number.
|
||||
8080).
|
||||
.SH BUGS
|
||||
.PP
|
||||
If you find a bug, please report it at http://bugs.qbittorrent.org
|
||||
If you find a bug, please report it at https://bugs.qbittorrent.org
|
||||
.SH AUTHORS
|
||||
Christophe Dumez <chris@qbittorrent.org>.
|
||||
|
||||
@@ -38,4 +38,4 @@ the default account user name is "admin" with "adminadmin" as a password.
|
||||
|
||||
|
||||
# BUGS
|
||||
If you find a bug, please report it at http://bugs.qbittorrent.org
|
||||
If you find a bug, please report it at https://bugs.qbittorrent.org
|
||||
|
||||
@@ -36,6 +36,6 @@ number.
|
||||
8080).
|
||||
.SH BUGS
|
||||
.PP
|
||||
If you find a bug, please report it at http://bugs.qbittorrent.org
|
||||
If you find a bug, please report it at https://bugs.qbittorrent.org
|
||||
.SH AUTHORS
|
||||
Christophe Dumez <chris@qbittorrent.org>.
|
||||
|
||||
@@ -33,4 +33,4 @@ FAST extension (mainline) and PeX support (utorrent compatible).
|
||||
|
||||
|
||||
# BUGS
|
||||
If you find a bug, please report it at http://bugs.qbittorrent.org
|
||||
If you find a bug, please report it at https://bugs.qbittorrent.org
|
||||
|
||||
@@ -224,8 +224,7 @@ namespace
|
||||
|
||||
Application::Application(int &argc, char **argv)
|
||||
: BaseApplication(argc, argv)
|
||||
, m_shutdownAct(ShutdownDialogAction::Exit)
|
||||
, m_commandLineArgs(parseCommandLine(this->arguments()))
|
||||
, m_commandLineArgs(parseCommandLine(Application::arguments()))
|
||||
, m_storeFileLoggerEnabled(FILELOGGER_SETTINGS_KEY(u"Enabled"_qs))
|
||||
, m_storeFileLoggerBackup(FILELOGGER_SETTINGS_KEY(u"Backup"_qs))
|
||||
, m_storeFileLoggerDeleteOld(FILELOGGER_SETTINGS_KEY(u"DeleteOld"_qs))
|
||||
@@ -563,6 +562,7 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
};
|
||||
|
||||
const QString logMsg = tr("Running external program. Torrent: \"%1\". Command: `%2`");
|
||||
const QString logMsgError = tr("Failed to run external program. Torrent: \"%1\". Command: `%2`");
|
||||
|
||||
// The processing sequenece is different for Windows and other OS, this is intentional
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -582,8 +582,6 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
for (int i = 1; i < argCount; ++i)
|
||||
argList += QString::fromWCharArray(args[i]);
|
||||
|
||||
LogMsg(logMsg.arg(torrent->name(), program));
|
||||
|
||||
QProcess proc;
|
||||
proc.setProgram(QString::fromWCharArray(args[0]));
|
||||
proc.setArguments(argList);
|
||||
@@ -608,7 +606,11 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
args->startupInfo->hStdOutput = nullptr;
|
||||
args->startupInfo->hStdError = nullptr;
|
||||
});
|
||||
proc.startDetached();
|
||||
|
||||
if (proc.startDetached())
|
||||
LogMsg(logMsg.arg(torrent->name(), program));
|
||||
else
|
||||
LogMsg(logMsgError.arg(torrent->name(), program));
|
||||
#else // Q_OS_WIN
|
||||
QStringList args = Utils::String::splitCommand(programTemplate);
|
||||
|
||||
@@ -624,11 +626,21 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
arg = replaceVariables(arg);
|
||||
}
|
||||
|
||||
// show intended command in log
|
||||
LogMsg(logMsg.arg(torrent->name(), replaceVariables(programTemplate)));
|
||||
|
||||
const QString command = args.takeFirst();
|
||||
QProcess::startDetached(command, args);
|
||||
QProcess proc;
|
||||
proc.setProgram(command);
|
||||
proc.setArguments(args);
|
||||
|
||||
if (proc.startDetached())
|
||||
{
|
||||
// show intended command in log
|
||||
LogMsg(logMsg.arg(torrent->name(), replaceVariables(programTemplate)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// show intended command in log
|
||||
LogMsg(logMsgError.arg(torrent->name(), replaceVariables(programTemplate)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -783,7 +795,7 @@ try
|
||||
actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_qs));
|
||||
actionExit->setMenuRole(QAction::QuitRole);
|
||||
actionExit->setShortcut(Qt::CTRL | Qt::Key_Q);
|
||||
connect(actionExit, &QAction::triggered, this, [this]()
|
||||
connect(actionExit, &QAction::triggered, this, []
|
||||
{
|
||||
QApplication::exit();
|
||||
});
|
||||
@@ -1110,7 +1122,22 @@ void Application::applyMemoryWorkingSetLimit() const
|
||||
if (::getrlimit(RLIMIT_RSS, &limit) != 0)
|
||||
return;
|
||||
|
||||
limit.rlim_cur = memoryWorkingSetLimit() * MiB;
|
||||
const size_t newSize = memoryWorkingSetLimit() * MiB;
|
||||
if (newSize > limit.rlim_max)
|
||||
{
|
||||
// try to raise the hard limit
|
||||
rlimit newLimit = limit;
|
||||
newLimit.rlim_max = newSize;
|
||||
if (::setrlimit(RLIMIT_RSS, &newLimit) != 0)
|
||||
{
|
||||
const auto message = QString::fromLocal8Bit(strerror(errno));
|
||||
LogMsg(tr("Failed to set physical memory (RAM) usage hard limit. Requested size: %1. System hard limit: %2. Error code: %3. Error message: \"%4\"")
|
||||
.arg(QString::number(newSize), QString::number(limit.rlim_max), QString::number(errno), message), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
limit.rlim_cur = newSize;
|
||||
if (::setrlimit(RLIMIT_RSS, &limit) != 0)
|
||||
{
|
||||
const auto message = QString::fromLocal8Bit(strerror(errno));
|
||||
|
||||
@@ -173,7 +173,7 @@ private:
|
||||
ApplicationInstanceManager *m_instanceManager = nullptr;
|
||||
QAtomicInt m_isCleanupRun;
|
||||
bool m_isProcessingParamsAllowed = false;
|
||||
ShutdownDialogAction m_shutdownAct;
|
||||
ShutdownDialogAction m_shutdownAct = ShutdownDialogAction::Exit;
|
||||
QBtCommandLineParameters m_commandLineArgs;
|
||||
|
||||
// FileLog
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -198,13 +199,15 @@ namespace
|
||||
|
||||
int value(const QString &arg) const
|
||||
{
|
||||
QString val = StringOption::value(arg);
|
||||
const QString val = StringOption::value(arg);
|
||||
bool ok = false;
|
||||
int res = val.toInt(&ok);
|
||||
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));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -217,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;
|
||||
}
|
||||
@@ -273,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));
|
||||
@@ -300,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;
|
||||
}
|
||||
@@ -386,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)
|
||||
@@ -394,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));
|
||||
}
|
||||
}
|
||||
@@ -497,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;
|
||||
}
|
||||
@@ -556,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();
|
||||
|
||||
@@ -152,10 +152,10 @@ bool QtLocalPeer::sendMessage(const QString &message, const int timeout)
|
||||
break;
|
||||
int ms = 250;
|
||||
#if defined(Q_OS_WIN)
|
||||
Sleep(DWORD(ms));
|
||||
::Sleep(DWORD(ms));
|
||||
#else
|
||||
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
|
||||
nanosleep(&ts, NULL);
|
||||
::nanosleep(&ts, nullptr);
|
||||
#endif
|
||||
}
|
||||
if (!connOk)
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace
|
||||
void abnormalExitHandler(const int signum)
|
||||
{
|
||||
const char msg[] = "\n\n*************************************************************\n"
|
||||
"Please file a bug report at http://bug.qbittorrent.org and provide the following information:\n\n"
|
||||
"Please file a bug report at https://bug.qbittorrent.org and provide the following information:\n\n"
|
||||
"qBittorrent version: " QBT_VERSION "\n\n"
|
||||
"Caught signal: ";
|
||||
const char *sigName = sysSigName[signum];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -110,6 +110,7 @@ add_library(qbt_base STATIC
|
||||
applicationcomponent.cpp
|
||||
asyncfilestorage.cpp
|
||||
bittorrent/abstractfilestorage.cpp
|
||||
bittorrent/addtorrentparams.cpp
|
||||
bittorrent/bandwidthscheduler.cpp
|
||||
bittorrent/bencoderesumedatastorage.cpp
|
||||
bittorrent/categoryoptions.cpp
|
||||
|
||||
@@ -110,6 +110,7 @@ SOURCES += \
|
||||
$$PWD/applicationcomponent.cpp \
|
||||
$$PWD/asyncfilestorage.cpp \
|
||||
$$PWD/bittorrent/abstractfilestorage.cpp \
|
||||
$$PWD/bittorrent/addtorrentparams.cpp \
|
||||
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
|
||||
$$PWD/bittorrent/categoryoptions.cpp \
|
||||
|
||||
170
src/base/bittorrent/addtorrentparams.cpp
Normal file
170
src/base/bittorrent/addtorrentparams.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* 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
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "addtorrentparams.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
||||
#include "base/utils/string.h"
|
||||
|
||||
const QString PARAM_CATEGORY = u"category"_qs;
|
||||
const QString PARAM_TAGS = u"tags"_qs;
|
||||
const QString PARAM_SAVEPATH = u"save_path"_qs;
|
||||
const QString PARAM_USEDOWNLOADPATH = u"use_download_path"_qs;
|
||||
const QString PARAM_DOWNLOADPATH = u"download_path"_qs;
|
||||
const QString PARAM_OPERATINGMODE = u"operating_mode"_qs;
|
||||
const QString PARAM_QUEUETOP = u"add_to_top_of_queue"_qs;
|
||||
const QString PARAM_STOPPED = u"stopped"_qs;
|
||||
const QString PARAM_SKIPCHECKING = u"skip_checking"_qs;
|
||||
const QString PARAM_CONTENTLAYOUT = u"content_layout"_qs;
|
||||
const QString PARAM_AUTOTMM = u"use_auto_tmm"_qs;
|
||||
const QString PARAM_UPLOADLIMIT = u"upload_limit"_qs;
|
||||
const QString PARAM_DOWNLOADLIMIT = u"download_limit"_qs;
|
||||
const QString PARAM_SEEDINGTIMELIMIT = u"seeding_time_limit"_qs;
|
||||
const QString PARAM_RATIOLIMIT = u"ratio_limit"_qs;
|
||||
|
||||
namespace
|
||||
{
|
||||
TagSet parseTagSet(const QJsonArray &jsonArr)
|
||||
{
|
||||
TagSet tags;
|
||||
for (const QJsonValue &jsonVal : jsonArr)
|
||||
tags.insert(jsonVal.toString());
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
QJsonArray serializeTagSet(const TagSet &tags)
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const QString &tag : tags)
|
||||
arr.append(tag);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
std::optional<bool> getOptionalBool(const QJsonObject &jsonObj, const QString &key)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
if (jsonVal.isUndefined() || jsonVal.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
return jsonVal.toBool();
|
||||
}
|
||||
|
||||
template <typename Enum>
|
||||
std::optional<Enum> getOptionalEnum(const QJsonObject &jsonObj, const QString &key)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
if (jsonVal.isUndefined() || jsonVal.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
return Utils::String::toEnum<Enum>(jsonVal.toString(), {});
|
||||
}
|
||||
|
||||
template <typename Enum>
|
||||
Enum getEnum(const QJsonObject &jsonObj, const QString &key)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
return Utils::String::toEnum<Enum>(jsonVal.toString(), {});
|
||||
}
|
||||
}
|
||||
|
||||
bool BitTorrent::operator==(const AddTorrentParams &lhs, const AddTorrentParams &rhs)
|
||||
{
|
||||
return std::tie(lhs.name, lhs.category, lhs.tags,
|
||||
lhs.savePath, lhs.useDownloadPath, lhs.downloadPath,
|
||||
lhs.sequential, lhs.firstLastPiecePriority, lhs.addForced,
|
||||
lhs.addToQueueTop, lhs.addPaused, lhs.stopCondition,
|
||||
lhs.filePaths, lhs.filePriorities, lhs.skipChecking,
|
||||
lhs.contentLayout, lhs.useAutoTMM, lhs.uploadLimit,
|
||||
lhs.downloadLimit, lhs.seedingTimeLimit, lhs.ratioLimit)
|
||||
== std::tie(rhs.name, rhs.category, rhs.tags,
|
||||
rhs.savePath, rhs.useDownloadPath, rhs.downloadPath,
|
||||
rhs.sequential, rhs.firstLastPiecePriority, rhs.addForced,
|
||||
rhs.addToQueueTop, rhs.addPaused, rhs.stopCondition,
|
||||
rhs.filePaths, rhs.filePriorities, rhs.skipChecking,
|
||||
rhs.contentLayout, rhs.useAutoTMM, rhs.uploadLimit,
|
||||
rhs.downloadLimit, rhs.seedingTimeLimit, rhs.ratioLimit);
|
||||
}
|
||||
|
||||
BitTorrent::AddTorrentParams BitTorrent::parseAddTorrentParams(const QJsonObject &jsonObj)
|
||||
{
|
||||
AddTorrentParams params;
|
||||
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
||||
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
|
||||
params.savePath = Path(jsonObj.value(PARAM_SAVEPATH).toString());
|
||||
params.useDownloadPath = getOptionalBool(jsonObj, PARAM_USEDOWNLOADPATH);
|
||||
params.downloadPath = Path(jsonObj.value(PARAM_DOWNLOADPATH).toString());
|
||||
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
||||
params.addToQueueTop = getOptionalBool(jsonObj, PARAM_QUEUETOP);
|
||||
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
||||
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
||||
params.contentLayout = getOptionalEnum<BitTorrent::TorrentContentLayout>(jsonObj, PARAM_CONTENTLAYOUT);
|
||||
params.useAutoTMM = getOptionalBool(jsonObj, PARAM_AUTOTMM);
|
||||
params.uploadLimit = jsonObj.value(PARAM_UPLOADLIMIT).toInt(-1);
|
||||
params.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1);
|
||||
params.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
params.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(BitTorrent::Torrent::USE_GLOBAL_RATIO);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
QJsonObject BitTorrent::serializeAddTorrentParams(const AddTorrentParams ¶ms)
|
||||
{
|
||||
QJsonObject jsonObj {
|
||||
{PARAM_CATEGORY, params.category},
|
||||
{PARAM_TAGS, serializeTagSet(params.tags)},
|
||||
{PARAM_SAVEPATH, params.savePath.data()},
|
||||
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||
{PARAM_UPLOADLIMIT, params.uploadLimit},
|
||||
{PARAM_DOWNLOADLIMIT, params.downloadLimit},
|
||||
{PARAM_SEEDINGTIMELIMIT, params.seedingTimeLimit},
|
||||
{PARAM_RATIOLIMIT, params.ratioLimit}
|
||||
};
|
||||
|
||||
if (params.addToQueueTop)
|
||||
jsonObj[PARAM_QUEUETOP] = *params.addToQueueTop;
|
||||
if (params.addPaused)
|
||||
jsonObj[PARAM_STOPPED] = *params.addPaused;
|
||||
if (params.contentLayout)
|
||||
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
|
||||
if (params.useAutoTMM)
|
||||
jsonObj[PARAM_AUTOTMM] = *params.useAutoTMM;
|
||||
if (params.useDownloadPath)
|
||||
jsonObj[PARAM_USEDOWNLOADPATH] = *params.useDownloadPath;
|
||||
|
||||
return jsonObj;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 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
|
||||
@@ -39,6 +39,8 @@
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
enum class DownloadPriority;
|
||||
@@ -67,6 +69,11 @@ namespace BitTorrent
|
||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
};
|
||||
|
||||
bool operator==(const AddTorrentParams &lhs, const AddTorrentParams &rhs);
|
||||
|
||||
AddTorrentParams parseAddTorrentParams(const QJsonObject &jsonObj);
|
||||
QJsonObject serializeAddTorrentParams(const AddTorrentParams ¶ms);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(BitTorrent::AddTorrentParams)
|
||||
|
||||
@@ -41,7 +41,6 @@ using namespace std::chrono_literals;
|
||||
|
||||
BandwidthScheduler::BandwidthScheduler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_lastAlternative(false)
|
||||
{
|
||||
connect(&m_timer, &QTimer::timeout, this, &BandwidthScheduler::onTimeout);
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@ private:
|
||||
void onTimeout();
|
||||
|
||||
QTimer m_timer;
|
||||
bool m_lastAlternative;
|
||||
bool m_lastAlternative = 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;
|
||||
|
||||
|
||||
@@ -120,7 +120,11 @@ void CustomDiskIOThread::async_move_storage(lt::storage_index_t storage, std::st
|
||||
m_nativeDiskIO->async_move_storage(storage, path, flags
|
||||
, [=, handler = std::move(handler)](lt::status_t status, const std::string &path, const lt::storage_error &error)
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 20100
|
||||
if ((status != lt::status_t::fatal_disk_error) && (status != lt::status_t::file_exist))
|
||||
#else
|
||||
if ((status != lt::disk_status::fatal_disk_error) && (status != lt::disk_status::file_exist))
|
||||
#endif
|
||||
m_storageData[storage].savePath = newSavePath;
|
||||
|
||||
handler(status, path, error);
|
||||
@@ -167,7 +171,7 @@ void CustomDiskIOThread::async_set_file_priority(lt::storage_index_t storage, lt
|
||||
, std::function<void (const lt::storage_error &, lt::aux::vector<lt::download_priority_t, lt::file_index_t>)> handler)
|
||||
{
|
||||
m_nativeDiskIO->async_set_file_priority(storage, priorities
|
||||
, [=, handler = std::move(handler)](const lt::storage_error &error, lt::aux::vector<lt::download_priority_t, lt::file_index_t> priorities)
|
||||
, [=, handler = std::move(handler)](const lt::storage_error &error, const lt::aux::vector<lt::download_priority_t, lt::file_index_t> &priorities)
|
||||
{
|
||||
m_storageData[storage].filePriorities = priorities;
|
||||
handler(error, priorities);
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QSet>
|
||||
#include <QSqlDatabase>
|
||||
@@ -703,8 +702,8 @@ void BitTorrent::DBResumeDataStorage::Worker::run()
|
||||
|
||||
void DBResumeDataStorage::Worker::requestInterruption()
|
||||
{
|
||||
m_waitCondition.wakeAll();
|
||||
QThread::requestInterruption();
|
||||
m_waitCondition.wakeAll();
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData)
|
||||
@@ -773,6 +772,7 @@ namespace
|
||||
DB_COLUMN_CATEGORY,
|
||||
DB_COLUMN_TAGS,
|
||||
DB_COLUMN_TARGET_SAVE_PATH,
|
||||
DB_COLUMN_DOWNLOAD_PATH,
|
||||
DB_COLUMN_CONTENT_LAYOUT,
|
||||
DB_COLUMN_RATIO_LIMIT,
|
||||
DB_COLUMN_SEEDING_TIME_LIMIT,
|
||||
@@ -830,7 +830,7 @@ namespace
|
||||
query.bindValue(DB_COLUMN_NAME.placeholder, m_resumeData.name);
|
||||
query.bindValue(DB_COLUMN_CATEGORY.placeholder, m_resumeData.category);
|
||||
query.bindValue(DB_COLUMN_TAGS.placeholder, (m_resumeData.tags.isEmpty()
|
||||
? QVariant(QVariant::String) : m_resumeData.tags.join(u","_qs)));
|
||||
? QVariant(QVariant::String) : m_resumeData.tags.join(u","_qs)));
|
||||
query.bindValue(DB_COLUMN_CONTENT_LAYOUT.placeholder, Utils::String::fromEnum(m_resumeData.contentLayout));
|
||||
query.bindValue(DB_COLUMN_RATIO_LIMIT.placeholder, static_cast<int>(m_resumeData.ratioLimit * 1000));
|
||||
query.bindValue(DB_COLUMN_SEEDING_TIME_LIMIT.placeholder, m_resumeData.seedingTimeLimit);
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace
|
||||
}
|
||||
|
||||
private:
|
||||
lt::address_v4::bytes_type m_buf;
|
||||
lt::address_v4::bytes_type m_buf {};
|
||||
};
|
||||
|
||||
bool parseIPAddress(const char *data, lt::address &address)
|
||||
@@ -111,7 +111,6 @@ namespace
|
||||
|
||||
FilterParserThread::FilterParserThread(QObject *parent)
|
||||
: QThread(parent)
|
||||
, m_abort(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -484,9 +483,9 @@ int FilterParserThread::parseP2BFilterFile()
|
||||
char buf[7];
|
||||
unsigned char version;
|
||||
if (!stream.readRawData(buf, sizeof(buf))
|
||||
|| memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7)
|
||||
|| (memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) != 0)
|
||||
|| !stream.readRawData(reinterpret_cast<char*>(&version), sizeof(version)))
|
||||
{
|
||||
{
|
||||
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ protected:
|
||||
void run() override;
|
||||
|
||||
private:
|
||||
int findAndNullDelimiter(char *const data, char delimiter, int start, int end, bool reverse = false);
|
||||
int trim(char *const data, int start, int end);
|
||||
int findAndNullDelimiter(char *data, char delimiter, int start, int end, bool reverse = false);
|
||||
int trim(char *data, int start, int end);
|
||||
int parseDATFilterFile();
|
||||
int parseP2PFilterFile();
|
||||
int getlineInStream(QDataStream &stream, std::string &name, char delim);
|
||||
int parseP2BFilterFile();
|
||||
|
||||
bool m_abort;
|
||||
bool m_abort = false;
|
||||
Path m_filePath;
|
||||
lt::ip_filter m_filter;
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ BitTorrent::InfoHash::operator WrappedType() const
|
||||
|
||||
BitTorrent::TorrentID BitTorrent::TorrentID::fromString(const QString &hashString)
|
||||
{
|
||||
return TorrentID(BaseType::fromString(hashString));
|
||||
return {BaseType::fromString(hashString)};
|
||||
}
|
||||
|
||||
BitTorrent::TorrentID BitTorrent::TorrentID::fromInfoHash(const BitTorrent::InfoHash &infoHash)
|
||||
@@ -103,7 +103,7 @@ BitTorrent::TorrentID BitTorrent::TorrentID::fromInfoHash(const BitTorrent::Info
|
||||
|
||||
BitTorrent::TorrentID BitTorrent::TorrentID::fromSHA1Hash(const SHA1Hash &hash)
|
||||
{
|
||||
return TorrentID(hash);
|
||||
return {hash};
|
||||
}
|
||||
|
||||
BitTorrent::TorrentID BitTorrent::TorrentID::fromSHA256Hash(const SHA256Hash &hash)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace BitTorrent
|
||||
bool firstLastPiecePriority = false;
|
||||
bool hasFinishedStatus = false;
|
||||
bool stopped = false;
|
||||
Torrent::StopCondition stopCondition;
|
||||
Torrent::StopCondition stopCondition = Torrent::StopCondition::None;
|
||||
|
||||
bool addToQueueTop = false; // only for new torrents
|
||||
|
||||
|
||||
@@ -75,8 +75,7 @@ using namespace BitTorrent;
|
||||
const int magnetUriId = qRegisterMetaType<MagnetUri>();
|
||||
|
||||
MagnetUri::MagnetUri(const QString &source)
|
||||
: m_valid(false)
|
||||
, m_url(source)
|
||||
: m_url(source)
|
||||
{
|
||||
if (source.isEmpty()) return;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace BitTorrent
|
||||
lt::add_torrent_params addTorrentParams() const;
|
||||
|
||||
private:
|
||||
bool m_valid;
|
||||
bool m_valid = false;
|
||||
QString m_url;
|
||||
InfoHash m_infoHash;
|
||||
QString m_name;
|
||||
|
||||
@@ -35,27 +35,6 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
void handleAddTorrentAlert([[maybe_unused]] const lt::add_torrent_alert *alert)
|
||||
{
|
||||
#ifndef QBT_USES_LIBTORRENT2
|
||||
if (alert->error)
|
||||
return;
|
||||
|
||||
// libtorrent < 2.0.7 has a bug that add_torrent_alert is posted too early
|
||||
// (before torrent is fully initialized and torrent extensions are created)
|
||||
// so we have to fill "extension data" in add_torrent_alert handler
|
||||
|
||||
// NOTE: `data` may not exist if a torrent is added behind the scenes to download metadata
|
||||
auto *data = static_cast<ExtensionData *>(alert->params.userdata);
|
||||
if (data)
|
||||
{
|
||||
data->status = alert->handle.status({});
|
||||
data->trackers = alert->handle.trackers();
|
||||
data->urlSeeds = alert->handle.url_seeds();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void handleFastresumeRejectedAlert(const lt::fastresume_rejected_alert *alert)
|
||||
{
|
||||
alert->handle.unset_flags(lt::torrent_flags::auto_managed);
|
||||
@@ -91,9 +70,6 @@ void NativeSessionExtension::on_alert(const lt::alert *alert)
|
||||
case lt::session_stats_alert::alert_type:
|
||||
handleSessionStatsAlert(static_cast<const lt::session_stats_alert *>(alert));
|
||||
break;
|
||||
case lt::add_torrent_alert::alert_type:
|
||||
handleAddTorrentAlert(static_cast<const lt::add_torrent_alert *>(alert));
|
||||
break;
|
||||
case lt::fastresume_rejected_alert::alert_type:
|
||||
handleFastresumeRejectedAlert(static_cast<const lt::fastresume_rejected_alert *>(alert));
|
||||
break;
|
||||
|
||||
@@ -36,19 +36,12 @@ NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrent
|
||||
{
|
||||
// NOTE: `data` may not exist if a torrent is added behind the scenes to download metadata
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
// libtorrent < 2.0.7 has a bug that add_torrent_alert is posted too early
|
||||
// (before torrent is fully initialized and torrent extensions are created)
|
||||
// so we have to fill "extension data" in add_torrent_alert handler and
|
||||
// we have it already filled at this point
|
||||
|
||||
if (m_data)
|
||||
{
|
||||
m_data->status = m_torrentHandle.status({});
|
||||
m_data->status = m_torrentHandle.status();
|
||||
m_data->trackers = m_torrentHandle.trackers();
|
||||
m_data->urlSeeds = m_torrentHandle.url_seeds();
|
||||
}
|
||||
#endif
|
||||
|
||||
on_state(m_data ? m_data->status.state : m_torrentHandle.status({}).state);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -170,6 +170,9 @@ namespace BitTorrent
|
||||
virtual bool useCategoryPathsInManualMode() const = 0;
|
||||
virtual void setUseCategoryPathsInManualMode(bool value) = 0;
|
||||
|
||||
virtual Path suggestedSavePath(const QString &categoryName, std::optional<bool> useAutoTMM) const = 0;
|
||||
virtual Path suggestedDownloadPath(const QString &categoryName, std::optional<bool> useAutoTMM) const = 0;
|
||||
|
||||
static bool isValidTag(const QString &tag);
|
||||
virtual QSet<QString> tags() const = 0;
|
||||
virtual bool hasTag(const QString &tag) const = 0;
|
||||
@@ -268,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;
|
||||
@@ -400,7 +411,7 @@ namespace BitTorrent
|
||||
virtual bool isTrackerFilteringEnabled() const = 0;
|
||||
virtual void setTrackerFilteringEnabled(bool enabled) = 0;
|
||||
virtual bool isExcludedFileNamesEnabled() const = 0;
|
||||
virtual void setExcludedFileNamesEnabled(const bool enabled) = 0;
|
||||
virtual void setExcludedFileNamesEnabled(bool enabled) = 0;
|
||||
virtual QStringList excludedFileNames() const = 0;
|
||||
virtual void setExcludedFileNames(const QStringList &newList) = 0;
|
||||
virtual bool isFilenameExcluded(const QString &fileName) const = 0;
|
||||
|
||||
@@ -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>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -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}
|
||||
@@ -759,11 +762,13 @@ void SessionImpl::setFinishedTorrentExportDirectory(const Path &path)
|
||||
|
||||
Path SessionImpl::savePath() const
|
||||
{
|
||||
// TODO: Make sure it is always non-empty
|
||||
return m_savePath;
|
||||
}
|
||||
|
||||
Path SessionImpl::downloadPath() const
|
||||
{
|
||||
// TODO: Make sure it is always non-empty
|
||||
return m_downloadPath;
|
||||
}
|
||||
|
||||
@@ -943,6 +948,21 @@ void SessionImpl::setUseCategoryPathsInManualMode(const bool value)
|
||||
m_useCategoryPathsInManualMode = value;
|
||||
}
|
||||
|
||||
Path SessionImpl::suggestedSavePath(const QString &categoryName, std::optional<bool> useAutoTMM) const
|
||||
{
|
||||
const bool useCategoryPaths = useAutoTMM.value_or(!isAutoTMMDisabledByDefault()) || useCategoryPathsInManualMode();
|
||||
const auto path = (useCategoryPaths ? categorySavePath(categoryName) : savePath());
|
||||
return path;
|
||||
}
|
||||
|
||||
Path SessionImpl::suggestedDownloadPath(const QString &categoryName, std::optional<bool> useAutoTMM) const
|
||||
{
|
||||
const bool useCategoryPaths = useAutoTMM.value_or(!isAutoTMMDisabledByDefault()) || useCategoryPathsInManualMode();
|
||||
const auto categoryDownloadPath = this->categoryDownloadPath(categoryName);
|
||||
const auto path = ((useCategoryPaths && !categoryDownloadPath.isEmpty()) ? categoryDownloadPath : downloadPath());
|
||||
return path;
|
||||
}
|
||||
|
||||
QSet<QString> SessionImpl::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
@@ -1434,27 +1454,44 @@ void SessionImpl::endStartup(ResumeSessionContext *context)
|
||||
}
|
||||
|
||||
context->deleteLater();
|
||||
|
||||
m_nativeSession->resume();
|
||||
if (m_refreshEnqueued)
|
||||
m_refreshEnqueued = false;
|
||||
else
|
||||
enqueueRefresh();
|
||||
|
||||
m_statisticsLastUpdateTimer.start();
|
||||
|
||||
// Regular saving of fastresume data
|
||||
connect(m_resumeDataTimer, &QTimer::timeout, this, &SessionImpl::generateResumeData);
|
||||
const int saveInterval = saveResumeDataInterval();
|
||||
if (saveInterval > 0)
|
||||
connect(context, &QObject::destroyed, this, [this]
|
||||
{
|
||||
m_resumeDataTimer->setInterval(std::chrono::minutes(saveInterval));
|
||||
m_resumeDataTimer->start();
|
||||
}
|
||||
m_nativeSession->resume();
|
||||
if (m_refreshEnqueued)
|
||||
m_refreshEnqueued = false;
|
||||
else
|
||||
enqueueRefresh();
|
||||
|
||||
m_isRestored = true;
|
||||
emit startupProgressUpdated(100);
|
||||
emit restored();
|
||||
m_statisticsLastUpdateTimer.start();
|
||||
|
||||
// Regular saving of fastresume data
|
||||
connect(m_resumeDataTimer, &QTimer::timeout, this, &SessionImpl::generateResumeData);
|
||||
const int saveInterval = saveResumeDataInterval();
|
||||
if (saveInterval > 0)
|
||||
{
|
||||
m_resumeDataTimer->setInterval(std::chrono::minutes(saveInterval));
|
||||
m_resumeDataTimer->start();
|
||||
}
|
||||
|
||||
m_wakeupCheckTimer = new QTimer(this);
|
||||
connect(m_wakeupCheckTimer, &QTimer::timeout, this, [this]
|
||||
{
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
if (m_wakeupCheckTimestamp.secsTo(now) > 100)
|
||||
{
|
||||
LogMsg(tr("System wake-up event detected. Re-announcing to all the trackers..."));
|
||||
reannounceToAllTrackers();
|
||||
}
|
||||
|
||||
m_wakeupCheckTimestamp = QDateTime::currentDateTime();
|
||||
});
|
||||
m_wakeupCheckTimestamp = QDateTime::currentDateTime();
|
||||
m_wakeupCheckTimer->start(30s);
|
||||
|
||||
m_isRestored = true;
|
||||
emit startupProgressUpdated(100);
|
||||
emit restored();
|
||||
});
|
||||
}
|
||||
|
||||
void SessionImpl::initializeNativeSession()
|
||||
@@ -1644,11 +1681,19 @@ 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())
|
||||
{
|
||||
const auto proxyManager = Net::ProxyConfigurationManager::instance();
|
||||
const auto *proxyManager = Net::ProxyConfigurationManager::instance();
|
||||
const Net::ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
|
||||
|
||||
switch (proxyConfig.type)
|
||||
@@ -2558,14 +2603,13 @@ LoadTorrentParams SessionImpl::initLoadTorrentParams(const AddTorrentParams &add
|
||||
LoadTorrentParams loadTorrentParams;
|
||||
|
||||
loadTorrentParams.name = addTorrentParams.name;
|
||||
loadTorrentParams.useAutoTMM = addTorrentParams.useAutoTMM.value_or(!isAutoTMMDisabledByDefault());
|
||||
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
|
||||
loadTorrentParams.hasFinishedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
|
||||
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
|
||||
loadTorrentParams.operatingMode = (addTorrentParams.addForced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged);
|
||||
loadTorrentParams.stopped = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
|
||||
loadTorrentParams.stopCondition = addTorrentParams.stopCondition.value_or(torrentStopCondition());
|
||||
loadTorrentParams.addToQueueTop = addTorrentParams.addToQueueTop.value_or(false);
|
||||
loadTorrentParams.addToQueueTop = addTorrentParams.addToQueueTop.value_or(isAddTorrentToQueueTop());
|
||||
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
||||
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
||||
|
||||
@@ -2575,28 +2619,32 @@ LoadTorrentParams SessionImpl::initLoadTorrentParams(const AddTorrentParams &add
|
||||
else
|
||||
loadTorrentParams.category = category;
|
||||
|
||||
const auto defaultSavePath = suggestedSavePath(loadTorrentParams.category, addTorrentParams.useAutoTMM);
|
||||
const auto defaultDownloadPath = suggestedDownloadPath(loadTorrentParams.category, addTorrentParams.useAutoTMM);
|
||||
|
||||
loadTorrentParams.useAutoTMM = addTorrentParams.useAutoTMM.value_or(
|
||||
addTorrentParams.savePath.isEmpty() && addTorrentParams.downloadPath.isEmpty() && !isAutoTMMDisabledByDefault());
|
||||
|
||||
if (!loadTorrentParams.useAutoTMM)
|
||||
{
|
||||
if (addTorrentParams.savePath.isAbsolute())
|
||||
{
|
||||
loadTorrentParams.savePath = addTorrentParams.savePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Path basePath = useCategoryPathsInManualMode() ? categorySavePath(loadTorrentParams.category) : savePath();
|
||||
loadTorrentParams.savePath = basePath / addTorrentParams.savePath;
|
||||
}
|
||||
loadTorrentParams.savePath = defaultSavePath / addTorrentParams.savePath;
|
||||
|
||||
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(isDownloadPathEnabled());
|
||||
// if useDownloadPath isn't specified but downloadPath is explicitly set we prefer to use it
|
||||
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(!addTorrentParams.downloadPath.isEmpty() || isDownloadPathEnabled());
|
||||
if (useDownloadPath)
|
||||
{
|
||||
// Overridden "Download path" settings
|
||||
|
||||
if (addTorrentParams.downloadPath.isAbsolute())
|
||||
{
|
||||
loadTorrentParams.downloadPath = addTorrentParams.downloadPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Path basePath = useCategoryPathsInManualMode() ? categoryDownloadPath(loadTorrentParams.category) : downloadPath();
|
||||
const Path basePath = (!defaultDownloadPath.isEmpty() ? defaultDownloadPath : downloadPath());
|
||||
loadTorrentParams.downloadPath = basePath / addTorrentParams.downloadPath;
|
||||
}
|
||||
}
|
||||
@@ -3603,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;
|
||||
@@ -4859,17 +4963,18 @@ void SessionImpl::handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoH
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionImpl::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, const MoveStorageMode mode)
|
||||
bool SessionImpl::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, const MoveStorageMode mode, const MoveStorageContext context)
|
||||
{
|
||||
Q_ASSERT(torrent);
|
||||
|
||||
const lt::torrent_handle torrentHandle = torrent->nativeHandle();
|
||||
const Path currentLocation = torrent->actualStorageLocation();
|
||||
const bool torrentHasActiveJob = !m_moveStorageQueue.isEmpty() && (m_moveStorageQueue.first().torrentHandle == torrentHandle);
|
||||
|
||||
if (m_moveStorageQueue.size() > 1)
|
||||
{
|
||||
auto iter = std::find_if((m_moveStorageQueue.begin() + 1), m_moveStorageQueue.end()
|
||||
, [&torrentHandle](const MoveStorageJob &job)
|
||||
, [&torrentHandle](const MoveStorageJob &job)
|
||||
{
|
||||
return job.torrentHandle == torrentHandle;
|
||||
});
|
||||
@@ -4877,20 +4982,13 @@ bool SessionImpl::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &new
|
||||
if (iter != m_moveStorageQueue.end())
|
||||
{
|
||||
// remove existing inactive job
|
||||
torrent->handleMoveStorageJobFinished(currentLocation, iter->context, torrentHasActiveJob);
|
||||
LogMsg(tr("Torrent move canceled. Torrent: \"%1\". Source: \"%2\". Destination: \"%3\"").arg(torrent->name(), currentLocation.toString(), iter->path.toString()));
|
||||
iter = m_moveStorageQueue.erase(iter);
|
||||
|
||||
iter = std::find_if(iter, m_moveStorageQueue.end(), [&torrentHandle](const MoveStorageJob &job)
|
||||
{
|
||||
return job.torrentHandle == torrentHandle;
|
||||
});
|
||||
|
||||
const bool torrentHasOutstandingJob = (iter != m_moveStorageQueue.end());
|
||||
torrent->handleMoveStorageJobFinished(currentLocation, torrentHasOutstandingJob);
|
||||
m_moveStorageQueue.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_moveStorageQueue.isEmpty() && (m_moveStorageQueue.first().torrentHandle == torrentHandle))
|
||||
if (torrentHasActiveJob)
|
||||
{
|
||||
// if there is active job for this torrent prevent creating meaningless
|
||||
// job that will move torrent to the same location as current one
|
||||
@@ -4911,7 +5009,7 @@ bool SessionImpl::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &new
|
||||
}
|
||||
}
|
||||
|
||||
const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode};
|
||||
const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode, context};
|
||||
m_moveStorageQueue << moveStorageJob;
|
||||
LogMsg(tr("Enqueued torrent move. Torrent: \"%1\". Source: \"%2\". Destination: \"%3\"").arg(torrent->name(), currentLocation.toString(), newPath.toString()));
|
||||
|
||||
@@ -4942,7 +5040,7 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
|
||||
moveTorrentStorage(m_moveStorageQueue.first());
|
||||
|
||||
const auto iter = std::find_if(m_moveStorageQueue.cbegin(), m_moveStorageQueue.cend()
|
||||
, [&finishedJob](const MoveStorageJob &job)
|
||||
, [&finishedJob](const MoveStorageJob &job)
|
||||
{
|
||||
return job.torrentHandle == finishedJob.torrentHandle;
|
||||
});
|
||||
@@ -4952,7 +5050,7 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
|
||||
TorrentImpl *torrent = m_torrents.value(finishedJob.torrentHandle.info_hash());
|
||||
if (torrent)
|
||||
{
|
||||
torrent->handleMoveStorageJobFinished(newPath, torrentHasOutstandingJob);
|
||||
torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob);
|
||||
}
|
||||
else if (!torrentHasOutstandingJob)
|
||||
{
|
||||
@@ -4989,7 +5087,7 @@ void SessionImpl::upgradeCategories()
|
||||
const auto legacyCategories = SettingValue<QVariantMap>(u"BitTorrent/Session/Categories"_qs).get();
|
||||
for (auto it = legacyCategories.cbegin(); it != legacyCategories.cend(); ++it)
|
||||
{
|
||||
const QString categoryName = it.key();
|
||||
const QString &categoryName = it.key();
|
||||
CategoryOptions categoryOptions;
|
||||
categoryOptions.savePath = Path(it.value().toString());
|
||||
m_categories[categoryName] = categoryOptions;
|
||||
@@ -5002,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 ==
|
||||
@@ -5014,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;
|
||||
}
|
||||
|
||||
@@ -5232,7 +5331,7 @@ void SessionImpl::handleAddTorrentAlerts(const std::vector<lt::alert *> &alerts)
|
||||
if (a->type() != lt::add_torrent_alert::alert_type)
|
||||
continue;
|
||||
|
||||
auto alert = static_cast<const lt::add_torrent_alert *>(a);
|
||||
const auto *alert = static_cast<const lt::add_torrent_alert *>(a);
|
||||
if (alert->error)
|
||||
{
|
||||
const QString msg = QString::fromStdString(alert->message());
|
||||
@@ -5839,7 +5938,7 @@ void SessionImpl::handleStorageMovedFailedAlert(const lt::storage_moved_failed_a
|
||||
TorrentImpl *torrent = m_torrents.value(id);
|
||||
const QString torrentName = (torrent ? torrent->name() : id.toString());
|
||||
const Path currentLocation = (torrent ? torrent->actualStorageLocation()
|
||||
: Path(p->handle.status(lt::torrent_handle::query_save_path).save_path));
|
||||
: Path(p->handle.status(lt::torrent_handle::query_save_path).save_path));
|
||||
const QString errorMessage = QString::fromStdString(p->message());
|
||||
LogMsg(tr("Failed to move torrent. Torrent: \"%1\". Source: \"%2\". Destination: \"%3\". Reason: \"%4\"")
|
||||
.arg(torrentName, currentLocation.toString(), currentJob.path.toString(), errorMessage), Log::WARNING);
|
||||
|
||||
@@ -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>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <libtorrent/torrent_handle.hpp>
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QDateTime>
|
||||
#include <QElapsedTimer>
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
@@ -87,6 +88,7 @@ namespace BitTorrent
|
||||
struct LoadTorrentParams;
|
||||
|
||||
enum class MoveStorageMode;
|
||||
enum class MoveStorageContext;
|
||||
|
||||
struct SessionMetricIndices
|
||||
{
|
||||
@@ -159,6 +161,9 @@ namespace BitTorrent
|
||||
bool useCategoryPathsInManualMode() const override;
|
||||
void setUseCategoryPathsInManualMode(bool value) override;
|
||||
|
||||
Path suggestedSavePath(const QString &categoryName, std::optional<bool> useAutoTMM) const override;
|
||||
Path suggestedDownloadPath(const QString &categoryName, std::optional<bool> useAutoTMM) const override;
|
||||
|
||||
QSet<QString> tags() const override;
|
||||
bool hasTag(const QString &tag) const override;
|
||||
bool addTag(const QString &tag) override;
|
||||
@@ -245,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;
|
||||
@@ -377,9 +390,9 @@ namespace BitTorrent
|
||||
bool isTrackerFilteringEnabled() const override;
|
||||
void setTrackerFilteringEnabled(bool enabled) override;
|
||||
bool isExcludedFileNamesEnabled() const override;
|
||||
void setExcludedFileNamesEnabled(const bool enabled) override;
|
||||
void setExcludedFileNamesEnabled(bool enabled) override;
|
||||
QStringList excludedFileNames() const override;
|
||||
void setExcludedFileNames(const QStringList &newList) override;
|
||||
void setExcludedFileNames(const QStringList &excludedFileNames) override;
|
||||
bool isFilenameExcluded(const QString &fileName) const override;
|
||||
QStringList bannedIPs() const override;
|
||||
void setBannedIPs(const QStringList &newList) override;
|
||||
@@ -419,27 +432,27 @@ namespace BitTorrent
|
||||
void handleTorrentNeedSaveResumeData(const TorrentImpl *torrent);
|
||||
void handleTorrentSaveResumeDataRequested(const TorrentImpl *torrent);
|
||||
void handleTorrentSaveResumeDataFailed(const TorrentImpl *torrent);
|
||||
void handleTorrentShareLimitChanged(TorrentImpl *const torrent);
|
||||
void handleTorrentNameChanged(TorrentImpl *const torrent);
|
||||
void handleTorrentSavePathChanged(TorrentImpl *const torrent);
|
||||
void handleTorrentCategoryChanged(TorrentImpl *const torrent, const QString &oldCategory);
|
||||
void handleTorrentTagAdded(TorrentImpl *const torrent, const QString &tag);
|
||||
void handleTorrentTagRemoved(TorrentImpl *const torrent, const QString &tag);
|
||||
void handleTorrentSavingModeChanged(TorrentImpl *const torrent);
|
||||
void handleTorrentMetadataReceived(TorrentImpl *const torrent);
|
||||
void handleTorrentPaused(TorrentImpl *const torrent);
|
||||
void handleTorrentResumed(TorrentImpl *const torrent);
|
||||
void handleTorrentChecked(TorrentImpl *const torrent);
|
||||
void handleTorrentFinished(TorrentImpl *const torrent);
|
||||
void handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector<TrackerEntry> &newTrackers);
|
||||
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers);
|
||||
void handleTorrentTrackersChanged(TorrentImpl *const torrent);
|
||||
void handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
|
||||
void handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector<QUrl> &urlSeeds);
|
||||
void handleTorrentResumeDataReady(TorrentImpl *const torrent, const LoadTorrentParams &data);
|
||||
void handleTorrentShareLimitChanged(TorrentImpl *torrent);
|
||||
void handleTorrentNameChanged(TorrentImpl *torrent);
|
||||
void handleTorrentSavePathChanged(TorrentImpl *torrent);
|
||||
void handleTorrentCategoryChanged(TorrentImpl *torrent, const QString &oldCategory);
|
||||
void handleTorrentTagAdded(TorrentImpl *torrent, const QString &tag);
|
||||
void handleTorrentTagRemoved(TorrentImpl *torrent, const QString &tag);
|
||||
void handleTorrentSavingModeChanged(TorrentImpl *torrent);
|
||||
void handleTorrentMetadataReceived(TorrentImpl *torrent);
|
||||
void handleTorrentPaused(TorrentImpl *torrent);
|
||||
void handleTorrentResumed(TorrentImpl *torrent);
|
||||
void handleTorrentChecked(TorrentImpl *torrent);
|
||||
void handleTorrentFinished(TorrentImpl *torrent);
|
||||
void handleTorrentTrackersAdded(TorrentImpl *torrent, const QVector<TrackerEntry> &newTrackers);
|
||||
void handleTorrentTrackersRemoved(TorrentImpl *torrent, const QStringList &deletedTrackers);
|
||||
void handleTorrentTrackersChanged(TorrentImpl *torrent);
|
||||
void handleTorrentUrlSeedsAdded(TorrentImpl *torrent, const QVector<QUrl> &newUrlSeeds);
|
||||
void handleTorrentUrlSeedsRemoved(TorrentImpl *torrent, const QVector<QUrl> &urlSeeds);
|
||||
void handleTorrentResumeDataReady(TorrentImpl *torrent, const LoadTorrentParams &data);
|
||||
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
||||
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode);
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context);
|
||||
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||
@@ -481,14 +494,15 @@ namespace BitTorrent
|
||||
{
|
||||
lt::torrent_handle torrentHandle;
|
||||
Path path;
|
||||
MoveStorageMode mode;
|
||||
MoveStorageMode mode {};
|
||||
MoveStorageContext context {};
|
||||
};
|
||||
|
||||
struct RemovingTorrentData
|
||||
{
|
||||
QString name;
|
||||
Path pathToRemove;
|
||||
DeleteOption deleteOption;
|
||||
DeleteOption deleteOption {};
|
||||
};
|
||||
|
||||
explicit SessionImpl(QObject *parent = nullptr);
|
||||
@@ -693,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;
|
||||
|
||||
@@ -768,6 +786,9 @@ namespace BitTorrent
|
||||
bool m_isPortMappingEnabled = false;
|
||||
QHash<quint16, std::vector<lt::port_mapping_t>> m_mappedPorts;
|
||||
|
||||
QTimer *m_wakeupCheckTimer = nullptr;
|
||||
QDateTime m_wakeupCheckTimestamp;
|
||||
|
||||
friend void Session::initInstance();
|
||||
friend void Session::freeInstance();
|
||||
friend Session *Session::instance();
|
||||
|
||||
@@ -192,7 +192,6 @@ namespace BitTorrent
|
||||
virtual void setSavePath(const Path &savePath) = 0;
|
||||
virtual Path downloadPath() const = 0;
|
||||
virtual void setDownloadPath(const Path &downloadPath) = 0;
|
||||
virtual Path actualStorageLocation() const = 0;
|
||||
virtual Path rootPath() const = 0;
|
||||
virtual Path contentPath() const = 0;
|
||||
virtual QString category() const = 0;
|
||||
@@ -212,9 +211,7 @@ namespace BitTorrent
|
||||
virtual qreal ratioLimit() const = 0;
|
||||
virtual int seedingTimeLimit() const = 0;
|
||||
|
||||
virtual Path actualFilePath(int index) const = 0;
|
||||
virtual PathList filePaths() const = 0;
|
||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||
|
||||
virtual TorrentInfo info() const = 0;
|
||||
virtual bool isFinished() const = 0;
|
||||
@@ -232,7 +229,6 @@ namespace BitTorrent
|
||||
virtual bool isSequentialDownload() const = 0;
|
||||
virtual bool hasFirstLastPiecePriority() const = 0;
|
||||
virtual TorrentState state() const = 0;
|
||||
virtual bool hasMetadata() const = 0;
|
||||
virtual bool hasMissingFiles() const = 0;
|
||||
virtual bool hasError() const = 0;
|
||||
virtual int queuePosition() const = 0;
|
||||
@@ -244,7 +240,6 @@ namespace BitTorrent
|
||||
virtual qlonglong activeTime() const = 0;
|
||||
virtual qlonglong finishedTime() const = 0;
|
||||
virtual qlonglong eta() const = 0;
|
||||
virtual QVector<qreal> filesProgress() const = 0;
|
||||
virtual int seedsCount() const = 0;
|
||||
virtual int peersCount() const = 0;
|
||||
virtual int leechsCount() const = 0;
|
||||
@@ -277,13 +272,6 @@ namespace BitTorrent
|
||||
virtual int connectionsCount() const = 0;
|
||||
virtual int connectionsLimit() const = 0;
|
||||
virtual qlonglong nextAnnounce() const = 0;
|
||||
/**
|
||||
* @brief fraction of file pieces that are available at least from one peer
|
||||
*
|
||||
* This is not the same as torrrent availability, it is just a fraction of pieces
|
||||
* that can be downloaded right now. It varies between 0 to 1.
|
||||
*/
|
||||
virtual QVector<qreal> availableFileFractions() const = 0;
|
||||
|
||||
virtual void setName(const QString &name) = 0;
|
||||
virtual void setSequentialDownload(bool enable) = 0;
|
||||
@@ -301,7 +289,6 @@ namespace BitTorrent
|
||||
virtual void setDHTDisabled(bool disable) = 0;
|
||||
virtual void setPEXDisabled(bool disable) = 0;
|
||||
virtual void setLSDDisabled(bool disable) = 0;
|
||||
virtual void flushCache() const = 0;
|
||||
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
|
||||
virtual void removeTrackers(const QStringList &trackers) = 0;
|
||||
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
|
||||
|
||||
@@ -46,14 +46,14 @@ namespace BitTorrent
|
||||
|
||||
struct TorrentCreatorParams
|
||||
{
|
||||
bool isPrivate;
|
||||
bool isPrivate = false;
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
TorrentFormat torrentFormat;
|
||||
TorrentFormat torrentFormat = TorrentFormat::Hybrid;
|
||||
#else
|
||||
bool isAlignmentOptimized;
|
||||
int paddedFileSizeLimit;
|
||||
#endif
|
||||
int pieceSize;
|
||||
int pieceSize = 0;
|
||||
Path inputPath;
|
||||
Path savePath;
|
||||
QString comment;
|
||||
@@ -74,7 +74,7 @@ namespace BitTorrent
|
||||
void create(const TorrentCreatorParams ¶ms);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
static int calculateTotalPieces(const Path &inputPath, const int pieceSize, const TorrentFormat torrentFormat);
|
||||
static int calculateTotalPieces(const Path &inputPath, int pieceSize, TorrentFormat torrentFormat);
|
||||
#else
|
||||
static int calculateTotalPieces(const Path &inputPath
|
||||
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
@@ -98,7 +97,7 @@ namespace
|
||||
QString firstTrackerMessage;
|
||||
QString firstErrorMessage;
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1));
|
||||
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size()) * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1);
|
||||
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
|
||||
{
|
||||
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
|
||||
@@ -338,7 +337,7 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||
// == END UPGRADE CODE ==
|
||||
}
|
||||
|
||||
TorrentImpl::~TorrentImpl() {}
|
||||
TorrentImpl::~TorrentImpl() = default;
|
||||
|
||||
bool TorrentImpl::isValid() const
|
||||
{
|
||||
@@ -431,12 +430,16 @@ void TorrentImpl::setSavePath(const Path &path)
|
||||
if (resolvedPath == savePath())
|
||||
return;
|
||||
|
||||
m_savePath = resolvedPath;
|
||||
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
|
||||
if (isFinished() || m_hasFinishedStatus || downloadPath().isEmpty())
|
||||
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
||||
{
|
||||
moveStorage(resolvedPath, MoveStorageContext::ChangeSavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_savePath = resolvedPath;
|
||||
m_session->handleTorrentSavePathChanged(this);
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
}
|
||||
}
|
||||
|
||||
Path TorrentImpl::downloadPath() const
|
||||
@@ -454,13 +457,17 @@ void TorrentImpl::setDownloadPath(const Path &path)
|
||||
if (resolvedPath == m_downloadPath)
|
||||
return;
|
||||
|
||||
m_downloadPath = resolvedPath;
|
||||
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
|
||||
const bool isIncomplete = !(isFinished() || m_hasFinishedStatus);
|
||||
if (isIncomplete)
|
||||
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
||||
{
|
||||
moveStorage((resolvedPath.isEmpty() ? savePath() : resolvedPath), MoveStorageContext::ChangeDownloadPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_downloadPath = resolvedPath;
|
||||
m_session->handleTorrentSavePathChanged(this);
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
}
|
||||
}
|
||||
|
||||
Path TorrentImpl::rootPath() const
|
||||
@@ -769,7 +776,13 @@ qreal TorrentImpl::progress() const
|
||||
return 1.;
|
||||
|
||||
const qreal progress = static_cast<qreal>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
||||
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
||||
if ((progress < 0.f) || (progress > 1.f))
|
||||
{
|
||||
LogMsg(tr("Unexpected data detected. Torrent: %1. Data: total_wanted=%2 total_wanted_done=%3.")
|
||||
.arg(name(), QString::number(m_nativeStatus.total_wanted), QString::number(m_nativeStatus.total_wanted_done))
|
||||
, Log::WARNING);
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
@@ -1618,7 +1631,7 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
|
||||
{
|
||||
const auto nativeIndex = nativeIndexes.at(i);
|
||||
|
||||
const Path actualFilePath = fileNames.at(i);
|
||||
const Path &actualFilePath = fileNames.at(i);
|
||||
p.renamed_files[nativeIndex] = actualFilePath.toString().toStdString();
|
||||
|
||||
const Path filePath = actualFilePath.removedExtension(QB_EXT);
|
||||
@@ -1754,7 +1767,7 @@ void TorrentImpl::resume(const TorrentOperatingMode mode)
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode)
|
||||
void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageContext context)
|
||||
{
|
||||
if (!hasMetadata())
|
||||
{
|
||||
@@ -1762,7 +1775,9 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_session->addMoveTorrentStorageJob(this, newPath, mode))
|
||||
const auto mode = (context == MoveStorageContext::AdjustCurrentLocation)
|
||||
? MoveStorageMode::Overwrite : MoveStorageMode::KeepExistingFiles;
|
||||
if (m_session->addMoveTorrentStorageJob(this, newPath, mode, context))
|
||||
{
|
||||
m_storageIsMoving = true;
|
||||
updateState();
|
||||
@@ -1784,16 +1799,17 @@ void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||
updateStatus(nativeStatus);
|
||||
}
|
||||
|
||||
void TorrentImpl::handleMoveStorageJobFinished(const Path &path, const bool hasOutstandingJob)
|
||||
void TorrentImpl::handleMoveStorageJobFinished(const Path &path, const MoveStorageContext context, const bool hasOutstandingJob)
|
||||
{
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
if (context == MoveStorageContext::ChangeSavePath)
|
||||
m_savePath = path;
|
||||
else if (context == MoveStorageContext::ChangeDownloadPath)
|
||||
m_downloadPath = path;
|
||||
m_storageIsMoving = hasOutstandingJob;
|
||||
m_nativeStatus.save_path = path.toString().toStdString();
|
||||
|
||||
if (actualStorageLocation() != path)
|
||||
{
|
||||
m_nativeStatus.save_path = path.toString().toStdString();
|
||||
m_session->handleTorrentSavePathChanged(this);
|
||||
}
|
||||
m_session->handleTorrentSavePathChanged(this);
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
|
||||
if (!m_storageIsMoving)
|
||||
{
|
||||
@@ -2221,7 +2237,7 @@ void TorrentImpl::adjustStorageLocation()
|
||||
const Path targetPath = ((isFinished() || m_hasFinishedStatus || downloadPath.isEmpty()) ? savePath() : downloadPath);
|
||||
|
||||
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
||||
moveStorage(targetPath, MoveStorageMode::Overwrite);
|
||||
moveStorage(targetPath, MoveStorageContext::AdjustCurrentLocation);
|
||||
}
|
||||
|
||||
void TorrentImpl::doRenameFile(int index, const Path &path)
|
||||
|
||||
@@ -68,6 +68,13 @@ namespace BitTorrent
|
||||
Overwrite
|
||||
};
|
||||
|
||||
enum class MoveStorageContext
|
||||
{
|
||||
AdjustCurrentLocation,
|
||||
ChangeSavePath,
|
||||
ChangeDownloadPath
|
||||
};
|
||||
|
||||
enum class MaintenanceJob
|
||||
{
|
||||
None,
|
||||
@@ -77,7 +84,7 @@ namespace BitTorrent
|
||||
struct FileErrorInfo
|
||||
{
|
||||
lt::error_code error;
|
||||
lt::operation_t operation;
|
||||
lt::operation_t operation = lt::operation_t::unknown;
|
||||
};
|
||||
|
||||
class TorrentImpl final : public Torrent
|
||||
@@ -252,7 +259,7 @@ namespace BitTorrent
|
||||
void handleCategoryOptionsChanged();
|
||||
void handleAppendExtensionToggled();
|
||||
void saveResumeData(lt::resume_data_flags_t flags = {});
|
||||
void handleMoveStorageJobFinished(const Path &path, bool hasOutstandingJob);
|
||||
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
|
||||
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QMap<TrackerEntry::Endpoint, int> &updateInfo);
|
||||
|
||||
@@ -289,7 +296,7 @@ namespace BitTorrent
|
||||
Path wantedActualPath(int index, const Path &path) const;
|
||||
void adjustStorageLocation();
|
||||
void doRenameFile(int index, const Path &path);
|
||||
void moveStorage(const Path &newPath, MoveStorageMode mode);
|
||||
void moveStorage(const Path &newPath, MoveStorageContext context);
|
||||
void manageIncompleteFiles();
|
||||
void applyFirstLastPiecePriority(bool enabled);
|
||||
|
||||
|
||||
@@ -66,12 +66,6 @@ TorrentInfo::TorrentInfo(const lt::torrent_info &nativeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
TorrentInfo::TorrentInfo(const TorrentInfo &other)
|
||||
: m_nativeInfo {other.m_nativeInfo}
|
||||
, m_nativeIndexes {other.m_nativeIndexes}
|
||||
{
|
||||
}
|
||||
|
||||
TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
|
||||
{
|
||||
if (this != &other)
|
||||
@@ -109,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace BitTorrent
|
||||
|
||||
public:
|
||||
TorrentInfo() = default;
|
||||
TorrentInfo(const TorrentInfo &other);
|
||||
TorrentInfo(const TorrentInfo &other) = default;
|
||||
|
||||
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
Digest32() = default;
|
||||
Digest32(const Digest32 &other) = default;
|
||||
Digest32(Digest32 &&other) = default;
|
||||
Digest32(Digest32 &&other) noexcept = default;
|
||||
|
||||
Digest32(const UnderlyingType &nativeDigest)
|
||||
: m_dataPtr {new Data(nativeDigest)}
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
}
|
||||
|
||||
Digest32 &operator=(const Digest32 &other) = default;
|
||||
Digest32 &operator=(Digest32 &&other) = default;
|
||||
Digest32 &operator=(Digest32 &&other) noexcept = default;
|
||||
|
||||
operator UnderlyingType() const
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ constexpr typename std::add_const_t<T> &asConst(T &t) noexcept { return t; }
|
||||
|
||||
// Forward rvalue as const
|
||||
template <typename T>
|
||||
constexpr typename std::add_const_t<T> asConst(T &&t) noexcept { return std::move(t); }
|
||||
constexpr typename std::add_const_t<T> asConst(T &&t) noexcept { return std::forward<T>(t); }
|
||||
|
||||
// Prevent const rvalue arguments
|
||||
template <typename T>
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Http
|
||||
class IRequestHandler
|
||||
{
|
||||
public:
|
||||
virtual ~IRequestHandler() {}
|
||||
virtual ~IRequestHandler() = default;
|
||||
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,10 +75,6 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
RequestParser::RequestParser()
|
||||
{
|
||||
}
|
||||
|
||||
RequestParser::ParseResult RequestParser::parse(const QByteArray &data)
|
||||
{
|
||||
// Warning! Header names are converted to lowercase
|
||||
|
||||
@@ -47,9 +47,9 @@ namespace Http
|
||||
struct ParseResult
|
||||
{
|
||||
// when `status != ParseStatus::OK`, `request` & `frameSize` are undefined
|
||||
ParseStatus status;
|
||||
ParseStatus status = ParseStatus::BadRequest;
|
||||
Request request;
|
||||
long frameSize; // http request frame size (bytes)
|
||||
long frameSize = 0; // http request frame size (bytes)
|
||||
};
|
||||
|
||||
static ParseResult parse(const QByteArray &data);
|
||||
@@ -57,7 +57,7 @@ namespace Http
|
||||
static const long MAX_CONTENT_SIZE = 64 * 1024 * 1024; // 64 MB
|
||||
|
||||
private:
|
||||
RequestParser();
|
||||
RequestParser() = default;
|
||||
|
||||
ParseResult doParse(const QByteArray &data);
|
||||
bool parseStartLines(QStringView 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;
|
||||
@@ -79,10 +80,10 @@ namespace Http
|
||||
struct Environment
|
||||
{
|
||||
QHostAddress localAddress;
|
||||
quint16 localPort;
|
||||
quint16 localPort = 0;
|
||||
|
||||
QHostAddress clientAddress;
|
||||
quint16 clientPort;
|
||||
quint16 clientPort = 0;
|
||||
};
|
||||
|
||||
struct UploadedFile
|
||||
|
||||
@@ -36,8 +36,6 @@ IconProvider::IconProvider(QObject *parent)
|
||||
{
|
||||
}
|
||||
|
||||
IconProvider::~IconProvider() {}
|
||||
|
||||
void IconProvider::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
|
||||
protected:
|
||||
explicit IconProvider(QObject *parent = nullptr);
|
||||
~IconProvider();
|
||||
~IconProvider() = default;
|
||||
|
||||
static IconProvider *m_instance;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <QString>
|
||||
#include <QtContainerFwd>
|
||||
|
||||
const int MAX_LOG_MESSAGES = 20000;
|
||||
inline const int MAX_LOG_MESSAGES = 20000;
|
||||
|
||||
namespace Log
|
||||
{
|
||||
@@ -51,17 +51,17 @@ namespace Log
|
||||
|
||||
struct Msg
|
||||
{
|
||||
int id;
|
||||
MsgType type;
|
||||
qint64 timestamp;
|
||||
int id = -1;
|
||||
MsgType type = ALL;
|
||||
qint64 timestamp = -1;
|
||||
QString message;
|
||||
};
|
||||
|
||||
struct Peer
|
||||
{
|
||||
int id;
|
||||
bool blocked;
|
||||
qint64 timestamp;
|
||||
int id = -1;
|
||||
bool blocked = false;
|
||||
qint64 timestamp = -1;
|
||||
QString ip;
|
||||
QString reason;
|
||||
};
|
||||
|
||||
@@ -44,8 +44,6 @@ const std::chrono::seconds IP_CHECK_INTERVAL = 30min;
|
||||
|
||||
DNSUpdater::DNSUpdater(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_state(OK)
|
||||
, m_service(DNS::Service::None)
|
||||
{
|
||||
updateCredentials();
|
||||
|
||||
@@ -217,7 +215,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
|
||||
|
||||
if (code == u"badagent")
|
||||
{
|
||||
LogMsg(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please submit a bug report at http://bugs.qbittorrent.org."),
|
||||
LogMsg(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please submit a bug report at https://bugs.qbittorrent.org."),
|
||||
Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
@@ -225,7 +223,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
|
||||
|
||||
if (code == u"!donator")
|
||||
{
|
||||
LogMsg(tr("Dynamic DNS error: %1 was returned by the service, please submit a bug report at http://bugs.qbittorrent.org.").arg(u"!donator"_qs),
|
||||
LogMsg(tr("Dynamic DNS error: %1 was returned by the service, please submit a bug report at https://bugs.qbittorrent.org.").arg(u"!donator"_qs),
|
||||
Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
|
||||
@@ -74,9 +74,9 @@ namespace Net
|
||||
QHostAddress m_lastIP;
|
||||
QDateTime m_lastIPCheckTime;
|
||||
QTimer m_ipCheckTimer;
|
||||
int m_state;
|
||||
int m_state = OK;
|
||||
// Service creds
|
||||
DNS::Service m_service;
|
||||
DNS::Service m_service = DNS::Service::None;
|
||||
QString m_domain;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
|
||||
@@ -208,7 +208,7 @@ void Net::DownloadHandlerImpl::handleRedirection(const QUrl &newUrl)
|
||||
return;
|
||||
}
|
||||
|
||||
auto redirected = static_cast<DownloadHandlerImpl *>(
|
||||
auto *redirected = static_cast<DownloadHandlerImpl *>(
|
||||
m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString), useProxy()));
|
||||
redirected->m_redirectionCount = m_redirectionCount + 1;
|
||||
connect(redirected, &DownloadHandlerImpl::finished, this, [this](const DownloadResult &result)
|
||||
|
||||
@@ -163,7 +163,7 @@ Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &down
|
||||
const ServiceID id = ServiceID::fromURL(downloadRequest.url());
|
||||
const bool isSequentialService = m_sequentialServices.contains(id);
|
||||
|
||||
auto downloadHandler = new DownloadHandlerImpl(this, downloadRequest, useProxy);
|
||||
auto *downloadHandler = new DownloadHandlerImpl(this, downloadRequest, useProxy);
|
||||
connect(downloadHandler, &DownloadHandler::finished, downloadHandler, &QObject::deleteLater);
|
||||
connect(downloadHandler, &QObject::destroyed, this, [this, id, downloadHandler]()
|
||||
{
|
||||
@@ -274,7 +274,7 @@ void Net::DownloadManager::handleDownloadFinished(DownloadHandlerImpl *finishedH
|
||||
return;
|
||||
}
|
||||
|
||||
auto handler = waitingJobsIter.value().dequeue();
|
||||
auto *handler = waitingJobsIter.value().dequeue();
|
||||
qDebug("Downloading %s...", qUtf8Printable(handler->url()));
|
||||
processRequest(handler);
|
||||
handler->disconnect(this);
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Net
|
||||
struct DownloadResult
|
||||
{
|
||||
QString url;
|
||||
DownloadStatus status;
|
||||
DownloadStatus status = DownloadStatus::Failed;
|
||||
QString errorString;
|
||||
QByteArray data;
|
||||
Path filePath;
|
||||
|
||||
@@ -162,7 +162,7 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
||||
|
||||
// Connect to SMTP server
|
||||
const QStringList serverEndpoint = pref->getMailNotificationSMTP().split(u':');
|
||||
const QString serverAddress = serverEndpoint[0];
|
||||
const QString &serverAddress = serverEndpoint[0];
|
||||
const std::optional<int> serverPort = Utils::String::parseInt(serverEndpoint.value(1));
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
|
||||
@@ -589,11 +589,7 @@ void Preferences::setWebUiPort(const quint16 port)
|
||||
|
||||
bool Preferences::useUPnPForWebUIPort() const
|
||||
{
|
||||
#ifdef DISABLE_GUI
|
||||
return value(u"Preferences/WebUI/UseUPnP"_qs, true);
|
||||
#else
|
||||
return value(u"Preferences/WebUI/UseUPnP"_qs, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Preferences::setUPnPForWebUIPort(const bool enabled)
|
||||
@@ -1228,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
|
||||
{
|
||||
|
||||
@@ -253,7 +253,7 @@ public:
|
||||
void setUILocked(bool locked);
|
||||
|
||||
bool isAutoRunOnTorrentAddedEnabled() const;
|
||||
void setAutoRunOnTorrentAddedEnabled(const bool enabled);
|
||||
void setAutoRunOnTorrentAddedEnabled(bool enabled);
|
||||
QString getAutoRunOnTorrentAddedProgram() const;
|
||||
void setAutoRunOnTorrentAddedProgram(const QString &program);
|
||||
bool isAutoRunOnTorrentFinishedEnabled() 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);
|
||||
|
||||
@@ -116,9 +116,9 @@ Path Private::DefaultProfile::downloadLocation() const
|
||||
std::unique_ptr<QSettings> Private::DefaultProfile::applicationSettings(const QString &name) const
|
||||
{
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
||||
return std::unique_ptr<QSettings>(new QSettings(QSettings::IniFormat, QSettings::UserScope, profileName(), name));
|
||||
return std::make_unique<QSettings>(QSettings::IniFormat, QSettings::UserScope, profileName(), name);
|
||||
#else
|
||||
return std::unique_ptr<QSettings>(new QSettings(profileName(), name));
|
||||
return std::make_unique<QSettings>(profileName(), name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ std::unique_ptr<QSettings> Private::CustomProfile::applicationSettings(const QSt
|
||||
const auto CONF_FILE_EXTENSION = u".conf"_qs;
|
||||
#endif
|
||||
const Path settingsFilePath = configLocation() / Path(name + CONF_FILE_EXTENSION);
|
||||
return std::unique_ptr<QSettings>(new QSettings(settingsFilePath.data(), QSettings::IniFormat));
|
||||
return std::make_unique<QSettings>(settingsFilePath.data(), QSettings::IniFormat);
|
||||
}
|
||||
|
||||
Path Private::NoConvertConverter::fromPortablePath(const Path &portablePath) const
|
||||
|
||||
@@ -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
|
||||
@@ -382,13 +396,13 @@ void AutoDownloader::handleFeedURLChanged(Feed *feed, const QString &oldURL)
|
||||
}
|
||||
}
|
||||
|
||||
for (QSharedPointer<ProcessingJob> job : asConst(m_processingQueue))
|
||||
for (const QSharedPointer<ProcessingJob> &job : asConst(m_processingQueue))
|
||||
{
|
||||
if (job->feedURL == oldURL)
|
||||
job->feedURL = feed->url();
|
||||
}
|
||||
|
||||
for (QSharedPointer<ProcessingJob> job : asConst(m_waitingJobs))
|
||||
for (const QSharedPointer<ProcessingJob> &job : asConst(m_waitingJobs))
|
||||
{
|
||||
if (job->feedURL == oldURL)
|
||||
job->feedURL = feed->url();
|
||||
@@ -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,15 +468,11 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
|
||||
m_dirty = true;
|
||||
storeDeferred();
|
||||
|
||||
BitTorrent::AddTorrentParams params;
|
||||
params.savePath = rule.savePath();
|
||||
params.category = rule.assignedCategory();
|
||||
params.addPaused = rule.addPaused();
|
||||
params.contentLayout = rule.torrentContentLayout();
|
||||
if (!rule.savePath().isEmpty())
|
||||
params.useAutoTMM = false;
|
||||
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, params);
|
||||
BitTorrent::Session::instance()->addTorrent(torrentURL, rule.addTorrentParams());
|
||||
|
||||
if (BitTorrent::MagnetUri(torrentURL).isValid())
|
||||
{
|
||||
@@ -460,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)
|
||||
@@ -484,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)
|
||||
{
|
||||
@@ -500,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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2017-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -101,22 +101,25 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
const QString Str_Name = u"name"_qs;
|
||||
const QString Str_Enabled = u"enabled"_qs;
|
||||
const QString Str_UseRegex = u"useRegex"_qs;
|
||||
const QString Str_MustContain = u"mustContain"_qs;
|
||||
const QString Str_MustNotContain = u"mustNotContain"_qs;
|
||||
const QString Str_EpisodeFilter = u"episodeFilter"_qs;
|
||||
const QString Str_AffectedFeeds = u"affectedFeeds"_qs;
|
||||
const QString Str_SavePath = u"savePath"_qs;
|
||||
const QString Str_AssignedCategory = u"assignedCategory"_qs;
|
||||
const QString Str_LastMatch = u"lastMatch"_qs;
|
||||
const QString Str_IgnoreDays = u"ignoreDays"_qs;
|
||||
const QString Str_AddPaused = u"addPaused"_qs;
|
||||
const QString Str_CreateSubfolder = u"createSubfolder"_qs;
|
||||
const QString Str_ContentLayout = u"torrentContentLayout"_qs;
|
||||
const QString Str_SmartFilter = u"smartFilter"_qs;
|
||||
const QString Str_PreviouslyMatched = u"previouslyMatchedEpisodes"_qs;
|
||||
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;
|
||||
const QString S_EPISODE_FILTER = u"episodeFilter"_qs;
|
||||
const QString S_AFFECTED_FEEDS = u"affectedFeeds"_qs;
|
||||
const QString S_LAST_MATCH = u"lastMatch"_qs;
|
||||
const QString S_IGNORE_DAYS = u"ignoreDays"_qs;
|
||||
const QString S_SMART_FILTER = u"smartFilter"_qs;
|
||||
const QString S_PREVIOUSLY_MATCHED = u"previouslyMatchedEpisodes"_qs;
|
||||
|
||||
const QString S_SAVE_PATH = u"savePath"_qs;
|
||||
const QString S_ASSIGNED_CATEGORY = u"assignedCategory"_qs;
|
||||
const QString S_ADD_PAUSED = u"addPaused"_qs;
|
||||
const QString S_CONTENT_LAYOUT = u"torrentContentLayout"_qs;
|
||||
|
||||
const QString S_TORRENT_PARAMS = u"torrentParams"_qs;
|
||||
|
||||
namespace RSS
|
||||
{
|
||||
@@ -124,6 +127,7 @@ namespace RSS
|
||||
{
|
||||
QString name;
|
||||
bool enabled = true;
|
||||
int priority = 0;
|
||||
|
||||
QStringList mustContain;
|
||||
QStringList mustNotContain;
|
||||
@@ -133,10 +137,7 @@ namespace RSS
|
||||
int ignoreDays = 0;
|
||||
QDateTime lastMatch;
|
||||
|
||||
Path savePath;
|
||||
QString category;
|
||||
std::optional<bool> addPaused;
|
||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||
BitTorrent::AddTorrentParams addTorrentParams;
|
||||
|
||||
bool smartFilter = false;
|
||||
QStringList previouslyMatchedEpisodes;
|
||||
@@ -148,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)
|
||||
@@ -155,11 +157,8 @@ namespace RSS
|
||||
&& (left.useRegex == right.useRegex)
|
||||
&& (left.ignoreDays == right.ignoreDays)
|
||||
&& (left.lastMatch == right.lastMatch)
|
||||
&& (left.savePath == right.savePath)
|
||||
&& (left.category == right.category)
|
||||
&& (left.addPaused == right.addPaused)
|
||||
&& (left.contentLayout == right.contentLayout)
|
||||
&& (left.smartFilter == right.smartFilter);
|
||||
&& (left.smartFilter == right.smartFilter)
|
||||
&& (left.addTorrentParams == right.addTorrentParams);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -208,12 +207,9 @@ AutoDownloadRule::AutoDownloadRule(const QString &name)
|
||||
setName(name);
|
||||
}
|
||||
|
||||
AutoDownloadRule::AutoDownloadRule(const AutoDownloadRule &other)
|
||||
: m_dataPtr(other.m_dataPtr)
|
||||
{
|
||||
}
|
||||
AutoDownloadRule::AutoDownloadRule(const AutoDownloadRule &other) = default;
|
||||
|
||||
AutoDownloadRule::~AutoDownloadRule() {}
|
||||
AutoDownloadRule::~AutoDownloadRule() = default;
|
||||
|
||||
QRegularExpression AutoDownloadRule::cachedRegex(const QString &expression, const bool isRegex) const
|
||||
{
|
||||
@@ -461,64 +457,48 @@ AutoDownloadRule &AutoDownloadRule::operator=(const AutoDownloadRule &other)
|
||||
|
||||
QJsonObject AutoDownloadRule::toJsonObject() const
|
||||
{
|
||||
return {{Str_Enabled, isEnabled()}
|
||||
, {Str_UseRegex, useRegex()}
|
||||
, {Str_MustContain, mustContain()}
|
||||
, {Str_MustNotContain, mustNotContain()}
|
||||
, {Str_EpisodeFilter, episodeFilter()}
|
||||
, {Str_AffectedFeeds, QJsonArray::fromStringList(feedURLs())}
|
||||
, {Str_SavePath, savePath().toString()}
|
||||
, {Str_AssignedCategory, assignedCategory()}
|
||||
, {Str_LastMatch, lastMatch().toString(Qt::RFC2822Date)}
|
||||
, {Str_IgnoreDays, ignoreDays()}
|
||||
, {Str_AddPaused, toJsonValue(addPaused())}
|
||||
, {Str_ContentLayout, contentLayoutToJsonValue(torrentContentLayout())}
|
||||
, {Str_SmartFilter, useSmartFilter()}
|
||||
, {Str_PreviouslyMatched, QJsonArray::fromStringList(previouslyMatchedEpisodes())}};
|
||||
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()}
|
||||
, {S_EPISODE_FILTER, episodeFilter()}
|
||||
, {S_AFFECTED_FEEDS, QJsonArray::fromStringList(feedURLs())}
|
||||
, {S_LAST_MATCH, lastMatch().toString(Qt::RFC2822Date)}
|
||||
, {S_IGNORE_DAYS, ignoreDays()}
|
||||
, {S_SMART_FILTER, useSmartFilter()}
|
||||
, {S_PREVIOUSLY_MATCHED, QJsonArray::fromStringList(previouslyMatchedEpisodes())}
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.6.x.
|
||||
// === BEGIN DEPRECATED CODE === //
|
||||
, {S_ADD_PAUSED, toJsonValue(addTorrentParams.addPaused)}
|
||||
, {S_CONTENT_LAYOUT, contentLayoutToJsonValue(addTorrentParams.contentLayout)}
|
||||
, {S_SAVE_PATH, addTorrentParams.savePath.toString()}
|
||||
, {S_ASSIGNED_CATEGORY, addTorrentParams.category}
|
||||
// === END DEPRECATED CODE === //
|
||||
|
||||
, {S_TORRENT_PARAMS, BitTorrent::serializeAddTorrentParams(addTorrentParams)}
|
||||
};
|
||||
}
|
||||
|
||||
AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, const QString &name)
|
||||
{
|
||||
AutoDownloadRule rule(name.isEmpty() ? jsonObj.value(Str_Name).toString() : name);
|
||||
AutoDownloadRule rule {(name.isEmpty() ? jsonObj.value(S_NAME).toString() : name)};
|
||||
|
||||
rule.setUseRegex(jsonObj.value(Str_UseRegex).toBool(false));
|
||||
rule.setMustContain(jsonObj.value(Str_MustContain).toString());
|
||||
rule.setMustNotContain(jsonObj.value(Str_MustNotContain).toString());
|
||||
rule.setEpisodeFilter(jsonObj.value(Str_EpisodeFilter).toString());
|
||||
rule.setEnabled(jsonObj.value(Str_Enabled).toBool(true));
|
||||
rule.setSavePath(Path(jsonObj.value(Str_SavePath).toString()));
|
||||
rule.setCategory(jsonObj.value(Str_AssignedCategory).toString());
|
||||
rule.setAddPaused(toOptionalBool(jsonObj.value(Str_AddPaused)));
|
||||
rule.setEnabled(jsonObj.value(S_ENABLED).toBool(true));
|
||||
rule.setPriority(jsonObj.value(S_PRIORITY).toInt(0));
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
||||
// === BEGIN DEPRECATED CODE === //
|
||||
if (jsonObj.contains(Str_ContentLayout))
|
||||
{
|
||||
rule.setTorrentContentLayout(jsonValueToContentLayout(jsonObj.value(Str_ContentLayout)));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::optional<bool> createSubfolder = toOptionalBool(jsonObj.value(Str_CreateSubfolder));
|
||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||
if (createSubfolder.has_value())
|
||||
{
|
||||
contentLayout = (*createSubfolder
|
||||
? BitTorrent::TorrentContentLayout::Original
|
||||
: BitTorrent::TorrentContentLayout::NoSubfolder);
|
||||
}
|
||||
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.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));
|
||||
|
||||
rule.setTorrentContentLayout(contentLayout);
|
||||
}
|
||||
// === END DEPRECATED CODE === //
|
||||
// === BEGIN REPLACEMENT CODE === //
|
||||
// rule.setTorrentContentLayout(jsonValueToContentLayout(jsonObj.value(Str_ContentLayout)));
|
||||
// === END REPLACEMENT CODE === //
|
||||
|
||||
rule.setLastMatch(QDateTime::fromString(jsonObj.value(Str_LastMatch).toString(), Qt::RFC2822Date));
|
||||
rule.setIgnoreDays(jsonObj.value(Str_IgnoreDays).toInt());
|
||||
rule.setUseSmartFilter(jsonObj.value(Str_SmartFilter).toBool(false));
|
||||
|
||||
const QJsonValue feedsVal = jsonObj.value(Str_AffectedFeeds);
|
||||
const QJsonValue feedsVal = jsonObj.value(S_AFFECTED_FEEDS);
|
||||
QStringList feedURLs;
|
||||
if (feedsVal.isString())
|
||||
feedURLs << feedsVal.toString();
|
||||
@@ -526,7 +506,7 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
|
||||
feedURLs << urlVal.toString();
|
||||
rule.setFeedURLs(feedURLs);
|
||||
|
||||
const QJsonValue previouslyMatchedVal = jsonObj.value(Str_PreviouslyMatched);
|
||||
const QJsonValue previouslyMatchedVal = jsonObj.value(S_PREVIOUSLY_MATCHED);
|
||||
QStringList previouslyMatched;
|
||||
if (previouslyMatchedVal.isString())
|
||||
{
|
||||
@@ -539,20 +519,61 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
|
||||
}
|
||||
rule.setPreviouslyMatchedEpisodes(previouslyMatched);
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.6.x.
|
||||
// === BEGIN DEPRECATED CODE === //
|
||||
BitTorrent::AddTorrentParams addTorrentParams;
|
||||
if (auto it = jsonObj.find(S_TORRENT_PARAMS); it != jsonObj.end())
|
||||
{
|
||||
addTorrentParams = BitTorrent::parseAddTorrentParams(it->toObject());
|
||||
}
|
||||
else
|
||||
{
|
||||
addTorrentParams.savePath = Path(jsonObj.value(S_SAVE_PATH).toString());
|
||||
addTorrentParams.category = jsonObj.value(S_ASSIGNED_CATEGORY).toString();
|
||||
addTorrentParams.addPaused = toOptionalBool(jsonObj.value(S_ADD_PAUSED));
|
||||
if (!addTorrentParams.savePath.isEmpty())
|
||||
addTorrentParams.useAutoTMM = false;
|
||||
|
||||
if (jsonObj.contains(S_CONTENT_LAYOUT))
|
||||
{
|
||||
addTorrentParams.contentLayout = jsonValueToContentLayout(jsonObj.value(S_CONTENT_LAYOUT));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::optional<bool> createSubfolder = toOptionalBool(jsonObj.value(u"createSubfolder"));
|
||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||
if (createSubfolder.has_value())
|
||||
{
|
||||
contentLayout = (*createSubfolder
|
||||
? BitTorrent::TorrentContentLayout::Original
|
||||
: BitTorrent::TorrentContentLayout::NoSubfolder);
|
||||
}
|
||||
|
||||
addTorrentParams.contentLayout = contentLayout;
|
||||
}
|
||||
}
|
||||
rule.setAddTorrentParams(addTorrentParams);
|
||||
// === END DEPRECATED CODE === //
|
||||
// === BEGIN REPLACEMENT CODE === //
|
||||
// rule.setAddTorrentParams(BitTorrent::parseAddTorrentParams(jsonObj.value(S_TORRENT_PARAMS).object()));
|
||||
// === END REPLACEMENT CODE === //
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
QVariantHash AutoDownloadRule::toLegacyDict() const
|
||||
{
|
||||
const BitTorrent::AddTorrentParams &addTorrentParams = m_dataPtr->addTorrentParams;
|
||||
|
||||
return {{u"name"_qs, name()},
|
||||
{u"must_contain"_qs, mustContain()},
|
||||
{u"must_not_contain"_qs, mustNotContain()},
|
||||
{u"save_path"_qs, savePath().toString()},
|
||||
{u"save_path"_qs, addTorrentParams.savePath.toString()},
|
||||
{u"affected_feeds"_qs, feedURLs()},
|
||||
{u"enabled"_qs, isEnabled()},
|
||||
{u"category_assigned"_qs, assignedCategory()},
|
||||
{u"category_assigned"_qs, addTorrentParams.category},
|
||||
{u"use_regex"_qs, useRegex()},
|
||||
{u"add_paused"_qs, toAddPausedLegacy(addPaused())},
|
||||
{u"add_paused"_qs, toAddPausedLegacy(addTorrentParams.addPaused)},
|
||||
{u"episode_filter"_qs, episodeFilter()},
|
||||
{u"last_match"_qs, lastMatch()},
|
||||
{u"ignore_days"_qs, ignoreDays()}};
|
||||
@@ -560,7 +581,14 @@ QVariantHash AutoDownloadRule::toLegacyDict() const
|
||||
|
||||
AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
|
||||
{
|
||||
AutoDownloadRule rule(dict.value(u"name"_qs).toString());
|
||||
BitTorrent::AddTorrentParams addTorrentParams;
|
||||
addTorrentParams.savePath = Path(dict.value(u"save_path"_qs).toString());
|
||||
addTorrentParams.category = dict.value(u"category_assigned"_qs).toString();
|
||||
addTorrentParams.addPaused = addPausedLegacyToOptionalBool(dict.value(u"add_paused"_qs).toInt());
|
||||
if (!addTorrentParams.savePath.isEmpty())
|
||||
addTorrentParams.useAutoTMM = false;
|
||||
|
||||
AutoDownloadRule rule {dict.value(u"name"_qs).toString()};
|
||||
|
||||
rule.setUseRegex(dict.value(u"use_regex"_qs, false).toBool());
|
||||
rule.setMustContain(dict.value(u"must_contain"_qs).toString());
|
||||
@@ -568,11 +596,9 @@ AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
|
||||
rule.setEpisodeFilter(dict.value(u"episode_filter"_qs).toString());
|
||||
rule.setFeedURLs(dict.value(u"affected_feeds"_qs).toStringList());
|
||||
rule.setEnabled(dict.value(u"enabled"_qs, false).toBool());
|
||||
rule.setSavePath(Path(dict.value(u"save_path"_qs).toString()));
|
||||
rule.setCategory(dict.value(u"category_assigned"_qs).toString());
|
||||
rule.setAddPaused(addPausedLegacyToOptionalBool(dict.value(u"add_paused"_qs).toInt()));
|
||||
rule.setLastMatch(dict.value(u"last_match"_qs).toDateTime());
|
||||
rule.setIgnoreDays(dict.value(u"ignore_days"_qs).toInt());
|
||||
rule.setAddTorrentParams(addTorrentParams);
|
||||
|
||||
return rule;
|
||||
}
|
||||
@@ -625,44 +651,14 @@ void AutoDownloadRule::setName(const QString &name)
|
||||
m_dataPtr->name = name;
|
||||
}
|
||||
|
||||
Path AutoDownloadRule::savePath() const
|
||||
BitTorrent::AddTorrentParams AutoDownloadRule::addTorrentParams() const
|
||||
{
|
||||
return m_dataPtr->savePath;
|
||||
return m_dataPtr->addTorrentParams;
|
||||
}
|
||||
|
||||
void AutoDownloadRule::setSavePath(const Path &savePath)
|
||||
void AutoDownloadRule::setAddTorrentParams(BitTorrent::AddTorrentParams addTorrentParams)
|
||||
{
|
||||
m_dataPtr->savePath = savePath;
|
||||
}
|
||||
|
||||
std::optional<bool> AutoDownloadRule::addPaused() const
|
||||
{
|
||||
return m_dataPtr->addPaused;
|
||||
}
|
||||
|
||||
void AutoDownloadRule::setAddPaused(const std::optional<bool> addPaused)
|
||||
{
|
||||
m_dataPtr->addPaused = addPaused;
|
||||
}
|
||||
|
||||
std::optional<BitTorrent::TorrentContentLayout> AutoDownloadRule::torrentContentLayout() const
|
||||
{
|
||||
return m_dataPtr->contentLayout;
|
||||
}
|
||||
|
||||
void AutoDownloadRule::setTorrentContentLayout(const std::optional<BitTorrent::TorrentContentLayout> contentLayout)
|
||||
{
|
||||
m_dataPtr->contentLayout = contentLayout;
|
||||
}
|
||||
|
||||
QString AutoDownloadRule::assignedCategory() const
|
||||
{
|
||||
return m_dataPtr->category;
|
||||
}
|
||||
|
||||
void AutoDownloadRule::setCategory(const QString &category)
|
||||
{
|
||||
m_dataPtr->category = category;
|
||||
m_dataPtr->addTorrentParams = std::move(addTorrentParams);
|
||||
}
|
||||
|
||||
bool AutoDownloadRule::isEnabled() const
|
||||
@@ -675,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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2017-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <QVariant>
|
||||
|
||||
#include "base/global.h"
|
||||
#include "base/bittorrent/torrentcontentlayout.h"
|
||||
#include "base/bittorrent/addtorrentparams.h"
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
class QDateTime;
|
||||
@@ -49,7 +49,7 @@ namespace RSS
|
||||
class AutoDownloadRule
|
||||
{
|
||||
public:
|
||||
explicit AutoDownloadRule(const QString &name = u""_qs);
|
||||
explicit AutoDownloadRule(const QString &name = {});
|
||||
AutoDownloadRule(const AutoDownloadRule &other);
|
||||
~AutoDownloadRule();
|
||||
|
||||
@@ -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;
|
||||
@@ -81,14 +84,8 @@ namespace RSS
|
||||
QStringList previouslyMatchedEpisodes() const;
|
||||
void setPreviouslyMatchedEpisodes(const QStringList &previouslyMatchedEpisodes);
|
||||
|
||||
Path savePath() const;
|
||||
void setSavePath(const Path &savePath);
|
||||
std::optional<bool> addPaused() const;
|
||||
void setAddPaused(std::optional<bool> addPaused);
|
||||
std::optional<BitTorrent::TorrentContentLayout> torrentContentLayout() const;
|
||||
void setTorrentContentLayout(std::optional<BitTorrent::TorrentContentLayout> contentLayout);
|
||||
QString assignedCategory() const;
|
||||
void setCategory(const QString &category);
|
||||
BitTorrent::AddTorrentParams addTorrentParams() const;
|
||||
void setAddTorrentParams(BitTorrent::AddTorrentParams addTorrentParams);
|
||||
|
||||
bool matches(const QVariantHash &articleData) const;
|
||||
bool accepts(const QVariantHash &articleData);
|
||||
@@ -96,7 +93,7 @@ namespace RSS
|
||||
friend bool operator==(const AutoDownloadRule &left, const AutoDownloadRule &right);
|
||||
|
||||
QJsonObject toJsonObject() const;
|
||||
static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = u""_qs);
|
||||
static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = {});
|
||||
|
||||
QVariantHash toLegacyDict() const;
|
||||
static AutoDownloadRule fromLegacyDict(const QVariantHash &dict);
|
||||
|
||||
@@ -345,7 +345,7 @@ bool Feed::addArticle(const QVariantHash &articleData)
|
||||
|
||||
void Feed::removeOldestArticle()
|
||||
{
|
||||
auto oldestArticle = m_articlesByDate.last();
|
||||
auto *oldestArticle = m_articlesByDate.last();
|
||||
emit articleAboutToBeRemoved(oldestArticle);
|
||||
|
||||
m_articles.remove(oldestArticle->guid());
|
||||
|
||||
@@ -49,7 +49,7 @@ Folder::~Folder()
|
||||
{
|
||||
emit aboutToBeDestroyed(this);
|
||||
|
||||
for (auto item : asConst(items()))
|
||||
for (auto *item : asConst(items()))
|
||||
delete item;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ void Folder::addItem(Item *item)
|
||||
connect(item, &Item::articleAboutToBeRemoved, this, &Item::articleAboutToBeRemoved);
|
||||
connect(item, &Item::unreadCountChanged, this, &Folder::handleItemUnreadCountChanged);
|
||||
|
||||
for (auto article : asConst(item->articles()))
|
||||
for (auto *article : asConst(item->articles()))
|
||||
emit newArticle(article);
|
||||
|
||||
if (item->unreadCount() > 0)
|
||||
@@ -138,7 +138,7 @@ void Folder::removeItem(Item *item)
|
||||
{
|
||||
Q_ASSERT(m_items.contains(item));
|
||||
|
||||
for (auto article : asConst(item->articles()))
|
||||
for (auto *article : asConst(item->articles()))
|
||||
emit articleAboutToBeRemoved(article);
|
||||
|
||||
item->disconnect(this);
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace RSS
|
||||
|
||||
friend class Session;
|
||||
|
||||
explicit Folder(const QString &path = u""_qs);
|
||||
explicit Folder(const QString &path = {});
|
||||
~Folder() override;
|
||||
|
||||
public:
|
||||
|
||||
@@ -45,8 +45,6 @@ Item::Item(const QString &path)
|
||||
{
|
||||
}
|
||||
|
||||
Item::~Item() {}
|
||||
|
||||
void Item::setPath(const QString &path)
|
||||
{
|
||||
if (path != m_path)
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace RSS
|
||||
|
||||
protected:
|
||||
explicit Item(const QString &path);
|
||||
~Item() override;
|
||||
~Item() override = default;
|
||||
|
||||
virtual void cleanup() = 0;
|
||||
|
||||
|
||||
@@ -541,7 +541,7 @@ namespace
|
||||
|
||||
const int PARSINGRESULT_TYPEID = qRegisterMetaType<RSS::Private::ParsingResult>();
|
||||
|
||||
RSS::Private::Parser::Parser(const QString lastBuildDate)
|
||||
RSS::Private::Parser::Parser(const QString &lastBuildDate)
|
||||
{
|
||||
m_result.lastBuildDate = lastBuildDate;
|
||||
}
|
||||
@@ -723,13 +723,16 @@ void RSS::Private::Parser::parseAtomArticle(QXmlStreamReader &xml)
|
||||
: xml.attributes().value(u"href"_qs).toString());
|
||||
|
||||
if (link.startsWith(u"magnet:", Qt::CaseInsensitive))
|
||||
{
|
||||
article[Article::KeyTorrentURL] = link; // magnet link instead of a news URL
|
||||
}
|
||||
else
|
||||
{
|
||||
// Atom feeds can have relative links, work around this and
|
||||
// take the stress of figuring article full URI from UI
|
||||
// Assemble full URI
|
||||
article[Article::KeyLink] = (m_baseUrl.isEmpty() ? link : m_baseUrl + link);
|
||||
|
||||
}
|
||||
}
|
||||
else if ((name == u"summary") || (name == u"content"))
|
||||
{
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace RSS::Private
|
||||
Q_DISABLE_COPY_MOVE(Parser)
|
||||
|
||||
public:
|
||||
explicit Parser(QString lastBuildDate);
|
||||
explicit Parser(const QString &lastBuildDate);
|
||||
void parse(const QByteArray &feedData);
|
||||
|
||||
signals:
|
||||
|
||||
@@ -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"
|
||||
@@ -100,7 +101,7 @@ Session::Session()
|
||||
// Remove legacy/corrupted settings
|
||||
// (at least on Windows, QSettings is case-insensitive and it can get
|
||||
// confused when asked about settings that differ only in their case)
|
||||
auto settingsStorage = SettingsStorage::instance();
|
||||
auto *settingsStorage = SettingsStorage::instance();
|
||||
settingsStorage->removeValue(u"Rss/streamList"_qs);
|
||||
settingsStorage->removeValue(u"Rss/streamAlias"_qs);
|
||||
settingsStorage->removeValue(u"Rss/open_folders"_qs);
|
||||
@@ -140,7 +141,7 @@ nonstd::expected<void, QString> Session::addFolder(const QString &path)
|
||||
if (!result)
|
||||
return result.get_unexpected();
|
||||
|
||||
const auto destFolder = result.value();
|
||||
auto *destFolder = result.value();
|
||||
addItem(new Folder(path), destFolder);
|
||||
store();
|
||||
return {};
|
||||
@@ -155,7 +156,7 @@ nonstd::expected<void, QString> Session::addFeed(const QString &url, const QStri
|
||||
if (!result)
|
||||
return result.get_unexpected();
|
||||
|
||||
const auto destFolder = result.value();
|
||||
auto *destFolder = result.value();
|
||||
auto *feed = new Feed(generateUID(), url, path, this);
|
||||
addItem(feed, destFolder);
|
||||
store();
|
||||
@@ -198,7 +199,7 @@ nonstd::expected<void, QString> Session::moveItem(const QString &itemPath, const
|
||||
if (itemPath.isEmpty())
|
||||
return nonstd::make_unexpected(tr("Cannot move root folder."));
|
||||
|
||||
auto item = m_itemsByPath.value(itemPath);
|
||||
auto *item = m_itemsByPath.value(itemPath);
|
||||
if (!item)
|
||||
return nonstd::make_unexpected(tr("Item doesn't exist: %1.").arg(itemPath));
|
||||
|
||||
@@ -214,11 +215,11 @@ nonstd::expected<void, QString> Session::moveItem(Item *item, const QString &des
|
||||
if (!result)
|
||||
return result.get_unexpected();
|
||||
|
||||
const auto destFolder = result.value();
|
||||
auto *destFolder = result.value();
|
||||
if (static_cast<Item *>(destFolder) == item)
|
||||
return nonstd::make_unexpected(tr("Couldn't move folder into itself."));
|
||||
|
||||
auto srcFolder = static_cast<Folder *>(m_itemsByPath.value(Item::parentPath(item->path())));
|
||||
auto *srcFolder = static_cast<Folder *>(m_itemsByPath.value(Item::parentPath(item->path())));
|
||||
if (srcFolder != destFolder)
|
||||
{
|
||||
srcFolder->removeItem(item);
|
||||
@@ -242,7 +243,7 @@ nonstd::expected<void, QString> Session::removeItem(const QString &itemPath)
|
||||
emit itemAboutToBeRemoved(item);
|
||||
item->cleanup();
|
||||
|
||||
auto folder = static_cast<Folder *>(m_itemsByPath.value(Item::parentPath(item->path())));
|
||||
auto *folder = static_cast<Folder *>(m_itemsByPath.value(Item::parentPath(item->path())));
|
||||
folder->removeItem(item);
|
||||
delete item;
|
||||
store();
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -410,7 +413,7 @@ nonstd::expected<Folder *, QString> Session::prepareItemDest(const QString &path
|
||||
return nonstd::make_unexpected(tr("RSS item with given path already exists: %1.").arg(path));
|
||||
|
||||
const QString destFolderPath = Item::parentPath(path);
|
||||
const auto destFolder = qobject_cast<Folder *>(m_itemsByPath.value(destFolderPath));
|
||||
auto *destFolder = qobject_cast<Folder *>(m_itemsByPath.value(destFolderPath));
|
||||
if (!destFolder)
|
||||
return nonstd::make_unexpected(tr("Parent folder doesn't exist: %1.").arg(destFolderPath));
|
||||
|
||||
@@ -419,21 +422,21 @@ nonstd::expected<Folder *, QString> Session::prepareItemDest(const QString &path
|
||||
|
||||
Folder *Session::addSubfolder(const QString &name, Folder *parentFolder)
|
||||
{
|
||||
auto folder = new Folder(Item::joinPath(parentFolder->path(), name));
|
||||
auto *folder = new Folder(Item::joinPath(parentFolder->path(), name));
|
||||
addItem(folder, parentFolder);
|
||||
return folder;
|
||||
}
|
||||
|
||||
Feed *Session::addFeedToFolder(const QUuid &uid, const QString &url, const QString &name, Folder *parentFolder)
|
||||
{
|
||||
auto feed = new Feed(uid, url, Item::joinPath(parentFolder->path(), name), this);
|
||||
auto *feed = new Feed(uid, url, Item::joinPath(parentFolder->path(), name), this);
|
||||
addItem(feed, parentFolder);
|
||||
return feed;
|
||||
}
|
||||
|
||||
void Session::addItem(Item *item, Folder *destFolder)
|
||||
{
|
||||
if (auto feed = qobject_cast<Feed *>(item))
|
||||
if (auto *feed = qobject_cast<Feed *>(item))
|
||||
{
|
||||
connect(feed, &Feed::titleChanged, this, &Session::handleFeedTitleChanged);
|
||||
connect(feed, &Feed::iconLoaded, this, &Session::feedIconLoaded);
|
||||
@@ -530,7 +533,7 @@ QThread *Session::workingThread() const
|
||||
void Session::handleItemAboutToBeDestroyed(Item *item)
|
||||
{
|
||||
m_itemsByPath.remove(item->path());
|
||||
auto feed = qobject_cast<Feed *>(item);
|
||||
auto *feed = qobject_cast<Feed *>(item);
|
||||
if (feed)
|
||||
{
|
||||
m_feedsByUID.remove(feed->uid());
|
||||
|
||||
@@ -46,7 +46,8 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QStri
|
||||
, this, &SearchDownloadHandler::downloadProcessFinished);
|
||||
const QStringList params
|
||||
{
|
||||
(m_manager->engineLocation() / Path(u"nova2dl.py"_qs)).toString(),
|
||||
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
|
||||
(SearchPluginManager::engineLocation() / Path(u"nova2dl.py"_qs)).toString(),
|
||||
siteUrl,
|
||||
url
|
||||
};
|
||||
|
||||
@@ -73,7 +73,8 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co
|
||||
|
||||
const QStringList params
|
||||
{
|
||||
(m_manager->engineLocation() / Path(u"nova2.py"_qs)).toString(),
|
||||
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
|
||||
(SearchPluginManager::engineLocation() / Path(u"nova2.py"_qs)).toString(),
|
||||
m_usedPlugins.join(u','),
|
||||
m_category
|
||||
};
|
||||
|
||||
@@ -42,9 +42,9 @@ struct SearchResult
|
||||
{
|
||||
QString fileName;
|
||||
QString fileUrl;
|
||||
qlonglong fileSize;
|
||||
qlonglong nbSeeders;
|
||||
qlonglong nbLeechers;
|
||||
qlonglong fileSize = 0;
|
||||
qlonglong nbSeeders = 0;
|
||||
qlonglong nbLeechers = 0;
|
||||
QString siteUrl;
|
||||
QString descrLink;
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QDomNode>
|
||||
#include <QFile>
|
||||
#include <QPointer>
|
||||
#include <QProcess>
|
||||
#include <QUrl>
|
||||
@@ -87,7 +88,7 @@ namespace
|
||||
QPointer<SearchPluginManager> SearchPluginManager::m_instance = nullptr;
|
||||
|
||||
SearchPluginManager::SearchPluginManager()
|
||||
: m_updateUrl(u"http://searchplugins.qbittorrent.org/nova3/engines/"_qs)
|
||||
: m_updateUrl(u"https://searchplugins.qbittorrent.org/nova3/engines/"_qs)
|
||||
{
|
||||
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||
m_instance = this;
|
||||
@@ -366,7 +367,7 @@ QString SearchPluginManager::categoryFullName(const QString &categoryName)
|
||||
return categoryTable.value(categoryName);
|
||||
}
|
||||
|
||||
QString SearchPluginManager::pluginFullName(const QString &pluginName)
|
||||
QString SearchPluginManager::pluginFullName(const QString &pluginName) const
|
||||
{
|
||||
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -509,11 +509,16 @@ void SearchPluginManager::update()
|
||||
QProcess nova;
|
||||
nova.setProcessEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
|
||||
const QStringList params {(engineLocation() / Path(u"/nova2.py"_qs)).toString(), u"--capabilities"_qs};
|
||||
const QStringList params
|
||||
{
|
||||
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
|
||||
(engineLocation() / Path(u"/nova2.py"_qs)).toString(),
|
||||
u"--capabilities"_qs
|
||||
};
|
||||
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))
|
||||
{
|
||||
@@ -609,7 +614,7 @@ void SearchPluginManager::parseVersionInfo(const QByteArray &info)
|
||||
}
|
||||
}
|
||||
|
||||
bool SearchPluginManager::isUpdateNeeded(const QString &pluginName, const PluginVersion newVersion) const
|
||||
bool SearchPluginManager::isUpdateNeeded(const QString &pluginName, const PluginVersion &newVersion) const
|
||||
{
|
||||
const PluginInfo *plugin = pluginInfo(pluginName);
|
||||
if (!plugin) return true;
|
||||
@@ -625,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);
|
||||
|
||||
@@ -52,7 +52,7 @@ struct PluginInfo
|
||||
QString url;
|
||||
QStringList supportedCategories;
|
||||
Path iconPath;
|
||||
bool enabled;
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
class SearchDownloadHandler;
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
void updatePlugin(const QString &name);
|
||||
void installPlugin(const QString &source);
|
||||
bool uninstallPlugin(const QString &name);
|
||||
static void updateIconPath(PluginInfo *const plugin);
|
||||
static void updateIconPath(PluginInfo *plugin);
|
||||
void checkForUpdates();
|
||||
|
||||
SearchHandler *startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
|
||||
static PluginVersion getPluginVersion(const Path &filePath);
|
||||
static QString categoryFullName(const QString &categoryName);
|
||||
QString pluginFullName(const QString &pluginName);
|
||||
QString pluginFullName(const QString &pluginName) const;
|
||||
static Path pluginsLocation();
|
||||
static Path engineLocation();
|
||||
|
||||
@@ -109,7 +109,7 @@ private:
|
||||
void updateNova();
|
||||
void parseVersionInfo(const QByteArray &info);
|
||||
void installPlugin_impl(const QString &name, const Path &path);
|
||||
bool isUpdateNeeded(const QString &pluginName, PluginVersion newVersion) const;
|
||||
bool isUpdateNeeded(const QString &pluginName, const PluginVersion &newVersion) const;
|
||||
|
||||
void versionInfoDownloadFinished(const Net::DownloadResult &result);
|
||||
void pluginDownloadFinished(const Net::DownloadResult &result);
|
||||
|
||||
@@ -59,7 +59,6 @@ FileGuard::~FileGuard()
|
||||
TorrentFileGuard::TorrentFileGuard(const Path &path, const TorrentFileGuard::AutoDeleteMode mode)
|
||||
: FileGuard {mode != Never ? path : Path()}
|
||||
, m_mode {mode}
|
||||
, m_wasAdded {false}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -36,10 +36,8 @@
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QSet>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
@@ -70,124 +68,12 @@ const QString CONF_FILE_NAME = u"watched_folders.json"_qs;
|
||||
const QString OPTION_ADDTORRENTPARAMS = u"add_torrent_params"_qs;
|
||||
const QString OPTION_RECURSIVE = u"recursive"_qs;
|
||||
|
||||
const QString PARAM_CATEGORY = u"category"_qs;
|
||||
const QString PARAM_TAGS = u"tags"_qs;
|
||||
const QString PARAM_SAVEPATH = u"save_path"_qs;
|
||||
const QString PARAM_USEDOWNLOADPATH = u"use_download_path"_qs;
|
||||
const QString PARAM_DOWNLOADPATH = u"download_path"_qs;
|
||||
const QString PARAM_OPERATINGMODE = u"operating_mode"_qs;
|
||||
const QString PARAM_QUEUETOP = u"add_to_top_of_queue"_qs;
|
||||
const QString PARAM_STOPPED = u"stopped"_qs;
|
||||
const QString PARAM_SKIPCHECKING = u"skip_checking"_qs;
|
||||
const QString PARAM_CONTENTLAYOUT = u"content_layout"_qs;
|
||||
const QString PARAM_AUTOTMM = u"use_auto_tmm"_qs;
|
||||
const QString PARAM_UPLOADLIMIT = u"upload_limit"_qs;
|
||||
const QString PARAM_DOWNLOADLIMIT = u"download_limit"_qs;
|
||||
const QString PARAM_SEEDINGTIMELIMIT = u"seeding_time_limit"_qs;
|
||||
const QString PARAM_RATIOLIMIT = u"ratio_limit"_qs;
|
||||
|
||||
namespace
|
||||
{
|
||||
TagSet parseTagSet(const QJsonArray &jsonArr)
|
||||
{
|
||||
TagSet tags;
|
||||
for (const QJsonValue &jsonVal : jsonArr)
|
||||
tags.insert(jsonVal.toString());
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
QJsonArray serializeTagSet(const TagSet &tags)
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const QString &tag : tags)
|
||||
arr.append(tag);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
std::optional<bool> getOptionalBool(const QJsonObject &jsonObj, const QString &key)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
if (jsonVal.isUndefined() || jsonVal.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
return jsonVal.toBool();
|
||||
}
|
||||
|
||||
template <typename Enum>
|
||||
std::optional<Enum> getOptionalEnum(const QJsonObject &jsonObj, const QString &key)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
if (jsonVal.isUndefined() || jsonVal.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
return Utils::String::toEnum<Enum>(jsonVal.toString(), {});
|
||||
}
|
||||
|
||||
template <typename Enum>
|
||||
Enum getEnum(const QJsonObject &jsonObj, const QString &key)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonObj.value(key);
|
||||
return Utils::String::toEnum<Enum>(jsonVal.toString(), {});
|
||||
}
|
||||
|
||||
BitTorrent::AddTorrentParams parseAddTorrentParams(const QJsonObject &jsonObj)
|
||||
{
|
||||
BitTorrent::AddTorrentParams params;
|
||||
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
||||
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
|
||||
params.savePath = Path(jsonObj.value(PARAM_SAVEPATH).toString());
|
||||
params.useDownloadPath = getOptionalBool(jsonObj, PARAM_USEDOWNLOADPATH);
|
||||
params.downloadPath = Path(jsonObj.value(PARAM_DOWNLOADPATH).toString());
|
||||
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
||||
params.addToQueueTop = getOptionalBool(jsonObj, PARAM_QUEUETOP);
|
||||
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
||||
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
||||
params.contentLayout = getOptionalEnum<BitTorrent::TorrentContentLayout>(jsonObj, PARAM_CONTENTLAYOUT);
|
||||
params.useAutoTMM = getOptionalBool(jsonObj, PARAM_AUTOTMM);
|
||||
params.uploadLimit = jsonObj.value(PARAM_UPLOADLIMIT).toInt(-1);
|
||||
params.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1);
|
||||
params.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
params.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(BitTorrent::Torrent::USE_GLOBAL_RATIO);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
QJsonObject serializeAddTorrentParams(const BitTorrent::AddTorrentParams ¶ms)
|
||||
{
|
||||
QJsonObject jsonObj {
|
||||
{PARAM_CATEGORY, params.category},
|
||||
{PARAM_TAGS, serializeTagSet(params.tags)},
|
||||
{PARAM_SAVEPATH, params.savePath.data()},
|
||||
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||
{PARAM_UPLOADLIMIT, params.uploadLimit},
|
||||
{PARAM_DOWNLOADLIMIT, params.downloadLimit},
|
||||
{PARAM_SEEDINGTIMELIMIT, params.seedingTimeLimit},
|
||||
{PARAM_RATIOLIMIT, params.ratioLimit}
|
||||
};
|
||||
|
||||
if (params.addToQueueTop)
|
||||
jsonObj[PARAM_QUEUETOP] = *params.addToQueueTop;
|
||||
if (params.addPaused)
|
||||
jsonObj[PARAM_STOPPED] = *params.addPaused;
|
||||
if (params.contentLayout)
|
||||
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
|
||||
if (params.useAutoTMM)
|
||||
jsonObj[PARAM_AUTOTMM] = *params.useAutoTMM;
|
||||
if (params.useDownloadPath)
|
||||
jsonObj[PARAM_USEDOWNLOADPATH] = *params.useDownloadPath;
|
||||
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
TorrentFilesWatcher::WatchedFolderOptions parseWatchedFolderOptions(const QJsonObject &jsonObj)
|
||||
{
|
||||
TorrentFilesWatcher::WatchedFolderOptions options;
|
||||
options.addTorrentParams = parseAddTorrentParams(jsonObj.value(OPTION_ADDTORRENTPARAMS).toObject());
|
||||
options.addTorrentParams = BitTorrent::parseAddTorrentParams(jsonObj.value(OPTION_ADDTORRENTPARAMS).toObject());
|
||||
options.recursive = jsonObj.value(OPTION_RECURSIVE).toBool();
|
||||
|
||||
return options;
|
||||
@@ -195,10 +81,8 @@ namespace
|
||||
|
||||
QJsonObject serializeWatchedFolderOptions(const TorrentFilesWatcher::WatchedFolderOptions &options)
|
||||
{
|
||||
return {
|
||||
{OPTION_ADDTORRENTPARAMS, serializeAddTorrentParams(options.addTorrentParams)},
|
||||
{OPTION_RECURSIVE, options.recursive}
|
||||
};
|
||||
return {{OPTION_ADDTORRENTPARAMS, BitTorrent::serializeAddTorrentParams(options.addTorrentParams)},
|
||||
{OPTION_RECURSIVE, options.recursive}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,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;
|
||||
}
|
||||
|
||||
@@ -542,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
|
||||
{
|
||||
|
||||
@@ -62,8 +62,7 @@ TorrentFilter::TorrentFilter(const Type type, const std::optional<TorrentIDSet>
|
||||
|
||||
TorrentFilter::TorrentFilter(const QString &filter, const std::optional<TorrentIDSet> &idSet
|
||||
, const std::optional<QString> &category, const std::optional<QString> &tag)
|
||||
: m_type(All)
|
||||
, m_category(category)
|
||||
: m_category(category)
|
||||
, m_tag(tag)
|
||||
, m_idSet(idSet)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -36,9 +37,11 @@ class QByteArray;
|
||||
namespace Utils::ByteArray
|
||||
{
|
||||
// Mimic QStringView(in).split(sep, behavior)
|
||||
QVector<QByteArray> splitToViews(const QByteArray &in, const QByteArray &sep, const Qt::SplitBehavior behavior = Qt::KeepEmptyParts);
|
||||
QVector<QByteArray> splitToViews(const QByteArray &in, const QByteArray &sep, Qt::SplitBehavior behavior = Qt::KeepEmptyParts);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ bool Utils::ForeignApps::PythonInfo::isValid() const
|
||||
|
||||
bool Utils::ForeignApps::PythonInfo::isSupportedVersion() const
|
||||
{
|
||||
return (version >= Version {3, 5, 0});
|
||||
return (version >= Version {3, 7, 0});
|
||||
}
|
||||
|
||||
PythonInfo Utils::ForeignApps::pythonInfo()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user