mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-02 21:52:32 -06:00
Compare commits
387 Commits
v4_5_x
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bc49423c7 | ||
|
|
3d3ce1c5d7 | ||
|
|
832d68d5cb | ||
|
|
ec8802203d | ||
|
|
4beee27701 | ||
|
|
716aca17f8 | ||
|
|
d85ed84e83 | ||
|
|
20985f9960 | ||
|
|
e6cde0b4b4 | ||
|
|
a84259dd1a | ||
|
|
4f3dbf7f59 | ||
|
|
d877215018 | ||
|
|
7512b92a36 | ||
|
|
81a3479fd7 | ||
|
|
edeb62c25d | ||
|
|
45f0b27ed1 | ||
|
|
ffb3f60a22 | ||
|
|
c14b08bd1d | ||
|
|
91560e6e60 | ||
|
|
7df54ad534 | ||
|
|
1e88650bae | ||
|
|
604986e90f | ||
|
|
c77466abb0 | ||
|
|
a9d8cf2ea9 | ||
|
|
22420339a5 | ||
|
|
9076ff8876 | ||
|
|
af20233dfc | ||
|
|
dfd735f2dc | ||
|
|
2a04a4d077 | ||
|
|
67d340ad63 | ||
|
|
2b69cabc2c | ||
|
|
062e35e6b0 | ||
|
|
3088b38d7e | ||
|
|
f5b5570a3b | ||
|
|
80bb19701c | ||
|
|
8376707379 | ||
|
|
483ccb39bf | ||
|
|
41e44d22ea | ||
|
|
766fce82b1 | ||
|
|
938adca47d | ||
|
|
5bb02cbd90 | ||
|
|
66777f3304 | ||
|
|
508896c4f2 | ||
|
|
c1d64cc3ea | ||
|
|
57085ca126 | ||
|
|
7b4b7c2b81 | ||
|
|
6a3a5fac83 | ||
|
|
35e18498d9 | ||
|
|
f99a98306d | ||
|
|
b0cfe53329 | ||
|
|
060466e326 | ||
|
|
392949b313 | ||
|
|
b17307f283 | ||
|
|
e739d38061 | ||
|
|
20f4d0c4e3 | ||
|
|
4f6038c350 | ||
|
|
f08556be30 | ||
|
|
543745b3f2 | ||
|
|
cb0c09769f | ||
|
|
e8f5a3b44e | ||
|
|
5a660fc8a9 | ||
|
|
488464731d | ||
|
|
fb68604ee7 | ||
|
|
4ef9a6444a | ||
|
|
66dfe8545d | ||
|
|
fff7b1dcbd | ||
|
|
b5b1f51cee | ||
|
|
33875aa70a | ||
|
|
9d7dad4a61 | ||
|
|
213b6e316c | ||
|
|
ffc3d8d345 | ||
|
|
7ec80263e1 | ||
|
|
66e533f505 | ||
|
|
045b4f0a06 | ||
|
|
80c637bf99 | ||
|
|
48d5e3326b | ||
|
|
80791e328d | ||
|
|
08a771468d | ||
|
|
c3fc96dfe6 | ||
|
|
715a4f3eb6 | ||
|
|
8655e48336 | ||
|
|
3c139ca333 | ||
|
|
f4deb1050f | ||
|
|
889ed5bce9 | ||
|
|
6680fdda18 | ||
|
|
88bd4f270f | ||
|
|
86a5ad3241 | ||
|
|
1c654d8f47 | ||
|
|
c90863f217 | ||
|
|
deec2ae1b1 | ||
|
|
f37fff31ae | ||
|
|
dffb93a6aa | ||
|
|
3b948b0130 | ||
|
|
b3d2ba7d07 | ||
|
|
03d3552ee0 | ||
|
|
379b0dbe40 | ||
|
|
f213f81727 | ||
|
|
1f2a6455b6 | ||
|
|
fc8c74989b | ||
|
|
b01a48879a | ||
|
|
e780b3a9b7 | ||
|
|
2bbfd317ce | ||
|
|
e6d85a468b | ||
|
|
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 | ||
|
|
5dcc14153f | ||
|
|
4a66d705b8 | ||
|
|
9d7fcea5d6 | ||
|
|
b8cd614775 | ||
|
|
a9ab2d9b9e | ||
|
|
cecf2d28e6 | ||
|
|
a01f1014b9 | ||
|
|
77411760a0 | ||
|
|
0dcb65bb7c | ||
|
|
d40be79c69 | ||
|
|
b55d4b1733 | ||
|
|
40e28930a4 | ||
|
|
5a3579a3f9 | ||
|
|
9de8abadb6 | ||
|
|
05c5cdab96 | ||
|
|
1e1c1725ab | ||
|
|
b5c57af869 | ||
|
|
eb875ac8c1 | ||
|
|
a36358d7d0 | ||
|
|
679e592a5c | ||
|
|
b922441a7c | ||
|
|
941c587c68 | ||
|
|
77bd09bb8b | ||
|
|
8bcac1bed2 | ||
|
|
cdded6cef7 | ||
|
|
8cbe4a571c | ||
|
|
ee9d2173e0 | ||
|
|
a450a7c6e1 | ||
|
|
d41a77841d | ||
|
|
ae06daba6a | ||
|
|
77aa85fbd3 | ||
|
|
989b1e6c2c | ||
|
|
9ef23d524d | ||
|
|
f16e903623 | ||
|
|
0bb0829a9a | ||
|
|
fa30b70453 | ||
|
|
e4f90730b2 | ||
|
|
6fd522472c | ||
|
|
0f32de9d8c | ||
|
|
f630d84858 | ||
|
|
ee6f699b48 | ||
|
|
ce9bdaef5c | ||
|
|
37c04fdeed | ||
|
|
c51aa2d573 | ||
|
|
b922e1ae73 | ||
|
|
dd48f62d66 | ||
|
|
f5b5312cf0 | ||
|
|
58a654a70f | ||
|
|
ff0f3b4975 | ||
|
|
8df68ac878 | ||
|
|
2f9b313287 | ||
|
|
cbf591a8b5 | ||
|
|
96da685e5d | ||
|
|
6ac14d0c57 | ||
|
|
4745a40f0b | ||
|
|
8993d87b32 | ||
|
|
8df80b67f9 | ||
|
|
466314675c | ||
|
|
d75fd3fcde | ||
|
|
7ae83df5a5 | ||
|
|
19f55512c1 | ||
|
|
6e25db444e | ||
|
|
d2c4b69f47 | ||
|
|
4170b4e21b | ||
|
|
9fb9ca47f6 | ||
|
|
917190d936 | ||
|
|
1e913f46f0 | ||
|
|
4c0ebc0e0f | ||
|
|
1b0f5b8567 | ||
|
|
6a4bb5c1b7 | ||
|
|
3fea9f5a33 | ||
|
|
7600f59f3a | ||
|
|
915121a0dd | ||
|
|
1be5b3abd8 | ||
|
|
e37661d53a | ||
|
|
d06f78dbbd | ||
|
|
5d4766edbe | ||
|
|
72ac92ec68 | ||
|
|
22ea508ff6 | ||
|
|
b2213ded6d | ||
|
|
1ea2fe5b8d | ||
|
|
7227d2b2b2 | ||
|
|
0dcbf9f698 | ||
|
|
09e58df03f | ||
|
|
d256db5072 | ||
|
|
10153f0063 | ||
|
|
c6a1b977b3 | ||
|
|
870bb42e4f | ||
|
|
b61c7b7220 | ||
|
|
c58fb92365 | ||
|
|
5e952a561b | ||
|
|
ca72360b6f | ||
|
|
630b4ed3b9 | ||
|
|
cba9680ef9 | ||
|
|
2310dcd136 | ||
|
|
ee00a80796 | ||
|
|
051bac5e59 | ||
|
|
771c58d000 | ||
|
|
53cec6db09 | ||
|
|
43e059801e | ||
|
|
ce35a06ec3 | ||
|
|
32e4371208 | ||
|
|
0d376e7fd6 | ||
|
|
2b20d5b260 | ||
|
|
719e4afd8c | ||
|
|
9cdf660ddb | ||
|
|
5dbccf3473 | ||
|
|
8db2d04dbb | ||
|
|
209850064a | ||
|
|
e628b7d527 | ||
|
|
61dbb211c0 | ||
|
|
71f4a5667c | ||
|
|
b33dc7d831 | ||
|
|
c5a4a0db2c | ||
|
|
b9e045e80b | ||
|
|
cfd0c5433e | ||
|
|
ebad387c1a | ||
|
|
3f39bd9f35 | ||
|
|
f8236eb397 | ||
|
|
23a56c95e3 | ||
|
|
6f8aa07a10 | ||
|
|
594f9e8632 | ||
|
|
aeae065007 | ||
|
|
b12fdcf018 | ||
|
|
84fabf14c8 | ||
|
|
0ec47db9cd | ||
|
|
3cf0004665 | ||
|
|
40258f6a2f | ||
|
|
b335114219 | ||
|
|
998b08f5d8 | ||
|
|
991c30943a | ||
|
|
ad2be39c33 | ||
|
|
c3936cd4b6 | ||
|
|
d2e595aac3 | ||
|
|
b8aa9e5609 | ||
|
|
2109c4e1ae | ||
|
|
ac3ad17a9e | ||
|
|
31c7306bd2 | ||
|
|
4741aab7a3 | ||
|
|
679e3b8bea | ||
|
|
25ea0d274b | ||
|
|
12b58be8c2 | ||
|
|
e4f1485c82 | ||
|
|
1b2ff0f6f8 | ||
|
|
a31755bbc8 | ||
|
|
311e0f21eb | ||
|
|
b86366f243 | ||
|
|
58d1c80b12 | ||
|
|
182915f801 | ||
|
|
50c08e55cd | ||
|
|
4307a09621 | ||
|
|
d531d6d221 | ||
|
|
4cf94a6fa0 | ||
|
|
4cb60f4870 | ||
|
|
d82edb2838 | ||
|
|
c91eefe469 | ||
|
|
327affa340 | ||
|
|
4e7c2589e4 | ||
|
|
17ce07230d | ||
|
|
fda6c9a3d9 | ||
|
|
92af2922c7 | ||
|
|
1cee69da6c | ||
|
|
f54b66eb75 | ||
|
|
3563bad5fc | ||
|
|
1f3f96f7aa | ||
|
|
7022adb89b | ||
|
|
bac57de5f5 | ||
|
|
bdd56a52d3 | ||
|
|
dcdbd02102 | ||
|
|
b68c4e2106 | ||
|
|
67cb75e9d3 | ||
|
|
f9eefe866c | ||
|
|
c636618cf3 | ||
|
|
0f82c16936 | ||
|
|
d328eeb5be | ||
|
|
d90ea0d3be | ||
|
|
e7ece66717 | ||
|
|
6c9c40fd7c | ||
|
|
529c1ec9f4 | ||
|
|
93429840c8 | ||
|
|
6aee7f95b7 | ||
|
|
0b70ccf9e9 | ||
|
|
da586828be | ||
|
|
2e4431f0b8 | ||
|
|
162273da47 | ||
|
|
22fb9797c4 | ||
|
|
f6735401f4 | ||
|
|
06c4c58613 | ||
|
|
c80238d66f | ||
|
|
6a560016dd | ||
|
|
99b7663fa9 | ||
|
|
be0f34a69e | ||
|
|
5f2d807861 | ||
|
|
106adf135c |
@@ -50,7 +50,7 @@ before_build:
|
|||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- cd "%REPO_DIR%"
|
- cd "%REPO_DIR%"
|
||||||
# lupdate chokes when it parses headers from system inludes, especially Boost
|
# lupdate chokes when it parses headers from system includes, especially Boost
|
||||||
# it also chokes with the sources from src/app/qtlocalpeer (formerly qtsingleapplication)
|
# it also chokes with the sources from src/app/qtlocalpeer (formerly qtsingleapplication)
|
||||||
# Workaround: temporarily rename them to run lupdate with the .pro file
|
# Workaround: temporarily rename them to run lupdate with the .pro file
|
||||||
- RENAME conf.pri conf.pri.temp
|
- RENAME conf.pri conf.pri.temp
|
||||||
|
|||||||
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: "*"
|
||||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -5,3 +5,5 @@ core.eol=lf
|
|||||||
*.png binary
|
*.png binary
|
||||||
*.qm binary
|
*.qm binary
|
||||||
*.zip binary
|
*.zip binary
|
||||||
|
|
||||||
|
test/testdata/crlf.txt text eol=crlf
|
||||||
|
|||||||
34
.github/workflows/ci_macos.yaml
vendored
34
.github/workflows/ci_macos.yaml
vendored
@@ -17,12 +17,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.8", "1.2.18"]
|
libt_version: ["2.0.9", "1.2.19"]
|
||||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||||
qt_version: ["5.15.2", "6.4.0"]
|
qt_version: ["5.15.2", "6.5.0"]
|
||||||
exclude:
|
exclude:
|
||||||
- libt_version: "1.2.18"
|
- libt_version: "1.2.19"
|
||||||
qt_version: "6.4.0"
|
qt_version: "6.5.0"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
@@ -33,14 +33,18 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
uses: Wandalen/wretry.action@v1
|
||||||
export \
|
env:
|
||||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 \
|
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||||
HOMEBREW_NO_INSTALL_CLEANUP=1
|
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||||
brew update > /dev/null
|
with:
|
||||||
brew install \
|
attempt_delay: 20000
|
||||||
cmake ninja \
|
attempt_limit: 6
|
||||||
openssl@1.1 zlib
|
command: |
|
||||||
|
brew update > /dev/null
|
||||||
|
brew install \
|
||||||
|
cmake ninja \
|
||||||
|
openssl@1.1 zlib
|
||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
uses: Chocobo1/setup-ccache-action@v1
|
uses: Chocobo1/setup-ccache-action@v1
|
||||||
@@ -52,7 +56,7 @@ jobs:
|
|||||||
curl \
|
curl \
|
||||||
-L \
|
-L \
|
||||||
-o "${{ runner.temp }}/boost.tar.bz2" \
|
-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 }}/.."
|
tar -xf "${{ runner.temp }}/boost.tar.bz2" -C "${{ github.workspace }}/.."
|
||||||
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
||||||
|
|
||||||
@@ -85,7 +89,7 @@ jobs:
|
|||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
|
|
||||||
- name: Build qBittorrent (Qt5)
|
- name: Build qBittorrent (Qt5)
|
||||||
if: ${{ startsWith(matrix.qt_version, 5) }}
|
if: startsWith(matrix.qt_version, 5)
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
@@ -104,7 +108,7 @@ jobs:
|
|||||||
cmake --build build --target check
|
cmake --build build --target check
|
||||||
|
|
||||||
- name: Build qBittorrent (Qt6)
|
- name: Build qBittorrent (Qt6)
|
||||||
if: ${{ startsWith(matrix.qt_version, 6) }}
|
if: startsWith(matrix.qt_version, 6)
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS -Wno-gnu-zero-variadic-macro-arguments -Werror -Wno-error=deprecated-declarations" \
|
CXXFLAGS="$CXXFLAGS -Wno-gnu-zero-variadic-macro-arguments -Werror -Wno-error=deprecated-declarations" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
|
|||||||
24
.github/workflows/ci_ubuntu.yaml
vendored
24
.github/workflows/ci_ubuntu.yaml
vendored
@@ -4,6 +4,7 @@ on: [pull_request, push]
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
|
security-events: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@@ -17,11 +18,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.8", "1.2.18"]
|
libt_version: ["2.0.9", "1.2.19"]
|
||||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||||
qt_version: ["5.15.2", "6.2.0"]
|
qt_version: ["5.15.2", "6.2.0"]
|
||||||
exclude:
|
exclude:
|
||||||
- libt_version: "1.2.18"
|
- libt_version: "1.2.19"
|
||||||
qt_version: "6.2.0"
|
qt_version: "6.2.0"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -59,14 +60,23 @@ jobs:
|
|||||||
cmake \
|
cmake \
|
||||||
-B build \
|
-B build \
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
cmake --build build
|
cmake --build build
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
|
|
||||||
|
# to avoid scanning 3rdparty codebases, initialize it just before building qbt
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
if: startsWith(matrix.libt_version, 2) && (matrix.qbt_gui == 'GUI=ON') && startsWith(matrix.qt_version, 6)
|
||||||
|
with:
|
||||||
|
config-file: ./.github/workflows/helper/codeql/cpp.yaml
|
||||||
|
languages: cpp
|
||||||
|
|
||||||
- name: Build qBittorrent (Qt5)
|
- name: Build qBittorrent (Qt5)
|
||||||
if: ${{ startsWith(matrix.qt_version, 5) }}
|
if: startsWith(matrix.qt_version, 5)
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
@@ -85,7 +95,7 @@ jobs:
|
|||||||
DESTDIR="qbittorrent" cmake --install build
|
DESTDIR="qbittorrent" cmake --install build
|
||||||
|
|
||||||
- name: Build qBittorrent (Qt6)
|
- name: Build qBittorrent (Qt6)
|
||||||
if: ${{ startsWith(matrix.qt_version, 6) }}
|
if: startsWith(matrix.qt_version, 6)
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS -Werror" \
|
CXXFLAGS="$CXXFLAGS -Werror" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
@@ -104,6 +114,12 @@ jobs:
|
|||||||
cmake --build build --target check
|
cmake --build build --target check
|
||||||
DESTDIR="qbittorrent" cmake --install build
|
DESTDIR="qbittorrent" cmake --install build
|
||||||
|
|
||||||
|
- name: Run CodeQL analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
if: startsWith(matrix.libt_version, 2) && (matrix.qbt_gui == 'GUI=ON') && startsWith(matrix.qt_version, 6)
|
||||||
|
with:
|
||||||
|
category: ${{ github.base_ref || github.ref_name }}
|
||||||
|
|
||||||
- name: Prepare build artifacts
|
- name: Prepare build artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir upload
|
mkdir upload
|
||||||
|
|||||||
12
.github/workflows/ci_webui.yaml
vendored
12
.github/workflows/ci_webui.yaml
vendored
@@ -2,7 +2,8 @@ name: CI - WebUI
|
|||||||
|
|
||||||
on: [pull_request, push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
permissions: {}
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@@ -36,3 +37,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
npm run format
|
npm run format
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
config-file: ./.github/workflows/helper/codeql/js.yaml
|
||||||
|
languages: javascript
|
||||||
|
|
||||||
|
- name: Run CodeQL analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
6
.github/workflows/ci_windows.yaml
vendored
6
.github/workflows/ci_windows.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.8", "1.2.18"]
|
libt_version: ["2.0.9", "1.2.19"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
- name: Install boost
|
- name: Install boost
|
||||||
run: |
|
run: |
|
||||||
aria2c `
|
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 }}" `
|
-d "${{ runner.temp }}" `
|
||||||
-o "boost.7z"
|
-o "boost.7z"
|
||||||
7z x "${{ runner.temp }}/boost.7z" -o"${{ github.workspace }}/.."
|
7z x "${{ runner.temp }}/boost.7z" -o"${{ github.workspace }}/.."
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
version: "6.4.0"
|
version: "6.5.0"
|
||||||
archives: qtbase qtsvg qttools
|
archives: qtbase qtsvg qttools
|
||||||
|
|
||||||
- name: Install libtorrent
|
- name: Install libtorrent
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ jobs:
|
|||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
version: "6.4.0"
|
version: "6.5.0"
|
||||||
archives: icu qtbase qtsvg qttools
|
archives: icu qtbase qtsvg qttools
|
||||||
|
|
||||||
- name: Install libtorrent
|
- name: Install libtorrent
|
||||||
run: |
|
run: |
|
||||||
git clone \
|
git clone \
|
||||||
--branch "v2.0.8" \
|
--branch "v2.0.9" \
|
||||||
--depth 1 \
|
--depth 1 \
|
||||||
--recurse-submodules \
|
--recurse-submodules \
|
||||||
https://github.com/arvidn/libtorrent.git
|
https://github.com/arvidn/libtorrent.git
|
||||||
@@ -69,7 +69,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Submit the result to Coverity Scan
|
- name: Submit the result to Coverity Scan
|
||||||
run: |
|
run: |
|
||||||
tar caf qbittorrent.xz cov-int
|
tar -caf qbittorrent.xz cov-int
|
||||||
curl \
|
curl \
|
||||||
--form token="${{ secrets.COVERITY_SCAN_TOKEN }}" \
|
--form token="${{ secrets.COVERITY_SCAN_TOKEN }}" \
|
||||||
--form email=sledgehammer999@qbittorrent.org \
|
--form email=sledgehammer999@qbittorrent.org \
|
||||||
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
|
# this file is called from AppRun so 'root_dir' will point to where AppRun is
|
||||||
root_dir="$(readlink -f "$(dirname "$0")")"
|
root_dir="$(readlink -f "$(dirname "$0")")"
|
||||||
|
|
||||||
# Insert the default values because after the test we prepend our path
|
# 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 it will create problems with DEs (eg KDE) that don't set the variable
|
||||||
# and rely on the default paths
|
# 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/"
|
XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
14
.github/workflows/helper/codeql/cpp.yaml
vendored
Normal file
14
.github/workflows/helper/codeql/cpp.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: "CodeQL config for C++"
|
||||||
|
|
||||||
|
queries:
|
||||||
|
- uses: security-and-quality
|
||||||
|
|
||||||
|
query-filters:
|
||||||
|
- exclude:
|
||||||
|
id: cpp/commented-out-code
|
||||||
|
- exclude:
|
||||||
|
id: cpp/include-non-header
|
||||||
|
- exclude:
|
||||||
|
id: cpp/loop-variable-changed
|
||||||
|
- exclude:
|
||||||
|
id: cpp/useless-expression
|
||||||
11
.github/workflows/helper/codeql/js.yaml
vendored
Normal file
11
.github/workflows/helper/codeql/js.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
name: "CodeQL config for Javascript"
|
||||||
|
|
||||||
|
paths-ignore:
|
||||||
|
- "**/lib/*"
|
||||||
|
|
||||||
|
queries:
|
||||||
|
- uses: security-and-quality
|
||||||
|
|
||||||
|
query-filters:
|
||||||
|
- exclude:
|
||||||
|
id: js/superfluous-trailing-arguments
|
||||||
18
.github/workflows/helper/pre-commit/.typos.toml
vendored
Normal file
18
.github/workflows/helper/pre-commit/.typos.toml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# https://github.com/crate-ci/typos/blob/master/docs/reference.md
|
||||||
|
# https://github.com/crate-ci/typos/blob/master/docs/design.md#identifiers-and-words
|
||||||
|
|
||||||
|
# try adding to `identifiers` list first, if doesn't work then `words` list
|
||||||
|
|
||||||
|
[default.extend-identifiers]
|
||||||
|
additionals = "additionals"
|
||||||
|
caf = "caf"
|
||||||
|
curren = "curren"
|
||||||
|
FO = "FO"
|
||||||
|
ket = "ket"
|
||||||
|
Q_INVOKABLE = "Q_INVOKABLE"
|
||||||
|
switchs = "switchs"
|
||||||
|
ths = "ths"
|
||||||
|
|
||||||
|
[default.extend-words]
|
||||||
|
BA = "BA"
|
||||||
|
helo = "helo"
|
||||||
2
.github/workflows/stale_bot.yaml
vendored
2
.github/workflows/stale_bot.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Mark and close stale PRs
|
- name: Mark and close stale PRs
|
||||||
uses: actions/stale@v5
|
uses: actions/stale@v8
|
||||||
with:
|
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."
|
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."
|
close-pr-message: "This PR was closed because it has been stalled for some time with no activity."
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: check-translation-tag
|
- id: check-translation-tag
|
||||||
name: Check newline characters in <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
|
language: script
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
@@ -13,7 +13,7 @@ repos:
|
|||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
||||||
rev: v4.3.0
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-json
|
- id: check-json
|
||||||
name: Check JSON files
|
name: Check JSON files
|
||||||
@@ -33,17 +33,19 @@ repos:
|
|||||||
args: ["--fix=lf"]
|
args: ["--fix=lf"]
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
compile_commands.json |
|
src/webui/www/private/css/lib/.* |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.* |
|
||||||
|
test/testdata/crlf.txt
|
||||||
)$
|
)$
|
||||||
|
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
name: Check trailing newlines
|
name: Check trailing newlines
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
compile_commands.json |
|
|
||||||
configure |
|
configure |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/css/lib/.* |
|
||||||
|
src/webui/www/private/scripts/lib/.* |
|
||||||
|
test/testdata/crlf.txt
|
||||||
)$
|
)$
|
||||||
exclude_types:
|
exclude_types:
|
||||||
- svg
|
- svg
|
||||||
@@ -53,7 +55,54 @@ repos:
|
|||||||
name: Check trailing whitespaces
|
name: Check trailing whitespaces
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
|
src/webui/www/private/css/lib/.* |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.*
|
||||||
)$
|
)$
|
||||||
exclude_types:
|
exclude_types:
|
||||||
- ts
|
- ts
|
||||||
|
|
||||||
|
- repo: https://github.com/codespell-project/codespell
|
||||||
|
rev: v2.2.5
|
||||||
|
hooks:
|
||||||
|
- id: codespell
|
||||||
|
name: Check spelling (codespell)
|
||||||
|
args: ["--ignore-words-list", "additionals,curren,fo,ket,superseeding,te,ths"]
|
||||||
|
exclude: |
|
||||||
|
(?x)^(
|
||||||
|
.*\.desktop |
|
||||||
|
.*\.qrc |
|
||||||
|
build-aux/.* |
|
||||||
|
Changelog |
|
||||||
|
dist/windows/installer-translations/.* |
|
||||||
|
m4/.* |
|
||||||
|
src/base/3rdparty/.* |
|
||||||
|
src/searchengine/nova3/socks.py |
|
||||||
|
src/webui/www/private/scripts/lib/.*
|
||||||
|
)$
|
||||||
|
exclude_types:
|
||||||
|
- ts
|
||||||
|
|
||||||
|
- repo: https://github.com/crate-ci/typos
|
||||||
|
rev: v1.15.5
|
||||||
|
hooks:
|
||||||
|
- id: typos
|
||||||
|
name: Check spelling (typos)
|
||||||
|
args: ["--config", ".github/workflows/helper/pre-commit/.typos.toml"]
|
||||||
|
exclude: |
|
||||||
|
(?x)^(
|
||||||
|
.*\.asc |
|
||||||
|
.*\.desktop |
|
||||||
|
.*\.qrc |
|
||||||
|
\.pre-commit-config\.yaml |
|
||||||
|
build-aux/.* |
|
||||||
|
Changelog |
|
||||||
|
configure.* |
|
||||||
|
dist/windows/installer-translations/.* |
|
||||||
|
m4/.* |
|
||||||
|
src/base/3rdparty/.* |
|
||||||
|
src/searchengine/nova3/socks.py |
|
||||||
|
src/webui/www/private/scripts/lib/.*
|
||||||
|
)$
|
||||||
|
exclude_types:
|
||||||
|
- svg
|
||||||
|
- ts
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
|
|
||||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_v45x]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_master]
|
||||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||||
source_file = src/lang/qbittorrent_en.ts
|
source_file = src/lang/qbittorrent_en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
@@ -9,7 +9,7 @@ type = QT
|
|||||||
minimum_perc = 23
|
minimum_perc = 23
|
||||||
lang_map = pt: pt_PT, zh: zh_CN
|
lang_map = pt: pt_PT, zh: zh_CN
|
||||||
|
|
||||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_v45x]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui]
|
||||||
file_filter = src/webui/www/translations/webui_<lang>.ts
|
file_filter = src/webui/www/translations/webui_<lang>.ts
|
||||||
source_file = src/webui/www/translations/webui_en.ts
|
source_file = src/webui/www/translations/webui_en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
|||||||
6
AUTHORS
6
AUTHORS
@@ -29,13 +29,17 @@ Code from other projects:
|
|||||||
copyright: Dan Haim <negativeiq@users.sourceforge.net>
|
copyright: Dan Haim <negativeiq@users.sourceforge.net>
|
||||||
license: BSD
|
license: BSD
|
||||||
|
|
||||||
|
* files src/webui/www/private/css/lib/vanillaSelectBox.css src/webui/www/private/scripts/lib/vanillaSelectBox.js
|
||||||
|
copyright: Philippe Meyer <pmg.meyer@gmail.com>
|
||||||
|
license: MIT
|
||||||
|
|
||||||
Images Authors:
|
Images Authors:
|
||||||
* files: src/icons/qbittorrent-tray.svg
|
* files: src/icons/qbittorrent-tray.svg
|
||||||
copyright: Provided by HVS <hvs linuxmail org> (raster first proposal) and Atif Afzal(@atfzl github) <atif5801@gmail.com> (vectorized and modified)
|
copyright: Provided by HVS <hvs linuxmail org> (raster first proposal) and Atif Afzal(@atfzl github) <atif5801@gmail.com> (vectorized and modified)
|
||||||
license: GPLv2+
|
license: GPLv2+
|
||||||
|
|
||||||
* files: src/qbittorrent_file.ico src/icons/fileicon.svg
|
* files: src/qbittorrent_file.ico src/icons/fileicon.svg
|
||||||
copyright: 'uknown.svg' (LGPLv3+) from Oxygen Icon Theme was used as base which was slightly modified and 'qbittorrent-tray.svg' (GPLv2+) was overlayed above it.
|
copyright: 'unknown.svg' (LGPLv3+) from Oxygen Icon Theme was used as base which was slightly modified and 'qbittorrent-tray.svg' (GPLv2+) was overlaid above it.
|
||||||
license: GPLv3+
|
license: GPLv3+
|
||||||
|
|
||||||
* files: src/icons/flags/*.svg
|
* files: src/icons/flags/*.svg
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ set(minBoostVersion 1.71)
|
|||||||
set(minQt5Version 5.15.2)
|
set(minQt5Version 5.15.2)
|
||||||
set(minQt6Version 6.2)
|
set(minQt6Version 6.2)
|
||||||
set(minOpenSSLVersion 1.1.1)
|
set(minOpenSSLVersion 1.1.1)
|
||||||
set(minLibtorrent1Version 1.2.18)
|
set(minLibtorrent1Version 1.2.19)
|
||||||
set(minLibtorrentVersion 2.0.8)
|
set(minLibtorrentVersion 2.0.9)
|
||||||
set(minZlibVersion 1.2.11)
|
set(minZlibVersion 1.2.11)
|
||||||
|
|
||||||
include(CheckCXXSourceCompiles) # TODO: migrate to CheckSourceCompiles in CMake >= 3.19
|
include(CheckCXXSourceCompiles) # TODO: migrate to CheckSourceCompiles in CMake >= 3.19
|
||||||
@@ -30,16 +30,21 @@ feature_option(STACKTRACE "Enable stacktrace support" ON)
|
|||||||
feature_option(TESTING "Build internal testing suite" OFF)
|
feature_option(TESTING "Build internal testing suite" OFF)
|
||||||
feature_option(VERBOSE_CONFIGURE "Show information about PACKAGES_FOUND and PACKAGES_NOT_FOUND in the configure output (only useful for debugging the CMake build scripts)" OFF)
|
feature_option(VERBOSE_CONFIGURE "Show information about PACKAGES_FOUND and PACKAGES_NOT_FOUND in the configure output (only useful for debugging the CMake build scripts)" OFF)
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
feature_option_dependent(DBUS
|
feature_option_dependent(DBUS
|
||||||
"Enable support for notifications and power-management features via D-Bus on Linux"
|
"Enable support for notifications and power-management features via D-Bus"
|
||||||
ON "GUI" OFF
|
ON "GUI" OFF
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
feature_option_dependent(SYSTEMD
|
feature_option_dependent(SYSTEMD
|
||||||
"Install systemd service file. Target directory is overridable with `SYSTEMD_SERVICES_INSTALL_DIR` variable"
|
"Install systemd service file. Target directory is overridable with `SYSTEMD_SERVICES_INSTALL_DIR` variable"
|
||||||
OFF "NOT GUI" OFF
|
OFF "NOT GUI" OFF
|
||||||
)
|
)
|
||||||
elseif (MSVC)
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
feature_option(MSVC_RUNTIME_DYNAMIC "Use MSVC dynamic runtime library (-MD) instead of static (-MT)" ON)
|
feature_option(MSVC_RUNTIME_DYNAMIC "Use MSVC dynamic runtime library (-MD) instead of static (-MT)" ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -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-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
|
[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
|
[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
|
[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
|
[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
|
[merging-vs-rebasing-url]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing
|
||||||
|
|||||||
133
Changelog
133
Changelog
@@ -1,76 +1,65 @@
|
|||||||
Tue Aug 29 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.5
|
Sun Oct 22nd 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.0
|
||||||
- BUGFIX: Fix transfer list tab hotkey (thalieht)
|
- FEATURE: Add (experimental) I2P support (glassez)
|
||||||
- BUGFIX: Don't forget to enable the Apply button in the Options dialog (glassez)
|
- FEATURE: Provide UI editor for the default theme (glassez)
|
||||||
- BUGFIX: Immediately update torrent status on moving files (glassez)
|
- FEATURE: Various UI theming improvements (glassez)
|
||||||
- BUGFIX: Improve performance when scrolling the file list of large torrents (gdim47)
|
- FEATURE: Implement torrent tags editing dialog (glassez)
|
||||||
- BUGFIX: Don't operate on random torrents when multiple are selected and a sort/filter is applied (glassez)
|
- FEATURE: Revamp "Watched folder options" and "Automated RSS downloader" dialog (glassez)
|
||||||
- RSS: Fix overwriting feeds.json with an incomplete load of it (Omar Abdul Azeez)
|
- FEATURE: Allow to use another icons in dark mode (glassez)
|
||||||
- WINDOWS: Software update check logic is disabled for < Win10 (sledgehammer999)
|
- FEATURE: Allow to add new torrents to queue top (glassez)
|
||||||
- WINDOWS: NSIS: Update Turkish and French translations (Burak Yavuz, MarcDrieu)
|
- FEATURE: Allow to filter torrent list by save path (Tom)
|
||||||
- WINDOWS: NSIS: Add Romanian translation (rusu-afanasie)
|
- FEATURE: Expose 'socket send/receive buffer size' options (Chocobo1)
|
||||||
|
- FEATURE: Expose 'max torrent file size' setting (Chocobo1)
|
||||||
|
- FEATURE: Expose 'bdecode limits' settings (Chocobo1)
|
||||||
|
- FEATURE: Add options to adjust behavior of merging trackers to existing torrent (glassez)
|
||||||
|
- FEATURE: Add option to stop seeding when torrent has been inactive (Christopher)
|
||||||
|
- FEATURE: Allow to use proxy per subsystem (glassez)
|
||||||
|
- FEATURE: Expand the scope of "Proxy hostname lookup" option (glassez)
|
||||||
|
- FEATURE: Add shortcut for "Ban peer permanently" function (Luka Čelebić)
|
||||||
|
- FEATURE: Add option to auto hide zero status filters (glassez)
|
||||||
|
- FEATURE: Allow to disable confirmation of Pause/Resume All (glassez)
|
||||||
|
- FEATURE: Add alternative shortcut CTRL+E for CTRL+F (Luka Čelebić)
|
||||||
|
- FEATURE: Show filtered port numbers in logs (Hanabishi)
|
||||||
|
- FEATURE: Add button to copy library versions to clipboard (Chocobo1)
|
||||||
|
- BUGFIX: Ensure ongoing storage moving job will be completed when shutting down (Chocobo1)
|
||||||
|
- BUGFIX: Refactored many areas to call non UI blocking code (glassez)
|
||||||
|
- BUGFIX: Various improvements to the SQLite backend (glassez)
|
||||||
|
- BUGFIX: Improve startup window state handling (glassez)
|
||||||
|
- BUGFIX: Use tray icon from system theme only if option is set (glassez)
|
||||||
|
- BUGFIX: Inhibit system sleep while torrents are moving (Sentox6)
|
||||||
|
- BUGFIX: Use hostname instead of domain name in tracker filter list (tearfur)
|
||||||
|
- BUGFIX: Visually validate input path in torrent creator dialog (Chocobo1)
|
||||||
|
- BUGFIX: Disable symlink resolving in Torrent creator (Ignat Loskutov)
|
||||||
|
- BUGFIX: Change default value for `file pool size` and `stop tracker timeout` settings (stalkerok)
|
||||||
|
- BUGFIX: Log when duplicate torrents are being added (glassez)
|
||||||
|
- BUGFIX: Inhibit suspend instead of screen idle (axet)
|
||||||
|
- BUGFIX: Ensure file name is valid when exporting torrents (glassez)
|
||||||
|
- BUGFIX: Open "Save path" if torrent has no metadata (Xu Chao)
|
||||||
|
- BUGFIX: Prevent torrent starting unexpectedly edge case with magnet (Xu Chao)
|
||||||
|
- BUGFIX: Better ergonomics of the "Add new torrent" dialog (Xu Chao, glassez)
|
||||||
|
- WEBUI: Add log viewer (brvphoenix)
|
||||||
|
- WEBUI: WebAPI: Allow to specify session cookie name (glassez)
|
||||||
|
- WEBUI: Improve sync API performance (glassez)
|
||||||
|
- WEBUI: Add filelog settings (brvphoenix)
|
||||||
|
- WEBUI: Add multi-file renaming (loligans)
|
||||||
|
- WEBUI: Add "Add to top of queue" option (thalieht)
|
||||||
|
- WEBUI: Implement subcategories (Bartu Özen)
|
||||||
|
- WEBUI: Set "SameSite=None" if CSRF Protection is disabled (七海千秋)
|
||||||
|
- WEBUI: Show only hosts in tracker filter list (ttys3)
|
||||||
|
- WEBUI: Set Connection status and Speed limits tooltips (Raymond Ha)
|
||||||
|
- WEBUI: set Cross Origin Opener Policy to `same-origin` (Chocobo1)
|
||||||
|
- WEBUI: Fix response for HTTP HEAD method (Chocobo1)
|
||||||
|
- WEBUI: Preserve the network interfaces when connection is down (Fabricio Silva)
|
||||||
|
- WEBUI: Add "Add Tags" field for RSS rules (Matic Babnik)
|
||||||
|
- WEBUI: Fix missing error icon (Trim21)
|
||||||
|
- RSS: Add "Rename rule" button to RSS Downloader (BallsOfSpaghetti)
|
||||||
|
- RSS: Allow to edit RSS feed URL (glassez)
|
||||||
|
- RSS: Allow to assign priority to RSS download rule (glassez)
|
||||||
|
- SEARCH: Use python isolate mode (Chocobo1)
|
||||||
|
- SEARCH: Bump python version minimum requirement to 3.7.0 (Chocobo1)
|
||||||
|
- OTHER: Enable DBUS cmake option on FreeBSD (yuri@FreeBSD)
|
||||||
|
- OTHER: Numerous code improvements and refactorings (glassez, Chocobo1)
|
||||||
|
|
||||||
Sun Jun 18 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.4
|
Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.0
|
||||||
- BUGFIX: Allow to disable confirmation of Pause/Resume All (glassez)
|
|
||||||
- BUGFIX: Sync flag icons with upstream (Priit Uring)
|
|
||||||
- WEBUI: Fix category save path (Raymond Ha)
|
|
||||||
|
|
||||||
Sun May 28 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.3
|
|
||||||
- BUGFIX: Correctly check if database needs to be updated (glassez)
|
|
||||||
- BUGFIX: Prevent incorrect log message about torrent content deletion (glassez)
|
|
||||||
- BUGFIX: Improve finished torrent handling (glassez)
|
|
||||||
- BUGFIX: Correctly initialize group box children as disabled in Preferences (thalieht)
|
|
||||||
- BUGFIX: Don't miss saving "download path" in SQLite storage (glassez)
|
|
||||||
- BUGFIX: Improve logging of running external program (glassez)
|
|
||||||
- WEBUI: Disable UPnP for web UI by default (glassez)
|
|
||||||
- WEBUI: Use workaround for IOS file picker (DivineHawk)
|
|
||||||
- WEBUI: Work around Chrome download limit (Chocobo1)
|
|
||||||
- WEBUI: Improve 'exporting torrent' behavior (Chocobo1)
|
|
||||||
- WINDOWS: NSIS: Add Slovak translation (Christian Danížek)
|
|
||||||
|
|
||||||
Tue Feb 28 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.2
|
|
||||||
- BUGFIX: Don't unexpectedly activate queued torrents when prefetching metadata for added magnets (glassez)
|
|
||||||
- BUGFIX: Update the cached torrent state once recheck is started (glassez)
|
|
||||||
- BUGFIX: Be more likely to allow the system to use power saving modes (glassez)
|
|
||||||
- WEBUI: Migrate away from unsafe function (Chocobo1)
|
|
||||||
- WEBUI: Blacklist bad ciphers for TLS in the server (sledgehammer999)
|
|
||||||
- WEBUI: Allow only TLS 1.2+ in the server (sledgehammer999)
|
|
||||||
- WEBUI: Allow to set read-only directory as torrent location (glassez)
|
|
||||||
- WEBUI: Reject requests that contain backslash in path (glassez)
|
|
||||||
- RSS: Prevent RSS folder from being moved into itself (glassez)
|
|
||||||
- WINDOWS: NSIS: Update Turkish, Uzbek translation (Burak Yavuz, shitcod3r)
|
|
||||||
|
|
||||||
Sun Feb 12 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.1
|
|
||||||
- FEATURE: Re-allow to use icons from system theme (glassez)
|
|
||||||
- BUGFIX: Fix Speed limit icon size (Nowshed H. Imran)
|
|
||||||
- BUGFIX: Revise and fix some text colors (Chocobo1, Nowshed H. Imran)
|
|
||||||
- BUGFIX: Correctly load folder based UI theme (glassez)
|
|
||||||
- BUGFIX: Fix crash due to invalid encoding of tracker URLs (glassez)
|
|
||||||
- BUGFIX: Don't drop !qB extension when renaming incomplete file (glassez)
|
|
||||||
- BUGFIX: Correctly count the number of torrents in subcategories (glassez)
|
|
||||||
- BUGFIX: Use "additional trackers" when metadata retrieving (glassez)
|
|
||||||
- BUGFIX: Apply correct tab order to Category options dialog (glassez)
|
|
||||||
- BUGFIX: Add all torrents passed via the command line (glassez)
|
|
||||||
- BUGFIX: Fix startup performance on Qt5 (glassez)
|
|
||||||
- BUGFIX: Automatic move will now overwrite existing files (aka previous behavior) (glassez)
|
|
||||||
- BUGFIX: Some fixes for loading Chinese locales (sledgehammer999)
|
|
||||||
- BUGFIX: New Pause icon color for toolbar/menu (Nowshed H. Imran, sledgehammer999)
|
|
||||||
- BUGFIX: Adjust env variable for PDB discovery (sledgehammer999)
|
|
||||||
- WEBUI: Fix missing "queued" icon (thalieht)
|
|
||||||
- WEBUI: Return paths using platform-independent separator format (glassez)
|
|
||||||
- WEBUI: Change order of accepted types of file input (Jason Carr)
|
|
||||||
- WEBUI: Add missing icons (brvphoenix)
|
|
||||||
- WEBUI: Add "Resume data storage type" option (thalieht)
|
|
||||||
- WEBUI: Make rename file dialog resizable (Torsten Schwarz)
|
|
||||||
- WEBUI: Prevent incorrect line breaking (David Xuang)
|
|
||||||
- WEBUI: Improve hotkeys (Fidel Selva)
|
|
||||||
- WEBUI: Remove suggestions while searching for torrents (Midhun V Nadh)
|
|
||||||
- WEBUI: Expose "IS PRIVATE" flag (sotiris-bos)
|
|
||||||
- WEBUI: Return name/hash/infohash_v1/infohash_v2 torrent properties (qbittorrentfan)
|
|
||||||
- WINDOWS: Correctly detect drive letter in path (glassez)
|
|
||||||
- WINDOWS: NSIS: Update Swedish, Lithuanian translations (Jonatan, Deividas)
|
|
||||||
- LINUX: Fix tray icon issues (glassez)
|
|
||||||
|
|
||||||
Sat Nov 26 2022 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.0
|
|
||||||
- FEATURE: Add `Auto resize columns` functionality (Chocobo1)
|
- FEATURE: Add `Auto resize columns` functionality (Chocobo1)
|
||||||
- FEATURE: Allow to use Category paths in `Manual` mode (glassez)
|
- FEATURE: Allow to use Category paths in `Manual` mode (glassez)
|
||||||
- FEATURE: Allow to disable Automatic mode when default "temp" path changed (glassez)
|
- FEATURE: Allow to disable Automatic mode when default "temp" path changed (glassez)
|
||||||
|
|||||||
6
INSTALL
6
INSTALL
@@ -5,7 +5,7 @@ qBittorrent - A BitTorrent client in C++ / Qt
|
|||||||
|
|
||||||
- Boost >= 1.71
|
- 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/
|
* By Arvid Norberg, https://www.libtorrent.org/
|
||||||
* Be careful: another library (the one used by rTorrent) uses a similar name
|
* 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 *
|
- pkg-config *
|
||||||
* Compile-time only on *nix systems
|
* Compile-time only on *nix systems
|
||||||
|
|
||||||
- Python >= 3.5.0
|
- Python >= 3.7.0
|
||||||
* Optional, run-time only
|
* Optional, run-time only
|
||||||
* Used by the bundled search engine
|
* 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.
|
will install and execute qBittorrent.
|
||||||
|
|
||||||
DOCUMENTATION:
|
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>
|
sledgehammer999 <sledgehammer999@qbittorrent.org>
|
||||||
|
|||||||
@@ -37,13 +37,13 @@ For more information please visit:
|
|||||||
https://www.qbittorrent.org
|
https://www.qbittorrent.org
|
||||||
|
|
||||||
or our wiki here:
|
or our wiki here:
|
||||||
http://wiki.qbittorrent.org
|
https://wiki.qbittorrent.org
|
||||||
|
|
||||||
Use the forum for troubleshooting before reporting bugs:
|
Use the forum for troubleshooting before reporting bugs:
|
||||||
http://forum.qbittorrent.org
|
https://forum.qbittorrent.org
|
||||||
|
|
||||||
Please report any bug (or feature request) to:
|
Please report any bug (or feature request) to:
|
||||||
http://bugs.qbittorrent.org
|
https://bugs.qbittorrent.org
|
||||||
|
|
||||||
Official IRC channel:
|
Official IRC channel:
|
||||||
[#qbittorrent on irc.libera.chat](ircs://irc.libera.chat:6697/qbittorrent)
|
[#qbittorrent on irc.libera.chat](ircs://irc.libera.chat:6697/qbittorrent)
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ if (MSVC)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (DBUS)
|
||||||
|
target_compile_definitions(qbt_common_cfg INTERFACE QBT_USES_DBUS)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (LibtorrentRasterbar_VERSION VERSION_GREATER_EQUAL ${minLibtorrentVersion})
|
if (LibtorrentRasterbar_VERSION VERSION_GREATER_EQUAL ${minLibtorrentVersion})
|
||||||
target_compile_definitions(qbt_common_cfg INTERFACE QBT_USES_LIBTORRENT2)
|
target_compile_definitions(qbt_common_cfg INTERFACE QBT_USES_LIBTORRENT2)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
86
configure
vendored
86
configure
vendored
@@ -1,6 +1,6 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.71 for qbittorrent v4.5.5.
|
# Generated by GNU Autoconf 2.71 for qbittorrent v4.6.0.
|
||||||
#
|
#
|
||||||
# Report bugs to <bugs.qbittorrent.org>.
|
# Report bugs to <bugs.qbittorrent.org>.
|
||||||
#
|
#
|
||||||
@@ -611,8 +611,8 @@ MAKEFLAGS=
|
|||||||
# Identity of this package.
|
# Identity of this package.
|
||||||
PACKAGE_NAME='qbittorrent'
|
PACKAGE_NAME='qbittorrent'
|
||||||
PACKAGE_TARNAME='qbittorrent'
|
PACKAGE_TARNAME='qbittorrent'
|
||||||
PACKAGE_VERSION='v4.5.5'
|
PACKAGE_VERSION='v4.6.0'
|
||||||
PACKAGE_STRING='qbittorrent v4.5.5'
|
PACKAGE_STRING='qbittorrent v4.6.0'
|
||||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||||
PACKAGE_URL='https://www.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.
|
# 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.
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||||
cat <<_ACEOF
|
cat <<_ACEOF
|
||||||
\`configure' configures qbittorrent v4.5.5 to adapt to many kinds of systems.
|
\`configure' configures qbittorrent v4.6.0 to adapt to many kinds of systems.
|
||||||
|
|
||||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
|
|
||||||
@@ -1400,7 +1400,7 @@ fi
|
|||||||
|
|
||||||
if test -n "$ac_init_help"; then
|
if test -n "$ac_init_help"; then
|
||||||
case $ac_init_help in
|
case $ac_init_help in
|
||||||
short | recursive ) echo "Configuration of qbittorrent v4.5.5:";;
|
short | recursive ) echo "Configuration of qbittorrent v4.6.0:";;
|
||||||
esac
|
esac
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
|
|
||||||
@@ -1533,7 +1533,7 @@ fi
|
|||||||
test -n "$ac_init_help" && exit $ac_status
|
test -n "$ac_init_help" && exit $ac_status
|
||||||
if $ac_init_version; then
|
if $ac_init_version; then
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
qbittorrent configure v4.5.5
|
qbittorrent configure v4.6.0
|
||||||
generated by GNU Autoconf 2.71
|
generated by GNU Autoconf 2.71
|
||||||
|
|
||||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||||
@@ -1648,7 +1648,7 @@ cat >config.log <<_ACEOF
|
|||||||
This file contains any messages produced by compilers while
|
This file contains any messages produced by compilers while
|
||||||
running configure, to aid debugging if configure makes a mistake.
|
running configure, to aid debugging if configure makes a mistake.
|
||||||
|
|
||||||
It was created by qbittorrent $as_me v4.5.5, which was
|
It was created by qbittorrent $as_me v4.6.0, which was
|
||||||
generated by GNU Autoconf 2.71. Invocation command line was
|
generated by GNU Autoconf 2.71. Invocation command line was
|
||||||
|
|
||||||
$ $0$ac_configure_args_raw
|
$ $0$ac_configure_args_raw
|
||||||
@@ -4779,7 +4779,7 @@ fi
|
|||||||
|
|
||||||
# Define the identity of the package.
|
# Define the identity of the package.
|
||||||
PACKAGE='qbittorrent'
|
PACKAGE='qbittorrent'
|
||||||
VERSION='v4.5.5'
|
VERSION='v4.6.0'
|
||||||
|
|
||||||
|
|
||||||
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||||
@@ -5649,7 +5649,7 @@ fi
|
|||||||
then :
|
then :
|
||||||
as_fn_error $? "Could not find QtDBus" "$LINENO" 5
|
as_fn_error $? "Could not find QtDBus" "$LINENO" 5
|
||||||
else $as_nop
|
else $as_nop
|
||||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG dbus"
|
QBT_ADD_CONFIG="$QBT_ADD_CONFIG dbus" QBT_ADD_DEFINES="$QBT_ADD_DEFINES QBT_USES_DBUS"
|
||||||
|
|
||||||
fi ;; #(
|
fi ;; #(
|
||||||
"xno") :
|
"xno") :
|
||||||
@@ -6024,19 +6024,19 @@ LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
|||||||
|
|
||||||
|
|
||||||
pkg_failed=no
|
pkg_failed=no
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 2.0.8" >&5
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 2.0.9" >&5
|
||||||
printf %s "checking for libtorrent-rasterbar >= 2.0.8... " >&6; }
|
printf %s "checking for libtorrent-rasterbar >= 2.0.9... " >&6; }
|
||||||
|
|
||||||
if test -n "$libtorrent_CFLAGS"; then
|
if test -n "$libtorrent_CFLAGS"; then
|
||||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||||
elif test -n "$PKG_CONFIG"; then
|
elif test -n "$PKG_CONFIG"; then
|
||||||
if test -n "$PKG_CONFIG" && \
|
if test -n "$PKG_CONFIG" && \
|
||||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.8\""; } >&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.8") 2>&5
|
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.9") 2>&5
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
test $ac_status = 0; }; then
|
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
|
test "x$?" != "x0" && pkg_failed=yes
|
||||||
else
|
else
|
||||||
pkg_failed=yes
|
pkg_failed=yes
|
||||||
@@ -6048,12 +6048,12 @@ if test -n "$libtorrent_LIBS"; then
|
|||||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||||
elif test -n "$PKG_CONFIG"; then
|
elif test -n "$PKG_CONFIG"; then
|
||||||
if test -n "$PKG_CONFIG" && \
|
if test -n "$PKG_CONFIG" && \
|
||||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.8\""; } >&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.8") 2>&5
|
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.9") 2>&5
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
test $ac_status = 0; }; then
|
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
|
test "x$?" != "x0" && pkg_failed=yes
|
||||||
else
|
else
|
||||||
pkg_failed=yes
|
pkg_failed=yes
|
||||||
@@ -6074,28 +6074,28 @@ else
|
|||||||
_pkg_short_errors_supported=no
|
_pkg_short_errors_supported=no
|
||||||
fi
|
fi
|
||||||
if test $_pkg_short_errors_supported = yes; then
|
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
|
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
|
fi
|
||||||
# Put the nasty error message in config.log where it belongs
|
# Put the nasty error message in config.log where it belongs
|
||||||
echo "$libtorrent_PKG_ERRORS" >&5
|
echo "$libtorrent_PKG_ERRORS" >&5
|
||||||
|
|
||||||
|
|
||||||
pkg_failed=no
|
pkg_failed=no
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" >&5
|
{ 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.18 libtorrent-rasterbar < 2... " >&6; }
|
printf %s "checking for libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2... " >&6; }
|
||||||
|
|
||||||
if test -n "$libtorrent_CFLAGS"; then
|
if test -n "$libtorrent_CFLAGS"; then
|
||||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||||
elif test -n "$PKG_CONFIG"; then
|
elif test -n "$PKG_CONFIG"; then
|
||||||
if test -n "$PKG_CONFIG" && \
|
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
|
{ { 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.18 libtorrent-rasterbar < 2") 2>&5
|
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
test $ac_status = 0; }; then
|
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
|
test "x$?" != "x0" && pkg_failed=yes
|
||||||
else
|
else
|
||||||
pkg_failed=yes
|
pkg_failed=yes
|
||||||
@@ -6107,12 +6107,12 @@ if test -n "$libtorrent_LIBS"; then
|
|||||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||||
elif test -n "$PKG_CONFIG"; then
|
elif test -n "$PKG_CONFIG"; then
|
||||||
if test -n "$PKG_CONFIG" && \
|
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
|
{ { 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.18 libtorrent-rasterbar < 2") 2>&5
|
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
test $ac_status = 0; }; then
|
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
|
test "x$?" != "x0" && pkg_failed=yes
|
||||||
else
|
else
|
||||||
pkg_failed=yes
|
pkg_failed=yes
|
||||||
@@ -6133,14 +6133,14 @@ else
|
|||||||
_pkg_short_errors_supported=no
|
_pkg_short_errors_supported=no
|
||||||
fi
|
fi
|
||||||
if test $_pkg_short_errors_supported = yes; then
|
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
|
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
|
fi
|
||||||
# Put the nasty error message in config.log where it belongs
|
# Put the nasty error message in config.log where it belongs
|
||||||
echo "$libtorrent_PKG_ERRORS" >&5
|
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
|
$libtorrent_PKG_ERRORS
|
||||||
|
|
||||||
@@ -6177,19 +6177,19 @@ elif test $pkg_failed = untried; then
|
|||||||
printf "%s\n" "no" >&6; }
|
printf "%s\n" "no" >&6; }
|
||||||
|
|
||||||
pkg_failed=no
|
pkg_failed=no
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.18 libtorrent-rasterbar < 2" >&5
|
{ 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.18 libtorrent-rasterbar < 2... " >&6; }
|
printf %s "checking for libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2... " >&6; }
|
||||||
|
|
||||||
if test -n "$libtorrent_CFLAGS"; then
|
if test -n "$libtorrent_CFLAGS"; then
|
||||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||||
elif test -n "$PKG_CONFIG"; then
|
elif test -n "$PKG_CONFIG"; then
|
||||||
if test -n "$PKG_CONFIG" && \
|
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
|
{ { 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.18 libtorrent-rasterbar < 2") 2>&5
|
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
test $ac_status = 0; }; then
|
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
|
test "x$?" != "x0" && pkg_failed=yes
|
||||||
else
|
else
|
||||||
pkg_failed=yes
|
pkg_failed=yes
|
||||||
@@ -6201,12 +6201,12 @@ if test -n "$libtorrent_LIBS"; then
|
|||||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||||
elif test -n "$PKG_CONFIG"; then
|
elif test -n "$PKG_CONFIG"; then
|
||||||
if test -n "$PKG_CONFIG" && \
|
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
|
{ { 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.18 libtorrent-rasterbar < 2") 2>&5
|
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.19 libtorrent-rasterbar < 2") 2>&5
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
test $ac_status = 0; }; then
|
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
|
test "x$?" != "x0" && pkg_failed=yes
|
||||||
else
|
else
|
||||||
pkg_failed=yes
|
pkg_failed=yes
|
||||||
@@ -6227,14 +6227,14 @@ else
|
|||||||
_pkg_short_errors_supported=no
|
_pkg_short_errors_supported=no
|
||||||
fi
|
fi
|
||||||
if test $_pkg_short_errors_supported = yes; then
|
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
|
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
|
fi
|
||||||
# Put the nasty error message in config.log where it belongs
|
# Put the nasty error message in config.log where it belongs
|
||||||
echo "$libtorrent_PKG_ERRORS" >&5
|
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
|
$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
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by qbittorrent $as_me v4.5.5, which was
|
This file was extended by qbittorrent $as_me v4.6.0, which was
|
||||||
generated by GNU Autoconf 2.71. Invocation command line was
|
generated by GNU Autoconf 2.71. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
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
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config='$ac_cs_config_escaped'
|
ac_cs_config='$ac_cs_config_escaped'
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
qbittorrent config.status v4.5.5
|
qbittorrent config.status v4.6.0
|
||||||
configured by $0, generated by GNU Autoconf 2.71,
|
configured by $0, generated by GNU Autoconf 2.71,
|
||||||
with options \\"\$ac_cs_config\\"
|
with options \\"\$ac_cs_config\\"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([qbittorrent], [v4.5.5], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
AC_INIT([qbittorrent], [v4.6.0], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
: ${CFLAGS=""}
|
: ${CFLAGS=""}
|
||||||
@@ -165,7 +165,7 @@ AS_CASE(["x$enable_qt_dbus"],
|
|||||||
FIND_QTDBUS()
|
FIND_QTDBUS()
|
||||||
AS_IF([test "x$HAVE_QTDBUS" = "xfalse"],
|
AS_IF([test "x$HAVE_QTDBUS" = "xfalse"],
|
||||||
[AC_MSG_ERROR([Could not find QtDBus])],
|
[AC_MSG_ERROR([Could not find QtDBus])],
|
||||||
[QBT_ADD_CONFIG="$QBT_ADD_CONFIG dbus"]
|
[QBT_ADD_CONFIG="$QBT_ADD_CONFIG dbus" QBT_ADD_DEFINES="$QBT_ADD_DEFINES QBT_USES_DBUS"]
|
||||||
)],
|
)],
|
||||||
["xno"],
|
["xno"],
|
||||||
[AC_MSG_RESULT([no])
|
[AC_MSG_RESULT([no])
|
||||||
@@ -188,10 +188,10 @@ m4_define([DETECT_BOOST_VERSION_PROGRAM],
|
|||||||
[[(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));]])])
|
[[(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));]])])
|
||||||
|
|
||||||
PKG_CHECK_MODULES(libtorrent,
|
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"],
|
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS" QBT_ADD_DEFINES="$QBT_ADD_DEFINES QBT_USES_LIBTORRENT2"],
|
||||||
[PKG_CHECK_MODULES(libtorrent,
|
[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"])])
|
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS"])])
|
||||||
|
|
||||||
PKG_CHECK_MODULES(openssl,
|
PKG_CHECK_MODULES(openssl,
|
||||||
|
|||||||
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -55,7 +55,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.5.5</string>
|
<string>4.6.0</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
|||||||
@@ -68,12 +68,12 @@
|
|||||||
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
|
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
|
||||||
<developer_name>The qBittorrent Project</developer_name>
|
<developer_name>The qBittorrent Project</developer_name>
|
||||||
<url type="homepage">https://www.qbittorrent.org/</url>
|
<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="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>
|
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
|
||||||
<content_rating type="oars-1.1"/>
|
<content_rating type="oars-1.1"/>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="4.5.5" date="2023-08-29"/>
|
<release version="4.6.0" date="2023-10-22"/>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
138
dist/unix/org.qbittorrent.qBittorrent.desktop
vendored
138
dist/unix/org.qbittorrent.qBittorrent.desktop
vendored
@@ -14,216 +14,216 @@ Keywords=bittorrent;torrent;magnet;download;p2p;
|
|||||||
SingleMainWindow=true
|
SingleMainWindow=true
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
Comment[af]=Aflaai en deel lêers oor BitTorrent
|
|
||||||
GenericName[af]=BitTorrent kliënt
|
GenericName[af]=BitTorrent kliënt
|
||||||
|
Comment[af]=Aflaai en deel lêers oor BitTorrent
|
||||||
Name[af]=qBittorrent
|
Name[af]=qBittorrent
|
||||||
Comment[ar]=نزّل وشارك الملفات عبر كيوبتتورنت
|
|
||||||
GenericName[ar]=عميل بتتورنت
|
GenericName[ar]=عميل بتتورنت
|
||||||
|
Comment[ar]=نزّل وشارك الملفات عبر كيوبتتورنت
|
||||||
Name[ar]=qBittorrent
|
Name[ar]=qBittorrent
|
||||||
Comment[be]=Спампоўванне і раздача файлаў праз пратакол BitTorrent
|
|
||||||
GenericName[be]=Кліент BitTorrent
|
GenericName[be]=Кліент BitTorrent
|
||||||
|
Comment[be]=Спампоўванне і раздача файлаў праз пратакол BitTorrent
|
||||||
Name[be]=qBittorrent
|
Name[be]=qBittorrent
|
||||||
Comment[bg]=Сваляне и споделяне на файлове чрез BitTorrent
|
|
||||||
GenericName[bg]=BitTorrent клиент
|
GenericName[bg]=BitTorrent клиент
|
||||||
|
Comment[bg]=Сваляне и споделяне на файлове чрез BitTorrent
|
||||||
Name[bg]=qBittorrent
|
Name[bg]=qBittorrent
|
||||||
Comment[bn]=বিটটরেন্টে ফাইল ডাউনলোড এবং শেয়ার করুন
|
|
||||||
GenericName[bn]=বিটটরেন্ট ক্লায়েন্ট
|
GenericName[bn]=বিটটরেন্ট ক্লায়েন্ট
|
||||||
|
Comment[bn]=বিটটরেন্টে ফাইল ডাউনলোড এবং শেয়ার করুন
|
||||||
Name[bn]=qBittorrent
|
Name[bn]=qBittorrent
|
||||||
Comment[zh]=通过 BitTorrent 下载和分享文件
|
|
||||||
GenericName[zh]=BitTorrent 客户端
|
GenericName[zh]=BitTorrent 客户端
|
||||||
|
Comment[zh]=通过 BitTorrent 下载和分享文件
|
||||||
Name[zh]=qBittorrent
|
Name[zh]=qBittorrent
|
||||||
Comment[bs]=Preuzmi i dijeli datoteke preko BitTorrent-a
|
|
||||||
GenericName[bs]=BitTorrent klijent
|
GenericName[bs]=BitTorrent klijent
|
||||||
|
Comment[bs]=Preuzmi i dijeli datoteke preko BitTorrent-a
|
||||||
Name[bs]=qBittorrent
|
Name[bs]=qBittorrent
|
||||||
Comment[ca]=Baixeu i compartiu fitxers amb el BitTorrent
|
|
||||||
GenericName[ca]=Client de BitTorrent
|
GenericName[ca]=Client de BitTorrent
|
||||||
|
Comment[ca]=Baixeu i compartiu fitxers amb el BitTorrent
|
||||||
Name[ca]=qBittorrent
|
Name[ca]=qBittorrent
|
||||||
Comment[cs]=Stahování a sdílení souborů přes síť BitTorrent
|
|
||||||
GenericName[cs]=BitTorrent klient
|
GenericName[cs]=BitTorrent klient
|
||||||
|
Comment[cs]=Stahování a sdílení souborů přes síť BitTorrent
|
||||||
Name[cs]=qBittorrent
|
Name[cs]=qBittorrent
|
||||||
Comment[da]=Download og del filer over BitTorrent
|
|
||||||
GenericName[da]=BitTorrent-klient
|
GenericName[da]=BitTorrent-klient
|
||||||
|
Comment[da]=Download og del filer over BitTorrent
|
||||||
Name[da]=qBittorrent
|
Name[da]=qBittorrent
|
||||||
Comment[de]=Über BitTorrent Dateien herunterladen und teilen
|
|
||||||
GenericName[de]=BitTorrent Client
|
GenericName[de]=BitTorrent Client
|
||||||
|
Comment[de]=Über BitTorrent Dateien herunterladen und teilen
|
||||||
Name[de]=qBittorrent
|
Name[de]=qBittorrent
|
||||||
Comment[el]=Κάντε λήψη και μοιραστείτε αρχεία μέσω BitTorrent
|
|
||||||
GenericName[el]=BitTorrent client
|
GenericName[el]=BitTorrent client
|
||||||
|
Comment[el]=Κάντε λήψη και μοιραστείτε αρχεία μέσω BitTorrent
|
||||||
Name[el]=qBittorrent
|
Name[el]=qBittorrent
|
||||||
Comment[en_GB]=Download and share files over BitTorrent
|
|
||||||
GenericName[en_GB]=BitTorrent client
|
GenericName[en_GB]=BitTorrent client
|
||||||
|
Comment[en_GB]=Download and share files over BitTorrent
|
||||||
Name[en_GB]=qBittorrent
|
Name[en_GB]=qBittorrent
|
||||||
Comment[es]=Descargue y comparta archivos por BitTorrent
|
|
||||||
GenericName[es]=Cliente BitTorrent
|
GenericName[es]=Cliente BitTorrent
|
||||||
|
Comment[es]=Descargue y comparta archivos por BitTorrent
|
||||||
Name[es]=qBittorrent
|
Name[es]=qBittorrent
|
||||||
Comment[et]=Lae alla ja jaga faile üle BitTorrenti
|
|
||||||
GenericName[et]=BitTorrent klient
|
GenericName[et]=BitTorrent klient
|
||||||
|
Comment[et]=Lae alla ja jaga faile üle BitTorrenti
|
||||||
Name[et]=qBittorrent
|
Name[et]=qBittorrent
|
||||||
Comment[eu]=Jeitsi eta elkarbanatu agiriak BitTorrent bidez
|
|
||||||
GenericName[eu]=BitTorrent bezeroa
|
GenericName[eu]=BitTorrent bezeroa
|
||||||
|
Comment[eu]=Jeitsi eta elkarbanatu agiriak BitTorrent bidez
|
||||||
Name[eu]=qBittorrent
|
Name[eu]=qBittorrent
|
||||||
Comment[fa]=دانلود و به اشتراک گذاری فایل های بوسیله بیت تورنت
|
|
||||||
GenericName[fa]=بیت تورنت نسخه کلاینت
|
GenericName[fa]=بیت تورنت نسخه کلاینت
|
||||||
|
Comment[fa]=دانلود و به اشتراک گذاری فایل های بوسیله بیت تورنت
|
||||||
Name[fa]=qBittorrent
|
Name[fa]=qBittorrent
|
||||||
Comment[fi]=Lataa ja jaa tiedostoja BitTorrentia käyttäen
|
|
||||||
GenericName[fi]=BitTorrent-asiakasohjelma
|
GenericName[fi]=BitTorrent-asiakasohjelma
|
||||||
|
Comment[fi]=Lataa ja jaa tiedostoja BitTorrentia käyttäen
|
||||||
Name[fi]=qBittorrent
|
Name[fi]=qBittorrent
|
||||||
Comment[fr]=Télécharger et partager des fichiers sur BitTorrent
|
|
||||||
GenericName[fr]=Client BitTorrent
|
GenericName[fr]=Client BitTorrent
|
||||||
|
Comment[fr]=Télécharger et partager des fichiers sur BitTorrent
|
||||||
Name[fr]=qBittorrent
|
Name[fr]=qBittorrent
|
||||||
Comment[gl]=Descargar e compartir ficheiros co protocolo BitTorrent
|
|
||||||
GenericName[gl]=Cliente BitTorrent
|
GenericName[gl]=Cliente BitTorrent
|
||||||
|
Comment[gl]=Descargar e compartir ficheiros co protocolo BitTorrent
|
||||||
Name[gl]=qBittorrent
|
Name[gl]=qBittorrent
|
||||||
Comment[gu]=બિટ્ટોરેંટ પર ફાઈલો ડાઉનલોડ અને શેર કરો
|
|
||||||
GenericName[gu]=બિટ્ટોરેંટ ક્લાયન્ટ
|
GenericName[gu]=બિટ્ટોરેંટ ક્લાયન્ટ
|
||||||
|
Comment[gu]=બિટ્ટોરેંટ પર ફાઈલો ડાઉનલોડ અને શેર કરો
|
||||||
Name[gu]=qBittorrent
|
Name[gu]=qBittorrent
|
||||||
Comment[he]=הורד ושתף קבצים על גבי ביטורנט
|
|
||||||
GenericName[he]=לקוח ביטורנט
|
GenericName[he]=לקוח ביטורנט
|
||||||
|
Comment[he]=הורד ושתף קבצים על גבי ביטורנט
|
||||||
Name[he]=qBittorrent
|
Name[he]=qBittorrent
|
||||||
Comment[hr]=Preuzmite i dijelite datoteke putem BitTorrenta
|
|
||||||
GenericName[hr]=BitTorrent klijent
|
GenericName[hr]=BitTorrent klijent
|
||||||
|
Comment[hr]=Preuzmite i dijelite datoteke putem BitTorrenta
|
||||||
Name[hr]=qBittorrent
|
Name[hr]=qBittorrent
|
||||||
Comment[hu]=Fájlok letöltése és megosztása a BitTorrent hálózaton keresztül
|
|
||||||
GenericName[hu]=BitTorrent kliens
|
GenericName[hu]=BitTorrent kliens
|
||||||
|
Comment[hu]=Fájlok letöltése és megosztása a BitTorrent hálózaton keresztül
|
||||||
Name[hu]=qBittorrent
|
Name[hu]=qBittorrent
|
||||||
Comment[hy]=Նիշքերի փոխանցում BitTorrent-ի միջոցով
|
|
||||||
GenericName[hy]=BitTorrent սպասառու
|
GenericName[hy]=BitTorrent սպասառու
|
||||||
|
Comment[hy]=Նիշքերի փոխանցում BitTorrent-ի միջոցով
|
||||||
Name[hy]=qBittorrent
|
Name[hy]=qBittorrent
|
||||||
Comment[id]=Unduh dan berbagi berkas melalui BitTorrent
|
|
||||||
GenericName[id]=Klien BitTorrent
|
GenericName[id]=Klien BitTorrent
|
||||||
|
Comment[id]=Unduh dan berbagi berkas melalui BitTorrent
|
||||||
Name[id]=qBittorrent
|
Name[id]=qBittorrent
|
||||||
Comment[is]=Sækja og deila skrám yfir BitTorrent
|
|
||||||
GenericName[is]=BitTorrent biðlarar
|
GenericName[is]=BitTorrent biðlarar
|
||||||
|
Comment[is]=Sækja og deila skrám yfir BitTorrent
|
||||||
Name[is]=qBittorrent
|
Name[is]=qBittorrent
|
||||||
Comment[it]=Scarica e condividi file tramite BitTorrent
|
|
||||||
GenericName[it]=Client BitTorrent
|
GenericName[it]=Client BitTorrent
|
||||||
|
Comment[it]=Scarica e condividi file tramite BitTorrent
|
||||||
Name[it]=qBittorrent
|
Name[it]=qBittorrent
|
||||||
Comment[ja]=BitTorrentでファイルのダウンロードと共有
|
|
||||||
GenericName[ja]=BitTorrentクライアント
|
GenericName[ja]=BitTorrentクライアント
|
||||||
|
Comment[ja]=BitTorrentでファイルのダウンロードと共有
|
||||||
Name[ja]=qBittorrent
|
Name[ja]=qBittorrent
|
||||||
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
|
||||||
GenericName[ka]=BitTorrent კლიენტი
|
GenericName[ka]=BitTorrent კლიენტი
|
||||||
|
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
||||||
Name[ka]=qBittorrent
|
Name[ka]=qBittorrent
|
||||||
Comment[ko]=BitTorrent를 통한 파일 내려받기 및 공유
|
|
||||||
GenericName[ko]=BitTorrent 클라이언트
|
GenericName[ko]=BitTorrent 클라이언트
|
||||||
|
Comment[ko]=BitTorrent를 통한 파일 내려받기 및 공유
|
||||||
Name[ko]=qBittorrent
|
Name[ko]=qBittorrent
|
||||||
Comment[lt]=Atsisiųskite bei dalinkitės failais BitTorrent tinkle
|
|
||||||
GenericName[lt]=BitTorrent klientas
|
GenericName[lt]=BitTorrent klientas
|
||||||
|
Comment[lt]=Atsisiųskite bei dalinkitės failais BitTorrent tinkle
|
||||||
Name[lt]=qBittorrent
|
Name[lt]=qBittorrent
|
||||||
Comment[mk]=Превземајте и споделувајте фајлови преку BitTorrent
|
|
||||||
GenericName[mk]=BitTorrent клиент
|
GenericName[mk]=BitTorrent клиент
|
||||||
|
Comment[mk]=Превземајте и споделувајте фајлови преку BitTorrent
|
||||||
Name[mk]=qBittorrent
|
Name[mk]=qBittorrent
|
||||||
Comment[my]=တောရန့်ဖြင့်ဖိုင်များဒေါင်းလုဒ်ဆွဲရန်နှင့်မျှဝေရန်
|
|
||||||
GenericName[my]=တောရန့်စီမံခန့်ခွဲသည့်အရာ
|
GenericName[my]=တောရန့်စီမံခန့်ခွဲသည့်အရာ
|
||||||
|
Comment[my]=တောရန့်ဖြင့်ဖိုင်များဒေါင်းလုဒ်ဆွဲရန်နှင့်မျှဝေရန်
|
||||||
Name[my]=qBittorrent
|
Name[my]=qBittorrent
|
||||||
Comment[nb]=Last ned og del filer over BitTorrent
|
|
||||||
GenericName[nb]=BitTorrent-klient
|
GenericName[nb]=BitTorrent-klient
|
||||||
|
Comment[nb]=Last ned og del filer over BitTorrent
|
||||||
Name[nb]=qBittorrent
|
Name[nb]=qBittorrent
|
||||||
Comment[nl]=Bestanden downloaden en delen via BitTorrent
|
|
||||||
GenericName[nl]=BitTorrent-client
|
GenericName[nl]=BitTorrent-client
|
||||||
|
Comment[nl]=Bestanden downloaden en delen via BitTorrent
|
||||||
Name[nl]=qBittorrent
|
Name[nl]=qBittorrent
|
||||||
Comment[pl]=Pobieraj i dziel się plikami przez BitTorrent
|
|
||||||
GenericName[pl]=Klient BitTorrent
|
GenericName[pl]=Klient BitTorrent
|
||||||
|
Comment[pl]=Pobieraj i dziel się plikami przez BitTorrent
|
||||||
Name[pl]=qBittorrent
|
Name[pl]=qBittorrent
|
||||||
Comment[pt]=Transferir e partilhar ficheiros por BitTorrent
|
|
||||||
GenericName[pt]=Cliente BitTorrent
|
GenericName[pt]=Cliente BitTorrent
|
||||||
|
Comment[pt]=Transferir e partilhar ficheiros por BitTorrent
|
||||||
Name[pt]=qBittorrent
|
Name[pt]=qBittorrent
|
||||||
Comment[pt_BR]=Baixe e compartilhe arquivos pelo BitTorrent
|
|
||||||
GenericName[pt_BR]=Cliente BitTorrent
|
GenericName[pt_BR]=Cliente BitTorrent
|
||||||
|
Comment[pt_BR]=Baixe e compartilhe arquivos pelo BitTorrent
|
||||||
Name[pt_BR]=qBittorrent
|
Name[pt_BR]=qBittorrent
|
||||||
Comment[ro]=Descărcați și partajați fișiere prin BitTorrent
|
|
||||||
GenericName[ro]=Client BitTorrent
|
GenericName[ro]=Client BitTorrent
|
||||||
|
Comment[ro]=Descărcați și partajați fișiere prin BitTorrent
|
||||||
Name[ro]=qBittorrent
|
Name[ro]=qBittorrent
|
||||||
Comment[ru]=Обмен файлами по сети БитТоррент
|
|
||||||
GenericName[ru]=Клиент сети БитТоррент
|
GenericName[ru]=Клиент сети БитТоррент
|
||||||
|
Comment[ru]=Обмен файлами по сети БитТоррент
|
||||||
Name[ru]=qBittorrent
|
Name[ru]=qBittorrent
|
||||||
Comment[sk]=Sťahovanie a zdieľanie súborov prostredníctvom siete BitTorrent
|
|
||||||
GenericName[sk]=Klient siete BitTorrent
|
GenericName[sk]=Klient siete BitTorrent
|
||||||
|
Comment[sk]=Sťahovanie a zdieľanie súborov prostredníctvom siete BitTorrent
|
||||||
Name[sk]=qBittorrent
|
Name[sk]=qBittorrent
|
||||||
Comment[sl]=Prenesite in delite datoteke preko BitTorrenta
|
|
||||||
GenericName[sl]=BitTorrent odjemalec
|
GenericName[sl]=BitTorrent odjemalec
|
||||||
|
Comment[sl]=Prenesite in delite datoteke preko BitTorrenta
|
||||||
Name[sl]=qBittorrent
|
Name[sl]=qBittorrent
|
||||||
Name[sq]=qBittorrent
|
Name[sq]=qBittorrent
|
||||||
Comment[sr]=Преузимајте и делите фајлове преко BitTorrent протокола
|
|
||||||
GenericName[sr]=BitTorrent-клијент
|
GenericName[sr]=BitTorrent-клијент
|
||||||
|
Comment[sr]=Преузимајте и делите фајлове преко BitTorrent протокола
|
||||||
Name[sr]=qBittorrent
|
Name[sr]=qBittorrent
|
||||||
Comment[sr@latin]=Preuzimanje i deljenje fajlova preko BitTorrent-a
|
|
||||||
GenericName[sr@latin]=BitTorrent klijent
|
GenericName[sr@latin]=BitTorrent klijent
|
||||||
|
Comment[sr@latin]=Preuzimanje i deljenje fajlova preko BitTorrent-a
|
||||||
Name[sr@latin]=qBittorrent
|
Name[sr@latin]=qBittorrent
|
||||||
Comment[sv]=Hämta och dela filer över BitTorrent
|
|
||||||
GenericName[sv]=BitTorrent-klient
|
GenericName[sv]=BitTorrent-klient
|
||||||
|
Comment[sv]=Hämta och dela filer över BitTorrent
|
||||||
Name[sv]=qBittorrent
|
Name[sv]=qBittorrent
|
||||||
Comment[ta]=BitTorrent வழியாக கோப்புகளை பதிவிறக்க மற்றும் பகிர
|
|
||||||
GenericName[ta]=BitTorrent வாடிக்கையாளர்
|
GenericName[ta]=BitTorrent வாடிக்கையாளர்
|
||||||
|
Comment[ta]=BitTorrent வழியாக கோப்புகளை பதிவிறக்க மற்றும் பகிர
|
||||||
Name[ta]=qBittorrent
|
Name[ta]=qBittorrent
|
||||||
Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్స్ దిగుమతి చేసుకోండి , పంచుకోండి
|
|
||||||
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
|
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
|
||||||
|
Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్స్ దిగుమతి చేసుకోండి , పంచుకోండి
|
||||||
Name[te]=qBittorrent
|
Name[te]=qBittorrent
|
||||||
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่าน BitTorrent
|
|
||||||
GenericName[th]=โปรแกรมบิททอเร้นท์
|
GenericName[th]=โปรแกรมบิททอเร้นท์
|
||||||
|
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่าน BitTorrent
|
||||||
Name[th]=qBittorrent
|
Name[th]=qBittorrent
|
||||||
Comment[tr]=Dosyaları BitTorrent üzerinden indirin ve paylaşın
|
|
||||||
GenericName[tr]=BitTorrent istemcisi
|
GenericName[tr]=BitTorrent istemcisi
|
||||||
|
Comment[tr]=Dosyaları BitTorrent üzerinden indirin ve paylaşın
|
||||||
Name[tr]=qBittorrent
|
Name[tr]=qBittorrent
|
||||||
Comment[ur]=BitTorrent پر فائلوں کو ڈاؤن لوڈ کریں اور اشتراک کریں
|
|
||||||
GenericName[ur]=قیو بٹ ٹورنٹ کلائنٹ
|
GenericName[ur]=قیو بٹ ٹورنٹ کلائنٹ
|
||||||
|
Comment[ur]=BitTorrent پر فائلوں کو ڈاؤن لوڈ کریں اور اشتراک کریں
|
||||||
Name[ur]=qBittorrent
|
Name[ur]=qBittorrent
|
||||||
Comment[uk]=Завантажуйте та поширюйте файли через BitTorrent
|
|
||||||
GenericName[uk]=BitTorrent-клієнт
|
GenericName[uk]=BitTorrent-клієнт
|
||||||
|
Comment[uk]=Завантажуйте та поширюйте файли через BitTorrent
|
||||||
Name[uk]=qBittorrent
|
Name[uk]=qBittorrent
|
||||||
Comment[vi]=Tải xuống và chia sẻ tệp qua BitTorrent
|
|
||||||
GenericName[vi]=Máy khách BitTorrent
|
GenericName[vi]=Máy khách BitTorrent
|
||||||
|
Comment[vi]=Tải xuống và chia sẻ tệp qua BitTorrent
|
||||||
Name[vi]=qBittorrent
|
Name[vi]=qBittorrent
|
||||||
Comment[zh_HK]=經由BitTorrent下載並分享檔案
|
|
||||||
GenericName[zh_HK]=BitTorrent用戶端
|
GenericName[zh_HK]=BitTorrent用戶端
|
||||||
|
Comment[zh_HK]=經由BitTorrent下載並分享檔案
|
||||||
Name[zh_HK]=qBittorrent
|
Name[zh_HK]=qBittorrent
|
||||||
Comment[zh_TW]=經由 BitTorrent 下載並分享檔案
|
|
||||||
GenericName[zh_TW]=BitTorrent 用戶端
|
GenericName[zh_TW]=BitTorrent 用戶端
|
||||||
|
Comment[zh_TW]=經由 BitTorrent 下載並分享檔案
|
||||||
Name[zh_TW]=qBittorrent
|
Name[zh_TW]=qBittorrent
|
||||||
Comment[eo]=Elŝutu kaj kunhavigu dosierojn per BitTorrent
|
|
||||||
GenericName[eo]=BitTorrent-kliento
|
GenericName[eo]=BitTorrent-kliento
|
||||||
|
Comment[eo]=Elŝutu kaj kunhavigu dosierojn per BitTorrent
|
||||||
Name[eo]=qBittorrent
|
Name[eo]=qBittorrent
|
||||||
Comment[kk]=BitTorrent арқылы файл жүктеу және бөлісу
|
|
||||||
GenericName[kk]=BitTorrent клиенті
|
GenericName[kk]=BitTorrent клиенті
|
||||||
|
Comment[kk]=BitTorrent арқылы файл жүктеу және бөлісу
|
||||||
Name[kk]=qBittorrent
|
Name[kk]=qBittorrent
|
||||||
Comment[en_AU]=Download and share files over BitTorrent
|
|
||||||
GenericName[en_AU]=BitTorrent client
|
GenericName[en_AU]=BitTorrent client
|
||||||
|
Comment[en_AU]=Download and share files over BitTorrent
|
||||||
Name[en_AU]=qBittorrent
|
Name[en_AU]=qBittorrent
|
||||||
Name[rm]=qBittorrent
|
Name[rm]=qBittorrent
|
||||||
Name[jv]=qBittorrent
|
Name[jv]=qBittorrent
|
||||||
Comment[oc]=Telecargar e partejar de fichièrs amb BitTorrent
|
|
||||||
GenericName[oc]=Client BitTorrent
|
GenericName[oc]=Client BitTorrent
|
||||||
|
Comment[oc]=Telecargar e partejar de fichièrs amb BitTorrent
|
||||||
Name[oc]=qBittorrent
|
Name[oc]=qBittorrent
|
||||||
Name[ug]=qBittorrent
|
Name[ug]=qBittorrent
|
||||||
Name[yi]=qBittorrent
|
Name[yi]=qBittorrent
|
||||||
Comment[nqo]=ߞߐߕߐ߯ߘߐ ߟߎ߬ ߟߊߖߌ߰ ߞߊ߬ ߓߊ߲߫ ߞߵߊ߬ߟߎ߬ ߘߐߕߟߊ߫ ߓߌߙߏߙߍ߲ߕ ߞߊ߲߬
|
|
||||||
GenericName[nqo]=ߓߌߙߏߙߍ߲ߕ ߕߣߐ߬ߓߐ߬ߟߊ
|
GenericName[nqo]=ߓߌߙߏߙߍ߲ߕ ߕߣߐ߬ߓߐ߬ߟߊ
|
||||||
|
Comment[nqo]=ߞߐߕߐ߯ߘߐ ߟߎ߬ ߟߊߖߌ߰ ߞߊ߬ ߓߊ߲߫ ߞߵߊ߬ߟߎ߬ ߘߐߕߟߊ߫ ߓߌߙߏߙߍ߲ߕ ߞߊ߲߬
|
||||||
Name[nqo]=qBittorrent
|
Name[nqo]=qBittorrent
|
||||||
Comment[uz@Latn]=BitTorrent orqali fayllarni yuklab olish va baham ko‘rish
|
|
||||||
GenericName[uz@Latn]=BitTorrent mijozi
|
GenericName[uz@Latn]=BitTorrent mijozi
|
||||||
|
Comment[uz@Latn]=BitTorrent orqali fayllarni yuklab olish va baham ko‘rish
|
||||||
Name[uz@Latn]=qBittorrent
|
Name[uz@Latn]=qBittorrent
|
||||||
Comment[ltg]=Atsasyuteit i daleit failus ar BitTorrent
|
|
||||||
GenericName[ltg]=BitTorrent klients
|
GenericName[ltg]=BitTorrent klients
|
||||||
|
Comment[ltg]=Atsasyuteit i daleit failus ar BitTorrent
|
||||||
Name[ltg]=qBittorrent
|
Name[ltg]=qBittorrent
|
||||||
Comment[hi_IN]=BitTorrent द्वारा फाइल डाउनलोड व सहभाजन
|
|
||||||
GenericName[hi_IN]=Bittorrent साधन
|
GenericName[hi_IN]=Bittorrent साधन
|
||||||
|
Comment[hi_IN]=BitTorrent द्वारा फाइल डाउनलोड व सहभाजन
|
||||||
Name[hi_IN]=qBittorrent
|
Name[hi_IN]=qBittorrent
|
||||||
Comment[az@latin]=Faylları BitTorrent vasitəsilə endirin və paylaşın
|
|
||||||
GenericName[az@latin]=BitTorrent client
|
GenericName[az@latin]=BitTorrent client
|
||||||
|
Comment[az@latin]=Faylları BitTorrent vasitəsilə endirin və paylaşın
|
||||||
Name[az@latin]=qBittorrent
|
Name[az@latin]=qBittorrent
|
||||||
Comment[lv_LV]=Lejupielādēt un koplietot failus ar BitTorrent
|
|
||||||
GenericName[lv_LV]=BitTorrent klients
|
GenericName[lv_LV]=BitTorrent klients
|
||||||
|
Comment[lv_LV]=Lejupielādēt un koplietot failus ar BitTorrent
|
||||||
Name[lv_LV]=qBittorrent
|
Name[lv_LV]=qBittorrent
|
||||||
Comment[ms_MY]=Muat turun dan kongsi fail melalui BitTorrent
|
|
||||||
GenericName[ms_MY]=Klien BitTorrent
|
GenericName[ms_MY]=Klien BitTorrent
|
||||||
|
Comment[ms_MY]=Muat turun dan kongsi fail melalui BitTorrent
|
||||||
Name[ms_MY]=qBittorrent
|
Name[ms_MY]=qBittorrent
|
||||||
Comment[mn_MN]=BitTorrent-оор файлуудаа тат, түгээ
|
|
||||||
GenericName[mn_MN]=BitTorrent татагч
|
GenericName[mn_MN]=BitTorrent татагч
|
||||||
|
Comment[mn_MN]=BitTorrent-оор файлуудаа тат, түгээ
|
||||||
Name[mn_MN]=qBittorrent
|
Name[mn_MN]=qBittorrent
|
||||||
Comment[ne_NP]=फाइलहरू डाउनलोड गर्नुहोस् र BitTorrent मा साझा गर्नुहोस्
|
|
||||||
GenericName[ne_NP]=BitTorrent क्लाइन्ट
|
GenericName[ne_NP]=BitTorrent क्लाइन्ट
|
||||||
|
Comment[ne_NP]=फाइलहरू डाउनलोड गर्नुहोस् र BitTorrent मा साझा गर्नुहोस्
|
||||||
Name[ne_NP]=qBittorrent
|
Name[ne_NP]=qBittorrent
|
||||||
Comment[pt_PT]=Transferir e partilhar ficheiros por BitTorrent
|
|
||||||
GenericName[pt_PT]=Cliente BitTorrent
|
GenericName[pt_PT]=Cliente BitTorrent
|
||||||
|
Comment[pt_PT]=Transferir e partilhar ficheiros por BitTorrent
|
||||||
Name[pt_PT]=qBittorrent
|
Name[pt_PT]=qBittorrent
|
||||||
Name[si_LK]=qBittorrent
|
Name[si_LK]=qBittorrent
|
||||||
|
|||||||
2
dist/windows/config.nsi
vendored
2
dist/windows/config.nsi
vendored
@@ -25,7 +25,7 @@
|
|||||||
; 4.5.1.3 -> good
|
; 4.5.1.3 -> good
|
||||||
; 4.5.1.3.2 -> bad
|
; 4.5.1.3.2 -> bad
|
||||||
; 4.5.0beta -> bad
|
; 4.5.0beta -> bad
|
||||||
!define /ifndef QBT_VERSION "4.5.5"
|
!define /ifndef QBT_VERSION "4.6.0"
|
||||||
|
|
||||||
; Option that controls the installer's window name
|
; Option that controls the installer's window name
|
||||||
; If set, its value will be used like this:
|
; If set, its value will be used like this:
|
||||||
|
|||||||
4
dist/windows/installer.nsi
vendored
4
dist/windows/installer.nsi
vendored
@@ -109,7 +109,7 @@ Section $(inst_torrent) ;"Open .torrent files with qBittorrent"
|
|||||||
|
|
||||||
!insertmacro UAC_AsUser_Call Function inst_torrent_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
|
!insertmacro UAC_AsUser_Call Function inst_torrent_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
|
||||||
|
|
||||||
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
|
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p 0, p 0)'
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ Section $(inst_magnet) ;"Open magnet links with qBittorrent"
|
|||||||
|
|
||||||
!insertmacro UAC_AsUser_Call Function inst_magnet_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
|
!insertmacro UAC_AsUser_Call Function inst_magnet_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
|
||||||
|
|
||||||
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
|
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p 0, p 0)'
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
|||||||
11
dist/windows/uninstaller.nsi
vendored
11
dist/windows/uninstaller.nsi
vendored
@@ -26,17 +26,17 @@ Section "un.$(remove_associations)" ;"un.Remove file associations"
|
|||||||
DetailPrint "$(uninst_tor_warn) $0"
|
DetailPrint "$(uninst_tor_warn) $0"
|
||||||
DeleteRegValue HKLM "Software\Classes\.torrent" ""
|
DeleteRegValue HKLM "Software\Classes\.torrent" ""
|
||||||
DeleteRegKey /ifempty HKLM "Software\Classes\.torrent"
|
DeleteRegKey /ifempty HKLM "Software\Classes\.torrent"
|
||||||
|
|
||||||
torrent_end:
|
torrent_end:
|
||||||
|
|
||||||
ReadRegStr $0 HKLM "Software\Classes\magnet\shell\open\command" ""
|
ReadRegStr $0 HKLM "Software\Classes\magnet\shell\open\command" ""
|
||||||
StrCmp $0 '"$INSTDIR\qbittorrent.exe" "%1"' 0 magnet_end
|
StrCmp $0 '"$INSTDIR\qbittorrent.exe" "%1"' 0 magnet_end
|
||||||
DetailPrint "$(uninst_mag_warn) $0"
|
DetailPrint "$(uninst_mag_warn) $0"
|
||||||
DeleteRegKey HKLM "Software\Classes\magnet"
|
DeleteRegKey HKLM "Software\Classes\magnet"
|
||||||
|
|
||||||
magnet_end:
|
magnet_end:
|
||||||
|
|
||||||
!insertmacro UAC_AsUser_Call Function un.remove_associations_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
|
!insertmacro UAC_AsUser_Call Function un.remove_associations_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
|
||||||
|
|
||||||
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
|
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p 0, p 0)'
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
Function un.remove_associations_user
|
Function un.remove_associations_user
|
||||||
@@ -45,13 +45,12 @@ Function un.remove_associations_user
|
|||||||
DetailPrint "$(uninst_tor_warn) $0"
|
DetailPrint "$(uninst_tor_warn) $0"
|
||||||
DeleteRegValue HKCU "Software\Classes\.torrent" ""
|
DeleteRegValue HKCU "Software\Classes\.torrent" ""
|
||||||
DeleteRegKey /ifempty HKCU "Software\Classes\.torrent"
|
DeleteRegKey /ifempty HKCU "Software\Classes\.torrent"
|
||||||
|
|
||||||
torrent_end:
|
torrent_end:
|
||||||
|
|
||||||
ReadRegStr $0 HKCU "Software\Classes\magnet\shell\open\command" ""
|
ReadRegStr $0 HKCU "Software\Classes\magnet\shell\open\command" ""
|
||||||
StrCmp $0 '"$INSTDIR\qbittorrent.exe" "%1"' 0 magnet_end
|
StrCmp $0 '"$INSTDIR\qbittorrent.exe" "%1"' 0 magnet_end
|
||||||
DetailPrint "$(uninst_mag_warn) $0"
|
DetailPrint "$(uninst_mag_warn) $0"
|
||||||
DeleteRegKey HKCU "Software\Classes\magnet"
|
DeleteRegKey HKCU "Software\Classes\magnet"
|
||||||
|
|
||||||
magnet_end:
|
magnet_end:
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ Section "un.$(remove_registry)" ;"un.Remove registry keys"
|
|||||||
DeleteRegKey HKLM "Software\qBittorrent"
|
DeleteRegKey HKLM "Software\qBittorrent"
|
||||||
DeleteRegKey HKLM "Software\Classes\qBittorrent"
|
DeleteRegKey HKLM "Software\Classes\qBittorrent"
|
||||||
|
|
||||||
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
|
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p 0, p 0)'
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
Section "un.$(remove_firewall)" ;
|
Section "un.$(remove_firewall)" ;
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ number.
|
|||||||
8080).
|
8080).
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
.PP
|
.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
|
.SH AUTHORS
|
||||||
Christophe Dumez <chris@qbittorrent.org>.
|
Christophe Dumez <chris@qbittorrent.org>.
|
||||||
|
|||||||
@@ -38,4 +38,4 @@ the default account user name is "admin" with "adminadmin" as a password.
|
|||||||
|
|
||||||
|
|
||||||
# BUGS
|
# 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).
|
8080).
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
.PP
|
.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
|
.SH AUTHORS
|
||||||
Christophe Dumez <chris@qbittorrent.org>.
|
Christophe Dumez <chris@qbittorrent.org>.
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ FAST extension (mainline) and PeX support (utorrent compatible).
|
|||||||
|
|
||||||
|
|
||||||
# BUGS
|
# 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
|
||||||
|
|||||||
@@ -97,6 +97,11 @@
|
|||||||
#include "gui/shutdownconfirmdialog.h"
|
#include "gui/shutdownconfirmdialog.h"
|
||||||
#include "gui/uithememanager.h"
|
#include "gui/uithememanager.h"
|
||||||
#include "gui/utils.h"
|
#include "gui/utils.h"
|
||||||
|
#include "gui/windowstate.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "base/utils/os.h"
|
||||||
|
#endif // Q_OS_WIN
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
@@ -107,12 +112,12 @@ namespace
|
|||||||
{
|
{
|
||||||
#define SETTINGS_KEY(name) u"Application/" name
|
#define SETTINGS_KEY(name) u"Application/" name
|
||||||
#define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY(u"FileLogger/") name)
|
#define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY(u"FileLogger/") name)
|
||||||
#define NOTIFICATIONS_SETTINGS_KEY(name) (SETTINGS_KEY(u"GUI/Notifications/"_qs) name)
|
#define NOTIFICATIONS_SETTINGS_KEY(name) (SETTINGS_KEY(u"GUI/Notifications/"_s) name)
|
||||||
|
|
||||||
const QString LOG_FOLDER = u"logs"_qs;
|
const QString LOG_FOLDER = u"logs"_s;
|
||||||
const QChar PARAMS_SEPARATOR = u'|';
|
const QChar PARAMS_SEPARATOR = u'|';
|
||||||
|
|
||||||
const Path DEFAULT_PORTABLE_MODE_PROFILE_DIR {u"profile"_qs};
|
const Path DEFAULT_PORTABLE_MODE_PROFILE_DIR {u"profile"_s};
|
||||||
|
|
||||||
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
||||||
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
||||||
@@ -121,34 +126,132 @@ namespace
|
|||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
|
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QString serializeParams(const QBtCommandLineParameters ¶ms)
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
// Because we're passing a string list to the currently running
|
||||||
|
// qBittorrent process, we need some way of passing along the options
|
||||||
|
// the user has specified. Here we place special strings that are
|
||||||
|
// almost certainly not going to collide with a file path or URL
|
||||||
|
// specified by the user, and placing them at the beginning of the
|
||||||
|
// string list so that they will be processed before the list of
|
||||||
|
// torrent paths or URLs.
|
||||||
|
|
||||||
|
const BitTorrent::AddTorrentParams &addTorrentParams = params.addTorrentParams;
|
||||||
|
|
||||||
|
if (!addTorrentParams.savePath.isEmpty())
|
||||||
|
result.append(u"@savePath=" + addTorrentParams.savePath.data());
|
||||||
|
|
||||||
|
if (addTorrentParams.addPaused.has_value())
|
||||||
|
result.append(*addTorrentParams.addPaused ? u"@addPaused=1"_s : u"@addPaused=0"_s);
|
||||||
|
|
||||||
|
if (addTorrentParams.skipChecking)
|
||||||
|
result.append(u"@skipChecking"_s);
|
||||||
|
|
||||||
|
if (!addTorrentParams.category.isEmpty())
|
||||||
|
result.append(u"@category=" + addTorrentParams.category);
|
||||||
|
|
||||||
|
if (addTorrentParams.sequential)
|
||||||
|
result.append(u"@sequential"_s);
|
||||||
|
|
||||||
|
if (addTorrentParams.firstLastPiecePriority)
|
||||||
|
result.append(u"@firstLastPiecePriority"_s);
|
||||||
|
|
||||||
|
if (params.skipDialog.has_value())
|
||||||
|
result.append(*params.skipDialog ? u"@skipDialog=1"_s : u"@skipDialog=0"_s);
|
||||||
|
|
||||||
|
result += params.torrentSources;
|
||||||
|
|
||||||
|
return result.join(PARAMS_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
QBtCommandLineParameters parseParams(const QString &str)
|
||||||
|
{
|
||||||
|
QBtCommandLineParameters parsedParams;
|
||||||
|
BitTorrent::AddTorrentParams &addTorrentParams = parsedParams.addTorrentParams;
|
||||||
|
|
||||||
|
for (QString param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts)))
|
||||||
|
{
|
||||||
|
param = param.trimmed();
|
||||||
|
|
||||||
|
// Process strings indicating options specified by the user.
|
||||||
|
|
||||||
|
if (param.startsWith(u"@savePath="))
|
||||||
|
{
|
||||||
|
addTorrentParams.savePath = Path(param.mid(10));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.startsWith(u"@addPaused="))
|
||||||
|
{
|
||||||
|
addTorrentParams.addPaused = (QStringView(param).mid(11).toInt() != 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param == u"@skipChecking")
|
||||||
|
{
|
||||||
|
addTorrentParams.skipChecking = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.startsWith(u"@category="))
|
||||||
|
{
|
||||||
|
addTorrentParams.category = param.mid(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param == u"@sequential")
|
||||||
|
{
|
||||||
|
addTorrentParams.sequential = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param == u"@firstLastPiecePriority")
|
||||||
|
{
|
||||||
|
addTorrentParams.firstLastPiecePriority = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.startsWith(u"@skipDialog="))
|
||||||
|
{
|
||||||
|
parsedParams.skipDialog = (QStringView(param).mid(12).toInt() != 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedParams.torrentSources.append(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedParams;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Application(int &argc, char **argv)
|
Application::Application(int &argc, char **argv)
|
||||||
: BaseApplication(argc, argv)
|
: BaseApplication(argc, argv)
|
||||||
, m_shutdownAct(ShutdownDialogAction::Exit)
|
, m_commandLineArgs(parseCommandLine(Application::arguments()))
|
||||||
, m_commandLineArgs(parseCommandLine(this->arguments()))
|
, m_storeFileLoggerEnabled(FILELOGGER_SETTINGS_KEY(u"Enabled"_s))
|
||||||
, m_storeFileLoggerEnabled(FILELOGGER_SETTINGS_KEY(u"Enabled"_qs))
|
, m_storeFileLoggerBackup(FILELOGGER_SETTINGS_KEY(u"Backup"_s))
|
||||||
, m_storeFileLoggerBackup(FILELOGGER_SETTINGS_KEY(u"Backup"_qs))
|
, m_storeFileLoggerDeleteOld(FILELOGGER_SETTINGS_KEY(u"DeleteOld"_s))
|
||||||
, m_storeFileLoggerDeleteOld(FILELOGGER_SETTINGS_KEY(u"DeleteOld"_qs))
|
, m_storeFileLoggerMaxSize(FILELOGGER_SETTINGS_KEY(u"MaxSizeBytes"_s))
|
||||||
, m_storeFileLoggerMaxSize(FILELOGGER_SETTINGS_KEY(u"MaxSizeBytes"_qs))
|
, m_storeFileLoggerAge(FILELOGGER_SETTINGS_KEY(u"Age"_s))
|
||||||
, m_storeFileLoggerAge(FILELOGGER_SETTINGS_KEY(u"Age"_qs))
|
, m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY(u"AgeType"_s))
|
||||||
, m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY(u"AgeType"_qs))
|
, m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY(u"Path"_s))
|
||||||
, m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY(u"Path"_qs))
|
, m_storeMemoryWorkingSetLimit(SETTINGS_KEY(u"MemoryWorkingSetLimit"_s))
|
||||||
, m_storeMemoryWorkingSetLimit(SETTINGS_KEY(u"MemoryWorkingSetLimit"_qs))
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
, m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_qs))
|
, m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_s))
|
||||||
#endif
|
#endif
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
, m_storeNotificationTorrentAdded(NOTIFICATIONS_SETTINGS_KEY(u"TorrentAdded"_qs))
|
, m_startUpWindowState(u"GUI/StartUpWindowState"_s)
|
||||||
|
, m_storeNotificationTorrentAdded(NOTIFICATIONS_SETTINGS_KEY(u"TorrentAdded"_s))
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
qRegisterMetaType<Log::Msg>("Log::Msg");
|
qRegisterMetaType<Log::Msg>("Log::Msg");
|
||||||
qRegisterMetaType<Log::Peer>("Log::Peer");
|
qRegisterMetaType<Log::Peer>("Log::Peer");
|
||||||
|
|
||||||
setApplicationName(u"qBittorrent"_qs);
|
setApplicationName(u"qBittorrent"_s);
|
||||||
setOrganizationDomain(u"qbittorrent.org"_qs);
|
setOrganizationDomain(u"qbittorrent.org"_s);
|
||||||
#if !defined(DISABLE_GUI)
|
#if !defined(DISABLE_GUI)
|
||||||
setDesktopFileName(u"org.qbittorrent.qBittorrent"_qs);
|
setDesktopFileName(u"org.qbittorrent.qBittorrent"_s);
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
|
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
|
||||||
#endif
|
#endif
|
||||||
@@ -176,7 +279,7 @@ Application::Application(int &argc, char **argv)
|
|||||||
if (!firstTimeUser)
|
if (!firstTimeUser)
|
||||||
{
|
{
|
||||||
if (!upgrade())
|
if (!upgrade())
|
||||||
throw RuntimeError(u"Failed migration of old settings"_qs); // Not translatable. Translation isn't configured yet.
|
throw RuntimeError(u"Failed migration of old settings"_s); // Not translatable. Translation isn't configured yet.
|
||||||
handleChangedDefaults(DefaultPreferencesMode::Legacy);
|
handleChangedDefaults(DefaultPreferencesMode::Legacy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -197,7 +300,7 @@ Application::Application(int &argc, char **argv)
|
|||||||
{
|
{
|
||||||
LogMsg(tr("Running in portable mode. Auto detected profile folder at: %1").arg(profileDir.toString()));
|
LogMsg(tr("Running in portable mode. Auto detected profile folder at: %1").arg(profileDir.toString()));
|
||||||
if (m_commandLineArgs.relativeFastresumePaths)
|
if (m_commandLineArgs.relativeFastresumePaths)
|
||||||
LogMsg(tr("Redundant command line flag detected: \"%1\". Portable mode implies relative fastresume.").arg(u"--relative-fastresume"_qs), Log::WARNING); // to avoid translating the `--relative-fastresume` string
|
LogMsg(tr("Redundant command line flag detected: \"%1\". Portable mode implies relative fastresume.").arg(u"--relative-fastresume"_s), Log::WARNING); // to avoid translating the `--relative-fastresume` string
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -212,7 +315,7 @@ Application::Application(int &argc, char **argv)
|
|||||||
|
|
||||||
if (m_commandLineArgs.torrentingPort > 0) // it will be -1 when user did not set any value
|
if (m_commandLineArgs.torrentingPort > 0) // it will be -1 when user did not set any value
|
||||||
{
|
{
|
||||||
SettingValue<int> port {u"BitTorrent/Session/Port"_qs};
|
SettingValue<int> port {u"BitTorrent/Session/Port"_s};
|
||||||
port = m_commandLineArgs.torrentingPort;
|
port = m_commandLineArgs.torrentingPort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,6 +338,16 @@ MainWindow *Application::mainWindow()
|
|||||||
return m_window;
|
return m_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowState Application::startUpWindowState() const
|
||||||
|
{
|
||||||
|
return m_startUpWindowState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::setStartUpWindowState(const WindowState windowState)
|
||||||
|
{
|
||||||
|
m_startUpWindowState = windowState;
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::isTorrentAddedNotificationsEnabled() const
|
bool Application::isTorrentAddedNotificationsEnabled() const
|
||||||
{
|
{
|
||||||
return m_storeNotificationTorrentAdded;
|
return m_storeNotificationTorrentAdded;
|
||||||
@@ -378,7 +491,7 @@ void Application::processMessage(const QString &message)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const AddTorrentParams params = parseParams(message.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts));
|
const QBtCommandLineParameters params = parseParams(message);
|
||||||
// If Application is not allowed to process params immediately
|
// If Application is not allowed to process params immediately
|
||||||
// (i.e., other components are not ready) store params
|
// (i.e., other components are not ready) store params
|
||||||
if (m_isProcessingParamsAllowed)
|
if (m_isProcessingParamsAllowed)
|
||||||
@@ -414,13 +527,13 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
|||||||
str.replace(i, 2, torrent->contentPath().toString());
|
str.replace(i, 2, torrent->contentPath().toString());
|
||||||
break;
|
break;
|
||||||
case u'G':
|
case u'G':
|
||||||
str.replace(i, 2, torrent->tags().join(u","_qs));
|
str.replace(i, 2, torrent->tags().join(u","_s));
|
||||||
break;
|
break;
|
||||||
case u'I':
|
case u'I':
|
||||||
str.replace(i, 2, (torrent->infoHash().v1().isValid() ? torrent->infoHash().v1().toString() : u"-"_qs));
|
str.replace(i, 2, (torrent->infoHash().v1().isValid() ? torrent->infoHash().v1().toString() : u"-"_s));
|
||||||
break;
|
break;
|
||||||
case u'J':
|
case u'J':
|
||||||
str.replace(i, 2, (torrent->infoHash().v2().isValid() ? torrent->infoHash().v2().toString() : u"-"_qs));
|
str.replace(i, 2, (torrent->infoHash().v2().isValid() ? torrent->infoHash().v2().toString() : u"-"_s));
|
||||||
break;
|
break;
|
||||||
case u'K':
|
case u'K':
|
||||||
str.replace(i, 2, torrent->id().toString());
|
str.replace(i, 2, torrent->id().toString());
|
||||||
@@ -626,71 +739,12 @@ void Application::allTorrentsFinished()
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::sendParams(const QStringList ¶ms)
|
bool Application::callMainInstance()
|
||||||
{
|
{
|
||||||
return m_instanceManager->sendMessage(params.join(PARAMS_SEPARATOR));
|
return m_instanceManager->sendMessage(serializeParams(commandLineArgs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::AddTorrentParams Application::parseParams(const QStringList ¶ms) const
|
void Application::processParams(const QBtCommandLineParameters ¶ms)
|
||||||
{
|
|
||||||
AddTorrentParams parsedParams;
|
|
||||||
BitTorrent::AddTorrentParams &torrentParams = parsedParams.torrentParams;
|
|
||||||
|
|
||||||
for (QString param : params)
|
|
||||||
{
|
|
||||||
param = param.trimmed();
|
|
||||||
|
|
||||||
// Process strings indicating options specified by the user.
|
|
||||||
|
|
||||||
if (param.startsWith(u"@savePath="))
|
|
||||||
{
|
|
||||||
torrentParams.savePath = Path(param.mid(10));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.startsWith(u"@addPaused="))
|
|
||||||
{
|
|
||||||
torrentParams.addPaused = (QStringView(param).mid(11).toInt() != 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param == u"@skipChecking")
|
|
||||||
{
|
|
||||||
torrentParams.skipChecking = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.startsWith(u"@category="))
|
|
||||||
{
|
|
||||||
torrentParams.category = param.mid(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param == u"@sequential")
|
|
||||||
{
|
|
||||||
torrentParams.sequential = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param == u"@firstLastPiecePriority")
|
|
||||||
{
|
|
||||||
torrentParams.firstLastPiecePriority = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.startsWith(u"@skipDialog="))
|
|
||||||
{
|
|
||||||
parsedParams.skipTorrentDialog = (QStringView(param).mid(12).toInt() != 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedParams.torrentSources.append(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::processParams(const AddTorrentParams ¶ms)
|
|
||||||
{
|
{
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
// There are two circumstances in which we want to show the torrent
|
// There are two circumstances in which we want to show the torrent
|
||||||
@@ -698,22 +752,21 @@ void Application::processParams(const AddTorrentParams ¶ms)
|
|||||||
// be shown and skipTorrentDialog is undefined. The other is when
|
// be shown and skipTorrentDialog is undefined. The other is when
|
||||||
// skipTorrentDialog is false, meaning that the application setting
|
// skipTorrentDialog is false, meaning that the application setting
|
||||||
// should be overridden.
|
// should be overridden.
|
||||||
const bool showDialogForThisTorrent = !params.skipTorrentDialog.value_or(!AddNewTorrentDialog::isEnabled());
|
const bool showDialog = !params.skipDialog.value_or(!AddNewTorrentDialog::isEnabled());
|
||||||
if (showDialogForThisTorrent)
|
if (showDialog)
|
||||||
{
|
{
|
||||||
for (const QString &torrentSource : params.torrentSources)
|
for (const QString &torrentSource : params.torrentSources)
|
||||||
AddNewTorrentDialog::show(torrentSource, params.torrentParams, m_window);
|
AddNewTorrentDialog::show(torrentSource, params.addTorrentParams, m_window);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
for (const QString &torrentSource : params.torrentSources)
|
for (const QString &torrentSource : params.torrentSources)
|
||||||
BitTorrent::Session::instance()->addTorrent(torrentSource, params.torrentParams);
|
BitTorrent::Session::instance()->addTorrent(torrentSource, params.addTorrentParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Application::exec(const QStringList ¶ms)
|
int Application::exec()
|
||||||
try
|
|
||||||
{
|
{
|
||||||
#if !defined(DISABLE_WEBUI) && defined(DISABLE_GUI)
|
#if !defined(DISABLE_WEBUI) && defined(DISABLE_GUI)
|
||||||
const QString loadingStr = tr("WebUI will be started shortly after internal preparations. Please wait...");
|
const QString loadingStr = tr("WebUI will be started shortly after internal preparations. Please wait...");
|
||||||
@@ -742,7 +795,7 @@ try
|
|||||||
#ifndef Q_OS_MACOS
|
#ifndef Q_OS_MACOS
|
||||||
auto *desktopIntegrationMenu = new QMenu;
|
auto *desktopIntegrationMenu = new QMenu;
|
||||||
auto *actionExit = new QAction(tr("E&xit"), desktopIntegrationMenu);
|
auto *actionExit = new QAction(tr("E&xit"), desktopIntegrationMenu);
|
||||||
actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_qs));
|
actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_s));
|
||||||
actionExit->setMenuRole(QAction::QuitRole);
|
actionExit->setMenuRole(QAction::QuitRole);
|
||||||
actionExit->setShortcut(Qt::CTRL | Qt::Key_Q);
|
actionExit->setShortcut(Qt::CTRL | Qt::Key_Q);
|
||||||
connect(actionExit, &QAction::triggered, this, []
|
connect(actionExit, &QAction::triggered, this, []
|
||||||
@@ -752,11 +805,8 @@ try
|
|||||||
desktopIntegrationMenu->addAction(actionExit);
|
desktopIntegrationMenu->addAction(actionExit);
|
||||||
|
|
||||||
m_desktopIntegration->setMenu(desktopIntegrationMenu);
|
m_desktopIntegration->setMenu(desktopIntegrationMenu);
|
||||||
#endif
|
|
||||||
|
|
||||||
const auto *pref = Preferences::instance();
|
const bool isHidden = m_desktopIntegration->isActive() && (startUpWindowState() == WindowState::Hidden);
|
||||||
#ifndef Q_OS_MACOS
|
|
||||||
const bool isHidden = m_desktopIntegration->isActive() && pref->startMinimized() && pref->minimizeToTray();
|
|
||||||
#else
|
#else
|
||||||
const bool isHidden = false;
|
const bool isHidden = false;
|
||||||
#endif
|
#endif
|
||||||
@@ -766,7 +816,7 @@ try
|
|||||||
createStartupProgressDialog();
|
createStartupProgressDialog();
|
||||||
// Add a small delay to avoid "flashing" the progress dialog in case there are not many torrents to restore.
|
// Add a small delay to avoid "flashing" the progress dialog in case there are not many torrents to restore.
|
||||||
m_startupProgressDialog->setMinimumDuration(1000);
|
m_startupProgressDialog->setMinimumDuration(1000);
|
||||||
if (pref->startMinimized())
|
if (startUpWindowState() != WindowState::Normal)
|
||||||
m_startupProgressDialog->setWindowState(Qt::WindowMinimized);
|
m_startupProgressDialog->setWindowState(Qt::WindowMinimized);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -819,20 +869,26 @@ try
|
|||||||
});
|
});
|
||||||
|
|
||||||
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
||||||
const MainWindow::State windowState = (!m_startupProgressDialog || (m_startupProgressDialog->windowState() & Qt::WindowMinimized))
|
#ifndef Q_OS_MACOS
|
||||||
? MainWindow::Minimized : MainWindow::Normal;
|
const WindowState windowState = !m_startupProgressDialog ? WindowState::Hidden
|
||||||
|
: (m_startupProgressDialog->windowState() & Qt::WindowMinimized) ? WindowState::Minimized
|
||||||
|
: WindowState::Normal;
|
||||||
|
#else
|
||||||
|
const WindowState windowState = (m_startupProgressDialog->windowState() & Qt::WindowMinimized)
|
||||||
|
? WindowState::Minimized : WindowState::Normal;
|
||||||
|
#endif
|
||||||
m_window = new MainWindow(this, windowState);
|
m_window = new MainWindow(this, windowState);
|
||||||
delete m_startupProgressDialog;
|
delete m_startupProgressDialog;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
auto *pref = Preferences::instance();
|
auto *pref = Preferences::instance();
|
||||||
if (!pref->neverCheckFileAssoc() && (!Preferences::isTorrentFileAssocSet() || !Preferences::isMagnetLinkAssocSet()))
|
if (!pref->neverCheckFileAssoc() && (!Utils::OS::isTorrentFileAssocSet() || !Utils::OS::isMagnetLinkAssocSet()))
|
||||||
{
|
{
|
||||||
if (QMessageBox::question(m_window, tr("Torrent file association")
|
if (QMessageBox::question(m_window, tr("Torrent file association")
|
||||||
, tr("qBittorrent is not the default application for opening torrent files or Magnet links.\nDo you want to make qBittorrent the default application for these?")
|
, tr("qBittorrent is not the default application for opening torrent files or Magnet links.\nDo you want to make qBittorrent the default application for these?")
|
||||||
, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes)
|
, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes)
|
||||||
{
|
{
|
||||||
pref->setTorrentFileAssoc(true);
|
Utils::OS::setTorrentFileAssoc(true);
|
||||||
pref->setMagnetLinkAssoc(true);
|
Utils::OS::setMagnetLinkAssoc(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -851,16 +907,16 @@ try
|
|||||||
|
|
||||||
const Preferences *pref = Preferences::instance();
|
const Preferences *pref = Preferences::instance();
|
||||||
|
|
||||||
const auto scheme = pref->isWebUiHttpsEnabled() ? u"https"_qs : u"http"_qs;
|
const auto scheme = pref->isWebUiHttpsEnabled() ? u"https"_s : u"http"_s;
|
||||||
const auto url = u"%1://localhost:%2\n"_qs.arg(scheme, QString::number(pref->getWebUiPort()));
|
const auto url = u"%1://localhost:%2\n"_s.arg(scheme, QString::number(pref->getWebUiPort()));
|
||||||
const QString mesg = u"\n******** %1 ********\n"_qs.arg(tr("Information"))
|
const QString mesg = u"\n******** %1 ********\n"_s.arg(tr("Information"))
|
||||||
+ tr("To control qBittorrent, access the WebUI at: %1").arg(url);
|
+ tr("To control qBittorrent, access the WebUI at: %1").arg(url);
|
||||||
printf("%s\n", qUtf8Printable(mesg));
|
printf("%s\n", qUtf8Printable(mesg));
|
||||||
|
|
||||||
if (pref->getWebUIPassword() == QByteArrayLiteral("ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ=="))
|
if (pref->getWebUIPassword() == QByteArrayLiteral("ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ=="))
|
||||||
{
|
{
|
||||||
const QString warning = tr("The Web UI administrator username is: %1").arg(pref->getWebUiUsername()) + u'\n'
|
const QString warning = tr("The Web UI administrator username is: %1").arg(pref->getWebUiUsername()) + u'\n'
|
||||||
+ tr("The Web UI administrator password has not been changed from the default: %1").arg(u"adminadmin"_qs) + u'\n'
|
+ tr("The Web UI administrator password has not been changed from the default: %1").arg(u"adminadmin"_s) + u'\n'
|
||||||
+ tr("This is a security risk, please change your password in program preferences.") + u'\n';
|
+ tr("This is a security risk, please change your password in program preferences.") + u'\n';
|
||||||
printf("%s", qUtf8Printable(warning));
|
printf("%s", qUtf8Printable(warning));
|
||||||
}
|
}
|
||||||
@@ -868,31 +924,17 @@ try
|
|||||||
#endif // DISABLE_WEBUI
|
#endif // DISABLE_WEBUI
|
||||||
|
|
||||||
m_isProcessingParamsAllowed = true;
|
m_isProcessingParamsAllowed = true;
|
||||||
for (const AddTorrentParams ¶ms : m_paramsQueue)
|
for (const QBtCommandLineParameters ¶ms : m_paramsQueue)
|
||||||
processParams(params);
|
processParams(params);
|
||||||
m_paramsQueue.clear();
|
m_paramsQueue.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!params.isEmpty())
|
const QBtCommandLineParameters params = commandLineArgs();
|
||||||
m_paramsQueue.append(parseParams(params));
|
if (!params.torrentSources.isEmpty())
|
||||||
|
m_paramsQueue.append(params);
|
||||||
|
|
||||||
return BaseApplication::exec();
|
return BaseApplication::exec();
|
||||||
}
|
}
|
||||||
catch (const RuntimeError &err)
|
|
||||||
{
|
|
||||||
#ifdef DISABLE_GUI
|
|
||||||
fprintf(stderr, "%s", qPrintable(err.message()));
|
|
||||||
#else
|
|
||||||
QMessageBox msgBox;
|
|
||||||
msgBox.setIcon(QMessageBox::Critical);
|
|
||||||
msgBox.setText(QCoreApplication::translate("Application", "Application failed to start."));
|
|
||||||
msgBox.setInformativeText(err.message());
|
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
|
||||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
|
||||||
msgBox.exec();
|
|
||||||
#endif
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::isRunning()
|
bool Application::isRunning()
|
||||||
{
|
{
|
||||||
@@ -960,7 +1002,8 @@ bool Application::event(QEvent *ev)
|
|||||||
path = static_cast<QFileOpenEvent *>(ev)->url().toString();
|
path = static_cast<QFileOpenEvent *>(ev)->url().toString();
|
||||||
qDebug("Received a mac file open event: %s", qUtf8Printable(path));
|
qDebug("Received a mac file open event: %s", qUtf8Printable(path));
|
||||||
|
|
||||||
const AddTorrentParams params = parseParams({path});
|
QBtCommandLineParameters params;
|
||||||
|
params.torrentSources.append(path);
|
||||||
// If Application is not allowed to process params immediately
|
// If Application is not allowed to process params immediately
|
||||||
// (i.e., other components are not ready) store params
|
// (i.e., other components are not ready) store params
|
||||||
if (m_isProcessingParamsAllowed)
|
if (m_isProcessingParamsAllowed)
|
||||||
@@ -1067,7 +1110,22 @@ void Application::applyMemoryWorkingSetLimit() const
|
|||||||
if (::getrlimit(RLIMIT_RSS, &limit) != 0)
|
if (::getrlimit(RLIMIT_RSS, &limit) != 0)
|
||||||
return;
|
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)
|
if (::setrlimit(RLIMIT_RSS, &limit) != 0)
|
||||||
{
|
{
|
||||||
const auto message = QString::fromLocal8Bit(strerror(errno));
|
const auto message = QString::fromLocal8Bit(strerror(errno));
|
||||||
@@ -1095,12 +1153,12 @@ void Application::setProcessMemoryPriority(const MemoryPriority priority)
|
|||||||
void Application::applyMemoryPriority() const
|
void Application::applyMemoryPriority() const
|
||||||
{
|
{
|
||||||
using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
|
using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
|
||||||
const auto setProcessInformation = Utils::Misc::loadWinAPI<SETPROCESSINFORMATION>(u"Kernel32.dll"_qs, "SetProcessInformation");
|
const auto setProcessInformation = Utils::Misc::loadWinAPI<SETPROCESSINFORMATION>(u"Kernel32.dll"_s, "SetProcessInformation");
|
||||||
if (!setProcessInformation) // only available on Windows >= 8
|
if (!setProcessInformation) // only available on Windows >= 8
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using SETTHREADINFORMATION = BOOL (WINAPI *)(HANDLE, THREAD_INFORMATION_CLASS, LPVOID, DWORD);
|
using SETTHREADINFORMATION = BOOL (WINAPI *)(HANDLE, THREAD_INFORMATION_CLASS, LPVOID, DWORD);
|
||||||
const auto setThreadInformation = Utils::Misc::loadWinAPI<SETTHREADINFORMATION>(u"Kernel32.dll"_qs, "SetThreadInformation");
|
const auto setThreadInformation = Utils::Misc::loadWinAPI<SETTHREADINFORMATION>(u"Kernel32.dll"_s, "SetThreadInformation");
|
||||||
if (!setThreadInformation) // only available on Windows >= 8
|
if (!setThreadInformation) // only available on Windows >= 8
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -96,10 +96,10 @@ public:
|
|||||||
Application(int &argc, char **argv);
|
Application(int &argc, char **argv);
|
||||||
~Application() override;
|
~Application() override;
|
||||||
|
|
||||||
int exec(const QStringList ¶ms);
|
int exec();
|
||||||
|
|
||||||
bool isRunning();
|
bool isRunning();
|
||||||
bool sendParams(const QStringList ¶ms);
|
bool callMainInstance();
|
||||||
const QBtCommandLineParameters &commandLineArgs() const;
|
const QBtCommandLineParameters &commandLineArgs() const;
|
||||||
|
|
||||||
// FileLogger properties
|
// FileLogger properties
|
||||||
@@ -130,6 +130,9 @@ public:
|
|||||||
DesktopIntegration *desktopIntegration() override;
|
DesktopIntegration *desktopIntegration() override;
|
||||||
MainWindow *mainWindow() override;
|
MainWindow *mainWindow() override;
|
||||||
|
|
||||||
|
WindowState startUpWindowState() const override;
|
||||||
|
void setStartUpWindowState(WindowState windowState) override;
|
||||||
|
|
||||||
bool isTorrentAddedNotificationsEnabled() const override;
|
bool isTorrentAddedNotificationsEnabled() const override;
|
||||||
void setTorrentAddedNotificationsEnabled(bool value) override;
|
void setTorrentAddedNotificationsEnabled(bool value) override;
|
||||||
#endif
|
#endif
|
||||||
@@ -146,16 +149,8 @@ private slots:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AddTorrentParams
|
|
||||||
{
|
|
||||||
QStringList torrentSources;
|
|
||||||
BitTorrent::AddTorrentParams torrentParams;
|
|
||||||
std::optional<bool> skipTorrentDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
void initializeTranslation();
|
void initializeTranslation();
|
||||||
AddTorrentParams parseParams(const QStringList ¶ms) const;
|
void processParams(const QBtCommandLineParameters ¶ms);
|
||||||
void processParams(const AddTorrentParams ¶ms);
|
|
||||||
void runExternalProgram(const QString &programTemplate, const BitTorrent::Torrent *torrent) const;
|
void runExternalProgram(const QString &programTemplate, const BitTorrent::Torrent *torrent) const;
|
||||||
void sendNotificationEmail(const BitTorrent::Torrent *torrent);
|
void sendNotificationEmail(const BitTorrent::Torrent *torrent);
|
||||||
|
|
||||||
@@ -178,7 +173,7 @@ private:
|
|||||||
ApplicationInstanceManager *m_instanceManager = nullptr;
|
ApplicationInstanceManager *m_instanceManager = nullptr;
|
||||||
QAtomicInt m_isCleanupRun;
|
QAtomicInt m_isCleanupRun;
|
||||||
bool m_isProcessingParamsAllowed = false;
|
bool m_isProcessingParamsAllowed = false;
|
||||||
ShutdownDialogAction m_shutdownAct;
|
ShutdownDialogAction m_shutdownAct = ShutdownDialogAction::Exit;
|
||||||
QBtCommandLineParameters m_commandLineArgs;
|
QBtCommandLineParameters m_commandLineArgs;
|
||||||
|
|
||||||
// FileLog
|
// FileLog
|
||||||
@@ -187,7 +182,7 @@ private:
|
|||||||
QTranslator m_qtTranslator;
|
QTranslator m_qtTranslator;
|
||||||
QTranslator m_translator;
|
QTranslator m_translator;
|
||||||
|
|
||||||
QList<AddTorrentParams> m_paramsQueue;
|
QList<QBtCommandLineParameters> m_paramsQueue;
|
||||||
|
|
||||||
SettingValue<bool> m_storeFileLoggerEnabled;
|
SettingValue<bool> m_storeFileLoggerEnabled;
|
||||||
SettingValue<bool> m_storeFileLoggerBackup;
|
SettingValue<bool> m_storeFileLoggerBackup;
|
||||||
@@ -203,6 +198,7 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
|
SettingValue<WindowState> m_startUpWindowState;
|
||||||
SettingValue<bool> m_storeNotificationTorrentAdded;
|
SettingValue<bool> m_storeNotificationTorrentAdded;
|
||||||
|
|
||||||
DesktopIntegration *m_desktopIntegration = nullptr;
|
DesktopIntegration *m_desktopIntegration = nullptr;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
@@ -151,16 +152,16 @@ namespace
|
|||||||
{
|
{
|
||||||
QStringList parts = arg.split(u'=');
|
QStringList parts = arg.split(u'=');
|
||||||
if (parts.size() == 2)
|
if (parts.size() == 2)
|
||||||
return Utils::String::unquote(parts[1], u"'\""_qs);
|
return Utils::String::unquote(parts[1], u"'\""_s);
|
||||||
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'")
|
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'")
|
||||||
.arg(fullParameter(), u"<value>"_qs));
|
.arg(fullParameter(), u"<value>"_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString value(const QProcessEnvironment &env, const QString &defaultValue = {}) const
|
QString value(const QProcessEnvironment &env, const QString &defaultValue = {}) const
|
||||||
{
|
{
|
||||||
QString val = env.value(envVarName());
|
QString val = env.value(envVarName());
|
||||||
return val.isEmpty() ? defaultValue : Utils::String::unquote(val, u"'\""_qs);
|
return val.isEmpty() ? defaultValue : Utils::String::unquote(val, u"'\""_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString usage(const QString &valueName) const
|
QString usage(const QString &valueName) const
|
||||||
@@ -198,13 +199,15 @@ namespace
|
|||||||
|
|
||||||
int value(const QString &arg) const
|
int value(const QString &arg) const
|
||||||
{
|
{
|
||||||
QString val = StringOption::value(arg);
|
const QString val = StringOption::value(arg);
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
int res = val.toInt(&ok);
|
const int res = val.toInt(&ok);
|
||||||
if (!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>'")
|
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=<value>'")
|
||||||
.arg(fullParameter(), u"<integer value>"_qs));
|
.arg(fullParameter(), u"<integer value>"_s));
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +220,7 @@ namespace
|
|||||||
int res = val.toInt(&ok);
|
int res = val.toInt(&ok);
|
||||||
if (!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);
|
.arg(envVarName(), val);
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@@ -273,15 +276,15 @@ 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 "
|
"e.g. Parameter '--add-paused' must follow syntax "
|
||||||
"'--add-paused=<true|false>'")
|
"'--add-paused=<true|false>'")
|
||||||
.arg(fullParameter(), u"<true|false>"_qs));
|
.arg(fullParameter(), u"<true|false>"_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<bool> value(const QProcessEnvironment &env) const
|
std::optional<bool> value(const QProcessEnvironment &env) const
|
||||||
{
|
{
|
||||||
const QString val = env.value(envVarName(), u"-1"_qs);
|
const QString val = env.value(envVarName(), u"-1"_s);
|
||||||
|
|
||||||
if (val.isEmpty())
|
if (val.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -300,8 +303,8 @@ namespace
|
|||||||
return false;
|
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);
|
.arg(u"true|false"_s, envVarName(), val);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,14 +343,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
|
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
|
||||||
: showHelp(false)
|
: relativeFastresumePaths(RELATIVE_FASTRESUME.value(env))
|
||||||
, relativeFastresumePaths(RELATIVE_FASTRESUME.value(env))
|
|
||||||
, skipChecking(SKIP_HASH_CHECK_OPTION.value(env))
|
|
||||||
, sequential(SEQUENTIAL_OPTION.value(env))
|
|
||||||
, firstLastPiecePriority(FIRST_AND_LAST_OPTION.value(env))
|
|
||||||
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
|
||||||
, showVersion(false)
|
|
||||||
#endif
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
, noSplash(NO_SPLASH_OPTION.value(env))
|
, noSplash(NO_SPLASH_OPTION.value(env))
|
||||||
#elif !defined(Q_OS_WIN)
|
#elif !defined(Q_OS_WIN)
|
||||||
@@ -355,49 +351,16 @@ QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &en
|
|||||||
#endif
|
#endif
|
||||||
, webUiPort(WEBUI_PORT_OPTION.value(env, -1))
|
, webUiPort(WEBUI_PORT_OPTION.value(env, -1))
|
||||||
, torrentingPort(TORRENTING_PORT_OPTION.value(env, -1))
|
, torrentingPort(TORRENTING_PORT_OPTION.value(env, -1))
|
||||||
, addPaused(PAUSED_OPTION.value(env))
|
|
||||||
, skipDialog(SKIP_DIALOG_OPTION.value(env))
|
, skipDialog(SKIP_DIALOG_OPTION.value(env))
|
||||||
, profileDir(PROFILE_OPTION.value(env))
|
, profileDir(PROFILE_OPTION.value(env))
|
||||||
, configurationName(CONFIGURATION_OPTION.value(env))
|
, configurationName(CONFIGURATION_OPTION.value(env))
|
||||||
, savePath(SAVE_PATH_OPTION.value(env))
|
|
||||||
, category(CATEGORY_OPTION.value(env))
|
|
||||||
{
|
{
|
||||||
}
|
addTorrentParams.savePath = Path(SAVE_PATH_OPTION.value(env));
|
||||||
|
addTorrentParams.category = CATEGORY_OPTION.value(env);
|
||||||
QStringList QBtCommandLineParameters::paramList() const
|
addTorrentParams.skipChecking = SKIP_HASH_CHECK_OPTION.value(env);
|
||||||
{
|
addTorrentParams.sequential = SEQUENTIAL_OPTION.value(env);
|
||||||
QStringList result;
|
addTorrentParams.firstLastPiecePriority = FIRST_AND_LAST_OPTION.value(env);
|
||||||
// Because we're passing a string list to the currently running
|
addTorrentParams.addPaused = PAUSED_OPTION.value(env);
|
||||||
// qBittorrent process, we need some way of passing along the options
|
|
||||||
// the user has specified. Here we place special strings that are
|
|
||||||
// almost certainly not going to collide with a file path or URL
|
|
||||||
// specified by the user, and placing them at the beginning of the
|
|
||||||
// string list so that they will be processed before the list of
|
|
||||||
// torrent paths or URLs.
|
|
||||||
|
|
||||||
if (!savePath.isEmpty())
|
|
||||||
result.append(u"@savePath=" + savePath.data());
|
|
||||||
|
|
||||||
if (addPaused.has_value())
|
|
||||||
result.append(*addPaused ? u"@addPaused=1"_qs : u"@addPaused=0"_qs);
|
|
||||||
|
|
||||||
if (skipChecking)
|
|
||||||
result.append(u"@skipChecking"_qs);
|
|
||||||
|
|
||||||
if (!category.isEmpty())
|
|
||||||
result.append(u"@category=" + category);
|
|
||||||
|
|
||||||
if (sequential)
|
|
||||||
result.append(u"@sequential"_qs);
|
|
||||||
|
|
||||||
if (firstLastPiecePriority)
|
|
||||||
result.append(u"@firstLastPiecePriority"_qs);
|
|
||||||
|
|
||||||
if (skipDialog.has_value())
|
|
||||||
result.append(*skipDialog ? u"@skipDialog=1"_qs : u"@skipDialog=0"_qs);
|
|
||||||
|
|
||||||
result += torrents;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
||||||
@@ -426,16 +389,16 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||||||
{
|
{
|
||||||
result.webUiPort = WEBUI_PORT_OPTION.value(arg);
|
result.webUiPort = WEBUI_PORT_OPTION.value(arg);
|
||||||
if ((result.webUiPort < 1) || (result.webUiPort > 65535))
|
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));
|
.arg(u"--webui-port"_s));
|
||||||
}
|
}
|
||||||
else if (arg == TORRENTING_PORT_OPTION)
|
else if (arg == TORRENTING_PORT_OPTION)
|
||||||
{
|
{
|
||||||
result.torrentingPort = TORRENTING_PORT_OPTION.value(arg);
|
result.torrentingPort = TORRENTING_PORT_OPTION.value(arg);
|
||||||
if ((result.torrentingPort < 1) || (result.torrentingPort > 65535))
|
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));
|
.arg(u"--torrenting-port"_s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
@@ -463,27 +426,27 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||||||
}
|
}
|
||||||
else if (arg == SAVE_PATH_OPTION)
|
else if (arg == SAVE_PATH_OPTION)
|
||||||
{
|
{
|
||||||
result.savePath = Path(SAVE_PATH_OPTION.value(arg));
|
result.addTorrentParams.savePath = Path(SAVE_PATH_OPTION.value(arg));
|
||||||
}
|
}
|
||||||
else if (arg == PAUSED_OPTION)
|
else if (arg == PAUSED_OPTION)
|
||||||
{
|
{
|
||||||
result.addPaused = PAUSED_OPTION.value(arg);
|
result.addTorrentParams.addPaused = PAUSED_OPTION.value(arg);
|
||||||
}
|
}
|
||||||
else if (arg == SKIP_HASH_CHECK_OPTION)
|
else if (arg == SKIP_HASH_CHECK_OPTION)
|
||||||
{
|
{
|
||||||
result.skipChecking = true;
|
result.addTorrentParams.skipChecking = true;
|
||||||
}
|
}
|
||||||
else if (arg == CATEGORY_OPTION)
|
else if (arg == CATEGORY_OPTION)
|
||||||
{
|
{
|
||||||
result.category = CATEGORY_OPTION.value(arg);
|
result.addTorrentParams.category = CATEGORY_OPTION.value(arg);
|
||||||
}
|
}
|
||||||
else if (arg == SEQUENTIAL_OPTION)
|
else if (arg == SEQUENTIAL_OPTION)
|
||||||
{
|
{
|
||||||
result.sequential = true;
|
result.addTorrentParams.sequential = true;
|
||||||
}
|
}
|
||||||
else if (arg == FIRST_AND_LAST_OPTION)
|
else if (arg == FIRST_AND_LAST_OPTION)
|
||||||
{
|
{
|
||||||
result.firstLastPiecePriority = true;
|
result.addTorrentParams.firstLastPiecePriority = true;
|
||||||
}
|
}
|
||||||
else if (arg == SKIP_DIALOG_OPTION)
|
else if (arg == SKIP_DIALOG_OPTION)
|
||||||
{
|
{
|
||||||
@@ -502,9 +465,9 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||||||
torrentPath.setFile(arg);
|
torrentPath.setFile(arg);
|
||||||
|
|
||||||
if (torrentPath.exists())
|
if (torrentPath.exists())
|
||||||
result.torrents += torrentPath.absoluteFilePath();
|
result.torrentSources += torrentPath.absoluteFilePath();
|
||||||
else
|
else
|
||||||
result.torrents += arg;
|
result.torrentSources += arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,58 +500,58 @@ QString makeUsage(const QString &prgName)
|
|||||||
{
|
{
|
||||||
const QString indentation {USAGE_INDENTATION, u' '};
|
const QString indentation {USAGE_INDENTATION, u' '};
|
||||||
|
|
||||||
const QString text = QObject::tr("Usage:") + u'\n'
|
const QString text = QCoreApplication::translate("CMD Options", "Usage:") + u'\n'
|
||||||
+ indentation + prgName + u' ' + QObject::tr("[options] [(<filename> | <url>)...]") + 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)
|
#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
|
#endif
|
||||||
+ SHOW_HELP_OPTION.usage() + wrapText(QObject::tr("Display this help message and exit")) + u'\n'
|
+ SHOW_HELP_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Display this help message and exit")) + u'\n'
|
||||||
+ WEBUI_PORT_OPTION.usage(QObject::tr("port"))
|
+ WEBUI_PORT_OPTION.usage(QCoreApplication::translate("CMD Options", "port"))
|
||||||
+ wrapText(QObject::tr("Change the Web UI port"))
|
+ wrapText(QCoreApplication::translate("CMD Options", "Change the Web UI port"))
|
||||||
+ u'\n'
|
+ u'\n'
|
||||||
+ TORRENTING_PORT_OPTION.usage(QObject::tr("port"))
|
+ TORRENTING_PORT_OPTION.usage(QCoreApplication::translate("CMD Options", "port"))
|
||||||
+ wrapText(QObject::tr("Change the torrenting port"))
|
+ wrapText(QCoreApplication::translate("CMD Options", "Change the torrenting port"))
|
||||||
+ u'\n'
|
+ u'\n'
|
||||||
#ifndef DISABLE_GUI
|
#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)
|
#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
|
#endif
|
||||||
//: Use appropriate short form or abbreviation of "directory"
|
//: Use appropriate short form or abbreviation of "directory"
|
||||||
+ PROFILE_OPTION.usage(QObject::tr("dir"))
|
+ PROFILE_OPTION.usage(QCoreApplication::translate("CMD Options", "dir"))
|
||||||
+ wrapText(QObject::tr("Store configuration files in <dir>")) + u'\n'
|
+ wrapText(QCoreApplication::translate("CMD Options", "Store configuration files in <dir>")) + u'\n'
|
||||||
+ CONFIGURATION_OPTION.usage(QObject::tr("name"))
|
+ CONFIGURATION_OPTION.usage(QCoreApplication::translate("CMD Options", "name"))
|
||||||
+ wrapText(QObject::tr("Store configuration files in directories qBittorrent_<name>")) + u'\n'
|
+ wrapText(QCoreApplication::translate("CMD Options", "Store configuration files in directories qBittorrent_<name>")) + u'\n'
|
||||||
+ RELATIVE_FASTRESUME.usage()
|
+ 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'
|
"to the profile directory")) + u'\n'
|
||||||
+ Option::padUsageText(QObject::tr("files or URLs"))
|
+ Option::padUsageText(QCoreApplication::translate("CMD Options", "files or URLs"))
|
||||||
+ wrapText(QObject::tr("Download the torrents passed by the user")) + u'\n'
|
+ wrapText(QCoreApplication::translate("CMD Options", "Download the torrents passed by the user")) + u'\n'
|
||||||
+ u'\n'
|
+ u'\n'
|
||||||
|
|
||||||
+ wrapText(QObject::tr("Options when adding new torrents:"), 0) + u'\n'
|
+ wrapText(QCoreApplication::translate("CMD Options", "Options when adding new torrents:"), 0) + u'\n'
|
||||||
+ SAVE_PATH_OPTION.usage(QObject::tr("path")) + wrapText(QObject::tr("Torrent save path")) + 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(QObject::tr("Add torrents as started or paused")) + u'\n'
|
+ PAUSED_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Add torrents as started or paused")) + u'\n'
|
||||||
+ SKIP_HASH_CHECK_OPTION.usage() + wrapText(QObject::tr("Skip hash check")) + u'\n'
|
+ SKIP_HASH_CHECK_OPTION.usage() + wrapText(QCoreApplication::translate("CMD Options", "Skip hash check")) + u'\n'
|
||||||
+ CATEGORY_OPTION.usage(QObject::tr("name"))
|
+ CATEGORY_OPTION.usage(QCoreApplication::translate("CMD Options", "name"))
|
||||||
+ wrapText(QObject::tr("Assign torrents to category. If the category doesn't exist, it will be "
|
+ wrapText(QCoreApplication::translate("CMD Options", "Assign torrents to category. If the category doesn't exist, it will be "
|
||||||
"created.")) + u'\n'
|
"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()
|
+ 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()
|
+ 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'
|
"torrent.")) + u'\n'
|
||||||
+ 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 "
|
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
|
||||||
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
|
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
|
||||||
"'TRUE'. For example, to disable the splash screen: "), 0) + u'\n'
|
"'TRUE'. For example, to disable the splash screen: "), 0) + u'\n'
|
||||||
+ u"QBT_NO_SPLASH=1 " + prgName + 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;
|
return text;
|
||||||
}
|
}
|
||||||
@@ -596,7 +559,7 @@ QString makeUsage(const QString &prgName)
|
|||||||
void displayUsage(const QString &prgName)
|
void displayUsage(const QString &prgName)
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
#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.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "base/bittorrent/addtorrentparams.h"
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
|
||||||
@@ -42,32 +43,29 @@ class QProcessEnvironment;
|
|||||||
|
|
||||||
struct QBtCommandLineParameters
|
struct QBtCommandLineParameters
|
||||||
{
|
{
|
||||||
bool showHelp;
|
bool showHelp = false;
|
||||||
bool relativeFastresumePaths;
|
bool relativeFastresumePaths = false;
|
||||||
bool skipChecking;
|
|
||||||
bool sequential;
|
|
||||||
bool firstLastPiecePriority;
|
|
||||||
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
||||||
bool showVersion;
|
bool showVersion = false;
|
||||||
#endif
|
#endif
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
bool noSplash;
|
bool noSplash = false;
|
||||||
#elif !defined(Q_OS_WIN)
|
#elif !defined(Q_OS_WIN)
|
||||||
bool shouldDaemonize;
|
bool shouldDaemonize = false;
|
||||||
#endif
|
#endif
|
||||||
int webUiPort;
|
int webUiPort = -1;
|
||||||
int torrentingPort;
|
int torrentingPort = -1;
|
||||||
std::optional<bool> addPaused;
|
|
||||||
std::optional<bool> skipDialog;
|
std::optional<bool> skipDialog;
|
||||||
QStringList torrents;
|
|
||||||
Path profileDir;
|
Path profileDir;
|
||||||
QString configurationName;
|
QString configurationName;
|
||||||
Path savePath;
|
|
||||||
QString category;
|
QStringList torrentSources;
|
||||||
|
BitTorrent::AddTorrentParams addTorrentParams;
|
||||||
|
|
||||||
QString unknownParameter;
|
QString unknownParameter;
|
||||||
|
|
||||||
|
QBtCommandLineParameters() = default;
|
||||||
explicit QBtCommandLineParameters(const QProcessEnvironment &);
|
explicit QBtCommandLineParameters(const QProcessEnvironment &);
|
||||||
QStringList paramList() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandLineParameterError : public RuntimeError
|
class CommandLineParameterError : public RuntimeError
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ void FileLogger::changePath(const Path &newPath)
|
|||||||
|
|
||||||
closeLogFile();
|
closeLogFile();
|
||||||
|
|
||||||
m_path = newPath / Path(u"qbittorrent.log"_qs);
|
m_path = newPath / Path(u"qbittorrent.log"_s);
|
||||||
m_logFile.setFileName(m_path.data());
|
m_logFile.setFileName(m_path.data());
|
||||||
|
|
||||||
Utils::Fs::mkpath(newPath);
|
Utils::Fs::mkpath(newPath);
|
||||||
@@ -89,7 +89,7 @@ void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
|||||||
{
|
{
|
||||||
const QDateTime date = QDateTime::currentDateTime();
|
const QDateTime date = QDateTime::currentDateTime();
|
||||||
const QDir dir {m_path.parentPath().data()};
|
const QDir dir {m_path.parentPath().data()};
|
||||||
const QFileInfoList fileList = dir.entryInfoList(QStringList(u"qbittorrent.log.bak*"_qs)
|
const QFileInfoList fileList = dir.entryInfoList(QStringList(u"qbittorrent.log.bak*"_s)
|
||||||
, (QDir::Files | QDir::Writable), (QDir::Time | QDir::Reversed));
|
, (QDir::Files | QDir::Writable), (QDir::Time | QDir::Reversed));
|
||||||
|
|
||||||
for (const QFileInfo &file : fileList)
|
for (const QFileInfo &file : fileList)
|
||||||
|
|||||||
@@ -45,7 +45,8 @@
|
|||||||
#include <io.h>
|
#include <io.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QCoreApplication>
|
||||||
|
#include <QString>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
@@ -85,6 +86,7 @@ using namespace std::chrono_literals;
|
|||||||
void displayVersion();
|
void displayVersion();
|
||||||
bool userAgreesWithLegalNotice();
|
bool userAgreesWithLegalNotice();
|
||||||
void displayBadArgMessage(const QString &message);
|
void displayBadArgMessage(const QString &message);
|
||||||
|
void displayErrorMessage(const QString &message);
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
void showSplashScreen();
|
void showSplashScreen();
|
||||||
@@ -113,10 +115,12 @@ int main(int argc, char *argv[])
|
|||||||
Application::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
Application::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// `app` must be declared out of try block to allow display message box in case of exception
|
||||||
|
std::unique_ptr<Application> app;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Create Application
|
// Create Application
|
||||||
auto app = std::make_unique<Application>(argc, argv);
|
app = std::make_unique<Application>(argc, argv);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
// QCoreApplication::applicationDirPath() needs an Application object instantiated first
|
// QCoreApplication::applicationDirPath() needs an Application object instantiated first
|
||||||
@@ -126,13 +130,13 @@ int main(int argc, char *argv[])
|
|||||||
if (envValue.isEmpty())
|
if (envValue.isEmpty())
|
||||||
qputenv(envName, Application::applicationDirPath().toLocal8Bit());
|
qputenv(envName, Application::applicationDirPath().toLocal8Bit());
|
||||||
else
|
else
|
||||||
qputenv(envName, u"%1;%2"_qs.arg(envValue, Application::applicationDirPath()).toLocal8Bit());
|
qputenv(envName, u"%1;%2"_s.arg(envValue, Application::applicationDirPath()).toLocal8Bit());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const QBtCommandLineParameters params = app->commandLineArgs();
|
const QBtCommandLineParameters params = app->commandLineArgs();
|
||||||
if (!params.unknownParameter.isEmpty())
|
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.")
|
"--random-parameter is an unknown command line parameter.")
|
||||||
.arg(params.unknownParameter));
|
.arg(params.unknownParameter));
|
||||||
}
|
}
|
||||||
@@ -144,8 +148,8 @@ int main(int argc, char *argv[])
|
|||||||
displayVersion();
|
displayVersion();
|
||||||
return EXIT_SUCCESS;
|
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));
|
.arg(u"-v (or --version)"_s));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (params.showHelp)
|
if (params.showHelp)
|
||||||
@@ -155,8 +159,8 @@ int main(int argc, char *argv[])
|
|||||||
displayUsage(QString::fromLocal8Bit(argv[0]));
|
displayUsage(QString::fromLocal8Bit(argv[0]));
|
||||||
return EXIT_SUCCESS;
|
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));
|
.arg(u"-h (or --help)"_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool firstTimeUser = !Preferences::instance()->getAcceptedLegal();
|
const bool firstTimeUser = !Preferences::instance()->getAcceptedLegal();
|
||||||
@@ -187,13 +191,13 @@ int main(int argc, char *argv[])
|
|||||||
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
||||||
if (params.shouldDaemonize)
|
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));
|
.arg(u"-d (or --daemon)"_s));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QThread::msleep(300);
|
QThread::msleep(300);
|
||||||
app->sendParams(params.paramList());
|
app->callMainInstance();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -258,7 +262,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
registerSignalHandlers();
|
registerSignalHandlers();
|
||||||
|
|
||||||
return app->exec(params.paramList());
|
return app->exec();
|
||||||
}
|
}
|
||||||
catch (const CommandLineParameterError &er)
|
catch (const CommandLineParameterError &er)
|
||||||
{
|
{
|
||||||
@@ -267,7 +271,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
catch (const RuntimeError &er)
|
catch (const RuntimeError &er)
|
||||||
{
|
{
|
||||||
qDebug() << er.message();
|
displayErrorMessage(er.message());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,11 +279,11 @@ int main(int argc, char *argv[])
|
|||||||
#if !defined(DISABLE_GUI)
|
#if !defined(DISABLE_GUI)
|
||||||
void showSplashScreen()
|
void showSplashScreen()
|
||||||
{
|
{
|
||||||
QPixmap splashImg(u":/icons/splash.png"_qs);
|
QPixmap splashImg(u":/icons/splash.png"_s);
|
||||||
QPainter painter(&splashImg);
|
QPainter painter(&splashImg);
|
||||||
const auto version = QStringLiteral(QBT_VERSION);
|
const auto version = QStringLiteral(QBT_VERSION);
|
||||||
painter.setPen(QPen(Qt::white));
|
painter.setPen(QPen(Qt::white));
|
||||||
painter.setFont(QFont(u"Arial"_qs, 22, QFont::Black));
|
painter.setFont(QFont(u"Arial"_s, 22, QFont::Black));
|
||||||
painter.drawText(224 - painter.fontMetrics().horizontalAdvance(version), 270, version);
|
painter.drawText(224 - painter.fontMetrics().horizontalAdvance(version), 270, version);
|
||||||
QSplashScreen *splash = new QSplashScreen(splashImg);
|
QSplashScreen *splash = new QSplashScreen(splashImg);
|
||||||
splash->show();
|
splash->show();
|
||||||
@@ -295,31 +299,55 @@ void displayVersion()
|
|||||||
|
|
||||||
void displayBadArgMessage(const QString &message)
|
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)
|
#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);
|
(message + u'\n' + help), QMessageBox::Ok);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
#else
|
#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'
|
+ message + u'\n'
|
||||||
+ help + u'\n';
|
+ help + u'\n';
|
||||||
fprintf(stderr, "%s", qUtf8Printable(errMsg));
|
fprintf(stderr, "%s", qUtf8Printable(errMsg));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void displayErrorMessage(const QString &message)
|
||||||
|
{
|
||||||
|
#ifndef DISABLE_GUI
|
||||||
|
if (QApplication::instance())
|
||||||
|
{
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setText(QCoreApplication::translate("Main", "An unrecoverable error occurred."));
|
||||||
|
msgBox.setInformativeText(message);
|
||||||
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
|
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const QString errMsg = QCoreApplication::translate("Main", "qBittorrent has encountered an unrecoverable error.") + u'\n' + message + u'\n';
|
||||||
|
fprintf(stderr, "%s", qUtf8Printable(errMsg));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const QString errMsg = QCoreApplication::translate("Main", "qBittorrent has encountered an unrecoverable error.") + u'\n' + message + u'\n';
|
||||||
|
fprintf(stderr, "%s", qUtf8Printable(errMsg));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool userAgreesWithLegalNotice()
|
bool userAgreesWithLegalNotice()
|
||||||
{
|
{
|
||||||
Preferences *const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
Q_ASSERT(!pref->getAcceptedLegal());
|
Q_ASSERT(!pref->getAcceptedLegal());
|
||||||
|
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
const QString eula = u"\n*** %1 ***\n"_qs.arg(QObject::tr("Legal Notice"))
|
const QString eula = u"\n*** %1 ***\n"_s.arg(QCoreApplication::translate("Main", "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"
|
+ 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"
|
||||||
+ QObject::tr("No further notices will be issued.") + u"\n\n"
|
+ QCoreApplication::translate("Main", "No further notices will be issued.") + u"\n\n"
|
||||||
+ QObject::tr("Press %1 key to accept and continue...").arg(u"'y'"_qs) + u'\n';
|
+ QCoreApplication::translate("Main", "Press %1 key to accept and continue...").arg(u"'y'"_s) + u'\n';
|
||||||
printf("%s", qUtf8Printable(eula));
|
printf("%s", qUtf8Printable(eula));
|
||||||
|
|
||||||
const char ret = getchar(); // Read pressed key
|
const char ret = getchar(); // Read pressed key
|
||||||
@@ -331,10 +359,10 @@ bool userAgreesWithLegalNotice()
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
QMessageBox msgBox;
|
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.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(QObject::tr("Legal notice"));
|
msgBox.setWindowTitle(QCoreApplication::translate("Main", "Legal notice"));
|
||||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
msgBox.addButton(QCoreApplication::translate("Main", "Cancel"), QMessageBox::RejectRole);
|
||||||
const QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
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.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
msgBox.move(Utils::Gui::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|||||||
@@ -152,10 +152,10 @@ bool QtLocalPeer::sendMessage(const QString &message, const int timeout)
|
|||||||
break;
|
break;
|
||||||
int ms = 250;
|
int ms = 250;
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
Sleep(DWORD(ms));
|
::Sleep(DWORD(ms));
|
||||||
#else
|
#else
|
||||||
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
|
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
|
||||||
nanosleep(&ts, NULL);
|
::nanosleep(&ts, nullptr);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (!connOk)
|
if (!connOk)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QMetaObject>
|
||||||
|
|
||||||
#include "base/version.h"
|
#include "base/version.h"
|
||||||
|
|
||||||
@@ -89,14 +90,14 @@ namespace
|
|||||||
const char *msgs[] = {"Catching signal: ", sysSigName[signum], "\nExiting cleanly\n"};
|
const char *msgs[] = {"Catching signal: ", sysSigName[signum], "\nExiting cleanly\n"};
|
||||||
std::for_each(std::begin(msgs), std::end(msgs), safePrint);
|
std::for_each(std::begin(msgs), std::end(msgs), safePrint);
|
||||||
signal(signum, SIG_DFL);
|
signal(signum, SIG_DFL);
|
||||||
QCoreApplication::exit(); // unsafe, but exit anyway
|
QMetaObject::invokeMethod(qApp, [] { QCoreApplication::exit(); }, Qt::QueuedConnection); // unsafe, but exit anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef STACKTRACE
|
#ifdef STACKTRACE
|
||||||
void abnormalExitHandler(const int signum)
|
void abnormalExitHandler(const int signum)
|
||||||
{
|
{
|
||||||
const char msg[] = "\n\n*************************************************************\n"
|
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"
|
"qBittorrent version: " QBT_VERSION "\n\n"
|
||||||
"Caught signal: ";
|
"Caught signal: ";
|
||||||
const char *sigName = sysSigName[signum];
|
const char *sigName = sysSigName[signum];
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "upgrade.h"
|
#include "upgrade.h"
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
|
|
||||||
#include "base/bittorrent/torrentcontentlayout.h"
|
#include "base/bittorrent/torrentcontentlayout.h"
|
||||||
@@ -39,14 +40,13 @@
|
|||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/settingsstorage.h"
|
#include "base/settingsstorage.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const int MIGRATION_VERSION = 4;
|
const int MIGRATION_VERSION = 6;
|
||||||
const QString MIGRATION_VERSION_KEY = u"Meta/MigrationVersion"_qs;
|
const QString MIGRATION_VERSION_KEY = u"Meta/MigrationVersion"_s;
|
||||||
|
|
||||||
void exportWebUIHttpsFiles()
|
void exportWebUIHttpsFiles()
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,7 @@ namespace
|
|||||||
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
||||||
const auto oldData {settingsStorage->loadValue<QByteArray>(oldKey)};
|
const auto oldData {settingsStorage->loadValue<QByteArray>(oldKey)};
|
||||||
const auto newData {settingsStorage->loadValue<QString>(newKey)};
|
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())
|
if (!newData.isEmpty() || oldData.isEmpty())
|
||||||
return;
|
return;
|
||||||
@@ -70,23 +70,23 @@ namespace
|
|||||||
settingsStorage->storeValue(newKey, savePath);
|
settingsStorage->storeValue(newKey, savePath);
|
||||||
settingsStorage->removeValue(oldKey);
|
settingsStorage->removeValue(oldKey);
|
||||||
|
|
||||||
LogMsg(QObject::tr("Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath.toString())
|
LogMsg(QCoreApplication::translate("Upgrade", "Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath.toString())
|
||||||
, Log::INFO);
|
, Log::INFO);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Path configPath = specialFolderLocation(SpecialFolder::Config);
|
const Path configPath = specialFolderLocation(SpecialFolder::Config);
|
||||||
migrate(u"Preferences/WebUI/HTTPS/Certificate"_qs
|
migrate(u"Preferences/WebUI/HTTPS/Certificate"_s
|
||||||
, u"Preferences/WebUI/HTTPS/CertificatePath"_qs
|
, u"Preferences/WebUI/HTTPS/CertificatePath"_s
|
||||||
, (configPath / Path(u"WebUICertificate.crt"_qs)));
|
, (configPath / Path(u"WebUICertificate.crt"_s)));
|
||||||
migrate(u"Preferences/WebUI/HTTPS/Key"_qs
|
migrate(u"Preferences/WebUI/HTTPS/Key"_s
|
||||||
, u"Preferences/WebUI/HTTPS/KeyPath"_qs
|
, u"Preferences/WebUI/HTTPS/KeyPath"_s
|
||||||
, (configPath / Path(u"WebUIPrivateKey.pem"_qs)));
|
, (configPath / Path(u"WebUIPrivateKey.pem"_s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void upgradeTorrentContentLayout()
|
void upgradeTorrentContentLayout()
|
||||||
{
|
{
|
||||||
const QString oldKey = u"BitTorrent/Session/CreateTorrentSubfolder"_qs;
|
const QString oldKey = u"BitTorrent/Session/CreateTorrentSubfolder"_s;
|
||||||
const QString newKey = u"BitTorrent/Session/TorrentContentLayout"_qs;
|
const QString newKey = u"BitTorrent/Session/TorrentContentLayout"_s;
|
||||||
|
|
||||||
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
||||||
const auto oldData {settingsStorage->loadValue<QVariant>(oldKey)};
|
const auto oldData {settingsStorage->loadValue<QVariant>(oldKey)};
|
||||||
@@ -105,8 +105,8 @@ namespace
|
|||||||
|
|
||||||
void upgradeListenPortSettings()
|
void upgradeListenPortSettings()
|
||||||
{
|
{
|
||||||
const auto oldKey = u"BitTorrent/Session/UseRandomPort"_qs;
|
const auto oldKey = u"BitTorrent/Session/UseRandomPort"_s;
|
||||||
const auto newKey = u"Preferences/Connection/PortRangeMin"_qs;
|
const auto newKey = u"Preferences/Connection/PortRangeMin"_s;
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
|
|
||||||
if (settingsStorage->hasKey(oldKey))
|
if (settingsStorage->hasKey(oldKey))
|
||||||
@@ -121,7 +121,7 @@ namespace
|
|||||||
void upgradeSchedulerDaysSettings()
|
void upgradeSchedulerDaysSettings()
|
||||||
{
|
{
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
const auto key = u"Preferences/Scheduler/days"_qs;
|
const auto key = u"Preferences/Scheduler/days"_s;
|
||||||
const auto value = settingsStorage->loadValue<QString>(key);
|
const auto value = settingsStorage->loadValue<QString>(key);
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -162,7 +162,7 @@ namespace
|
|||||||
settingsStorage->storeValue(key, Scheduler::Days::Sunday);
|
settingsStorage->storeValue(key, Scheduler::Days::Sunday);
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
.arg(key, QString::number(number)), Log::WARNING);
|
||||||
settingsStorage->removeValue(key);
|
settingsStorage->removeValue(key);
|
||||||
break;
|
break;
|
||||||
@@ -173,7 +173,7 @@ namespace
|
|||||||
void upgradeDNSServiceSettings()
|
void upgradeDNSServiceSettings()
|
||||||
{
|
{
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
const auto key = u"Preferences/DynDNS/Service"_qs;
|
const auto key = u"Preferences/DynDNS/Service"_s;
|
||||||
const auto value = settingsStorage->loadValue<QString>(key);
|
const auto value = settingsStorage->loadValue<QString>(key);
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -193,7 +193,7 @@ namespace
|
|||||||
settingsStorage->storeValue(key, DNS::Service::NoIP);
|
settingsStorage->storeValue(key, DNS::Service::NoIP);
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
.arg(key, QString::number(number)), Log::WARNING);
|
||||||
settingsStorage->removeValue(key);
|
settingsStorage->removeValue(key);
|
||||||
break;
|
break;
|
||||||
@@ -204,7 +204,7 @@ namespace
|
|||||||
void upgradeTrayIconStyleSettings()
|
void upgradeTrayIconStyleSettings()
|
||||||
{
|
{
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
const auto key = u"Preferences/Advanced/TrayIconStyle"_qs;
|
const auto key = u"Preferences/Advanced/TrayIconStyle"_s;
|
||||||
const auto value = settingsStorage->loadValue<QString>(key);
|
const auto value = settingsStorage->loadValue<QString>(key);
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -224,7 +224,7 @@ namespace
|
|||||||
settingsStorage->storeValue(key, TrayIcon::Style::MonoLight);
|
settingsStorage->storeValue(key, TrayIcon::Style::MonoLight);
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
.arg(key, QString::number(number)), Log::WARNING);
|
||||||
settingsStorage->removeValue(key);
|
settingsStorage->removeValue(key);
|
||||||
break;
|
break;
|
||||||
@@ -242,80 +242,80 @@ namespace
|
|||||||
|
|
||||||
const KeyMapping mappings[] =
|
const KeyMapping mappings[] =
|
||||||
{
|
{
|
||||||
{u"AddNewTorrentDialog/Enabled"_qs, u"Preferences/Downloads/NewAdditionDialog"_qs},
|
{u"AddNewTorrentDialog/Enabled"_s, u"Preferences/Downloads/NewAdditionDialog"_s},
|
||||||
{u"AddNewTorrentDialog/Expanded"_qs, u"AddNewTorrentDialog/expanded"_qs},
|
{u"AddNewTorrentDialog/Expanded"_s, u"AddNewTorrentDialog/expanded"_s},
|
||||||
{u"AddNewTorrentDialog/Position"_qs, u"AddNewTorrentDialog/y"_qs},
|
{u"AddNewTorrentDialog/Position"_s, u"AddNewTorrentDialog/y"_s},
|
||||||
{u"AddNewTorrentDialog/SavePathHistory"_qs, u"TorrentAdditionDlg/save_path_history"_qs},
|
{u"AddNewTorrentDialog/SavePathHistory"_s, u"TorrentAdditionDlg/save_path_history"_s},
|
||||||
{u"AddNewTorrentDialog/TopLevel"_qs, u"Preferences/Downloads/NewAdditionDialogFront"_qs},
|
{u"AddNewTorrentDialog/TopLevel"_s, u"Preferences/Downloads/NewAdditionDialogFront"_s},
|
||||||
{u"AddNewTorrentDialog/TreeHeaderState"_qs, u"AddNewTorrentDialog/qt5/treeHeaderState"_qs},
|
{u"AddNewTorrentDialog/TreeHeaderState"_s, u"AddNewTorrentDialog/qt5/treeHeaderState"_s},
|
||||||
{u"AddNewTorrentDialog/Width"_qs, u"AddNewTorrentDialog/width"_qs},
|
{u"AddNewTorrentDialog/Width"_s, u"AddNewTorrentDialog/width"_s},
|
||||||
{u"BitTorrent/Session/AddExtensionToIncompleteFiles"_qs, u"Preferences/Downloads/UseIncompleteExtension"_qs},
|
{u"BitTorrent/Session/AddExtensionToIncompleteFiles"_s, u"Preferences/Downloads/UseIncompleteExtension"_s},
|
||||||
{u"BitTorrent/Session/AdditionalTrackers"_qs, u"Preferences/Bittorrent/TrackersList"_qs},
|
{u"BitTorrent/Session/AdditionalTrackers"_s, u"Preferences/Bittorrent/TrackersList"_s},
|
||||||
{u"BitTorrent/Session/AddTorrentPaused"_qs, u"Preferences/Downloads/StartInPause"_qs},
|
{u"BitTorrent/Session/AddTorrentPaused"_s, u"Preferences/Downloads/StartInPause"_s},
|
||||||
{u"BitTorrent/Session/AddTrackersEnabled"_qs, u"Preferences/Bittorrent/AddTrackers"_qs},
|
{u"BitTorrent/Session/AddTrackersEnabled"_s, u"Preferences/Bittorrent/AddTrackers"_s},
|
||||||
{u"BitTorrent/Session/AlternativeGlobalDLSpeedLimit"_qs, u"Preferences/Connection/GlobalDLLimitAlt"_qs},
|
{u"BitTorrent/Session/AlternativeGlobalDLSpeedLimit"_s, u"Preferences/Connection/GlobalDLLimitAlt"_s},
|
||||||
{u"BitTorrent/Session/AlternativeGlobalUPSpeedLimit"_qs, u"Preferences/Connection/GlobalUPLimitAlt"_qs},
|
{u"BitTorrent/Session/AlternativeGlobalUPSpeedLimit"_s, u"Preferences/Connection/GlobalUPLimitAlt"_s},
|
||||||
{u"BitTorrent/Session/AnnounceIP"_qs, u"Preferences/Connection/InetAddress"_qs},
|
{u"BitTorrent/Session/AnnounceIP"_s, u"Preferences/Connection/InetAddress"_s},
|
||||||
{u"BitTorrent/Session/AnnounceToAllTrackers"_qs, u"Preferences/Advanced/AnnounceToAllTrackers"_qs},
|
{u"BitTorrent/Session/AnnounceToAllTrackers"_s, u"Preferences/Advanced/AnnounceToAllTrackers"_s},
|
||||||
{u"BitTorrent/Session/AnonymousModeEnabled"_qs, u"Preferences/Advanced/AnonymousMode"_qs},
|
{u"BitTorrent/Session/AnonymousModeEnabled"_s, u"Preferences/Advanced/AnonymousMode"_s},
|
||||||
{u"BitTorrent/Session/BandwidthSchedulerEnabled"_qs, u"Preferences/Scheduler/Enabled"_qs},
|
{u"BitTorrent/Session/BandwidthSchedulerEnabled"_s, u"Preferences/Scheduler/Enabled"_s},
|
||||||
{u"BitTorrent/Session/DefaultSavePath"_qs, u"Preferences/Downloads/SavePath"_qs},
|
{u"BitTorrent/Session/DefaultSavePath"_s, u"Preferences/Downloads/SavePath"_s},
|
||||||
{u"BitTorrent/Session/DHTEnabled"_qs, u"Preferences/Bittorrent/DHT"_qs},
|
{u"BitTorrent/Session/DHTEnabled"_s, u"Preferences/Bittorrent/DHT"_s},
|
||||||
{u"BitTorrent/Session/DiskCacheSize"_qs, u"Preferences/Downloads/DiskWriteCacheSize"_qs},
|
{u"BitTorrent/Session/DiskCacheSize"_s, u"Preferences/Downloads/DiskWriteCacheSize"_s},
|
||||||
{u"BitTorrent/Session/DiskCacheTTL"_qs, u"Preferences/Downloads/DiskWriteCacheTTL"_qs},
|
{u"BitTorrent/Session/DiskCacheTTL"_s, u"Preferences/Downloads/DiskWriteCacheTTL"_s},
|
||||||
{u"BitTorrent/Session/Encryption"_qs, u"Preferences/Bittorrent/Encryption"_qs},
|
{u"BitTorrent/Session/Encryption"_s, u"Preferences/Bittorrent/Encryption"_s},
|
||||||
{u"BitTorrent/Session/FinishedTorrentExportDirectory"_qs, u"Preferences/Downloads/FinishedTorrentExportDir"_qs},
|
{u"BitTorrent/Session/FinishedTorrentExportDirectory"_s, u"Preferences/Downloads/FinishedTorrentExportDir"_s},
|
||||||
{u"BitTorrent/Session/ForceProxy"_qs, u"Preferences/Connection/ProxyForce"_qs},
|
{u"BitTorrent/Session/ForceProxy"_s, u"Preferences/Connection/ProxyForce"_s},
|
||||||
{u"BitTorrent/Session/GlobalDLSpeedLimit"_qs, u"Preferences/Connection/GlobalDLLimit"_qs},
|
{u"BitTorrent/Session/GlobalDLSpeedLimit"_s, u"Preferences/Connection/GlobalDLLimit"_s},
|
||||||
{u"BitTorrent/Session/GlobalMaxRatio"_qs, u"Preferences/Bittorrent/MaxRatio"_qs},
|
{u"BitTorrent/Session/GlobalMaxRatio"_s, u"Preferences/Bittorrent/MaxRatio"_s},
|
||||||
{u"BitTorrent/Session/GlobalUPSpeedLimit"_qs, u"Preferences/Connection/GlobalUPLimit"_qs},
|
{u"BitTorrent/Session/GlobalUPSpeedLimit"_s, u"Preferences/Connection/GlobalUPLimit"_s},
|
||||||
{u"BitTorrent/Session/IgnoreLimitsOnLAN"_qs, u"Preferences/Advanced/IgnoreLimitsLAN"_qs},
|
{u"BitTorrent/Session/IgnoreLimitsOnLAN"_s, u"Preferences/Advanced/IgnoreLimitsLAN"_s},
|
||||||
{u"BitTorrent/Session/IgnoreSlowTorrentsForQueueing"_qs, u"Preferences/Queueing/IgnoreSlowTorrents"_qs},
|
{u"BitTorrent/Session/IgnoreSlowTorrentsForQueueing"_s, u"Preferences/Queueing/IgnoreSlowTorrents"_s},
|
||||||
{u"BitTorrent/Session/IncludeOverheadInLimits"_qs, u"Preferences/Advanced/IncludeOverhead"_qs},
|
{u"BitTorrent/Session/IncludeOverheadInLimits"_s, u"Preferences/Advanced/IncludeOverhead"_s},
|
||||||
{u"BitTorrent/Session/Interface"_qs, u"Preferences/Connection/Interface"_qs},
|
{u"BitTorrent/Session/Interface"_s, u"Preferences/Connection/Interface"_s},
|
||||||
{u"BitTorrent/Session/InterfaceAddress"_qs, u"Preferences/Connection/InterfaceAddress"_qs},
|
{u"BitTorrent/Session/InterfaceAddress"_s, u"Preferences/Connection/InterfaceAddress"_s},
|
||||||
{u"BitTorrent/Session/InterfaceName"_qs, u"Preferences/Connection/InterfaceName"_qs},
|
{u"BitTorrent/Session/InterfaceName"_s, u"Preferences/Connection/InterfaceName"_s},
|
||||||
{u"BitTorrent/Session/IPFilter"_qs, u"Preferences/IPFilter/File"_qs},
|
{u"BitTorrent/Session/IPFilter"_s, u"Preferences/IPFilter/File"_s},
|
||||||
{u"BitTorrent/Session/IPFilteringEnabled"_qs, u"Preferences/IPFilter/Enabled"_qs},
|
{u"BitTorrent/Session/IPFilteringEnabled"_s, u"Preferences/IPFilter/Enabled"_s},
|
||||||
{u"BitTorrent/Session/LSDEnabled"_qs, u"Preferences/Bittorrent/LSD"_qs},
|
{u"BitTorrent/Session/LSDEnabled"_s, u"Preferences/Bittorrent/LSD"_s},
|
||||||
{u"BitTorrent/Session/MaxActiveDownloads"_qs, u"Preferences/Queueing/MaxActiveDownloads"_qs},
|
{u"BitTorrent/Session/MaxActiveDownloads"_s, u"Preferences/Queueing/MaxActiveDownloads"_s},
|
||||||
{u"BitTorrent/Session/MaxActiveTorrents"_qs, u"Preferences/Queueing/MaxActiveTorrents"_qs},
|
{u"BitTorrent/Session/MaxActiveTorrents"_s, u"Preferences/Queueing/MaxActiveTorrents"_s},
|
||||||
{u"BitTorrent/Session/MaxActiveUploads"_qs, u"Preferences/Queueing/MaxActiveUploads"_qs},
|
{u"BitTorrent/Session/MaxActiveUploads"_s, u"Preferences/Queueing/MaxActiveUploads"_s},
|
||||||
{u"BitTorrent/Session/MaxConnections"_qs, u"Preferences/Bittorrent/MaxConnecs"_qs},
|
{u"BitTorrent/Session/MaxConnections"_s, u"Preferences/Bittorrent/MaxConnecs"_s},
|
||||||
{u"BitTorrent/Session/MaxConnectionsPerTorrent"_qs, u"Preferences/Bittorrent/MaxConnecsPerTorrent"_qs},
|
{u"BitTorrent/Session/MaxConnectionsPerTorrent"_s, u"Preferences/Bittorrent/MaxConnecsPerTorrent"_s},
|
||||||
{u"BitTorrent/Session/MaxHalfOpenConnections"_qs, u"Preferences/Connection/MaxHalfOpenConnec"_qs},
|
{u"BitTorrent/Session/MaxHalfOpenConnections"_s, u"Preferences/Connection/MaxHalfOpenConnec"_s},
|
||||||
{u"BitTorrent/Session/MaxRatioAction"_qs, u"Preferences/Bittorrent/MaxRatioAction"_qs},
|
{u"BitTorrent/Session/MaxRatioAction"_s, u"Preferences/Bittorrent/MaxRatioAction"_s},
|
||||||
{u"BitTorrent/Session/MaxUploads"_qs, u"Preferences/Bittorrent/MaxUploads"_qs},
|
{u"BitTorrent/Session/MaxUploads"_s, u"Preferences/Bittorrent/MaxUploads"_s},
|
||||||
{u"BitTorrent/Session/MaxUploadsPerTorrent"_qs, u"Preferences/Bittorrent/MaxUploadsPerTorrent"_qs},
|
{u"BitTorrent/Session/MaxUploadsPerTorrent"_s, u"Preferences/Bittorrent/MaxUploadsPerTorrent"_s},
|
||||||
{u"BitTorrent/Session/OutgoingPortsMax"_qs, u"Preferences/Advanced/OutgoingPortsMax"_qs},
|
{u"BitTorrent/Session/OutgoingPortsMax"_s, u"Preferences/Advanced/OutgoingPortsMax"_s},
|
||||||
{u"BitTorrent/Session/OutgoingPortsMin"_qs, u"Preferences/Advanced/OutgoingPortsMin"_qs},
|
{u"BitTorrent/Session/OutgoingPortsMin"_s, u"Preferences/Advanced/OutgoingPortsMin"_s},
|
||||||
{u"BitTorrent/Session/PeXEnabled"_qs, u"Preferences/Bittorrent/PeX"_qs},
|
{u"BitTorrent/Session/PeXEnabled"_s, u"Preferences/Bittorrent/PeX"_s},
|
||||||
{u"BitTorrent/Session/Port"_qs, u"Preferences/Connection/PortRangeMin"_qs},
|
{u"BitTorrent/Session/Port"_s, u"Preferences/Connection/PortRangeMin"_s},
|
||||||
{u"BitTorrent/Session/Preallocation"_qs, u"Preferences/Downloads/PreAllocation"_qs},
|
{u"BitTorrent/Session/Preallocation"_s, u"Preferences/Downloads/PreAllocation"_s},
|
||||||
{u"BitTorrent/Session/ProxyPeerConnections"_qs, u"Preferences/Connection/ProxyPeerConnections"_qs},
|
{u"BitTorrent/Session/ProxyPeerConnections"_s, u"Preferences/Connection/ProxyPeerConnections"_s},
|
||||||
{u"BitTorrent/Session/QueueingSystemEnabled"_qs, u"Preferences/Queueing/QueueingEnabled"_qs},
|
{u"BitTorrent/Session/QueueingSystemEnabled"_s, u"Preferences/Queueing/QueueingEnabled"_s},
|
||||||
{u"BitTorrent/Session/RefreshInterval"_qs, u"Preferences/General/RefreshInterval"_qs},
|
{u"BitTorrent/Session/RefreshInterval"_s, u"Preferences/General/RefreshInterval"_s},
|
||||||
{u"BitTorrent/Session/SaveResumeDataInterval"_qs, u"Preferences/Downloads/SaveResumeDataInterval"_qs},
|
{u"BitTorrent/Session/SaveResumeDataInterval"_s, u"Preferences/Downloads/SaveResumeDataInterval"_s},
|
||||||
{u"BitTorrent/Session/SuperSeedingEnabled"_qs, u"Preferences/Advanced/SuperSeeding"_qs},
|
{u"BitTorrent/Session/SuperSeedingEnabled"_s, u"Preferences/Advanced/SuperSeeding"_s},
|
||||||
{u"BitTorrent/Session/TempPath"_qs, u"Preferences/Downloads/TempPath"_qs},
|
{u"BitTorrent/Session/TempPath"_s, u"Preferences/Downloads/TempPath"_s},
|
||||||
{u"BitTorrent/Session/TempPathEnabled"_qs, u"Preferences/Downloads/TempPathEnabled"_qs},
|
{u"BitTorrent/Session/TempPathEnabled"_s, u"Preferences/Downloads/TempPathEnabled"_s},
|
||||||
{u"BitTorrent/Session/TorrentExportDirectory"_qs, u"Preferences/Downloads/TorrentExportDir"_qs},
|
{u"BitTorrent/Session/TorrentExportDirectory"_s, u"Preferences/Downloads/TorrentExportDir"_s},
|
||||||
{u"BitTorrent/Session/TrackerFilteringEnabled"_qs, u"Preferences/IPFilter/FilterTracker"_qs},
|
{u"BitTorrent/Session/TrackerFilteringEnabled"_s, u"Preferences/IPFilter/FilterTracker"_s},
|
||||||
{u"BitTorrent/Session/UseAlternativeGlobalSpeedLimit"_qs, u"Preferences/Connection/alt_speeds_on"_qs},
|
{u"BitTorrent/Session/UseAlternativeGlobalSpeedLimit"_s, u"Preferences/Connection/alt_speeds_on"_s},
|
||||||
{u"BitTorrent/Session/UseOSCache"_qs, u"Preferences/Advanced/osCache"_qs},
|
{u"BitTorrent/Session/UseOSCache"_s, u"Preferences/Advanced/osCache"_s},
|
||||||
{u"BitTorrent/Session/UseRandomPort"_qs, u"Preferences/General/UseRandomPort"_qs},
|
{u"BitTorrent/Session/UseRandomPort"_s, u"Preferences/General/UseRandomPort"_s},
|
||||||
{u"BitTorrent/Session/uTPEnabled"_qs, u"Preferences/Bittorrent/uTP"_qs},
|
{u"BitTorrent/Session/uTPEnabled"_s, u"Preferences/Bittorrent/uTP"_s},
|
||||||
{u"BitTorrent/Session/uTPRateLimited"_qs, u"Preferences/Bittorrent/uTP_rate_limited"_qs},
|
{u"BitTorrent/Session/uTPRateLimited"_s, u"Preferences/Bittorrent/uTP_rate_limited"_s},
|
||||||
{u"BitTorrent/TrackerEnabled"_qs, u"Preferences/Advanced/trackerEnabled"_qs},
|
{u"BitTorrent/TrackerEnabled"_s, u"Preferences/Advanced/trackerEnabled"_s},
|
||||||
{u"Network/PortForwardingEnabled"_qs, u"Preferences/Connection/UPnP"_qs},
|
{u"Network/PortForwardingEnabled"_s, u"Preferences/Connection/UPnP"_s},
|
||||||
{u"Network/Proxy/Authentication"_qs, u"Preferences/Connection/Proxy/Authentication"_qs},
|
{u"Network/Proxy/Authentication"_s, u"Preferences/Connection/Proxy/Authentication"_s},
|
||||||
{u"Network/Proxy/IP"_qs, u"Preferences/Connection/Proxy/IP"_qs},
|
{u"Network/Proxy/IP"_s, u"Preferences/Connection/Proxy/IP"_s},
|
||||||
{u"Network/Proxy/OnlyForTorrents"_qs, u"Preferences/Connection/ProxyOnlyForTorrents"_qs},
|
{u"Network/Proxy/OnlyForTorrents"_s, u"Preferences/Connection/ProxyOnlyForTorrents"_s},
|
||||||
{u"Network/Proxy/Password"_qs, u"Preferences/Connection/Proxy/Password"_qs},
|
{u"Network/Proxy/Password"_s, u"Preferences/Connection/Proxy/Password"_s},
|
||||||
{u"Network/Proxy/Port"_qs, u"Preferences/Connection/Proxy/Port"_qs},
|
{u"Network/Proxy/Port"_s, u"Preferences/Connection/Proxy/Port"_s},
|
||||||
{u"Network/Proxy/Type"_qs, u"Preferences/Connection/ProxyType"_qs},
|
{u"Network/Proxy/Type"_s, u"Preferences/Connection/ProxyType"_s},
|
||||||
{u"Network/Proxy/Username"_qs, u"Preferences/Connection/Proxy/Username"_qs},
|
{u"Network/Proxy/Username"_s, u"Preferences/Connection/Proxy/Username"_s},
|
||||||
{u"State/BannedIPs"_qs, u"Preferences/IPFilter/BannedIPs"_qs}
|
{u"State/BannedIPs"_s, u"Preferences/IPFilter/BannedIPs"_s}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
@@ -333,7 +333,7 @@ namespace
|
|||||||
void migrateProxySettingsEnum()
|
void migrateProxySettingsEnum()
|
||||||
{
|
{
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
const auto key = u"Network/Proxy/Type"_qs;
|
const auto key = u"Network/Proxy/Type"_s;
|
||||||
const auto value = settingsStorage->loadValue<QString>(key);
|
const auto value = settingsStorage->loadValue<QString>(key);
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -353,29 +353,58 @@ namespace
|
|||||||
settingsStorage->storeValue(key, Net::ProxyType::SOCKS5);
|
settingsStorage->storeValue(key, Net::ProxyType::SOCKS5);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
settingsStorage->storeValue(key, Net::ProxyType::HTTP_PW);
|
settingsStorage->storeValue(key, u"HTTP_PW"_s);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
settingsStorage->storeValue(key, Net::ProxyType::SOCKS5_PW);
|
settingsStorage->storeValue(key, u"SOCKS5_PW"_s);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
settingsStorage->storeValue(key, Net::ProxyType::SOCKS4);
|
settingsStorage->storeValue(key, Net::ProxyType::SOCKS4);
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
.arg(key, QString::number(number)), Log::WARNING);
|
||||||
settingsStorage->removeValue(key);
|
settingsStorage->removeValue(key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void migrateProxySettings()
|
||||||
|
{
|
||||||
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
|
const auto proxyType = settingsStorage->loadValue<QString>(u"Network/Proxy/Type"_s, u"None"_s);
|
||||||
|
const auto onlyForTorrents = settingsStorage->loadValue<bool>(u"Network/Proxy/OnlyForTorrents"_s)
|
||||||
|
|| (proxyType == u"SOCKS4");
|
||||||
|
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/Profiles/BitTorrent"_s, true);
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/Profiles/RSS"_s, !onlyForTorrents);
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/Profiles/Misc"_s, !onlyForTorrents);
|
||||||
|
|
||||||
|
if (proxyType == u"HTTP_PW"_s)
|
||||||
|
{
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/Type"_s, Net::ProxyType::HTTP);
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/AuthEnabled"_s, true);
|
||||||
|
}
|
||||||
|
else if (proxyType == u"SOCKS5_PW"_s)
|
||||||
|
{
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/Type"_s, Net::ProxyType::SOCKS5);
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/AuthEnabled"_s, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsStorage->removeValue(u"Network/Proxy/OnlyForTorrents"_s);
|
||||||
|
|
||||||
|
const auto proxyHostnameLookup = settingsStorage->loadValue<bool>(u"BitTorrent/Session/ProxyHostnameLookup"_s);
|
||||||
|
settingsStorage->storeValue(u"Network/Proxy/HostnameLookupEnabled"_s, proxyHostnameLookup);
|
||||||
|
settingsStorage->removeValue(u"BitTorrent/Session/ProxyHostnameLookup"_s);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
void migrateMemoryPrioritySettings()
|
void migrateMemoryPrioritySettings()
|
||||||
{
|
{
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
const QString oldKey = u"BitTorrent/OSMemoryPriority"_qs;
|
const QString oldKey = u"BitTorrent/OSMemoryPriority"_s;
|
||||||
const QString newKey = u"Application/ProcessMemoryPriority"_qs;
|
const QString newKey = u"Application/ProcessMemoryPriority"_s;
|
||||||
|
|
||||||
if (settingsStorage->hasKey(oldKey))
|
if (settingsStorage->hasKey(oldKey))
|
||||||
{
|
{
|
||||||
@@ -385,15 +414,27 @@ namespace
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void migrateStartupWindowState()
|
||||||
|
{
|
||||||
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
|
if (settingsStorage->hasKey(u"Preferences/General/StartMinimized"_s))
|
||||||
|
{
|
||||||
|
const auto startMinimized = settingsStorage->loadValue<bool>(u"Preferences/General/StartMinimized"_s);
|
||||||
|
const auto minimizeToTray = settingsStorage->loadValue<bool>(u"Preferences/General/MinimizeToTray"_s);
|
||||||
|
const QString windowState = startMinimized ? (minimizeToTray ? u"Hidden"_s : u"Minimized"_s) : u"Normal"_s;
|
||||||
|
settingsStorage->storeValue(u"GUI/StartUpWindowState"_s, windowState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void migrateChineseLocale()
|
void migrateChineseLocale()
|
||||||
{
|
{
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
const auto key = u"Preferences/General/Locale"_qs;
|
const auto key = u"Preferences/General/Locale"_s;
|
||||||
if (settingsStorage->hasKey(key))
|
if (settingsStorage->hasKey(key))
|
||||||
{
|
{
|
||||||
const auto locale = settingsStorage->loadValue<QString>(key);
|
const auto locale = settingsStorage->loadValue<QString>(key);
|
||||||
if (locale.compare(u"zh"_qs, Qt::CaseInsensitive) == 0)
|
if (locale.compare(u"zh"_s, Qt::CaseInsensitive) == 0)
|
||||||
settingsStorage->storeValue(key, u"zh_CN"_qs);
|
settingsStorage->storeValue(key, u"zh_CN"_s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,9 +466,15 @@ bool upgrade()
|
|||||||
migrateMemoryPrioritySettings();
|
migrateMemoryPrioritySettings();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (version < 5)
|
||||||
{
|
{
|
||||||
|
migrateStartupWindowState();
|
||||||
migrateChineseLocale();
|
migrateChineseLocale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 6)
|
||||||
|
migrateProxySettings();
|
||||||
|
|
||||||
version = MIGRATION_VERSION;
|
version = MIGRATION_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +497,7 @@ void handleChangedDefaults(const DefaultPreferencesMode mode)
|
|||||||
|
|
||||||
const DefaultValue changedDefaults[] =
|
const DefaultValue changedDefaults[] =
|
||||||
{
|
{
|
||||||
{u"BitTorrent/Session/QueueingSystemEnabled"_qs, true, false}
|
{u"BitTorrent/Session/QueueingSystemEnabled"_s, true, false}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto *settingsStorage = SettingsStorage::instance();
|
auto *settingsStorage = SettingsStorage::instance();
|
||||||
|
|||||||
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_MAJOR 0
|
||||||
#define expected_lite_MINOR 6
|
#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)
|
#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
|
} // namespace detail
|
||||||
|
|
||||||
// is [nothow] swappable:
|
// is [nothrow] swappable:
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
|
struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
|
||||||
@@ -1002,11 +1002,12 @@ public:
|
|||||||
|
|
||||||
// x.x.5.2.4 Swap
|
// x.x.5.2.4 Swap
|
||||||
|
|
||||||
|
template< typename U=E >
|
||||||
nsel_REQUIRES_R( void,
|
nsel_REQUIRES_R( void,
|
||||||
std17::is_swappable<E>::value
|
std17::is_swappable<U>::value
|
||||||
)
|
)
|
||||||
swap( unexpected_type & other ) noexcept (
|
swap( unexpected_type & other ) noexcept (
|
||||||
std17::is_nothrow_swappable<E>::value
|
std17::is_nothrow_swappable<U>::value
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using std::swap;
|
using std::swap;
|
||||||
@@ -2164,10 +2165,24 @@ private:
|
|||||||
|
|
||||||
// x.x.4.6 expected<>: comparison operators
|
// 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 )
|
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 >
|
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);
|
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
|
#if nsel_P0323R <= 2
|
||||||
|
|
||||||
template< typename T, typename E >
|
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
|
// 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 )
|
constexpr bool operator==( expected<T1,E1> const & x, T2 const & v )
|
||||||
{
|
{
|
||||||
return bool(x) ? *x == v : false;
|
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 )
|
constexpr bool operator==(T2 const & v, expected<T1,E1> const & x )
|
||||||
{
|
{
|
||||||
return bool(x) ? v == *x : false;
|
return bool(x) ? v == *x : false;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ add_library(qbt_base STATIC
|
|||||||
bittorrent/sessionstatus.h
|
bittorrent/sessionstatus.h
|
||||||
bittorrent/speedmonitor.h
|
bittorrent/speedmonitor.h
|
||||||
bittorrent/torrent.h
|
bittorrent/torrent.h
|
||||||
|
bittorrent/torrentcontenthandler.h
|
||||||
bittorrent/torrentcontentlayout.h
|
bittorrent/torrentcontentlayout.h
|
||||||
bittorrent/torrentcreatorthread.h
|
bittorrent/torrentcreatorthread.h
|
||||||
bittorrent/torrentimpl.h
|
bittorrent/torrentimpl.h
|
||||||
@@ -98,6 +99,7 @@ add_library(qbt_base STATIC
|
|||||||
utils/io.h
|
utils/io.h
|
||||||
utils/misc.h
|
utils/misc.h
|
||||||
utils/net.h
|
utils/net.h
|
||||||
|
utils/os.h
|
||||||
utils/password.h
|
utils/password.h
|
||||||
utils/random.h
|
utils/random.h
|
||||||
utils/string.h
|
utils/string.h
|
||||||
@@ -109,6 +111,7 @@ add_library(qbt_base STATIC
|
|||||||
applicationcomponent.cpp
|
applicationcomponent.cpp
|
||||||
asyncfilestorage.cpp
|
asyncfilestorage.cpp
|
||||||
bittorrent/abstractfilestorage.cpp
|
bittorrent/abstractfilestorage.cpp
|
||||||
|
bittorrent/addtorrentparams.cpp
|
||||||
bittorrent/bandwidthscheduler.cpp
|
bittorrent/bandwidthscheduler.cpp
|
||||||
bittorrent/bencoderesumedatastorage.cpp
|
bittorrent/bencoderesumedatastorage.cpp
|
||||||
bittorrent/categoryoptions.cpp
|
bittorrent/categoryoptions.cpp
|
||||||
@@ -129,6 +132,7 @@ add_library(qbt_base STATIC
|
|||||||
bittorrent/sessionimpl.cpp
|
bittorrent/sessionimpl.cpp
|
||||||
bittorrent/speedmonitor.cpp
|
bittorrent/speedmonitor.cpp
|
||||||
bittorrent/torrent.cpp
|
bittorrent/torrent.cpp
|
||||||
|
bittorrent/torrentcontenthandler.cpp
|
||||||
bittorrent/torrentcreatorthread.cpp
|
bittorrent/torrentcreatorthread.cpp
|
||||||
bittorrent/torrentimpl.cpp
|
bittorrent/torrentimpl.cpp
|
||||||
bittorrent/torrentinfo.cpp
|
bittorrent/torrentinfo.cpp
|
||||||
@@ -181,6 +185,7 @@ add_library(qbt_base STATIC
|
|||||||
utils/io.cpp
|
utils/io.cpp
|
||||||
utils/misc.cpp
|
utils/misc.cpp
|
||||||
utils/net.cpp
|
utils/net.cpp
|
||||||
|
utils/os.cpp
|
||||||
utils/password.cpp
|
utils/password.cpp
|
||||||
utils/random.cpp
|
utils/random.cpp
|
||||||
utils/string.cpp
|
utils/string.cpp
|
||||||
|
|||||||
@@ -63,4 +63,11 @@ namespace Algorithm
|
|||||||
while (it != set.end())
|
while (it != set.end())
|
||||||
it = (p(*it) ? set.erase(it) : ++it);
|
it = (p(*it) ? set.erase(it) : ++it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename List>
|
||||||
|
List sorted(List list)
|
||||||
|
{
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
AsyncFileStorage::AsyncFileStorage(const Path &storageFolderPath, QObject *parent)
|
AsyncFileStorage::AsyncFileStorage(const Path &storageFolderPath, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_storageDir(storageFolderPath)
|
, m_storageDir(storageFolderPath)
|
||||||
, m_lockFile((m_storageDir / Path(u"storage.lock"_qs)).data())
|
, m_lockFile((m_storageDir / Path(u"storage.lock"_s)).data())
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_storageDir.isAbsolute());
|
Q_ASSERT(m_storageDir.isAbsolute());
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ HEADERS += \
|
|||||||
$$PWD/bittorrent/speedmonitor.h \
|
$$PWD/bittorrent/speedmonitor.h \
|
||||||
$$PWD/bittorrent/torrent.h \
|
$$PWD/bittorrent/torrent.h \
|
||||||
$$PWD/bittorrent/torrentcontentlayout.h \
|
$$PWD/bittorrent/torrentcontentlayout.h \
|
||||||
|
$$PWD/bittorrent/torrentcontenthandler.h \
|
||||||
$$PWD/bittorrent/torrentcreatorthread.h \
|
$$PWD/bittorrent/torrentcreatorthread.h \
|
||||||
$$PWD/bittorrent/torrentimpl.h \
|
$$PWD/bittorrent/torrentimpl.h \
|
||||||
$$PWD/bittorrent/torrentinfo.h \
|
$$PWD/bittorrent/torrentinfo.h \
|
||||||
@@ -98,6 +99,7 @@ HEADERS += \
|
|||||||
$$PWD/utils/io.h \
|
$$PWD/utils/io.h \
|
||||||
$$PWD/utils/misc.h \
|
$$PWD/utils/misc.h \
|
||||||
$$PWD/utils/net.h \
|
$$PWD/utils/net.h \
|
||||||
|
$$PWD/utils/os.h \
|
||||||
$$PWD/utils/password.h \
|
$$PWD/utils/password.h \
|
||||||
$$PWD/utils/random.h \
|
$$PWD/utils/random.h \
|
||||||
$$PWD/utils/string.h \
|
$$PWD/utils/string.h \
|
||||||
@@ -109,6 +111,7 @@ SOURCES += \
|
|||||||
$$PWD/applicationcomponent.cpp \
|
$$PWD/applicationcomponent.cpp \
|
||||||
$$PWD/asyncfilestorage.cpp \
|
$$PWD/asyncfilestorage.cpp \
|
||||||
$$PWD/bittorrent/abstractfilestorage.cpp \
|
$$PWD/bittorrent/abstractfilestorage.cpp \
|
||||||
|
$$PWD/bittorrent/addtorrentparams.cpp \
|
||||||
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
||||||
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
|
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
|
||||||
$$PWD/bittorrent/categoryoptions.cpp \
|
$$PWD/bittorrent/categoryoptions.cpp \
|
||||||
@@ -129,6 +132,7 @@ SOURCES += \
|
|||||||
$$PWD/bittorrent/sessionimpl.cpp \
|
$$PWD/bittorrent/sessionimpl.cpp \
|
||||||
$$PWD/bittorrent/speedmonitor.cpp \
|
$$PWD/bittorrent/speedmonitor.cpp \
|
||||||
$$PWD/bittorrent/torrent.cpp \
|
$$PWD/bittorrent/torrent.cpp \
|
||||||
|
$$PWD/bittorrent/torrentcontenthandler.h \
|
||||||
$$PWD/bittorrent/torrentcreatorthread.cpp \
|
$$PWD/bittorrent/torrentcreatorthread.cpp \
|
||||||
$$PWD/bittorrent/torrentimpl.cpp \
|
$$PWD/bittorrent/torrentimpl.cpp \
|
||||||
$$PWD/bittorrent/torrentinfo.cpp \
|
$$PWD/bittorrent/torrentinfo.cpp \
|
||||||
@@ -181,6 +185,7 @@ SOURCES += \
|
|||||||
$$PWD/utils/io.cpp \
|
$$PWD/utils/io.cpp \
|
||||||
$$PWD/utils/misc.cpp \
|
$$PWD/utils/misc.cpp \
|
||||||
$$PWD/utils/net.cpp \
|
$$PWD/utils/net.cpp \
|
||||||
|
$$PWD/utils/os.cpp \
|
||||||
$$PWD/utils/password.cpp \
|
$$PWD/utils/password.cpp \
|
||||||
$$PWD/utils/random.cpp \
|
$$PWD/utils/random.cpp \
|
||||||
$$PWD/utils/string.cpp \
|
$$PWD/utils/string.cpp \
|
||||||
|
|||||||
173
src/base/bittorrent/addtorrentparams.cpp
Normal file
173
src/base/bittorrent/addtorrentparams.cpp
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* 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"_s;
|
||||||
|
const QString PARAM_TAGS = u"tags"_s;
|
||||||
|
const QString PARAM_SAVEPATH = u"save_path"_s;
|
||||||
|
const QString PARAM_USEDOWNLOADPATH = u"use_download_path"_s;
|
||||||
|
const QString PARAM_DOWNLOADPATH = u"download_path"_s;
|
||||||
|
const QString PARAM_OPERATINGMODE = u"operating_mode"_s;
|
||||||
|
const QString PARAM_QUEUETOP = u"add_to_top_of_queue"_s;
|
||||||
|
const QString PARAM_STOPPED = u"stopped"_s;
|
||||||
|
const QString PARAM_SKIPCHECKING = u"skip_checking"_s;
|
||||||
|
const QString PARAM_CONTENTLAYOUT = u"content_layout"_s;
|
||||||
|
const QString PARAM_AUTOTMM = u"use_auto_tmm"_s;
|
||||||
|
const QString PARAM_UPLOADLIMIT = u"upload_limit"_s;
|
||||||
|
const QString PARAM_DOWNLOADLIMIT = u"download_limit"_s;
|
||||||
|
const QString PARAM_SEEDINGTIMELIMIT = u"seeding_time_limit"_s;
|
||||||
|
const QString PARAM_INACTIVESEEDINGTIMELIMIT = u"inactive_seeding_time_limit"_s;
|
||||||
|
const QString PARAM_RATIOLIMIT = u"ratio_limit"_s;
|
||||||
|
|
||||||
|
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.inactiveSeedingTimeLimit, 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.inactiveSeedingTimeLimit, 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.inactiveSeedingTimeLimit = jsonObj.value(PARAM_INACTIVESEEDINGTIMELIMIT).toInt(BitTorrent::Torrent::USE_GLOBAL_INACTIVE_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_INACTIVESEEDINGTIMELIMIT, params.inactiveSeedingTimeLimit},
|
||||||
|
{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.
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -39,6 +39,8 @@
|
|||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
#include "torrentcontentlayout.h"
|
#include "torrentcontentlayout.h"
|
||||||
|
|
||||||
|
class QJsonObject;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
enum class DownloadPriority;
|
enum class DownloadPriority;
|
||||||
@@ -54,6 +56,7 @@ namespace BitTorrent
|
|||||||
bool sequential = false;
|
bool sequential = false;
|
||||||
bool firstLastPiecePriority = false;
|
bool firstLastPiecePriority = false;
|
||||||
bool addForced = false;
|
bool addForced = false;
|
||||||
|
std::optional<bool> addToQueueTop;
|
||||||
std::optional<bool> addPaused;
|
std::optional<bool> addPaused;
|
||||||
std::optional<Torrent::StopCondition> stopCondition;
|
std::optional<Torrent::StopCondition> stopCondition;
|
||||||
PathList filePaths; // used if TorrentInfo is set
|
PathList filePaths; // used if TorrentInfo is set
|
||||||
@@ -64,8 +67,14 @@ namespace BitTorrent
|
|||||||
int uploadLimit = -1;
|
int uploadLimit = -1;
|
||||||
int downloadLimit = -1;
|
int downloadLimit = -1;
|
||||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||||
|
int inactiveSeedingTimeLimit = Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
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)
|
Q_DECLARE_METATYPE(BitTorrent::AddTorrentParams)
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ using namespace std::chrono_literals;
|
|||||||
|
|
||||||
BandwidthScheduler::BandwidthScheduler(QObject *parent)
|
BandwidthScheduler::BandwidthScheduler(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_lastAlternative(false)
|
|
||||||
{
|
{
|
||||||
connect(&m_timer, &QTimer::timeout, this, &BandwidthScheduler::onTimeout);
|
connect(&m_timer, &QTimer::timeout, this, &BandwidthScheduler::onTimeout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,5 +49,5 @@ private:
|
|||||||
void onTimeout();
|
void onTimeout();
|
||||||
|
|
||||||
QTimer m_timer;
|
QTimer m_timer;
|
||||||
bool m_lastAlternative;
|
bool m_lastAlternative = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
@@ -43,12 +44,12 @@
|
|||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "common.h"
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "loadtorrentparams.h"
|
#include "loadtorrentparams.h"
|
||||||
|
|
||||||
@@ -103,8 +104,8 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path,
|
|||||||
.arg(path.toString()));
|
.arg(path.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRegularExpression filenamePattern {u"^([A-Fa-f0-9]{40})\\.fastresume$"_qs};
|
const QRegularExpression filenamePattern {u"^([A-Fa-f0-9]{40})\\.fastresume$"_s};
|
||||||
const QStringList filenames = QDir(path.data()).entryList(QStringList(u"*.fastresume"_qs), QDir::Files, QDir::Unsorted);
|
const QStringList filenames = QDir(path.data()).entryList(QStringList(u"*.fastresume"_s), QDir::Files, QDir::Unsorted);
|
||||||
|
|
||||||
m_registeredTorrents.reserve(filenames.size());
|
m_registeredTorrents.reserve(filenames.size());
|
||||||
for (const QString &filename : filenames)
|
for (const QString &filename : filenames)
|
||||||
@@ -114,7 +115,7 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path,
|
|||||||
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
|
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadQueue(path / Path(u"queue"_qs));
|
loadQueue(path / Path(u"queue"_s));
|
||||||
|
|
||||||
qDebug() << "Registered torrents count: " << m_registeredTorrents.size();
|
qDebug() << "Registered torrents count: " << m_registeredTorrents.size();
|
||||||
|
|
||||||
@@ -133,18 +134,21 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::load(cons
|
|||||||
const QString idString = id.toString();
|
const QString idString = id.toString();
|
||||||
const Path fastresumePath = path() / Path(idString + u".fastresume");
|
const Path fastresumePath = path() / Path(idString + u".fastresume");
|
||||||
const Path torrentFilePath = path() / Path(idString + u".torrent");
|
const Path torrentFilePath = path() / Path(idString + u".torrent");
|
||||||
|
const qint64 torrentSizeLimit = Preferences::instance()->getTorrentFileSizeLimit();
|
||||||
|
|
||||||
QFile resumeDataFile {fastresumePath.data()};
|
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, torrentSizeLimit);
|
||||||
if (!resumeDataFile.open(QIODevice::ReadOnly))
|
if (!resumeDataReadResult)
|
||||||
return nonstd::make_unexpected(tr("Cannot read file %1: %2").arg(fastresumePath.toString(), resumeDataFile.errorString()));
|
return nonstd::make_unexpected(resumeDataReadResult.error().message);
|
||||||
|
|
||||||
QFile metadataFile {torrentFilePath.data()};
|
const auto metadataReadResult = Utils::IO::readFile(torrentFilePath, torrentSizeLimit);
|
||||||
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
|
if (!metadataReadResult)
|
||||||
return nonstd::make_unexpected(tr("Cannot read file %1: %2").arg(torrentFilePath.toString(), metadataFile.errorString()));
|
{
|
||||||
|
if (metadataReadResult.error().status != Utils::IO::ReadError::NotExist)
|
||||||
const QByteArray data = resumeDataFile.readAll();
|
return nonstd::make_unexpected(metadataReadResult.error().message);
|
||||||
const QByteArray metadata = (metadataFile.isOpen() ? metadataFile.readAll() : "");
|
}
|
||||||
|
|
||||||
|
const QByteArray data = resumeDataReadResult.value();
|
||||||
|
const QByteArray metadata = metadataReadResult.value_or(QByteArray());
|
||||||
return loadTorrentResumeData(data, metadata);
|
return loadTorrentResumeData(data, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +166,8 @@ void BitTorrent::BencodeResumeDataStorage::doLoadAll() const
|
|||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||||
{
|
{
|
||||||
|
const int lineMaxLength = 48;
|
||||||
|
|
||||||
QFile queueFile {queueFilename.data()};
|
QFile queueFile {queueFilename.data()};
|
||||||
if (!queueFile.exists())
|
if (!queueFile.exists())
|
||||||
return;
|
return;
|
||||||
@@ -172,11 +178,11 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRegularExpression hashPattern {u"^([A-Fa-f0-9]{40})$"_qs};
|
const QRegularExpression hashPattern {u"^([A-Fa-f0-9]{40})$"_s};
|
||||||
int start = 0;
|
int start = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
const auto line = QString::fromLatin1(queueFile.readLine().trimmed());
|
const auto line = QString::fromLatin1(queueFile.readLine(lineMaxLength).trimmed());
|
||||||
if (line.isEmpty())
|
if (line.isEmpty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -196,9 +202,11 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
|||||||
|
|
||||||
BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const
|
BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const
|
||||||
{
|
{
|
||||||
|
const auto *pref = Preferences::instance();
|
||||||
|
|
||||||
lt::error_code ec;
|
lt::error_code ec;
|
||||||
const lt::bdecode_node resumeDataRoot = lt::bdecode(data, ec
|
const lt::bdecode_node resumeDataRoot = lt::bdecode(data, ec
|
||||||
, nullptr, BENCODE_DEPTH_LIMIT, BENCODE_TOKEN_LIMIT);
|
, nullptr, pref->getBdecodeDepthLimit(), pref->getBdecodeTokenLimit());
|
||||||
if (ec)
|
if (ec)
|
||||||
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
|
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
|
||||||
@@ -208,9 +216,10 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
|||||||
LoadTorrentParams torrentParams;
|
LoadTorrentParams torrentParams;
|
||||||
torrentParams.category = fromLTString(resumeDataRoot.dict_find_string_value("qBt-category"));
|
torrentParams.category = fromLTString(resumeDataRoot.dict_find_string_value("qBt-category"));
|
||||||
torrentParams.name = fromLTString(resumeDataRoot.dict_find_string_value("qBt-name"));
|
torrentParams.name = fromLTString(resumeDataRoot.dict_find_string_value("qBt-name"));
|
||||||
torrentParams.hasSeedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus");
|
torrentParams.hasFinishedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus");
|
||||||
torrentParams.firstLastPiecePriority = resumeDataRoot.dict_find_int_value("qBt-firstLastPiecePriority");
|
torrentParams.firstLastPiecePriority = resumeDataRoot.dict_find_int_value("qBt-firstLastPiecePriority");
|
||||||
torrentParams.seedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
torrentParams.seedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||||
|
torrentParams.inactiveSeedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-inactiveSeedingTimeLimit", Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME);
|
||||||
|
|
||||||
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
||||||
Path(fromLTString(resumeDataRoot.dict_find_string_value("qBt-savePath"))));
|
Path(fromLTString(resumeDataRoot.dict_find_string_value("qBt-savePath"))));
|
||||||
@@ -265,8 +274,9 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
|||||||
|
|
||||||
if (!metadata.isEmpty())
|
if (!metadata.isEmpty())
|
||||||
{
|
{
|
||||||
|
const auto *pref = Preferences::instance();
|
||||||
const lt::bdecode_node torentInfoRoot = lt::bdecode(metadata, ec
|
const lt::bdecode_node torentInfoRoot = lt::bdecode(metadata, ec
|
||||||
, nullptr, BENCODE_DEPTH_LIMIT, BENCODE_TOKEN_LIMIT);
|
, nullptr, pref->getBdecodeDepthLimit(), pref->getBdecodeTokenLimit());
|
||||||
if (ec)
|
if (ec)
|
||||||
return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message())));
|
return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
|
||||||
@@ -368,7 +378,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||||||
metadataDict.insert(dataDict.extract("created by"));
|
metadataDict.insert(dataDict.extract("created by"));
|
||||||
metadataDict.insert(dataDict.extract("comment"));
|
metadataDict.insert(dataDict.extract("comment"));
|
||||||
|
|
||||||
const Path torrentFilepath = m_resumeDataDir / Path(u"%1.torrent"_qs.arg(id.toString()));
|
const Path torrentFilepath = m_resumeDataDir / Path(u"%1.torrent"_s.arg(id.toString()));
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(torrentFilepath, metadata);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(torrentFilepath, metadata);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
@@ -380,10 +390,11 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||||||
|
|
||||||
data["qBt-ratioLimit"] = static_cast<int>(resumeData.ratioLimit * 1000);
|
data["qBt-ratioLimit"] = static_cast<int>(resumeData.ratioLimit * 1000);
|
||||||
data["qBt-seedingTimeLimit"] = resumeData.seedingTimeLimit;
|
data["qBt-seedingTimeLimit"] = resumeData.seedingTimeLimit;
|
||||||
|
data["qBt-inactiveSeedingTimeLimit"] = resumeData.inactiveSeedingTimeLimit;
|
||||||
data["qBt-category"] = resumeData.category.toStdString();
|
data["qBt-category"] = resumeData.category.toStdString();
|
||||||
data["qBt-tags"] = setToEntryList(resumeData.tags);
|
data["qBt-tags"] = setToEntryList(resumeData.tags);
|
||||||
data["qBt-name"] = resumeData.name.toStdString();
|
data["qBt-name"] = resumeData.name.toStdString();
|
||||||
data["qBt-seedStatus"] = resumeData.hasSeedStatus;
|
data["qBt-seedStatus"] = resumeData.hasFinishedStatus;
|
||||||
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
|
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
|
||||||
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
|
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
|
||||||
data["qBt-stopCondition"] = Utils::String::fromEnum(resumeData.stopCondition).toStdString();
|
data["qBt-stopCondition"] = Utils::String::fromEnum(resumeData.stopCondition).toStdString();
|
||||||
@@ -394,7 +405,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||||||
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).data().toStdString();
|
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).data().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path resumeFilepath = m_resumeDataDir / Path(u"%1.fastresume"_qs.arg(id.toString()));
|
const Path resumeFilepath = m_resumeDataDir / Path(u"%1.fastresume"_s.arg(id.toString()));
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
@@ -405,10 +416,10 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
|
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
|
||||||
{
|
{
|
||||||
const Path resumeFilename {u"%1.fastresume"_qs.arg(id.toString())};
|
const Path resumeFilename {u"%1.fastresume"_s.arg(id.toString())};
|
||||||
Utils::Fs::removeFile(m_resumeDataDir / resumeFilename);
|
Utils::Fs::removeFile(m_resumeDataDir / resumeFilename);
|
||||||
|
|
||||||
const Path torrentFilename {u"%1.torrent"_qs.arg(id.toString())};
|
const Path torrentFilename {u"%1.torrent"_s.arg(id.toString())};
|
||||||
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,7 +430,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<Torr
|
|||||||
for (const BitTorrent::TorrentID &torrentID : queue)
|
for (const BitTorrent::TorrentID &torrentID : queue)
|
||||||
data += (torrentID.toString().toLatin1() + '\n');
|
data += (torrentID.toString().toLatin1() + '\n');
|
||||||
|
|
||||||
const Path filepath = m_resumeDataDir / Path(u"queue"_qs);
|
const Path filepath = m_resumeDataDir / Path(u"queue"_s);
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filepath, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filepath, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
|
||||||
const QString OPTION_SAVEPATH = u"save_path"_qs;
|
const QString OPTION_SAVEPATH = u"save_path"_s;
|
||||||
const QString OPTION_DOWNLOADPATH = u"download_path"_qs;
|
const QString OPTION_DOWNLOADPATH = u"download_path"_s;
|
||||||
|
|
||||||
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,8 +32,4 @@
|
|||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
|
||||||
inline const QString QB_EXT = u".!qB"_qs;
|
inline const QString QB_EXT = u".!qB"_s;
|
||||||
|
|
||||||
inline const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
|
|
||||||
inline const int BENCODE_DEPTH_LIMIT = 100;
|
|
||||||
inline const int BENCODE_TOKEN_LIMIT = 10'000'000;
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ bool CustomDiskIOThread::async_write(lt::storage_index_t storage, const lt::peer
|
|||||||
, const char *buf, std::shared_ptr<lt::disk_observer> diskObserver
|
, const char *buf, std::shared_ptr<lt::disk_observer> diskObserver
|
||||||
, std::function<void (const lt::storage_error &)> handler, lt::disk_job_flags_t flags)
|
, std::function<void (const lt::storage_error &)> handler, lt::disk_job_flags_t flags)
|
||||||
{
|
{
|
||||||
return m_nativeDiskIO->async_write(storage, peerRequest, buf, diskObserver, std::move(handler), flags);
|
return m_nativeDiskIO->async_write(storage, peerRequest, buf, std::move(diskObserver), std::move(handler), flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomDiskIOThread::async_hash(lt::storage_index_t storage, lt::piece_index_t piece
|
void CustomDiskIOThread::async_hash(lt::storage_index_t storage, lt::piece_index_t piece
|
||||||
@@ -120,7 +120,11 @@ void CustomDiskIOThread::async_move_storage(lt::storage_index_t storage, std::st
|
|||||||
m_nativeDiskIO->async_move_storage(storage, path, flags
|
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)
|
, [=, 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))
|
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;
|
m_storageData[storage].savePath = newSavePath;
|
||||||
|
|
||||||
handler(status, path, error);
|
handler(status, path, error);
|
||||||
@@ -137,7 +141,7 @@ void CustomDiskIOThread::async_check_files(lt::storage_index_t storage, const lt
|
|||||||
, std::function<void (lt::status_t, const lt::storage_error &)> handler)
|
, std::function<void (lt::status_t, const lt::storage_error &)> handler)
|
||||||
{
|
{
|
||||||
handleCompleteFiles(storage, m_storageData[storage].savePath);
|
handleCompleteFiles(storage, m_storageData[storage].savePath);
|
||||||
m_nativeDiskIO->async_check_files(storage, resume_data, links, std::move(handler));
|
m_nativeDiskIO->async_check_files(storage, resume_data, std::move(links), std::move(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomDiskIOThread::async_stop_torrent(lt::storage_index_t storage, std::function<void ()> handler)
|
void CustomDiskIOThread::async_stop_torrent(lt::storage_index_t storage, std::function<void ()> handler)
|
||||||
@@ -166,8 +170,8 @@ void CustomDiskIOThread::async_delete_files(lt::storage_index_t storage, lt::rem
|
|||||||
void CustomDiskIOThread::async_set_file_priority(lt::storage_index_t storage, lt::aux::vector<lt::download_priority_t, lt::file_index_t> priorities
|
void CustomDiskIOThread::async_set_file_priority(lt::storage_index_t storage, lt::aux::vector<lt::download_priority_t, lt::file_index_t> priorities
|
||||||
, std::function<void (const lt::storage_error &, lt::aux::vector<lt::download_priority_t, lt::file_index_t>)> handler)
|
, 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
|
m_nativeDiskIO->async_set_file_priority(storage, std::move(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;
|
m_storageData[storage].filePriorities = priorities;
|
||||||
handler(error, priorities);
|
handler(error, priorities);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#include "dbresumedatastorage.h"
|
#include "dbresumedatastorage.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <libtorrent/bdecode.hpp>
|
#include <libtorrent/bdecode.hpp>
|
||||||
@@ -38,7 +40,8 @@
|
|||||||
#include <libtorrent/write_resume_data.hpp>
|
#include <libtorrent/write_resume_data.hpp>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QFile>
|
#include <QDebug>
|
||||||
|
#include <QMutex>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
@@ -46,28 +49,69 @@
|
|||||||
#include <QSqlRecord>
|
#include <QSqlRecord>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "common.h"
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "loadtorrentparams.h"
|
#include "loadtorrentparams.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_qs;
|
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s;
|
||||||
|
|
||||||
const int DB_VERSION = 3;
|
const int DB_VERSION = 5;
|
||||||
|
|
||||||
const QString DB_TABLE_META = u"meta"_qs;
|
const QString DB_TABLE_META = u"meta"_s;
|
||||||
const QString DB_TABLE_TORRENTS = u"torrents"_qs;
|
const QString DB_TABLE_TORRENTS = u"torrents"_s;
|
||||||
|
|
||||||
const QString META_VERSION = u"version"_qs;
|
const QString META_VERSION = u"version"_s;
|
||||||
|
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
class Job
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Job() = default;
|
||||||
|
virtual void perform(QSqlDatabase db) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StoreJob final : public Job
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StoreJob(const TorrentID &torrentID, const LoadTorrentParams &resumeData);
|
||||||
|
void perform(QSqlDatabase db) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const TorrentID m_torrentID;
|
||||||
|
const LoadTorrentParams m_resumeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RemoveJob final : public Job
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RemoveJob(const TorrentID &torrentID);
|
||||||
|
void perform(QSqlDatabase db) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const TorrentID m_torrentID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StoreQueueJob final : public Job
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StoreQueueJob(const QVector<TorrentID> &queue);
|
||||||
|
void perform(QSqlDatabase db) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QVector<TorrentID> m_queue;
|
||||||
|
};
|
||||||
|
|
||||||
struct Column
|
struct Column
|
||||||
{
|
{
|
||||||
@@ -91,6 +135,7 @@ namespace
|
|||||||
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
||||||
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
||||||
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
||||||
|
const Column DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT = makeColumn("inactive_seeding_time_limit");
|
||||||
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn("has_outer_pieces_priority");
|
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn("has_outer_pieces_priority");
|
||||||
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn("has_seed_status");
|
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn("has_seed_status");
|
||||||
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
|
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
|
||||||
@@ -115,7 +160,7 @@ namespace
|
|||||||
|
|
||||||
QString makeCreateTableStatement(const QString &tableName, const QStringList &items)
|
QString makeCreateTableStatement(const QString &tableName, const QStringList &items)
|
||||||
{
|
{
|
||||||
return u"CREATE TABLE %1 (%2)"_qs.arg(quoted(tableName), items.join(u','));
|
return u"CREATE TABLE %1 (%2)"_s.arg(quoted(tableName), items.join(u','));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<QString, QString> joinColumns(const QVector<Column> &columns)
|
std::pair<QString, QString> joinColumns(const QVector<Column> &columns)
|
||||||
@@ -146,115 +191,123 @@ namespace
|
|||||||
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u"INSERT INTO %1 (%2) VALUES (%3)"_qs
|
return u"INSERT INTO %1 (%2) VALUES (%3)"_s
|
||||||
.arg(quoted(tableName), names, values);
|
.arg(quoted(tableName), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeUpdateStatement(const QString &tableName, const QVector<Column> &columns)
|
QString makeUpdateStatement(const QString &tableName, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u"UPDATE %1 SET (%2) = (%3)"_qs
|
return u"UPDATE %1 SET (%2) = (%3)"_s
|
||||||
.arg(quoted(tableName), names, values);
|
.arg(quoted(tableName), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)"_qs
|
return u" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)"_s
|
||||||
.arg(quoted(constraint.name), names, values);
|
.arg(quoted(constraint.name), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeColumnDefinition(const Column &column, const char *definition)
|
QString makeColumnDefinition(const Column &column, const char *definition)
|
||||||
{
|
{
|
||||||
return u"%1 %2"_qs.arg(quoted(column.name), QString::fromLatin1(definition));
|
return u"%1 %2"_s.arg(quoted(column.name), QString::fromLatin1(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
|
||||||
|
{
|
||||||
|
LoadTorrentParams resumeData;
|
||||||
|
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
||||||
|
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
||||||
|
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
||||||
|
if (!tagsData.isEmpty())
|
||||||
|
{
|
||||||
|
const QStringList tagList = tagsData.split(u',');
|
||||||
|
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
||||||
|
}
|
||||||
|
resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
||||||
|
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
||||||
|
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
||||||
|
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
|
||||||
|
resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt();
|
||||||
|
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
|
||||||
|
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
|
||||||
|
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
|
||||||
|
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
|
||||||
|
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||||
|
resumeData.stopCondition = Utils::String::toEnum(
|
||||||
|
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
|
||||||
|
|
||||||
|
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||||
|
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||||
|
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||||
|
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
||||||
|
const auto *pref = Preferences::instance();
|
||||||
|
const int bdecodeDepthLimit = pref->getBdecodeDepthLimit();
|
||||||
|
const int bdecodeTokenLimit = pref->getBdecodeTokenLimit();
|
||||||
|
|
||||||
|
lt::error_code ec;
|
||||||
|
const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec
|
||||||
|
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
|
||||||
|
|
||||||
|
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||||
|
|
||||||
|
p = lt::read_resume_data(resumeDataRoot, ec);
|
||||||
|
|
||||||
|
if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray()
|
||||||
|
; !bencodedMetadata.isEmpty())
|
||||||
|
{
|
||||||
|
const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec
|
||||||
|
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
|
||||||
|
p.ti = std::make_shared<lt::torrent_info>(torentInfoRoot, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
|
||||||
|
.toString().toStdString();
|
||||||
|
|
||||||
|
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||||
|
{
|
||||||
|
p.flags &= ~lt::torrent_flags::stop_when_ready;
|
||||||
|
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resumeData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
class DBResumeDataStorage::Worker final : public QObject
|
class DBResumeDataStorage::Worker final : public QThread
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(Worker)
|
Q_DISABLE_COPY_MOVE(Worker)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Worker(const Path &dbPath, const QString &dbConnectionName, QReadWriteLock &dbLock);
|
Worker(const Path &dbPath, QReadWriteLock &dbLock, QObject *parent = nullptr);
|
||||||
|
|
||||||
void openDatabase() const;
|
void run() override;
|
||||||
void closeDatabase() const;
|
void requestInterruption();
|
||||||
|
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
void store(const TorrentID &id, const LoadTorrentParams &resumeData);
|
||||||
void remove(const TorrentID &id) const;
|
void remove(const TorrentID &id);
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
void storeQueue(const QVector<TorrentID> &queue);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void addJob(std::unique_ptr<Job> job);
|
||||||
|
|
||||||
|
const QString m_connectionName = u"ResumeDataStorageWorker"_s;
|
||||||
const Path m_path;
|
const Path m_path;
|
||||||
const QString m_connectionName;
|
|
||||||
QReadWriteLock &m_dbLock;
|
QReadWriteLock &m_dbLock;
|
||||||
|
|
||||||
|
std::queue<std::unique_ptr<Job>> m_jobs;
|
||||||
|
QMutex m_jobsMutex;
|
||||||
|
QWaitCondition m_waitCondition;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
|
|
||||||
{
|
|
||||||
LoadTorrentParams resumeData;
|
|
||||||
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
|
||||||
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
|
||||||
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
|
||||||
if (!tagsData.isEmpty())
|
|
||||||
{
|
|
||||||
const QStringList tagList = tagsData.split(u',');
|
|
||||||
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
|
||||||
}
|
|
||||||
resumeData.hasSeedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
|
||||||
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
|
||||||
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
|
||||||
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
|
|
||||||
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
|
|
||||||
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
|
|
||||||
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
|
|
||||||
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
|
|
||||||
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
|
||||||
resumeData.stopCondition = Utils::String::toEnum(
|
|
||||||
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
|
|
||||||
|
|
||||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
|
||||||
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
|
||||||
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
|
||||||
if (!resumeData.useAutoTMM)
|
|
||||||
{
|
|
||||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
|
||||||
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
|
||||||
|
|
||||||
lt::error_code ec;
|
|
||||||
const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec
|
|
||||||
, nullptr, BENCODE_DEPTH_LIMIT, BENCODE_TOKEN_LIMIT);
|
|
||||||
|
|
||||||
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
|
||||||
|
|
||||||
p = lt::read_resume_data(resumeDataRoot, ec);
|
|
||||||
|
|
||||||
if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray(); !bencodedMetadata.isEmpty())
|
|
||||||
{
|
|
||||||
const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec
|
|
||||||
, nullptr, BENCODE_DEPTH_LIMIT, BENCODE_TOKEN_LIMIT);
|
|
||||||
p.ti = std::make_shared<lt::torrent_info>(torentInfoRoot, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
|
|
||||||
.toString().toStdString();
|
|
||||||
|
|
||||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
|
||||||
{
|
|
||||||
p.flags &= ~lt::torrent_flags::stop_when_ready;
|
|
||||||
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
|
|
||||||
}
|
|
||||||
|
|
||||||
return resumeData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
||||||
@@ -263,7 +316,7 @@ BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject
|
|||||||
{
|
{
|
||||||
const bool needCreateDB = !dbPath.exists();
|
const bool needCreateDB = !dbPath.exists();
|
||||||
|
|
||||||
auto db = QSqlDatabase::addDatabase(u"QSQLITE"_qs, DB_CONNECTION_NAME);
|
auto db = QSqlDatabase::addDatabase(u"QSQLITE"_s, DB_CONNECTION_NAME);
|
||||||
db.setDatabaseName(dbPath.data());
|
db.setDatabaseName(dbPath.data());
|
||||||
if (!db.open())
|
if (!db.open())
|
||||||
throw RuntimeError(db.lastError().text());
|
throw RuntimeError(db.lastError().text());
|
||||||
@@ -279,37 +332,20 @@ BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject
|
|||||||
updateDB(dbVersion);
|
updateDB(dbVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_asyncWorker = new Worker(dbPath, u"ResumeDataStorageWorker"_qs, m_dbLock);
|
m_asyncWorker = new Worker(dbPath, m_dbLock, this);
|
||||||
m_asyncWorker->moveToThread(m_ioThread.get());
|
m_asyncWorker->start();
|
||||||
connect(m_ioThread.get(), &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
|
||||||
m_ioThread->start();
|
|
||||||
|
|
||||||
RuntimeError *errPtr = nullptr;
|
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, &errPtr]()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_asyncWorker->openDatabase();
|
|
||||||
}
|
|
||||||
catch (const RuntimeError &err)
|
|
||||||
{
|
|
||||||
errPtr = new RuntimeError(err);
|
|
||||||
}
|
|
||||||
}, Qt::BlockingQueuedConnection);
|
|
||||||
|
|
||||||
if (errPtr)
|
|
||||||
throw *errPtr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::~DBResumeDataStorage()
|
BitTorrent::DBResumeDataStorage::~DBResumeDataStorage()
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, &Worker::closeDatabase);
|
m_asyncWorker->requestInterruption();
|
||||||
|
m_asyncWorker->wait();
|
||||||
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
||||||
{
|
{
|
||||||
const auto selectTorrentIDStatement = u"SELECT %1 FROM %2 ORDER BY %3;"_qs
|
const auto selectTorrentIDStatement = u"SELECT %1 FROM %2 ORDER BY %3;"_s
|
||||||
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||||
|
|
||||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
@@ -328,7 +364,7 @@ QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorren
|
|||||||
|
|
||||||
BitTorrent::LoadResumeDataResult BitTorrent::DBResumeDataStorage::load(const TorrentID &id) const
|
BitTorrent::LoadResumeDataResult BitTorrent::DBResumeDataStorage::load(const TorrentID &id) const
|
||||||
{
|
{
|
||||||
const QString selectTorrentStatement = u"SELECT * FROM %1 WHERE %2 = %3;"_qs
|
const QString selectTorrentStatement = u"SELECT * FROM %1 WHERE %2 = %3;"_s
|
||||||
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
||||||
|
|
||||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
@@ -356,41 +392,32 @@ BitTorrent::LoadResumeDataResult BitTorrent::DBResumeDataStorage::load(const Tor
|
|||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
void BitTorrent::DBResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
|
m_asyncWorker->store(id, resumeData);
|
||||||
{
|
|
||||||
m_asyncWorker->store(id, resumeData);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) const
|
void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) const
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id]()
|
m_asyncWorker->remove(id);
|
||||||
{
|
|
||||||
m_asyncWorker->remove(id);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
m_asyncWorker->storeQueue(queue);
|
||||||
{
|
|
||||||
m_asyncWorker->storeQueue(queue);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::doLoadAll() const
|
void BitTorrent::DBResumeDataStorage::doLoadAll() const
|
||||||
{
|
{
|
||||||
const QString connectionName = u"ResumeDataStorageLoadAll"_qs;
|
const QString connectionName = u"ResumeDataStorageLoadAll"_s;
|
||||||
|
|
||||||
{
|
{
|
||||||
auto db = QSqlDatabase::addDatabase(u"QSQLITE"_qs, connectionName);
|
auto db = QSqlDatabase::addDatabase(u"QSQLITE"_s, connectionName);
|
||||||
db.setDatabaseName(path().data());
|
db.setDatabaseName(path().data());
|
||||||
if (!db.open())
|
if (!db.open())
|
||||||
throw RuntimeError(db.lastError().text());
|
throw RuntimeError(db.lastError().text());
|
||||||
|
|
||||||
QSqlQuery query {db};
|
QSqlQuery query {db};
|
||||||
|
|
||||||
const auto selectTorrentIDStatement = u"SELECT %1 FROM %2 ORDER BY %3;"_qs
|
const auto selectTorrentIDStatement = u"SELECT %1 FROM %2 ORDER BY %3;"_s
|
||||||
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||||
|
|
||||||
const QReadLocker locker {&m_dbLock};
|
const QReadLocker locker {&m_dbLock};
|
||||||
@@ -405,7 +432,7 @@ void BitTorrent::DBResumeDataStorage::doLoadAll() const
|
|||||||
|
|
||||||
emit const_cast<DBResumeDataStorage *>(this)->loadStarted(registeredTorrents);
|
emit const_cast<DBResumeDataStorage *>(this)->loadStarted(registeredTorrents);
|
||||||
|
|
||||||
const auto selectStatement = u"SELECT * FROM %1 ORDER BY %2;"_qs.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
const auto selectStatement = u"SELECT * FROM %1 ORDER BY %2;"_s.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||||
if (!query.exec(selectStatement))
|
if (!query.exec(selectStatement))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
@@ -423,7 +450,7 @@ void BitTorrent::DBResumeDataStorage::doLoadAll() const
|
|||||||
|
|
||||||
int BitTorrent::DBResumeDataStorage::currentDBVersion() const
|
int BitTorrent::DBResumeDataStorage::currentDBVersion() const
|
||||||
{
|
{
|
||||||
const auto selectDBVersionStatement = u"SELECT %1 FROM %2 WHERE %3 = %4;"_qs
|
const auto selectDBVersionStatement = u"SELECT %1 FROM %2 WHERE %3 = %4;"_s
|
||||||
.arg(quoted(DB_COLUMN_VALUE.name), quoted(DB_TABLE_META), quoted(DB_COLUMN_NAME.name), DB_COLUMN_NAME.placeholder);
|
.arg(quoted(DB_COLUMN_VALUE.name), quoted(DB_TABLE_META), quoted(DB_COLUMN_NAME.name), DB_COLUMN_NAME.placeholder);
|
||||||
|
|
||||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
@@ -452,9 +479,17 @@ int BitTorrent::DBResumeDataStorage::currentDBVersion() const
|
|||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::createDB() const
|
void BitTorrent::DBResumeDataStorage::createDB() const
|
||||||
{
|
{
|
||||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
try
|
||||||
|
{
|
||||||
|
enableWALMode();
|
||||||
|
}
|
||||||
|
catch (const RuntimeError &err)
|
||||||
|
{
|
||||||
|
LogMsg(tr("Couldn't enable Write-Ahead Logging (WAL) journaling mode. Error: %1.")
|
||||||
|
.arg(err.message()), Log::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
const QWriteLocker locker {&m_dbLock};
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
|
|
||||||
if (!db.transaction())
|
if (!db.transaction())
|
||||||
throw RuntimeError(db.lastError().text());
|
throw RuntimeError(db.lastError().text());
|
||||||
@@ -494,6 +529,7 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
|||||||
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, "TEXT NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, "TEXT NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
||||||
|
makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, "TEXT NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, "TEXT NOT NULL"),
|
||||||
@@ -506,6 +542,12 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
|||||||
if (!query.exec(createTableTorrentsQuery))
|
if (!query.exec(createTableTorrentsQuery))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
const QString torrentsQueuePositionIndexName = u"%1_%2_INDEX"_s.arg(DB_TABLE_TORRENTS, DB_COLUMN_QUEUE_POSITION.name);
|
||||||
|
const QString createTorrentsQueuePositionIndexQuery = u"CREATE INDEX %1 ON %2 (%3)"_s
|
||||||
|
.arg(quoted(torrentsQueuePositionIndexName), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||||
|
if (!query.exec(createTorrentsQueuePositionIndexQuery))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
if (!db.commit())
|
if (!db.commit())
|
||||||
throw RuntimeError(db.lastError().text());
|
throw RuntimeError(db.lastError().text());
|
||||||
}
|
}
|
||||||
@@ -534,11 +576,11 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||||||
{
|
{
|
||||||
if (fromVersion == 1)
|
if (fromVersion == 1)
|
||||||
{
|
{
|
||||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_qs
|
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s
|
||||||
.arg(quoted(DB_COLUMN_DOWNLOAD_PATH.name), quoted(DB_TABLE_TORRENTS));
|
.arg(quoted(DB_COLUMN_DOWNLOAD_PATH.name), quoted(DB_TABLE_TORRENTS));
|
||||||
if (!query.exec(testQuery))
|
if (!query.exec(testQuery))
|
||||||
{
|
{
|
||||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
|
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_s
|
||||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
|
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
|
||||||
if (!query.exec(alterTableTorrentsQuery))
|
if (!query.exec(alterTableTorrentsQuery))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
@@ -547,17 +589,39 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||||||
|
|
||||||
if (fromVersion <= 2)
|
if (fromVersion <= 2)
|
||||||
{
|
{
|
||||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_qs
|
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s
|
||||||
.arg(quoted(DB_COLUMN_STOP_CONDITION.name), quoted(DB_TABLE_TORRENTS));
|
.arg(quoted(DB_COLUMN_STOP_CONDITION.name), quoted(DB_TABLE_TORRENTS));
|
||||||
if (!query.exec(testQuery))
|
if (!query.exec(testQuery))
|
||||||
{
|
{
|
||||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
|
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_s
|
||||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"));
|
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"));
|
||||||
if (!query.exec(alterTableTorrentsQuery))
|
if (!query.exec(alterTableTorrentsQuery))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fromVersion <= 3)
|
||||||
|
{
|
||||||
|
const QString torrentsQueuePositionIndexName = u"%1_%2_INDEX"_s.arg(DB_TABLE_TORRENTS, DB_COLUMN_QUEUE_POSITION.name);
|
||||||
|
const QString createTorrentsQueuePositionIndexQuery = u"CREATE INDEX IF NOT EXISTS %1 ON %2 (%3)"_s
|
||||||
|
.arg(quoted(torrentsQueuePositionIndexName), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||||
|
if (!query.exec(createTorrentsQueuePositionIndexQuery))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromVersion <= 4)
|
||||||
|
{
|
||||||
|
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s
|
||||||
|
.arg(quoted(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name), quoted(DB_TABLE_TORRENTS));
|
||||||
|
if (!query.exec(testQuery))
|
||||||
|
{
|
||||||
|
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_s
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL DEFAULT -2"));
|
||||||
|
if (!query.exec(alterTableTorrentsQuery))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
||||||
if (!query.prepare(updateMetaVersionQuery))
|
if (!query.prepare(updateMetaVersionQuery))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
@@ -578,217 +642,308 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, const QString &dbConnectionName, QReadWriteLock &dbLock)
|
void BitTorrent::DBResumeDataStorage::enableWALMode() const
|
||||||
: m_path {dbPath}
|
{
|
||||||
, m_connectionName {dbConnectionName}
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
|
QSqlQuery query {db};
|
||||||
|
|
||||||
|
if (!query.exec(u"PRAGMA journal_mode = WAL;"_s))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
if (!query.next())
|
||||||
|
throw RuntimeError(tr("Couldn't obtain query result."));
|
||||||
|
|
||||||
|
const QString result = query.value(0).toString();
|
||||||
|
if (result.compare(u"WAL"_s, Qt::CaseInsensitive) != 0)
|
||||||
|
throw RuntimeError(tr("WAL mode is probably unsupported due to filesystem limitations."));
|
||||||
|
}
|
||||||
|
|
||||||
|
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, QReadWriteLock &dbLock, QObject *parent)
|
||||||
|
: QThread(parent)
|
||||||
|
, m_path {dbPath}
|
||||||
, m_dbLock {dbLock}
|
, m_dbLock {dbLock}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
|
void BitTorrent::DBResumeDataStorage::Worker::run()
|
||||||
{
|
{
|
||||||
auto db = QSqlDatabase::addDatabase(u"QSQLITE"_qs, m_connectionName);
|
{
|
||||||
db.setDatabaseName(m_path.data());
|
auto db = QSqlDatabase::addDatabase(u"QSQLITE"_s, m_connectionName);
|
||||||
if (!db.open())
|
db.setDatabaseName(m_path.data());
|
||||||
throw RuntimeError(db.lastError().text());
|
if (!db.open())
|
||||||
}
|
throw RuntimeError(db.lastError().text());
|
||||||
|
|
||||||
|
int64_t transactedJobsCount = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
m_jobsMutex.lock();
|
||||||
|
if (m_jobs.empty())
|
||||||
|
{
|
||||||
|
if (transactedJobsCount > 0)
|
||||||
|
{
|
||||||
|
db.commit();
|
||||||
|
m_dbLock.unlock();
|
||||||
|
|
||||||
|
qDebug() << "Resume data changes are committed. Transacted jobs:" << transactedJobsCount;
|
||||||
|
transactedJobsCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
{
|
||||||
|
m_jobsMutex.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_waitCondition.wait(&m_jobsMutex);
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
{
|
||||||
|
m_jobsMutex.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dbLock.lockForWrite();
|
||||||
|
if (!db.transaction())
|
||||||
|
{
|
||||||
|
LogMsg(tr("Couldn't begin transaction. Error: %1").arg(db.lastError().text()), Log::WARNING);
|
||||||
|
m_dbLock.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::unique_ptr<Job> job = std::move(m_jobs.front());
|
||||||
|
m_jobs.pop();
|
||||||
|
m_jobsMutex.unlock();
|
||||||
|
|
||||||
|
job->perform(db);
|
||||||
|
++transactedJobsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::closeDatabase() const
|
|
||||||
{
|
|
||||||
QSqlDatabase::removeDatabase(m_connectionName);
|
QSqlDatabase::removeDatabase(m_connectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
void DBResumeDataStorage::Worker::requestInterruption()
|
||||||
{
|
{
|
||||||
// We need to adjust native libtorrent resume data
|
QThread::requestInterruption();
|
||||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
m_waitCondition.wakeAll();
|
||||||
p.save_path = Profile::instance()->toPortablePath(Path(p.save_path))
|
}
|
||||||
.toString().toStdString();
|
|
||||||
if (resumeData.stopped)
|
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData)
|
||||||
|
{
|
||||||
|
addJob(std::make_unique<StoreJob>(id, resumeData));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitTorrent::DBResumeDataStorage::Worker::remove(const TorrentID &id)
|
||||||
|
{
|
||||||
|
addJob(std::make_unique<RemoveJob>(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitTorrent::DBResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue)
|
||||||
|
{
|
||||||
|
addJob(std::make_unique<StoreQueueJob>(queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitTorrent::DBResumeDataStorage::Worker::addJob(std::unique_ptr<Job> job)
|
||||||
|
{
|
||||||
|
m_jobsMutex.lock();
|
||||||
|
m_jobs.push(std::move(job));
|
||||||
|
m_jobsMutex.unlock();
|
||||||
|
|
||||||
|
m_waitCondition.wakeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
StoreJob::StoreJob(const TorrentID &torrentID, const LoadTorrentParams &resumeData)
|
||||||
|
: m_torrentID {torrentID}
|
||||||
|
, m_resumeData {resumeData}
|
||||||
{
|
{
|
||||||
p.flags |= lt::torrent_flags::paused;
|
|
||||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
void StoreJob::perform(QSqlDatabase db)
|
||||||
{
|
{
|
||||||
// Torrent can be actually "running" but temporarily "paused" to perform some
|
// We need to adjust native libtorrent resume data
|
||||||
// service jobs behind the scenes so we need to restore it as "running"
|
lt::add_torrent_params p = m_resumeData.ltAddTorrentParams;
|
||||||
if (resumeData.operatingMode == BitTorrent::TorrentOperatingMode::AutoManaged)
|
p.save_path = Profile::instance()->toPortablePath(Path(p.save_path))
|
||||||
|
.toString().toStdString();
|
||||||
|
if (m_resumeData.stopped)
|
||||||
{
|
{
|
||||||
p.flags |= lt::torrent_flags::auto_managed;
|
p.flags |= lt::torrent_flags::paused;
|
||||||
|
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
p.flags &= ~lt::torrent_flags::paused;
|
// Torrent can be actually "running" but temporarily "paused" to perform some
|
||||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
// service jobs behind the scenes so we need to restore it as "running"
|
||||||
|
if (m_resumeData.operatingMode == BitTorrent::TorrentOperatingMode::AutoManaged)
|
||||||
|
{
|
||||||
|
p.flags |= lt::torrent_flags::auto_managed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.flags &= ~lt::torrent_flags::paused;
|
||||||
|
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QVector<Column> columns {
|
QVector<Column> columns {
|
||||||
DB_COLUMN_TORRENT_ID,
|
DB_COLUMN_TORRENT_ID,
|
||||||
DB_COLUMN_NAME,
|
DB_COLUMN_NAME,
|
||||||
DB_COLUMN_CATEGORY,
|
DB_COLUMN_CATEGORY,
|
||||||
DB_COLUMN_TAGS,
|
DB_COLUMN_TAGS,
|
||||||
DB_COLUMN_TARGET_SAVE_PATH,
|
DB_COLUMN_TARGET_SAVE_PATH,
|
||||||
DB_COLUMN_DOWNLOAD_PATH,
|
DB_COLUMN_DOWNLOAD_PATH,
|
||||||
DB_COLUMN_CONTENT_LAYOUT,
|
DB_COLUMN_CONTENT_LAYOUT,
|
||||||
DB_COLUMN_RATIO_LIMIT,
|
DB_COLUMN_RATIO_LIMIT,
|
||||||
DB_COLUMN_SEEDING_TIME_LIMIT,
|
DB_COLUMN_SEEDING_TIME_LIMIT,
|
||||||
DB_COLUMN_HAS_OUTER_PIECES_PRIORITY,
|
DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT,
|
||||||
DB_COLUMN_HAS_SEED_STATUS,
|
DB_COLUMN_HAS_OUTER_PIECES_PRIORITY,
|
||||||
DB_COLUMN_OPERATING_MODE,
|
DB_COLUMN_HAS_SEED_STATUS,
|
||||||
DB_COLUMN_STOPPED,
|
DB_COLUMN_OPERATING_MODE,
|
||||||
DB_COLUMN_STOP_CONDITION,
|
DB_COLUMN_STOPPED,
|
||||||
DB_COLUMN_RESUMEDATA
|
DB_COLUMN_STOP_CONDITION,
|
||||||
};
|
DB_COLUMN_RESUMEDATA
|
||||||
|
};
|
||||||
|
|
||||||
lt::entry data = lt::write_resume_data(p);
|
lt::entry data = lt::write_resume_data(p);
|
||||||
|
|
||||||
// metadata is stored in separate column
|
// metadata is stored in separate column
|
||||||
QByteArray bencodedMetadata;
|
QByteArray bencodedMetadata;
|
||||||
if (p.ti)
|
if (p.ti)
|
||||||
{
|
|
||||||
lt::entry::dictionary_type &dataDict = data.dict();
|
|
||||||
lt::entry metadata {lt::entry::dictionary_t};
|
|
||||||
lt::entry::dictionary_type &metadataDict = metadata.dict();
|
|
||||||
metadataDict.insert(dataDict.extract("info"));
|
|
||||||
metadataDict.insert(dataDict.extract("creation date"));
|
|
||||||
metadataDict.insert(dataDict.extract("created by"));
|
|
||||||
metadataDict.insert(dataDict.extract("comment"));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
bencodedMetadata.reserve(512 * 1024);
|
lt::entry::dictionary_type &dataDict = data.dict();
|
||||||
lt::bencode(std::back_inserter(bencodedMetadata), metadata);
|
lt::entry metadata {lt::entry::dictionary_t};
|
||||||
}
|
lt::entry::dictionary_type &metadataDict = metadata.dict();
|
||||||
catch (const std::exception &err)
|
metadataDict.insert(dataDict.extract("info"));
|
||||||
{
|
metadataDict.insert(dataDict.extract("creation date"));
|
||||||
LogMsg(tr("Couldn't save torrent metadata. Error: %1.")
|
metadataDict.insert(dataDict.extract("created by"));
|
||||||
.arg(QString::fromLocal8Bit(err.what())), Log::CRITICAL);
|
metadataDict.insert(dataDict.extract("comment"));
|
||||||
return;
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bencodedMetadata.reserve(512 * 1024);
|
||||||
|
lt::bencode(std::back_inserter(bencodedMetadata), metadata);
|
||||||
|
}
|
||||||
|
catch (const std::exception &err)
|
||||||
|
{
|
||||||
|
LogMsg(ResumeDataStorage::tr("Couldn't save torrent metadata. Error: %1.")
|
||||||
|
.arg(QString::fromLocal8Bit(err.what())), Log::CRITICAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.append(DB_COLUMN_METADATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.append(DB_COLUMN_METADATA);
|
QByteArray bencodedResumeData;
|
||||||
}
|
bencodedResumeData.reserve(256 * 1024);
|
||||||
|
lt::bencode(std::back_inserter(bencodedResumeData), data);
|
||||||
QByteArray bencodedResumeData;
|
|
||||||
bencodedResumeData.reserve(256 * 1024);
|
|
||||||
lt::bencode(std::back_inserter(bencodedResumeData), data);
|
|
||||||
|
|
||||||
const QString insertTorrentStatement = makeInsertStatement(DB_TABLE_TORRENTS, columns)
|
|
||||||
+ makeOnConflictUpdateStatement(DB_COLUMN_TORRENT_ID, columns);
|
|
||||||
auto db = QSqlDatabase::database(m_connectionName);
|
|
||||||
QSqlQuery query {db};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!query.prepare(insertTorrentStatement))
|
|
||||||
throw RuntimeError(query.lastError().text());
|
|
||||||
|
|
||||||
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
|
|
||||||
query.bindValue(DB_COLUMN_NAME.placeholder, resumeData.name);
|
|
||||||
query.bindValue(DB_COLUMN_CATEGORY.placeholder, resumeData.category);
|
|
||||||
query.bindValue(DB_COLUMN_TAGS.placeholder, (resumeData.tags.isEmpty()
|
|
||||||
? QVariant(QVariant::String) : resumeData.tags.join(u","_qs)));
|
|
||||||
query.bindValue(DB_COLUMN_CONTENT_LAYOUT.placeholder, Utils::String::fromEnum(resumeData.contentLayout));
|
|
||||||
query.bindValue(DB_COLUMN_RATIO_LIMIT.placeholder, static_cast<int>(resumeData.ratioLimit * 1000));
|
|
||||||
query.bindValue(DB_COLUMN_SEEDING_TIME_LIMIT.placeholder, resumeData.seedingTimeLimit);
|
|
||||||
query.bindValue(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.placeholder, resumeData.firstLastPiecePriority);
|
|
||||||
query.bindValue(DB_COLUMN_HAS_SEED_STATUS.placeholder, resumeData.hasSeedStatus);
|
|
||||||
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(resumeData.operatingMode));
|
|
||||||
query.bindValue(DB_COLUMN_STOPPED.placeholder, resumeData.stopped);
|
|
||||||
query.bindValue(DB_COLUMN_STOP_CONDITION.placeholder, Utils::String::fromEnum(resumeData.stopCondition));
|
|
||||||
|
|
||||||
if (!resumeData.useAutoTMM)
|
|
||||||
{
|
|
||||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath).data());
|
|
||||||
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath).data());
|
|
||||||
}
|
|
||||||
|
|
||||||
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
|
||||||
if (!bencodedMetadata.isEmpty())
|
|
||||||
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
|
|
||||||
|
|
||||||
const QWriteLocker locker {&m_dbLock};
|
|
||||||
if (!query.exec())
|
|
||||||
throw RuntimeError(query.lastError().text());
|
|
||||||
}
|
|
||||||
catch (const RuntimeError &err)
|
|
||||||
{
|
|
||||||
LogMsg(tr("Couldn't store resume data for torrent '%1'. Error: %2")
|
|
||||||
.arg(id.toString(), err.message()), Log::CRITICAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::remove(const TorrentID &id) const
|
|
||||||
{
|
|
||||||
const auto deleteTorrentStatement = u"DELETE FROM %1 WHERE %2 = %3;"_qs
|
|
||||||
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
|
||||||
|
|
||||||
auto db = QSqlDatabase::database(m_connectionName);
|
|
||||||
QSqlQuery query {db};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!query.prepare(deleteTorrentStatement))
|
|
||||||
throw RuntimeError(query.lastError().text());
|
|
||||||
|
|
||||||
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
|
|
||||||
|
|
||||||
const QWriteLocker locker {&m_dbLock};
|
|
||||||
if (!query.exec())
|
|
||||||
throw RuntimeError(query.lastError().text());
|
|
||||||
}
|
|
||||||
catch (const RuntimeError &err)
|
|
||||||
{
|
|
||||||
LogMsg(tr("Couldn't delete resume data of torrent '%1'. Error: %2")
|
|
||||||
.arg(id.toString(), err.message()), Log::CRITICAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
|
|
||||||
{
|
|
||||||
const auto updateQueuePosStatement = u"UPDATE %1 SET %2 = %3 WHERE %4 = %5;"_qs
|
|
||||||
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name), DB_COLUMN_QUEUE_POSITION.placeholder
|
|
||||||
, quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
|
||||||
|
|
||||||
auto db = QSqlDatabase::database(m_connectionName);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const QWriteLocker locker {&m_dbLock};
|
|
||||||
|
|
||||||
if (!db.transaction())
|
|
||||||
throw RuntimeError(db.lastError().text());
|
|
||||||
|
|
||||||
|
const QString insertTorrentStatement = makeInsertStatement(DB_TABLE_TORRENTS, columns)
|
||||||
|
+ makeOnConflictUpdateStatement(DB_COLUMN_TORRENT_ID, columns);
|
||||||
QSqlQuery query {db};
|
QSqlQuery query {db};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (!query.prepare(insertTorrentStatement))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, m_torrentID.toString());
|
||||||
|
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","_s)));
|
||||||
|
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);
|
||||||
|
query.bindValue(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.placeholder, m_resumeData.inactiveSeedingTimeLimit);
|
||||||
|
query.bindValue(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.placeholder, m_resumeData.firstLastPiecePriority);
|
||||||
|
query.bindValue(DB_COLUMN_HAS_SEED_STATUS.placeholder, m_resumeData.hasFinishedStatus);
|
||||||
|
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(m_resumeData.operatingMode));
|
||||||
|
query.bindValue(DB_COLUMN_STOPPED.placeholder, m_resumeData.stopped);
|
||||||
|
query.bindValue(DB_COLUMN_STOP_CONDITION.placeholder, Utils::String::fromEnum(m_resumeData.stopCondition));
|
||||||
|
|
||||||
|
if (!m_resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(m_resumeData.savePath).data());
|
||||||
|
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(m_resumeData.downloadPath).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
||||||
|
if (!bencodedMetadata.isEmpty())
|
||||||
|
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
|
||||||
|
|
||||||
|
if (!query.exec())
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
}
|
||||||
|
catch (const RuntimeError &err)
|
||||||
|
{
|
||||||
|
LogMsg(ResumeDataStorage::tr("Couldn't store resume data for torrent '%1'. Error: %2")
|
||||||
|
.arg(m_torrentID.toString(), err.message()), Log::CRITICAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveJob::RemoveJob(const TorrentID &torrentID)
|
||||||
|
: m_torrentID {torrentID}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveJob::perform(QSqlDatabase db)
|
||||||
|
{
|
||||||
|
const auto deleteTorrentStatement = u"DELETE FROM %1 WHERE %2 = %3;"_s
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
||||||
|
|
||||||
|
QSqlQuery query {db};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!query.prepare(deleteTorrentStatement))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, m_torrentID.toString());
|
||||||
|
|
||||||
|
if (!query.exec())
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
}
|
||||||
|
catch (const RuntimeError &err)
|
||||||
|
{
|
||||||
|
LogMsg(ResumeDataStorage::tr("Couldn't delete resume data of torrent '%1'. Error: %2")
|
||||||
|
.arg(m_torrentID.toString(), err.message()), Log::CRITICAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreQueueJob::StoreQueueJob(const QVector<TorrentID> &queue)
|
||||||
|
: m_queue {queue}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StoreQueueJob::perform(QSqlDatabase db)
|
||||||
|
{
|
||||||
|
const auto updateQueuePosStatement = u"UPDATE %1 SET %2 = %3 WHERE %4 = %5;"_s
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name), DB_COLUMN_QUEUE_POSITION.placeholder
|
||||||
|
, quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QSqlQuery query {db};
|
||||||
|
|
||||||
if (!query.prepare(updateQueuePosStatement))
|
if (!query.prepare(updateQueuePosStatement))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
for (const TorrentID &torrentID : queue)
|
for (const TorrentID &torrentID : m_queue)
|
||||||
{
|
{
|
||||||
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, torrentID.toString());
|
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, torrentID.toString());
|
||||||
query.bindValue(DB_COLUMN_QUEUE_POSITION.placeholder, pos++);
|
query.bindValue(DB_COLUMN_QUEUE_POSITION.placeholder, pos++);
|
||||||
if (!query.exec())
|
if (!query.exec())
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!db.commit())
|
|
||||||
throw RuntimeError(db.lastError().text());
|
|
||||||
}
|
}
|
||||||
catch (const RuntimeError &)
|
catch (const RuntimeError &err)
|
||||||
{
|
{
|
||||||
db.rollback();
|
LogMsg(ResumeDataStorage::tr("Couldn't store torrents queue positions. Error: %1")
|
||||||
throw;
|
.arg(err.message()), Log::CRITICAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const RuntimeError &err)
|
|
||||||
{
|
|
||||||
LogMsg(tr("Couldn't store torrents queue positions. Error: %1")
|
|
||||||
.arg(err.message()), Log::CRITICAL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ namespace BitTorrent
|
|||||||
int currentDBVersion() const;
|
int currentDBVersion() const;
|
||||||
void createDB() const;
|
void createDB() const;
|
||||||
void updateDB(int fromVersion) const;
|
void updateDB(int fromVersion) const;
|
||||||
|
void enableWALMode() const;
|
||||||
|
|
||||||
Utils::Thread::UniquePtr m_ioThread;
|
Utils::Thread::UniquePtr m_ioThread;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <libtorrent/announce_entry.hpp>
|
#include <libtorrent/announce_entry.hpp>
|
||||||
@@ -44,4 +46,5 @@ struct ExtensionData
|
|||||||
{
|
{
|
||||||
lt::torrent_status status;
|
lt::torrent_status status;
|
||||||
std::vector<lt::announce_entry> trackers;
|
std::vector<lt::announce_entry> trackers;
|
||||||
|
std::set<std::string> urlSeeds;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
lt::address_v4::bytes_type m_buf;
|
lt::address_v4::bytes_type m_buf {};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool parseIPAddress(const char *data, lt::address &address)
|
bool parseIPAddress(const char *data, lt::address &address)
|
||||||
@@ -111,7 +111,6 @@ namespace
|
|||||||
|
|
||||||
FilterParserThread::FilterParserThread(QObject *parent)
|
FilterParserThread::FilterParserThread(QObject *parent)
|
||||||
: QThread(parent)
|
: QThread(parent)
|
||||||
, m_abort(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,9 +483,9 @@ int FilterParserThread::parseP2BFilterFile()
|
|||||||
char buf[7];
|
char buf[7];
|
||||||
unsigned char version;
|
unsigned char version;
|
||||||
if (!stream.readRawData(buf, sizeof(buf))
|
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)))
|
|| !stream.readRawData(reinterpret_cast<char*>(&version), sizeof(version)))
|
||||||
{
|
{
|
||||||
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
return ruleCount;
|
return ruleCount;
|
||||||
}
|
}
|
||||||
@@ -618,17 +617,17 @@ void FilterParserThread::run()
|
|||||||
{
|
{
|
||||||
qDebug("Processing filter file");
|
qDebug("Processing filter file");
|
||||||
int ruleCount = 0;
|
int ruleCount = 0;
|
||||||
if (m_filePath.hasExtension(u".p2p"_qs))
|
if (m_filePath.hasExtension(u".p2p"_s))
|
||||||
{
|
{
|
||||||
// PeerGuardian p2p file
|
// PeerGuardian p2p file
|
||||||
ruleCount = parseP2PFilterFile();
|
ruleCount = parseP2PFilterFile();
|
||||||
}
|
}
|
||||||
else if (m_filePath.hasExtension(u".p2b"_qs))
|
else if (m_filePath.hasExtension(u".p2b"_s))
|
||||||
{
|
{
|
||||||
// PeerGuardian p2b file
|
// PeerGuardian p2b file
|
||||||
ruleCount = parseP2BFilterFile();
|
ruleCount = parseP2BFilterFile();
|
||||||
}
|
}
|
||||||
else if (m_filePath.hasExtension(u".dat"_qs))
|
else if (m_filePath.hasExtension(u".dat"_s))
|
||||||
{
|
{
|
||||||
// eMule DAT format
|
// eMule DAT format
|
||||||
ruleCount = parseDATFilterFile();
|
ruleCount = parseDATFilterFile();
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ protected:
|
|||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int findAndNullDelimiter(char *const data, char delimiter, int start, int end, bool reverse = false);
|
int findAndNullDelimiter(char *data, char delimiter, int start, int end, bool reverse = false);
|
||||||
int trim(char *const data, int start, int end);
|
int trim(char *data, int start, int end);
|
||||||
int parseDATFilterFile();
|
int parseDATFilterFile();
|
||||||
int parseP2PFilterFile();
|
int parseP2PFilterFile();
|
||||||
int getlineInStream(QDataStream &stream, std::string &name, char delim);
|
int getlineInStream(QDataStream &stream, std::string &name, char delim);
|
||||||
int parseP2BFilterFile();
|
int parseP2BFilterFile();
|
||||||
|
|
||||||
bool m_abort;
|
bool m_abort = false;
|
||||||
Path m_filePath;
|
Path m_filePath;
|
||||||
lt::ip_filter m_filter;
|
lt::ip_filter m_filter;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ BitTorrent::InfoHash::operator WrappedType() const
|
|||||||
|
|
||||||
BitTorrent::TorrentID BitTorrent::TorrentID::fromString(const QString &hashString)
|
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)
|
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)
|
BitTorrent::TorrentID BitTorrent::TorrentID::fromSHA1Hash(const SHA1Hash &hash)
|
||||||
{
|
{
|
||||||
return TorrentID(hash);
|
return {hash};
|
||||||
}
|
}
|
||||||
|
|
||||||
BitTorrent::TorrentID BitTorrent::TorrentID::fromSHA256Hash(const SHA256Hash &hash)
|
BitTorrent::TorrentID BitTorrent::TorrentID::fromSHA256Hash(const SHA256Hash &hash)
|
||||||
|
|||||||
@@ -52,11 +52,14 @@ namespace BitTorrent
|
|||||||
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
||||||
bool useAutoTMM = false;
|
bool useAutoTMM = false;
|
||||||
bool firstLastPiecePriority = false;
|
bool firstLastPiecePriority = false;
|
||||||
bool hasSeedStatus = false;
|
bool hasFinishedStatus = false;
|
||||||
bool stopped = false;
|
bool stopped = false;
|
||||||
Torrent::StopCondition stopCondition;
|
Torrent::StopCondition stopCondition = Torrent::StopCondition::None;
|
||||||
|
|
||||||
|
bool addToQueueTop = false; // only for new torrents
|
||||||
|
|
||||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||||
|
int inactiveSeedingTimeLimit = Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ namespace
|
|||||||
const int V1_BASE32_SIZE = SHA1Hash::length() * 1.6;
|
const int V1_BASE32_SIZE = SHA1Hash::length() * 1.6;
|
||||||
|
|
||||||
return ((((string.size() == V1_HEX_SIZE))
|
return ((((string.size() == V1_HEX_SIZE))
|
||||||
&& !string.contains(QRegularExpression(u"[^0-9A-Fa-f]"_qs)))
|
&& !string.contains(QRegularExpression(u"[^0-9A-Fa-f]"_s)))
|
||||||
|| ((string.size() == V1_BASE32_SIZE)
|
|| ((string.size() == V1_BASE32_SIZE)
|
||||||
&& !string.contains(QRegularExpression(u"[^2-7A-Za-z]"_qs))));
|
&& !string.contains(QRegularExpression(u"[^2-7A-Za-z]"_s))));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isV2Hash(const QString &string)
|
bool isV2Hash(const QString &string)
|
||||||
@@ -66,7 +66,7 @@ namespace
|
|||||||
const int V2_HEX_SIZE = SHA256Hash::length() * 2;
|
const int V2_HEX_SIZE = SHA256Hash::length() * 2;
|
||||||
|
|
||||||
return (string.size() == V2_HEX_SIZE)
|
return (string.size() == V2_HEX_SIZE)
|
||||||
&& !string.contains(QRegularExpression(u"[^0-9A-Fa-f]"_qs));
|
&& !string.contains(QRegularExpression(u"[^0-9A-Fa-f]"_s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +75,7 @@ using namespace BitTorrent;
|
|||||||
const int magnetUriId = qRegisterMetaType<MagnetUri>();
|
const int magnetUriId = qRegisterMetaType<MagnetUri>();
|
||||||
|
|
||||||
MagnetUri::MagnetUri(const QString &source)
|
MagnetUri::MagnetUri(const QString &source)
|
||||||
: m_valid(false)
|
: m_url(source)
|
||||||
, m_url(source)
|
|
||||||
{
|
{
|
||||||
if (source.isEmpty()) return;
|
if (source.isEmpty()) return;
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace BitTorrent
|
|||||||
lt::add_torrent_params addTorrentParams() const;
|
lt::add_torrent_params addTorrentParams() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_valid;
|
bool m_valid = false;
|
||||||
QString m_url;
|
QString m_url;
|
||||||
InfoHash m_infoHash;
|
InfoHash m_infoHash;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2020-2022 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2020-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -42,6 +42,17 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NativeSessionExtension::isSessionListening() const
|
||||||
|
{
|
||||||
|
const QReadLocker locker {&m_lock};
|
||||||
|
return m_isSessionListening;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeSessionExtension::added(const lt::session_handle &nativeSession)
|
||||||
|
{
|
||||||
|
m_nativeSession = nativeSession;
|
||||||
|
}
|
||||||
|
|
||||||
lt::feature_flags_t NativeSessionExtension::implemented_features()
|
lt::feature_flags_t NativeSessionExtension::implemented_features()
|
||||||
{
|
{
|
||||||
return alert_feature;
|
return alert_feature;
|
||||||
@@ -56,6 +67,9 @@ void NativeSessionExtension::on_alert(const lt::alert *alert)
|
|||||||
{
|
{
|
||||||
switch (alert->type())
|
switch (alert->type())
|
||||||
{
|
{
|
||||||
|
case lt::session_stats_alert::alert_type:
|
||||||
|
handleSessionStatsAlert(static_cast<const lt::session_stats_alert *>(alert));
|
||||||
|
break;
|
||||||
case lt::fastresume_rejected_alert::alert_type:
|
case lt::fastresume_rejected_alert::alert_type:
|
||||||
handleFastresumeRejectedAlert(static_cast<const lt::fastresume_rejected_alert *>(alert));
|
handleFastresumeRejectedAlert(static_cast<const lt::fastresume_rejected_alert *>(alert));
|
||||||
break;
|
break;
|
||||||
@@ -63,3 +77,9 @@ void NativeSessionExtension::on_alert(const lt::alert *alert)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeSessionExtension::handleSessionStatsAlert([[maybe_unused]] const lt::session_stats_alert *alert)
|
||||||
|
{
|
||||||
|
const QWriteLocker locker {&m_lock};
|
||||||
|
m_isSessionListening = m_nativeSession.is_listening();
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,12 +29,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libtorrent/extensions.hpp>
|
#include <libtorrent/extensions.hpp>
|
||||||
|
#include <libtorrent/fwd.hpp>
|
||||||
|
#include <libtorrent/session_handle.hpp>
|
||||||
|
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
#include "extensiondata.h"
|
#include "extensiondata.h"
|
||||||
|
|
||||||
class NativeSessionExtension final : public lt::plugin
|
class NativeSessionExtension final : public lt::plugin
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
bool isSessionListening() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void added(const lt::session_handle &nativeSession) override;
|
||||||
lt::feature_flags_t implemented_features() override;
|
lt::feature_flags_t implemented_features() override;
|
||||||
std::shared_ptr<lt::torrent_plugin> new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData) override;
|
std::shared_ptr<lt::torrent_plugin> new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData) override;
|
||||||
void on_alert(const lt::alert *alert) override;
|
void on_alert(const lt::alert *alert) override;
|
||||||
|
|
||||||
|
void handleSessionStatsAlert(const lt::session_stats_alert *alert);
|
||||||
|
|
||||||
|
lt::session_handle m_nativeSession;
|
||||||
|
|
||||||
|
mutable QReadWriteLock m_lock;
|
||||||
|
bool m_isSessionListening = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2020-2022 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2020-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -40,6 +40,7 @@ NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrent
|
|||||||
{
|
{
|
||||||
m_data->status = m_torrentHandle.status();
|
m_data->status = m_torrentHandle.status();
|
||||||
m_data->trackers = m_torrentHandle.trackers();
|
m_data->trackers = m_torrentHandle.trackers();
|
||||||
|
m_data->urlSeeds = m_torrentHandle.url_seeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
on_state(m_data ? m_data->status.state : m_torrentHandle.status({}).state);
|
on_state(m_data ? m_data->status.state : m_torrentHandle.status({}).state);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -31,16 +31,16 @@
|
|||||||
#include <QBitArray>
|
#include <QBitArray>
|
||||||
|
|
||||||
#include "base/bittorrent/ltqbitarray.h"
|
#include "base/bittorrent/ltqbitarray.h"
|
||||||
#include "base/bittorrent/torrent.h"
|
|
||||||
#include "base/net/geoipmanager.h"
|
#include "base/net/geoipmanager.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
|
#include "base/utils/bytearray.h"
|
||||||
#include "peeraddress.h"
|
#include "peeraddress.h"
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
PeerInfo::PeerInfo(const Torrent *torrent, const lt::peer_info &nativeInfo)
|
PeerInfo::PeerInfo(const lt::peer_info &nativeInfo, const QBitArray &allPieces)
|
||||||
: m_nativeInfo(nativeInfo)
|
: m_nativeInfo(nativeInfo)
|
||||||
, m_relevance(calcRelevance(torrent))
|
, m_relevance(calcRelevance(allPieces))
|
||||||
{
|
{
|
||||||
determineFlags();
|
determineFlags();
|
||||||
}
|
}
|
||||||
@@ -169,6 +169,9 @@ bool PeerInfo::isPlaintextEncrypted() const
|
|||||||
|
|
||||||
PeerAddress PeerInfo::address() const
|
PeerAddress PeerInfo::address() const
|
||||||
{
|
{
|
||||||
|
if (useI2PSocket())
|
||||||
|
return {};
|
||||||
|
|
||||||
// fast path for platforms which boost.asio internal struct maps to `sockaddr`
|
// fast path for platforms which boost.asio internal struct maps to `sockaddr`
|
||||||
return {QHostAddress(m_nativeInfo.ip.data()), m_nativeInfo.ip.port()};
|
return {QHostAddress(m_nativeInfo.ip.data()), m_nativeInfo.ip.port()};
|
||||||
// slow path for the others
|
// slow path for the others
|
||||||
@@ -176,6 +179,23 @@ PeerAddress PeerInfo::address() const
|
|||||||
// , m_nativeInfo.ip.port()};
|
// , m_nativeInfo.ip.port()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PeerInfo::I2PAddress() const
|
||||||
|
{
|
||||||
|
if (!useI2PSocket())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
#if defined(QBT_USES_LIBTORRENT2) && TORRENT_USE_I2P
|
||||||
|
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
|
QString PeerInfo::client() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(m_nativeInfo.client);
|
return QString::fromStdString(m_nativeInfo.client);
|
||||||
@@ -242,13 +262,12 @@ QString PeerInfo::connectionType() const
|
|||||||
return C_UTP;
|
return C_UTP;
|
||||||
|
|
||||||
return (m_nativeInfo.connection_type == lt::peer_info::standard_bittorrent)
|
return (m_nativeInfo.connection_type == lt::peer_info::standard_bittorrent)
|
||||||
? u"BT"_qs
|
? u"BT"_s
|
||||||
: u"Web"_qs;
|
: u"Web"_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal PeerInfo::calcRelevance(const Torrent *torrent) const
|
qreal PeerInfo::calcRelevance(const QBitArray &allPieces) const
|
||||||
{
|
{
|
||||||
const QBitArray allPieces = torrent->pieces();
|
|
||||||
const int localMissing = allPieces.count(false);
|
const int localMissing = allPieces.count(false);
|
||||||
if (localMissing <= 0)
|
if (localMissing <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -268,7 +287,7 @@ void PeerInfo::determineFlags()
|
|||||||
const auto updateFlags = [this](const QChar specifier, const QString &explanation)
|
const auto updateFlags = [this](const QChar specifier, const QString &explanation)
|
||||||
{
|
{
|
||||||
m_flags += (specifier + u' ');
|
m_flags += (specifier + u' ');
|
||||||
m_flagsDescription += u"%1 = %2\n"_qs.arg(specifier, explanation);
|
m_flagsDescription += u"%1 = %2\n"_s.arg(specifier, explanation);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isInteresting())
|
if (isInteresting())
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -36,7 +36,6 @@ class QBitArray;
|
|||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
class Torrent;
|
|
||||||
struct PeerAddress;
|
struct PeerAddress;
|
||||||
|
|
||||||
class PeerInfo
|
class PeerInfo
|
||||||
@@ -45,7 +44,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
PeerInfo() = default;
|
PeerInfo() = default;
|
||||||
PeerInfo(const Torrent *torrent, const lt::peer_info &nativeInfo);
|
PeerInfo(const lt::peer_info &nativeInfo, const QBitArray &allPieces);
|
||||||
|
|
||||||
bool fromDHT() const;
|
bool fromDHT() const;
|
||||||
bool fromPeX() const;
|
bool fromPeX() const;
|
||||||
@@ -77,6 +76,7 @@ namespace BitTorrent
|
|||||||
bool isPlaintextEncrypted() const;
|
bool isPlaintextEncrypted() const;
|
||||||
|
|
||||||
PeerAddress address() const;
|
PeerAddress address() const;
|
||||||
|
QString I2PAddress() const;
|
||||||
QString client() const;
|
QString client() const;
|
||||||
QString peerIdClient() const;
|
QString peerIdClient() const;
|
||||||
qreal progress() const;
|
qreal progress() const;
|
||||||
@@ -93,7 +93,7 @@ namespace BitTorrent
|
|||||||
int downloadingPieceIndex() const;
|
int downloadingPieceIndex() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qreal calcRelevance(const Torrent *torrent) const;
|
qreal calcRelevance(const QBitArray &allPieces) const;
|
||||||
void determineFlags();
|
void determineFlags();
|
||||||
|
|
||||||
lt::peer_info m_nativeInfo = {};
|
lt::peer_info m_nativeInfo = {};
|
||||||
@@ -102,5 +102,6 @@ namespace BitTorrent
|
|||||||
QString m_flagsDescription;
|
QString m_flagsDescription;
|
||||||
|
|
||||||
mutable QString m_country;
|
mutable QString m_country;
|
||||||
|
mutable QString m_I2PAddress;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2019 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2019-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -28,14 +28,13 @@
|
|||||||
|
|
||||||
#include "portforwarderimpl.h"
|
#include "portforwarderimpl.h"
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/algorithm.h"
|
#include "base/bittorrent/sessionimpl.h"
|
||||||
#include "base/logger.h"
|
|
||||||
|
|
||||||
PortForwarderImpl::PortForwarderImpl(lt::session *provider, QObject *parent)
|
PortForwarderImpl::PortForwarderImpl(BitTorrent::SessionImpl *provider, QObject *parent)
|
||||||
: Net::PortForwarder {parent}
|
: Net::PortForwarder(parent)
|
||||||
, m_storeActive {u"Network/PortForwardingEnabled"_qs, true}
|
, m_storeActive {u"Network/PortForwardingEnabled"_s, true}
|
||||||
, m_provider {provider}
|
, m_provider {provider}
|
||||||
{
|
{
|
||||||
if (isEnabled())
|
if (isEnabled())
|
||||||
@@ -66,38 +65,13 @@ void PortForwarderImpl::setEnabled(const bool enabled)
|
|||||||
|
|
||||||
void PortForwarderImpl::setPorts(const QString &profile, QSet<quint16> ports)
|
void PortForwarderImpl::setPorts(const QString &profile, QSet<quint16> ports)
|
||||||
{
|
{
|
||||||
PortMapping &portMapping = m_portProfiles[profile];
|
const QSet<quint16> oldForwardedPorts = std::accumulate(m_portProfiles.cbegin(), m_portProfiles.cend(), QSet<quint16>());
|
||||||
Algorithm::removeIf(portMapping, [this, &ports](const quint16 port, const std::vector<lt::port_mapping_t> &handles)
|
|
||||||
{
|
|
||||||
// keep existing forwardings
|
|
||||||
const bool isAlreadyMapped = ports.remove(port);
|
|
||||||
if (isAlreadyMapped)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// remove outdated forwardings
|
m_portProfiles[profile] = std::move(ports);
|
||||||
for (const lt::port_mapping_t &handle : handles)
|
const QSet<quint16> newForwardedPorts = std::accumulate(m_portProfiles.cbegin(), m_portProfiles.cend(), QSet<quint16>());
|
||||||
m_provider->delete_port_mapping(handle);
|
|
||||||
m_forwardedPorts.remove(port);
|
|
||||||
|
|
||||||
return true;
|
m_provider->removeMappedPorts(oldForwardedPorts - newForwardedPorts);
|
||||||
});
|
m_provider->addMappedPorts(newForwardedPorts - oldForwardedPorts);
|
||||||
|
|
||||||
// add new forwardings
|
|
||||||
for (const quint16 port : ports)
|
|
||||||
{
|
|
||||||
// port already forwarded/taken by other profile, don't do anything
|
|
||||||
if (m_forwardedPorts.contains(port))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isEnabled())
|
|
||||||
portMapping.insert(port, m_provider->add_port_mapping(lt::session::tcp, port, port));
|
|
||||||
else
|
|
||||||
portMapping.insert(port, {});
|
|
||||||
m_forwardedPorts.insert(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portMapping.isEmpty())
|
|
||||||
m_portProfiles.remove(profile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortForwarderImpl::removePorts(const QString &profile)
|
void PortForwarderImpl::removePorts(const QString &profile)
|
||||||
@@ -107,40 +81,12 @@ void PortForwarderImpl::removePorts(const QString &profile)
|
|||||||
|
|
||||||
void PortForwarderImpl::start()
|
void PortForwarderImpl::start()
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack;
|
m_provider->enablePortMapping();
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_upnp, true);
|
for (const QSet<quint16> &ports : asConst(m_portProfiles))
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_natpmp, true);
|
m_provider->addMappedPorts(ports);
|
||||||
m_provider->apply_settings(std::move(settingsPack));
|
|
||||||
|
|
||||||
for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter)
|
|
||||||
{
|
|
||||||
PortMapping &portMapping = profileIter.value();
|
|
||||||
for (auto iter = portMapping.begin(); iter != portMapping.end(); ++iter)
|
|
||||||
{
|
|
||||||
Q_ASSERT(iter.value().empty());
|
|
||||||
|
|
||||||
const quint16 port = iter.key();
|
|
||||||
iter.value() = m_provider->add_port_mapping(lt::session::tcp, port, port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMsg(tr("UPnP/NAT-PMP support: ON"), Log::INFO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortForwarderImpl::stop()
|
void PortForwarderImpl::stop()
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack;
|
m_provider->disablePortMapping();
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_upnp, false);
|
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_natpmp, false);
|
|
||||||
m_provider->apply_settings(std::move(settingsPack));
|
|
||||||
|
|
||||||
// don't clear m_portProfiles so a later `start()` call can restore the port forwardings
|
|
||||||
for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter)
|
|
||||||
{
|
|
||||||
PortMapping &portMapping = profileIter.value();
|
|
||||||
for (auto iter = portMapping.begin(); iter != portMapping.end(); ++iter)
|
|
||||||
iter.value().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMsg(tr("UPnP/NAT-PMP support: OFF"), Log::INFO);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2019 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2019-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -28,24 +28,24 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <libtorrent/fwd.hpp>
|
|
||||||
#include <libtorrent/portmap.hpp>
|
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
#include "base/net/portforwarder.h"
|
#include "base/net/portforwarder.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class SessionImpl;
|
||||||
|
}
|
||||||
|
|
||||||
class PortForwarderImpl final : public Net::PortForwarder
|
class PortForwarderImpl final : public Net::PortForwarder
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(PortForwarderImpl)
|
Q_DISABLE_COPY_MOVE(PortForwarderImpl)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PortForwarderImpl(lt::session *provider, QObject *parent = nullptr);
|
explicit PortForwarderImpl(BitTorrent::SessionImpl *provider, QObject *parent = nullptr);
|
||||||
~PortForwarderImpl() override;
|
~PortForwarderImpl() override;
|
||||||
|
|
||||||
bool isEnabled() const override;
|
bool isEnabled() const override;
|
||||||
@@ -59,9 +59,7 @@ private:
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
CachedSettingValue<bool> m_storeActive;
|
CachedSettingValue<bool> m_storeActive;
|
||||||
lt::session *const m_provider = nullptr;
|
|
||||||
|
|
||||||
using PortMapping = QHash<quint16, std::vector<lt::port_mapping_t>>; // <port, handles>
|
BitTorrent::SessionImpl *const m_provider = nullptr;
|
||||||
QHash<QString, PortMapping> m_portProfiles;
|
QHash<QString, QSet<quint16>> m_portProfiles;
|
||||||
QSet<quint16> m_forwardedPorts;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* 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>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -170,6 +170,9 @@ namespace BitTorrent
|
|||||||
virtual bool useCategoryPathsInManualMode() const = 0;
|
virtual bool useCategoryPathsInManualMode() const = 0;
|
||||||
virtual void setUseCategoryPathsInManualMode(bool value) = 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);
|
static bool isValidTag(const QString &tag);
|
||||||
virtual QSet<QString> tags() const = 0;
|
virtual QSet<QString> tags() const = 0;
|
||||||
virtual bool hasTag(const QString &tag) const = 0;
|
virtual bool hasTag(const QString &tag) const = 0;
|
||||||
@@ -200,12 +203,16 @@ namespace BitTorrent
|
|||||||
virtual void setGlobalMaxRatio(qreal ratio) = 0;
|
virtual void setGlobalMaxRatio(qreal ratio) = 0;
|
||||||
virtual int globalMaxSeedingMinutes() const = 0;
|
virtual int globalMaxSeedingMinutes() const = 0;
|
||||||
virtual void setGlobalMaxSeedingMinutes(int minutes) = 0;
|
virtual void setGlobalMaxSeedingMinutes(int minutes) = 0;
|
||||||
|
virtual int globalMaxInactiveSeedingMinutes() const = 0;
|
||||||
|
virtual void setGlobalMaxInactiveSeedingMinutes(int minutes) = 0;
|
||||||
virtual bool isDHTEnabled() const = 0;
|
virtual bool isDHTEnabled() const = 0;
|
||||||
virtual void setDHTEnabled(bool enabled) = 0;
|
virtual void setDHTEnabled(bool enabled) = 0;
|
||||||
virtual bool isLSDEnabled() const = 0;
|
virtual bool isLSDEnabled() const = 0;
|
||||||
virtual void setLSDEnabled(bool enabled) = 0;
|
virtual void setLSDEnabled(bool enabled) = 0;
|
||||||
virtual bool isPeXEnabled() const = 0;
|
virtual bool isPeXEnabled() const = 0;
|
||||||
virtual void setPeXEnabled(bool enabled) = 0;
|
virtual void setPeXEnabled(bool enabled) = 0;
|
||||||
|
virtual bool isAddTorrentToQueueTop() const = 0;
|
||||||
|
virtual void setAddTorrentToQueueTop(bool value) = 0;
|
||||||
virtual bool isAddTorrentPaused() const = 0;
|
virtual bool isAddTorrentPaused() const = 0;
|
||||||
virtual void setAddTorrentPaused(bool value) = 0;
|
virtual void setAddTorrentPaused(bool value) = 0;
|
||||||
virtual Torrent::StopCondition torrentStopCondition() const = 0;
|
virtual Torrent::StopCondition torrentStopCondition() const = 0;
|
||||||
@@ -258,10 +265,24 @@ namespace BitTorrent
|
|||||||
virtual void setEncryption(int state) = 0;
|
virtual void setEncryption(int state) = 0;
|
||||||
virtual int maxActiveCheckingTorrents() const = 0;
|
virtual int maxActiveCheckingTorrents() const = 0;
|
||||||
virtual void setMaxActiveCheckingTorrents(int val) = 0;
|
virtual void setMaxActiveCheckingTorrents(int val) = 0;
|
||||||
|
virtual bool isI2PEnabled() const = 0;
|
||||||
|
virtual void setI2PEnabled(bool enabled) = 0;
|
||||||
|
virtual QString I2PAddress() const = 0;
|
||||||
|
virtual void setI2PAddress(const QString &address) = 0;
|
||||||
|
virtual int I2PPort() const = 0;
|
||||||
|
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 bool isProxyPeerConnectionsEnabled() const = 0;
|
||||||
virtual void setProxyPeerConnectionsEnabled(bool enabled) = 0;
|
virtual void setProxyPeerConnectionsEnabled(bool enabled) = 0;
|
||||||
virtual bool isProxyHostnameLookupEnabled() const = 0;
|
|
||||||
virtual void setProxyHostnameLookupEnabled(bool enabled) = 0;
|
|
||||||
virtual ChokingAlgorithm chokingAlgorithm() const = 0;
|
virtual ChokingAlgorithm chokingAlgorithm() const = 0;
|
||||||
virtual void setChokingAlgorithm(ChokingAlgorithm mode) = 0;
|
virtual void setChokingAlgorithm(ChokingAlgorithm mode) = 0;
|
||||||
virtual SeedChokingAlgorithm seedChokingAlgorithm() const = 0;
|
virtual SeedChokingAlgorithm seedChokingAlgorithm() const = 0;
|
||||||
@@ -320,6 +341,10 @@ namespace BitTorrent
|
|||||||
virtual void setSendBufferWatermarkFactor(int value) = 0;
|
virtual void setSendBufferWatermarkFactor(int value) = 0;
|
||||||
virtual int connectionSpeed() const = 0;
|
virtual int connectionSpeed() const = 0;
|
||||||
virtual void setConnectionSpeed(int value) = 0;
|
virtual void setConnectionSpeed(int value) = 0;
|
||||||
|
virtual int socketSendBufferSize() const = 0;
|
||||||
|
virtual void setSocketSendBufferSize(int value) = 0;
|
||||||
|
virtual int socketReceiveBufferSize() const = 0;
|
||||||
|
virtual void setSocketReceiveBufferSize(int value) = 0;
|
||||||
virtual int socketBacklogSize() const = 0;
|
virtual int socketBacklogSize() const = 0;
|
||||||
virtual void setSocketBacklogSize(int value) = 0;
|
virtual void setSocketBacklogSize(int value) = 0;
|
||||||
virtual bool isAnonymousModeEnabled() const = 0;
|
virtual bool isAnonymousModeEnabled() const = 0;
|
||||||
@@ -388,7 +413,7 @@ namespace BitTorrent
|
|||||||
virtual bool isTrackerFilteringEnabled() const = 0;
|
virtual bool isTrackerFilteringEnabled() const = 0;
|
||||||
virtual void setTrackerFilteringEnabled(bool enabled) = 0;
|
virtual void setTrackerFilteringEnabled(bool enabled) = 0;
|
||||||
virtual bool isExcludedFileNamesEnabled() const = 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 QStringList excludedFileNames() const = 0;
|
||||||
virtual void setExcludedFileNames(const QStringList &newList) = 0;
|
virtual void setExcludedFileNames(const QStringList &newList) = 0;
|
||||||
virtual bool isFilenameExcluded(const QString &fileName) const = 0;
|
virtual bool isFilenameExcluded(const QString &fileName) const = 0;
|
||||||
@@ -396,6 +421,8 @@ namespace BitTorrent
|
|||||||
virtual void setBannedIPs(const QStringList &newList) = 0;
|
virtual void setBannedIPs(const QStringList &newList) = 0;
|
||||||
virtual ResumeDataStorageType resumeDataStorageType() const = 0;
|
virtual ResumeDataStorageType resumeDataStorageType() const = 0;
|
||||||
virtual void setResumeDataStorageType(ResumeDataStorageType type) = 0;
|
virtual void setResumeDataStorageType(ResumeDataStorageType type) = 0;
|
||||||
|
virtual bool isMergeTrackersEnabled() const = 0;
|
||||||
|
virtual void setMergeTrackersEnabled(bool enabled) = 0;
|
||||||
|
|
||||||
virtual bool isRestored() const = 0;
|
virtual bool isRestored() const = 0;
|
||||||
|
|
||||||
@@ -431,6 +458,7 @@ namespace BitTorrent
|
|||||||
void allTorrentsFinished();
|
void allTorrentsFinished();
|
||||||
void categoryAdded(const QString &categoryName);
|
void categoryAdded(const QString &categoryName);
|
||||||
void categoryRemoved(const QString &categoryName);
|
void categoryRemoved(const QString &categoryName);
|
||||||
|
void categoryOptionsChanged(const QString &categoryName);
|
||||||
void downloadFromUrlFailed(const QString &url, const QString &reason);
|
void downloadFromUrlFailed(const QString &url, const QString &reason);
|
||||||
void downloadFromUrlFinished(const QString &url);
|
void downloadFromUrlFinished(const QString &url);
|
||||||
void fullDiskError(Torrent *torrent, const QString &msg);
|
void fullDiskError(Torrent *torrent, const QString &msg);
|
||||||
@@ -465,6 +493,6 @@ namespace BitTorrent
|
|||||||
void trackersRemoved(Torrent *torrent, const QStringList &trackers);
|
void trackersRemoved(Torrent *torrent, const QStringList &trackers);
|
||||||
void trackerSuccess(Torrent *torrent, const QString &tracker);
|
void trackerSuccess(Torrent *torrent, const QString &tracker);
|
||||||
void trackerWarning(Torrent *torrent, const QString &tracker);
|
void trackerWarning(Torrent *torrent, const QString &tracker);
|
||||||
void trackerEntriesUpdated(const QHash<Torrent *, QSet<QString>> &updateInfos);
|
void trackerEntriesUpdated(Torrent *torrent, const QHash<QString, TrackerEntry> &updatedTrackerEntries);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* 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>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -29,17 +29,20 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <libtorrent/fwd.hpp>
|
#include <libtorrent/fwd.hpp>
|
||||||
|
#include <libtorrent/portmap.hpp>
|
||||||
#include <libtorrent/torrent_handle.hpp>
|
#include <libtorrent/torrent_handle.hpp>
|
||||||
|
|
||||||
|
#include <QtContainerFwd>
|
||||||
|
#include <QDateTime>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QtContainerFwd>
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
@@ -60,12 +63,14 @@ class QNetworkConfigurationManager;
|
|||||||
#endif
|
#endif
|
||||||
class QString;
|
class QString;
|
||||||
class QThread;
|
class QThread;
|
||||||
|
class QThreadPool;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
class BandwidthScheduler;
|
class BandwidthScheduler;
|
||||||
class FileSearcher;
|
class FileSearcher;
|
||||||
class FilterParserThread;
|
class FilterParserThread;
|
||||||
|
class NativeSessionExtension;
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
@@ -83,6 +88,7 @@ namespace BitTorrent
|
|||||||
struct LoadTorrentParams;
|
struct LoadTorrentParams;
|
||||||
|
|
||||||
enum class MoveStorageMode;
|
enum class MoveStorageMode;
|
||||||
|
enum class MoveStorageContext;
|
||||||
|
|
||||||
struct SessionMetricIndices
|
struct SessionMetricIndices
|
||||||
{
|
{
|
||||||
@@ -155,6 +161,9 @@ namespace BitTorrent
|
|||||||
bool useCategoryPathsInManualMode() const override;
|
bool useCategoryPathsInManualMode() const override;
|
||||||
void setUseCategoryPathsInManualMode(bool value) 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;
|
QSet<QString> tags() const override;
|
||||||
bool hasTag(const QString &tag) const override;
|
bool hasTag(const QString &tag) const override;
|
||||||
bool addTag(const QString &tag) override;
|
bool addTag(const QString &tag) override;
|
||||||
@@ -173,12 +182,16 @@ namespace BitTorrent
|
|||||||
void setGlobalMaxRatio(qreal ratio) override;
|
void setGlobalMaxRatio(qreal ratio) override;
|
||||||
int globalMaxSeedingMinutes() const override;
|
int globalMaxSeedingMinutes() const override;
|
||||||
void setGlobalMaxSeedingMinutes(int minutes) override;
|
void setGlobalMaxSeedingMinutes(int minutes) override;
|
||||||
|
int globalMaxInactiveSeedingMinutes() const override;
|
||||||
|
void setGlobalMaxInactiveSeedingMinutes(int minutes) override;
|
||||||
bool isDHTEnabled() const override;
|
bool isDHTEnabled() const override;
|
||||||
void setDHTEnabled(bool enabled) override;
|
void setDHTEnabled(bool enabled) override;
|
||||||
bool isLSDEnabled() const override;
|
bool isLSDEnabled() const override;
|
||||||
void setLSDEnabled(bool enabled) override;
|
void setLSDEnabled(bool enabled) override;
|
||||||
bool isPeXEnabled() const override;
|
bool isPeXEnabled() const override;
|
||||||
void setPeXEnabled(bool enabled) override;
|
void setPeXEnabled(bool enabled) override;
|
||||||
|
bool isAddTorrentToQueueTop() const override;
|
||||||
|
void setAddTorrentToQueueTop(bool value) override;
|
||||||
bool isAddTorrentPaused() const override;
|
bool isAddTorrentPaused() const override;
|
||||||
void setAddTorrentPaused(bool value) override;
|
void setAddTorrentPaused(bool value) override;
|
||||||
Torrent::StopCondition torrentStopCondition() const override;
|
Torrent::StopCondition torrentStopCondition() const override;
|
||||||
@@ -231,10 +244,24 @@ namespace BitTorrent
|
|||||||
void setEncryption(int state) override;
|
void setEncryption(int state) override;
|
||||||
int maxActiveCheckingTorrents() const override;
|
int maxActiveCheckingTorrents() const override;
|
||||||
void setMaxActiveCheckingTorrents(int val) override;
|
void setMaxActiveCheckingTorrents(int val) override;
|
||||||
|
bool isI2PEnabled() const override;
|
||||||
|
void setI2PEnabled(bool enabled) override;
|
||||||
|
QString I2PAddress() const override;
|
||||||
|
void setI2PAddress(const QString &address) override;
|
||||||
|
int I2PPort() const override;
|
||||||
|
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;
|
bool isProxyPeerConnectionsEnabled() const override;
|
||||||
void setProxyPeerConnectionsEnabled(bool enabled) override;
|
void setProxyPeerConnectionsEnabled(bool enabled) override;
|
||||||
bool isProxyHostnameLookupEnabled() const override;
|
|
||||||
void setProxyHostnameLookupEnabled(bool enabled) override;
|
|
||||||
ChokingAlgorithm chokingAlgorithm() const override;
|
ChokingAlgorithm chokingAlgorithm() const override;
|
||||||
void setChokingAlgorithm(ChokingAlgorithm mode) override;
|
void setChokingAlgorithm(ChokingAlgorithm mode) override;
|
||||||
SeedChokingAlgorithm seedChokingAlgorithm() const override;
|
SeedChokingAlgorithm seedChokingAlgorithm() const override;
|
||||||
@@ -293,6 +320,10 @@ namespace BitTorrent
|
|||||||
void setSendBufferWatermarkFactor(int value) override;
|
void setSendBufferWatermarkFactor(int value) override;
|
||||||
int connectionSpeed() const override;
|
int connectionSpeed() const override;
|
||||||
void setConnectionSpeed(int value) override;
|
void setConnectionSpeed(int value) override;
|
||||||
|
int socketSendBufferSize() const override;
|
||||||
|
void setSocketSendBufferSize(int value) override;
|
||||||
|
int socketReceiveBufferSize() const override;
|
||||||
|
void setSocketReceiveBufferSize(int value) override;
|
||||||
int socketBacklogSize() const override;
|
int socketBacklogSize() const override;
|
||||||
void setSocketBacklogSize(int value) override;
|
void setSocketBacklogSize(int value) override;
|
||||||
bool isAnonymousModeEnabled() const override;
|
bool isAnonymousModeEnabled() const override;
|
||||||
@@ -361,14 +392,16 @@ namespace BitTorrent
|
|||||||
bool isTrackerFilteringEnabled() const override;
|
bool isTrackerFilteringEnabled() const override;
|
||||||
void setTrackerFilteringEnabled(bool enabled) override;
|
void setTrackerFilteringEnabled(bool enabled) override;
|
||||||
bool isExcludedFileNamesEnabled() const override;
|
bool isExcludedFileNamesEnabled() const override;
|
||||||
void setExcludedFileNamesEnabled(const bool enabled) override;
|
void setExcludedFileNamesEnabled(bool enabled) override;
|
||||||
QStringList excludedFileNames() const override;
|
QStringList excludedFileNames() const override;
|
||||||
void setExcludedFileNames(const QStringList &newList) override;
|
void setExcludedFileNames(const QStringList &excludedFileNames) override;
|
||||||
bool isFilenameExcluded(const QString &fileName) const override;
|
bool isFilenameExcluded(const QString &fileName) const override;
|
||||||
QStringList bannedIPs() const override;
|
QStringList bannedIPs() const override;
|
||||||
void setBannedIPs(const QStringList &newList) override;
|
void setBannedIPs(const QStringList &newList) override;
|
||||||
ResumeDataStorageType resumeDataStorageType() const override;
|
ResumeDataStorageType resumeDataStorageType() const override;
|
||||||
void setResumeDataStorageType(ResumeDataStorageType type) override;
|
void setResumeDataStorageType(ResumeDataStorageType type) override;
|
||||||
|
bool isMergeTrackersEnabled() const override;
|
||||||
|
void setMergeTrackersEnabled(bool enabled) override;
|
||||||
|
|
||||||
bool isRestored() const override;
|
bool isRestored() const override;
|
||||||
|
|
||||||
@@ -403,32 +436,45 @@ namespace BitTorrent
|
|||||||
void handleTorrentNeedSaveResumeData(const TorrentImpl *torrent);
|
void handleTorrentNeedSaveResumeData(const TorrentImpl *torrent);
|
||||||
void handleTorrentSaveResumeDataRequested(const TorrentImpl *torrent);
|
void handleTorrentSaveResumeDataRequested(const TorrentImpl *torrent);
|
||||||
void handleTorrentSaveResumeDataFailed(const TorrentImpl *torrent);
|
void handleTorrentSaveResumeDataFailed(const TorrentImpl *torrent);
|
||||||
void handleTorrentShareLimitChanged(TorrentImpl *const torrent);
|
void handleTorrentShareLimitChanged(TorrentImpl *torrent);
|
||||||
void handleTorrentNameChanged(TorrentImpl *const torrent);
|
void handleTorrentNameChanged(TorrentImpl *torrent);
|
||||||
void handleTorrentSavePathChanged(TorrentImpl *const torrent);
|
void handleTorrentSavePathChanged(TorrentImpl *torrent);
|
||||||
void handleTorrentCategoryChanged(TorrentImpl *const torrent, const QString &oldCategory);
|
void handleTorrentCategoryChanged(TorrentImpl *torrent, const QString &oldCategory);
|
||||||
void handleTorrentTagAdded(TorrentImpl *const torrent, const QString &tag);
|
void handleTorrentTagAdded(TorrentImpl *torrent, const QString &tag);
|
||||||
void handleTorrentTagRemoved(TorrentImpl *const torrent, const QString &tag);
|
void handleTorrentTagRemoved(TorrentImpl *torrent, const QString &tag);
|
||||||
void handleTorrentSavingModeChanged(TorrentImpl *const torrent);
|
void handleTorrentSavingModeChanged(TorrentImpl *torrent);
|
||||||
void handleTorrentMetadataReceived(TorrentImpl *const torrent);
|
void handleTorrentMetadataReceived(TorrentImpl *torrent);
|
||||||
void handleTorrentPaused(TorrentImpl *const torrent);
|
void handleTorrentPaused(TorrentImpl *torrent);
|
||||||
void handleTorrentResumed(TorrentImpl *const torrent);
|
void handleTorrentResumed(TorrentImpl *torrent);
|
||||||
void handleTorrentChecked(TorrentImpl *const torrent);
|
void handleTorrentChecked(TorrentImpl *torrent);
|
||||||
void handleTorrentFinished(TorrentImpl *const torrent);
|
void handleTorrentFinished(TorrentImpl *torrent);
|
||||||
void handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector<TrackerEntry> &newTrackers);
|
void handleTorrentTrackersAdded(TorrentImpl *torrent, const QVector<TrackerEntry> &newTrackers);
|
||||||
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers);
|
void handleTorrentTrackersRemoved(TorrentImpl *torrent, const QStringList &deletedTrackers);
|
||||||
void handleTorrentTrackersChanged(TorrentImpl *const torrent);
|
void handleTorrentTrackersChanged(TorrentImpl *torrent);
|
||||||
void handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
|
void handleTorrentUrlSeedsAdded(TorrentImpl *torrent, const QVector<QUrl> &newUrlSeeds);
|
||||||
void handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector<QUrl> &urlSeeds);
|
void handleTorrentUrlSeedsRemoved(TorrentImpl *torrent, const QVector<QUrl> &urlSeeds);
|
||||||
void handleTorrentResumeDataReady(TorrentImpl *const torrent, const LoadTorrentParams &data);
|
void handleTorrentResumeDataReady(TorrentImpl *torrent, const LoadTorrentParams &data);
|
||||||
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
||||||
void handleTorrentStorageMovingStateChanged(TorrentImpl *torrent);
|
void handleTorrentStorageMovingStateChanged(TorrentImpl *torrent);
|
||||||
|
|
||||||
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
|
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||||
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||||
|
|
||||||
|
void enablePortMapping();
|
||||||
|
void disablePortMapping();
|
||||||
|
void addMappedPorts(const QSet<quint16> &ports);
|
||||||
|
void removeMappedPorts(const QSet<quint16> &ports);
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void invoke(Func &&func)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, std::forward<Func>(func), Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invokeAsync(std::function<void ()> func);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void configureDeferred();
|
void configureDeferred();
|
||||||
void readAlerts();
|
void readAlerts();
|
||||||
@@ -453,14 +499,15 @@ namespace BitTorrent
|
|||||||
{
|
{
|
||||||
lt::torrent_handle torrentHandle;
|
lt::torrent_handle torrentHandle;
|
||||||
Path path;
|
Path path;
|
||||||
MoveStorageMode mode;
|
MoveStorageMode mode {};
|
||||||
|
MoveStorageContext context {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RemovingTorrentData
|
struct RemovingTorrentData
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
Path pathToRemove;
|
Path pathToRemove;
|
||||||
DeleteOption deleteOption;
|
DeleteOption deleteOption {};
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit SessionImpl(QObject *parent = nullptr);
|
explicit SessionImpl(QObject *parent = nullptr);
|
||||||
@@ -468,6 +515,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
bool hasPerTorrentRatioLimit() const;
|
bool hasPerTorrentRatioLimit() const;
|
||||||
bool hasPerTorrentSeedingTimeLimit() const;
|
bool hasPerTorrentSeedingTimeLimit() const;
|
||||||
|
bool hasPerTorrentInactiveSeedingTimeLimit() const;
|
||||||
|
|
||||||
// Session configuration
|
// Session configuration
|
||||||
Q_INVOKABLE void configure();
|
Q_INVOKABLE void configure();
|
||||||
@@ -516,11 +564,13 @@ namespace BitTorrent
|
|||||||
void handleListenSucceededAlert(const lt::listen_succeeded_alert *p);
|
void handleListenSucceededAlert(const lt::listen_succeeded_alert *p);
|
||||||
void handleListenFailedAlert(const lt::listen_failed_alert *p);
|
void handleListenFailedAlert(const lt::listen_failed_alert *p);
|
||||||
void handleExternalIPAlert(const lt::external_ip_alert *p);
|
void handleExternalIPAlert(const lt::external_ip_alert *p);
|
||||||
|
void handleSessionErrorAlert(const lt::session_error_alert *p) const;
|
||||||
void handleSessionStatsAlert(const lt::session_stats_alert *p);
|
void handleSessionStatsAlert(const lt::session_stats_alert *p);
|
||||||
void handleAlertsDroppedAlert(const lt::alerts_dropped_alert *p) const;
|
void handleAlertsDroppedAlert(const lt::alerts_dropped_alert *p) const;
|
||||||
void handleStorageMovedAlert(const lt::storage_moved_alert *p);
|
void handleStorageMovedAlert(const lt::storage_moved_alert *p);
|
||||||
void handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p);
|
void handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p);
|
||||||
void handleSocks5Alert(const lt::socks5_alert *p) const;
|
void handleSocks5Alert(const lt::socks5_alert *p) const;
|
||||||
|
void handleI2PAlert(const lt::i2p_alert *p) const;
|
||||||
void handleTrackerAlert(const lt::tracker_alert *a);
|
void handleTrackerAlert(const lt::tracker_alert *a);
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
void handleTorrentConflictAlert(const lt::torrent_conflict_alert *a);
|
void handleTorrentConflictAlert(const lt::torrent_conflict_alert *a);
|
||||||
@@ -529,8 +579,8 @@ namespace BitTorrent
|
|||||||
TorrentImpl *createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
TorrentImpl *createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
||||||
|
|
||||||
void saveResumeData();
|
void saveResumeData();
|
||||||
void saveTorrentsQueue() const;
|
void saveTorrentsQueue();
|
||||||
void removeTorrentsQueue() const;
|
void removeTorrentsQueue();
|
||||||
|
|
||||||
std::vector<lt::alert *> getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const;
|
std::vector<lt::alert *> getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const;
|
||||||
|
|
||||||
@@ -546,6 +596,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
// BitTorrent
|
// BitTorrent
|
||||||
lt::session *m_nativeSession = nullptr;
|
lt::session *m_nativeSession = nullptr;
|
||||||
|
NativeSessionExtension *m_nativeSessionExtension = nullptr;
|
||||||
|
|
||||||
bool m_deferredConfigureScheduled = false;
|
bool m_deferredConfigureScheduled = false;
|
||||||
bool m_IPFilteringConfigured = false;
|
bool m_IPFilteringConfigured = false;
|
||||||
@@ -576,6 +627,8 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<int> m_sendBufferLowWatermark;
|
CachedSettingValue<int> m_sendBufferLowWatermark;
|
||||||
CachedSettingValue<int> m_sendBufferWatermarkFactor;
|
CachedSettingValue<int> m_sendBufferWatermarkFactor;
|
||||||
CachedSettingValue<int> m_connectionSpeed;
|
CachedSettingValue<int> m_connectionSpeed;
|
||||||
|
CachedSettingValue<int> m_socketSendBufferSize;
|
||||||
|
CachedSettingValue<int> m_socketReceiveBufferSize;
|
||||||
CachedSettingValue<int> m_socketBacklogSize;
|
CachedSettingValue<int> m_socketBacklogSize;
|
||||||
CachedSettingValue<bool> m_isAnonymousModeEnabled;
|
CachedSettingValue<bool> m_isAnonymousModeEnabled;
|
||||||
CachedSettingValue<bool> m_isQueueingEnabled;
|
CachedSettingValue<bool> m_isQueueingEnabled;
|
||||||
@@ -612,6 +665,8 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<QString> m_additionalTrackers;
|
CachedSettingValue<QString> m_additionalTrackers;
|
||||||
CachedSettingValue<qreal> m_globalMaxRatio;
|
CachedSettingValue<qreal> m_globalMaxRatio;
|
||||||
CachedSettingValue<int> m_globalMaxSeedingMinutes;
|
CachedSettingValue<int> m_globalMaxSeedingMinutes;
|
||||||
|
CachedSettingValue<int> m_globalMaxInactiveSeedingMinutes;
|
||||||
|
CachedSettingValue<bool> m_isAddTorrentToQueueTop;
|
||||||
CachedSettingValue<bool> m_isAddTorrentPaused;
|
CachedSettingValue<bool> m_isAddTorrentPaused;
|
||||||
CachedSettingValue<Torrent::StopCondition> m_torrentStopCondition;
|
CachedSettingValue<Torrent::StopCondition> m_torrentStopCondition;
|
||||||
CachedSettingValue<TorrentContentLayout> m_torrentContentLayout;
|
CachedSettingValue<TorrentContentLayout> m_torrentContentLayout;
|
||||||
@@ -635,7 +690,6 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<int> m_encryption;
|
CachedSettingValue<int> m_encryption;
|
||||||
CachedSettingValue<int> m_maxActiveCheckingTorrents;
|
CachedSettingValue<int> m_maxActiveCheckingTorrents;
|
||||||
CachedSettingValue<bool> m_isProxyPeerConnectionsEnabled;
|
CachedSettingValue<bool> m_isProxyPeerConnectionsEnabled;
|
||||||
CachedSettingValue<bool> m_isProxyHostnameLookupEnabled;
|
|
||||||
CachedSettingValue<ChokingAlgorithm> m_chokingAlgorithm;
|
CachedSettingValue<ChokingAlgorithm> m_chokingAlgorithm;
|
||||||
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
||||||
CachedSettingValue<QStringList> m_storedTags;
|
CachedSettingValue<QStringList> m_storedTags;
|
||||||
@@ -658,6 +712,15 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<QStringList> m_excludedFileNames;
|
CachedSettingValue<QStringList> m_excludedFileNames;
|
||||||
CachedSettingValue<QStringList> m_bannedIPs;
|
CachedSettingValue<QStringList> m_bannedIPs;
|
||||||
CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType;
|
CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType;
|
||||||
|
CachedSettingValue<bool> m_isMergeTrackersEnabled;
|
||||||
|
CachedSettingValue<bool> m_isI2PEnabled;
|
||||||
|
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;
|
bool m_isRestored = false;
|
||||||
|
|
||||||
@@ -676,6 +739,8 @@ namespace BitTorrent
|
|||||||
qint64 m_previouslyUploaded = 0;
|
qint64 m_previouslyUploaded = 0;
|
||||||
qint64 m_previouslyDownloaded = 0;
|
qint64 m_previouslyDownloaded = 0;
|
||||||
|
|
||||||
|
bool m_torrentsQueueChanged = false;
|
||||||
|
bool m_needSaveTorrentsQueue = false;
|
||||||
bool m_refreshEnqueued = false;
|
bool m_refreshEnqueued = false;
|
||||||
QTimer *m_seedingLimitTimer = nullptr;
|
QTimer *m_seedingLimitTimer = nullptr;
|
||||||
QTimer *m_resumeDataTimer = nullptr;
|
QTimer *m_resumeDataTimer = nullptr;
|
||||||
@@ -686,10 +751,11 @@ namespace BitTorrent
|
|||||||
QPointer<Tracker> m_tracker;
|
QPointer<Tracker> m_tracker;
|
||||||
|
|
||||||
Utils::Thread::UniquePtr m_ioThread;
|
Utils::Thread::UniquePtr m_ioThread;
|
||||||
|
QThreadPool *m_asyncWorker = nullptr;
|
||||||
ResumeDataStorage *m_resumeDataStorage = nullptr;
|
ResumeDataStorage *m_resumeDataStorage = nullptr;
|
||||||
FileSearcher *m_fileSearcher = nullptr;
|
FileSearcher *m_fileSearcher = nullptr;
|
||||||
|
|
||||||
QSet<TorrentID> m_downloadedMetadata;
|
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
|
||||||
|
|
||||||
QHash<TorrentID, TorrentImpl *> m_torrents;
|
QHash<TorrentID, TorrentImpl *> m_torrents;
|
||||||
QHash<TorrentID, TorrentImpl *> m_hybridTorrentsByAltID;
|
QHash<TorrentID, TorrentImpl *> m_hybridTorrentsByAltID;
|
||||||
@@ -701,7 +767,9 @@ namespace BitTorrent
|
|||||||
QMap<QString, CategoryOptions> m_categories;
|
QMap<QString, CategoryOptions> m_categories;
|
||||||
QSet<QString> m_tags;
|
QSet<QString> m_tags;
|
||||||
|
|
||||||
QHash<Torrent *, QSet<QString>> m_updatedTrackerEntries;
|
// This field holds amounts of peers reported by trackers in their responses to announces
|
||||||
|
// (torrent.tracker_name.tracker_local_endpoint.num_peers)
|
||||||
|
QHash<lt::torrent_handle, QHash<std::string, QMap<TrackerEntry::Endpoint, int>>> m_updatedTrackerEntries;
|
||||||
|
|
||||||
// I/O errored torrents
|
// I/O errored torrents
|
||||||
QSet<TorrentID> m_recentErroredTorrents;
|
QSet<TorrentID> m_recentErroredTorrents;
|
||||||
@@ -722,6 +790,15 @@ namespace BitTorrent
|
|||||||
|
|
||||||
bool m_needUpgradeDownloadPath = false;
|
bool m_needUpgradeDownloadPath = false;
|
||||||
|
|
||||||
|
// All port mapping related routines are invoked from working thread
|
||||||
|
// so there are no synchronization used. If multithreaded access is
|
||||||
|
// ever required, synchronization should also be provided.
|
||||||
|
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::initInstance();
|
||||||
friend void Session::freeInstance();
|
friend void Session::freeInstance();
|
||||||
friend Session *Session::instance();
|
friend Session *Session::instance();
|
||||||
|
|||||||
@@ -52,8 +52,12 @@ namespace BitTorrent
|
|||||||
const int Torrent::USE_GLOBAL_SEEDING_TIME = -2;
|
const int Torrent::USE_GLOBAL_SEEDING_TIME = -2;
|
||||||
const int Torrent::NO_SEEDING_TIME_LIMIT = -1;
|
const int Torrent::NO_SEEDING_TIME_LIMIT = -1;
|
||||||
|
|
||||||
|
const int Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME = -2;
|
||||||
|
const int Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT = -1;
|
||||||
|
|
||||||
const qreal Torrent::MAX_RATIO = 9999;
|
const qreal Torrent::MAX_RATIO = 9999;
|
||||||
const int Torrent::MAX_SEEDING_TIME = 525600;
|
const int Torrent::MAX_SEEDING_TIME = 525600;
|
||||||
|
const int Torrent::MAX_INACTIVE_SEEDING_TIME = 525600;
|
||||||
|
|
||||||
TorrentID Torrent::id() const
|
TorrentID Torrent::id() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
#include "base/3rdparty/expected.hpp"
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "abstractfilestorage.h"
|
#include "torrentcontenthandler.h"
|
||||||
|
|
||||||
class QBitArray;
|
class QBitArray;
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
@@ -106,9 +106,10 @@ namespace BitTorrent
|
|||||||
uint qHash(TorrentState key, uint seed = 0);
|
uint qHash(TorrentState key, uint seed = 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Torrent : public AbstractFileStorage
|
class Torrent : public TorrentContentHandler
|
||||||
{
|
{
|
||||||
Q_GADGET
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(Torrent)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class StopCondition
|
enum class StopCondition
|
||||||
@@ -125,10 +126,14 @@ namespace BitTorrent
|
|||||||
static const int USE_GLOBAL_SEEDING_TIME;
|
static const int USE_GLOBAL_SEEDING_TIME;
|
||||||
static const int NO_SEEDING_TIME_LIMIT;
|
static const int NO_SEEDING_TIME_LIMIT;
|
||||||
|
|
||||||
|
static const int USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||||
|
static const int NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||||
|
|
||||||
static const qreal MAX_RATIO;
|
static const qreal MAX_RATIO;
|
||||||
static const int MAX_SEEDING_TIME;
|
static const int MAX_SEEDING_TIME;
|
||||||
|
static const int MAX_INACTIVE_SEEDING_TIME;
|
||||||
|
|
||||||
virtual ~Torrent() = default;
|
using TorrentContentHandler::TorrentContentHandler;
|
||||||
|
|
||||||
virtual InfoHash infoHash() const = 0;
|
virtual InfoHash infoHash() const = 0;
|
||||||
virtual QString name() const = 0;
|
virtual QString name() const = 0;
|
||||||
@@ -191,7 +196,6 @@ namespace BitTorrent
|
|||||||
virtual void setSavePath(const Path &savePath) = 0;
|
virtual void setSavePath(const Path &savePath) = 0;
|
||||||
virtual Path downloadPath() const = 0;
|
virtual Path downloadPath() const = 0;
|
||||||
virtual void setDownloadPath(const Path &downloadPath) = 0;
|
virtual void setDownloadPath(const Path &downloadPath) = 0;
|
||||||
virtual Path actualStorageLocation() const = 0;
|
|
||||||
virtual Path rootPath() const = 0;
|
virtual Path rootPath() const = 0;
|
||||||
virtual Path contentPath() const = 0;
|
virtual Path contentPath() const = 0;
|
||||||
virtual QString category() const = 0;
|
virtual QString category() const = 0;
|
||||||
@@ -210,13 +214,12 @@ namespace BitTorrent
|
|||||||
virtual QDateTime addedTime() const = 0;
|
virtual QDateTime addedTime() const = 0;
|
||||||
virtual qreal ratioLimit() const = 0;
|
virtual qreal ratioLimit() const = 0;
|
||||||
virtual int seedingTimeLimit() const = 0;
|
virtual int seedingTimeLimit() const = 0;
|
||||||
|
virtual int inactiveSeedingTimeLimit() const = 0;
|
||||||
|
|
||||||
virtual Path actualFilePath(int index) const = 0;
|
|
||||||
virtual PathList filePaths() const = 0;
|
virtual PathList filePaths() const = 0;
|
||||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
|
||||||
|
|
||||||
virtual TorrentInfo info() const = 0;
|
virtual TorrentInfo info() const = 0;
|
||||||
virtual bool isSeed() const = 0;
|
virtual bool isFinished() const = 0;
|
||||||
virtual bool isPaused() const = 0;
|
virtual bool isPaused() const = 0;
|
||||||
virtual bool isQueued() const = 0;
|
virtual bool isQueued() const = 0;
|
||||||
virtual bool isForced() const = 0;
|
virtual bool isForced() const = 0;
|
||||||
@@ -231,7 +234,6 @@ namespace BitTorrent
|
|||||||
virtual bool isSequentialDownload() const = 0;
|
virtual bool isSequentialDownload() const = 0;
|
||||||
virtual bool hasFirstLastPiecePriority() const = 0;
|
virtual bool hasFirstLastPiecePriority() const = 0;
|
||||||
virtual TorrentState state() const = 0;
|
virtual TorrentState state() const = 0;
|
||||||
virtual bool hasMetadata() const = 0;
|
|
||||||
virtual bool hasMissingFiles() const = 0;
|
virtual bool hasMissingFiles() const = 0;
|
||||||
virtual bool hasError() const = 0;
|
virtual bool hasError() const = 0;
|
||||||
virtual int queuePosition() const = 0;
|
virtual int queuePosition() const = 0;
|
||||||
@@ -243,7 +245,6 @@ namespace BitTorrent
|
|||||||
virtual qlonglong activeTime() const = 0;
|
virtual qlonglong activeTime() const = 0;
|
||||||
virtual qlonglong finishedTime() const = 0;
|
virtual qlonglong finishedTime() const = 0;
|
||||||
virtual qlonglong eta() const = 0;
|
virtual qlonglong eta() const = 0;
|
||||||
virtual QVector<qreal> filesProgress() const = 0;
|
|
||||||
virtual int seedsCount() const = 0;
|
virtual int seedsCount() const = 0;
|
||||||
virtual int peersCount() const = 0;
|
virtual int peersCount() const = 0;
|
||||||
virtual int leechsCount() const = 0;
|
virtual int leechsCount() const = 0;
|
||||||
@@ -268,6 +269,7 @@ namespace BitTorrent
|
|||||||
virtual qreal distributedCopies() const = 0;
|
virtual qreal distributedCopies() const = 0;
|
||||||
virtual qreal maxRatio() const = 0;
|
virtual qreal maxRatio() const = 0;
|
||||||
virtual int maxSeedingTime() const = 0;
|
virtual int maxSeedingTime() const = 0;
|
||||||
|
virtual int maxInactiveSeedingTime() const = 0;
|
||||||
virtual qreal realRatio() const = 0;
|
virtual qreal realRatio() const = 0;
|
||||||
virtual int uploadPayloadRate() const = 0;
|
virtual int uploadPayloadRate() const = 0;
|
||||||
virtual int downloadPayloadRate() const = 0;
|
virtual int downloadPayloadRate() const = 0;
|
||||||
@@ -276,13 +278,6 @@ namespace BitTorrent
|
|||||||
virtual int connectionsCount() const = 0;
|
virtual int connectionsCount() const = 0;
|
||||||
virtual int connectionsLimit() const = 0;
|
virtual int connectionsLimit() const = 0;
|
||||||
virtual qlonglong nextAnnounce() 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 setName(const QString &name) = 0;
|
||||||
virtual void setSequentialDownload(bool enable) = 0;
|
virtual void setSequentialDownload(bool enable) = 0;
|
||||||
@@ -292,16 +287,15 @@ namespace BitTorrent
|
|||||||
virtual void forceReannounce(int index = -1) = 0;
|
virtual void forceReannounce(int index = -1) = 0;
|
||||||
virtual void forceDHTAnnounce() = 0;
|
virtual void forceDHTAnnounce() = 0;
|
||||||
virtual void forceRecheck() = 0;
|
virtual void forceRecheck() = 0;
|
||||||
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
|
|
||||||
virtual void setRatioLimit(qreal limit) = 0;
|
virtual void setRatioLimit(qreal limit) = 0;
|
||||||
virtual void setSeedingTimeLimit(int limit) = 0;
|
virtual void setSeedingTimeLimit(int limit) = 0;
|
||||||
|
virtual void setInactiveSeedingTimeLimit(int limit) = 0;
|
||||||
virtual void setUploadLimit(int limit) = 0;
|
virtual void setUploadLimit(int limit) = 0;
|
||||||
virtual void setDownloadLimit(int limit) = 0;
|
virtual void setDownloadLimit(int limit) = 0;
|
||||||
virtual void setSuperSeeding(bool enable) = 0;
|
virtual void setSuperSeeding(bool enable) = 0;
|
||||||
virtual void setDHTDisabled(bool disable) = 0;
|
virtual void setDHTDisabled(bool disable) = 0;
|
||||||
virtual void setPEXDisabled(bool disable) = 0;
|
virtual void setPEXDisabled(bool disable) = 0;
|
||||||
virtual void setLSDDisabled(bool disable) = 0;
|
virtual void setLSDDisabled(bool disable) = 0;
|
||||||
virtual void flushCache() const = 0;
|
|
||||||
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
|
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
|
||||||
virtual void removeTrackers(const QStringList &trackers) = 0;
|
virtual void removeTrackers(const QStringList &trackers) = 0;
|
||||||
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
|
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
|
||||||
@@ -309,7 +303,7 @@ namespace BitTorrent
|
|||||||
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
||||||
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
||||||
virtual void clearPeers() = 0;
|
virtual void clearPeers() = 0;
|
||||||
virtual bool setMetadata(const TorrentInfo &torrentInfo) = 0;
|
virtual void setMetadata(const TorrentInfo &torrentInfo) = 0;
|
||||||
|
|
||||||
virtual StopCondition stopCondition() const = 0;
|
virtual StopCondition stopCondition() const = 0;
|
||||||
virtual void setStopCondition(StopCondition stopCondition) = 0;
|
virtual void setStopCondition(StopCondition stopCondition) = 0;
|
||||||
@@ -318,6 +312,11 @@ namespace BitTorrent
|
|||||||
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
||||||
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
||||||
|
|
||||||
|
virtual void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const = 0;
|
||||||
|
|
||||||
TorrentID id() const;
|
TorrentID id() const;
|
||||||
bool isResumed() const;
|
bool isResumed() const;
|
||||||
qlonglong remainingSize() const;
|
qlonglong remainingSize() const;
|
||||||
|
|||||||
29
src/base/bittorrent/torrentcontenthandler.cpp
Normal file
29
src/base/bittorrent/torrentcontenthandler.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "torrentcontenthandler.h"
|
||||||
61
src/base/bittorrent/torrentcontenthandler.h
Normal file
61
src/base/bittorrent/torrentcontenthandler.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2022-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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/pathfwd.h"
|
||||||
|
#include "abstractfilestorage.h"
|
||||||
|
#include "downloadpriority.h"
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class TorrentContentHandler : public QObject, public AbstractFileStorage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using QObject::QObject;
|
||||||
|
|
||||||
|
virtual bool hasMetadata() const = 0;
|
||||||
|
virtual Path actualStorageLocation() const = 0;
|
||||||
|
virtual Path actualFilePath(int fileIndex) const = 0;
|
||||||
|
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||||
|
virtual QVector<qreal> filesProgress() 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 fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const = 0;
|
||||||
|
|
||||||
|
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
|
||||||
|
virtual void flushCache() const = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -46,14 +46,14 @@ namespace BitTorrent
|
|||||||
|
|
||||||
struct TorrentCreatorParams
|
struct TorrentCreatorParams
|
||||||
{
|
{
|
||||||
bool isPrivate;
|
bool isPrivate = false;
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
TorrentFormat torrentFormat;
|
TorrentFormat torrentFormat = TorrentFormat::Hybrid;
|
||||||
#else
|
#else
|
||||||
bool isAlignmentOptimized;
|
bool isAlignmentOptimized;
|
||||||
int paddedFileSizeLimit;
|
int paddedFileSizeLimit;
|
||||||
#endif
|
#endif
|
||||||
int pieceSize;
|
int pieceSize = 0;
|
||||||
Path inputPath;
|
Path inputPath;
|
||||||
Path savePath;
|
Path savePath;
|
||||||
QString comment;
|
QString comment;
|
||||||
@@ -74,7 +74,7 @@ namespace BitTorrent
|
|||||||
void create(const TorrentCreatorParams ¶ms);
|
void create(const TorrentCreatorParams ¶ms);
|
||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#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
|
#else
|
||||||
static int calculateTotalPieces(const Path &inputPath
|
static int calculateTotalPieces(const Path &inputPath
|
||||||
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* 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>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -36,7 +36,6 @@
|
|||||||
#include <libtorrent/address.hpp>
|
#include <libtorrent/address.hpp>
|
||||||
#include <libtorrent/alert_types.hpp>
|
#include <libtorrent/alert_types.hpp>
|
||||||
#include <libtorrent/create_torrent.hpp>
|
#include <libtorrent/create_torrent.hpp>
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
#include <libtorrent/storage_defs.hpp>
|
#include <libtorrent/storage_defs.hpp>
|
||||||
#include <libtorrent/time.hpp>
|
#include <libtorrent/time.hpp>
|
||||||
@@ -47,11 +46,12 @@
|
|||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
@@ -82,10 +82,10 @@ namespace
|
|||||||
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
|
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
|
||||||
, const lt::info_hash_t &hashes, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
|
, const lt::info_hash_t &hashes, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
|
||||||
#else
|
#else
|
||||||
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
|
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
|
||||||
, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
|
, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url));
|
Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url));
|
||||||
@@ -98,7 +98,7 @@ namespace
|
|||||||
QString firstTrackerMessage;
|
QString firstTrackerMessage;
|
||||||
QString firstErrorMessage;
|
QString firstErrorMessage;
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#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 lt::announce_endpoint &endpoint : nativeEntry.endpoints)
|
||||||
{
|
{
|
||||||
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
|
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
|
||||||
@@ -236,7 +236,7 @@ namespace
|
|||||||
|
|
||||||
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||||
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
||||||
: QObject(session)
|
: Torrent(session)
|
||||||
, m_session(session)
|
, m_session(session)
|
||||||
, m_nativeSession(nativeSession)
|
, m_nativeSession(nativeSession)
|
||||||
, m_nativeHandle(nativeHandle)
|
, m_nativeHandle(nativeHandle)
|
||||||
@@ -252,9 +252,10 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
|||||||
, m_tags(params.tags)
|
, m_tags(params.tags)
|
||||||
, m_ratioLimit(params.ratioLimit)
|
, m_ratioLimit(params.ratioLimit)
|
||||||
, m_seedingTimeLimit(params.seedingTimeLimit)
|
, m_seedingTimeLimit(params.seedingTimeLimit)
|
||||||
|
, m_inactiveSeedingTimeLimit(params.inactiveSeedingTimeLimit)
|
||||||
, m_operatingMode(params.operatingMode)
|
, m_operatingMode(params.operatingMode)
|
||||||
, m_contentLayout(params.contentLayout)
|
, m_contentLayout(params.contentLayout)
|
||||||
, m_hasSeedStatus(params.hasSeedStatus)
|
, m_hasFinishedStatus(params.hasFinishedStatus)
|
||||||
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
|
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
|
||||||
, m_useAutoTMM(params.useAutoTMM)
|
, m_useAutoTMM(params.useAutoTMM)
|
||||||
, m_isStopped(params.stopped)
|
, m_isStopped(params.stopped)
|
||||||
@@ -279,6 +280,7 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
|||||||
, LT::toNative(m_ltAddTorrentParams.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
|
, LT::toNative(m_ltAddTorrentParams.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
|
||||||
|
|
||||||
m_completedFiles.fill(static_cast<bool>(m_ltAddTorrentParams.flags & lt::torrent_flags::seed_mode), filesCount);
|
m_completedFiles.fill(static_cast<bool>(m_ltAddTorrentParams.flags & lt::torrent_flags::seed_mode), filesCount);
|
||||||
|
m_filesProgress.resize(filesCount);
|
||||||
|
|
||||||
for (int i = 0; i < filesCount; ++i)
|
for (int i = 0; i < filesCount; ++i)
|
||||||
{
|
{
|
||||||
@@ -301,8 +303,14 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
|||||||
m_trackerEntries.reserve(static_cast<decltype(m_trackerEntries)::size_type>(extensionData->trackers.size()));
|
m_trackerEntries.reserve(static_cast<decltype(m_trackerEntries)::size_type>(extensionData->trackers.size()));
|
||||||
for (const lt::announce_entry &announceEntry : extensionData->trackers)
|
for (const lt::announce_entry &announceEntry : extensionData->trackers)
|
||||||
m_trackerEntries.append({QString::fromStdString(announceEntry.url), announceEntry.tier});
|
m_trackerEntries.append({QString::fromStdString(announceEntry.url), announceEntry.tier});
|
||||||
|
m_urlSeeds.reserve(static_cast<decltype(m_urlSeeds)::size_type>(extensionData->urlSeeds.size()));
|
||||||
|
for (const std::string &urlSeed : extensionData->urlSeeds)
|
||||||
|
m_urlSeeds.append(QString::fromStdString(urlSeed));
|
||||||
m_nativeStatus = extensionData->status;
|
m_nativeStatus = extensionData->status;
|
||||||
|
|
||||||
|
if (hasMetadata())
|
||||||
|
updateProgress();
|
||||||
|
|
||||||
updateState();
|
updateState();
|
||||||
|
|
||||||
if (hasMetadata())
|
if (hasMetadata())
|
||||||
@@ -324,14 +332,14 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
|||||||
|
|
||||||
// Remove .unwanted directory if empty
|
// Remove .unwanted directory if empty
|
||||||
const Path newPath = spath / newRelPath;
|
const Path newPath = spath / newRelPath;
|
||||||
qDebug() << "Attempting to remove \".unwanted\" folder at " << (newPath / Path(u".unwanted"_qs)).toString();
|
qDebug() << "Attempting to remove \".unwanted\" folder at " << (newPath / Path(u".unwanted"_s)).toString();
|
||||||
Utils::Fs::rmdir(newPath / Path(u".unwanted"_qs));
|
Utils::Fs::rmdir(newPath / Path(u".unwanted"_s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// == END UPGRADE CODE ==
|
// == END UPGRADE CODE ==
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentImpl::~TorrentImpl() {}
|
TorrentImpl::~TorrentImpl() = default;
|
||||||
|
|
||||||
bool TorrentImpl::isValid() const
|
bool TorrentImpl::isValid() const
|
||||||
{
|
{
|
||||||
@@ -424,13 +432,16 @@ void TorrentImpl::setSavePath(const Path &path)
|
|||||||
if (resolvedPath == savePath())
|
if (resolvedPath == savePath())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_savePath = resolvedPath;
|
if (isFinished() || m_hasFinishedStatus || downloadPath().isEmpty())
|
||||||
|
{
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
moveStorage(resolvedPath, MoveStorageContext::ChangeSavePath);
|
||||||
|
}
|
||||||
const bool isFinished = isSeed() || m_hasSeedStatus;
|
else
|
||||||
if (isFinished || downloadPath().isEmpty())
|
{
|
||||||
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
m_savePath = resolvedPath;
|
||||||
|
m_session->handleTorrentSavePathChanged(this);
|
||||||
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path TorrentImpl::downloadPath() const
|
Path TorrentImpl::downloadPath() const
|
||||||
@@ -448,13 +459,17 @@ void TorrentImpl::setDownloadPath(const Path &path)
|
|||||||
if (resolvedPath == m_downloadPath)
|
if (resolvedPath == m_downloadPath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_downloadPath = resolvedPath;
|
const bool isIncomplete = !(isFinished() || m_hasFinishedStatus);
|
||||||
|
if (isIncomplete)
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
{
|
||||||
|
moveStorage((resolvedPath.isEmpty() ? savePath() : resolvedPath), MoveStorageContext::ChangeDownloadPath);
|
||||||
const bool isFinished = isSeed() || m_hasSeedStatus;
|
}
|
||||||
if (!isFinished)
|
else
|
||||||
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
{
|
||||||
|
m_downloadPath = resolvedPath;
|
||||||
|
m_session->handleTorrentSavePathChanged(this);
|
||||||
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path TorrentImpl::rootPath() const
|
Path TorrentImpl::rootPath() const
|
||||||
@@ -533,9 +548,6 @@ Path TorrentImpl::wantedActualPath(int index, const Path &path) const
|
|||||||
|
|
||||||
QVector<TrackerEntry> TorrentImpl::trackers() const
|
QVector<TrackerEntry> TorrentImpl::trackers() const
|
||||||
{
|
{
|
||||||
if (!m_updatedTrackerEntries.isEmpty())
|
|
||||||
refreshTrackerEntries();
|
|
||||||
|
|
||||||
return m_trackerEntries;
|
return m_trackerEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,6 +600,9 @@ void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers)
|
|||||||
{
|
{
|
||||||
// TODO: use std::erase_if() in C++20
|
// TODO: use std::erase_if() in C++20
|
||||||
trackers.erase(std::remove_if(trackers.begin(), trackers.end(), [](const TrackerEntry &entry) { return entry.url.isEmpty(); }), trackers.end());
|
trackers.erase(std::remove_if(trackers.begin(), trackers.end(), [](const TrackerEntry &entry) { return entry.url.isEmpty(); }), trackers.end());
|
||||||
|
// Filter out duplicate trackers
|
||||||
|
const auto uniqueTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend());
|
||||||
|
trackers = QVector<TrackerEntry>(uniqueTrackers.cbegin(), uniqueTrackers.cend());
|
||||||
std::sort(trackers.begin(), trackers.end()
|
std::sort(trackers.begin(), trackers.end()
|
||||||
, [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; });
|
, [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; });
|
||||||
|
|
||||||
@@ -610,63 +625,95 @@ void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers)
|
|||||||
|
|
||||||
QVector<QUrl> TorrentImpl::urlSeeds() const
|
QVector<QUrl> TorrentImpl::urlSeeds() const
|
||||||
{
|
{
|
||||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
return m_urlSeeds;
|
||||||
|
|
||||||
QVector<QUrl> urlSeeds;
|
|
||||||
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
|
|
||||||
|
|
||||||
for (const std::string &urlSeed : currentSeeds)
|
|
||||||
urlSeeds.append(QString::fromStdString(urlSeed));
|
|
||||||
|
|
||||||
return urlSeeds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
m_session->invokeAsync([urlSeeds, session = m_session
|
||||||
|
, nativeHandle = m_nativeHandle
|
||||||
QVector<QUrl> addedUrlSeeds;
|
, thisTorrent = QPointer<TorrentImpl>(this)]
|
||||||
addedUrlSeeds.reserve(urlSeeds.size());
|
|
||||||
|
|
||||||
for (const QUrl &url : urlSeeds)
|
|
||||||
{
|
{
|
||||||
const std::string nativeUrl = url.toString().toStdString();
|
try
|
||||||
if (currentSeeds.find(nativeUrl) == currentSeeds.end())
|
|
||||||
{
|
{
|
||||||
m_nativeHandle.add_url_seed(nativeUrl);
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||||
addedUrlSeeds << url;
|
QVector<QUrl> currentSeeds;
|
||||||
}
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
||||||
}
|
for (const std::string &urlSeed : nativeSeeds)
|
||||||
|
currentSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
|
||||||
if (!addedUrlSeeds.isEmpty())
|
QVector<QUrl> addedUrlSeeds;
|
||||||
{
|
addedUrlSeeds.reserve(urlSeeds.size());
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
|
||||||
m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds);
|
for (const QUrl &url : urlSeeds)
|
||||||
}
|
{
|
||||||
|
if (!currentSeeds.contains(url))
|
||||||
|
{
|
||||||
|
nativeHandle.add_url_seed(url.toString().toStdString());
|
||||||
|
addedUrlSeeds.append(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSeeds.append(addedUrlSeeds);
|
||||||
|
session->invoke([session, thisTorrent, currentSeeds, addedUrlSeeds]
|
||||||
|
{
|
||||||
|
if (!thisTorrent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
thisTorrent->m_urlSeeds = currentSeeds;
|
||||||
|
if (!addedUrlSeeds.isEmpty())
|
||||||
|
{
|
||||||
|
session->handleTorrentNeedSaveResumeData(thisTorrent);
|
||||||
|
session->handleTorrentUrlSeedsAdded(thisTorrent, addedUrlSeeds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
|
void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
m_session->invokeAsync([urlSeeds, session = m_session
|
||||||
|
, nativeHandle = m_nativeHandle
|
||||||
QVector<QUrl> removedUrlSeeds;
|
, thisTorrent = QPointer<TorrentImpl>(this)]
|
||||||
removedUrlSeeds.reserve(urlSeeds.size());
|
|
||||||
|
|
||||||
for (const QUrl &url : urlSeeds)
|
|
||||||
{
|
{
|
||||||
const std::string nativeUrl = url.toString().toStdString();
|
try
|
||||||
if (currentSeeds.find(nativeUrl) != currentSeeds.end())
|
|
||||||
{
|
{
|
||||||
m_nativeHandle.remove_url_seed(nativeUrl);
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||||
removedUrlSeeds << url;
|
QVector<QUrl> currentSeeds;
|
||||||
}
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
||||||
}
|
for (const std::string &urlSeed : nativeSeeds)
|
||||||
|
currentSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
|
||||||
if (!removedUrlSeeds.isEmpty())
|
QVector<QUrl> removedUrlSeeds;
|
||||||
{
|
removedUrlSeeds.reserve(urlSeeds.size());
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
|
||||||
m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds);
|
for (const QUrl &url : urlSeeds)
|
||||||
}
|
{
|
||||||
|
if (currentSeeds.removeOne(url))
|
||||||
|
{
|
||||||
|
nativeHandle.remove_url_seed(url.toString().toStdString());
|
||||||
|
removedUrlSeeds.append(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->invoke([session, thisTorrent, currentSeeds, removedUrlSeeds]
|
||||||
|
{
|
||||||
|
if (!thisTorrent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
thisTorrent->m_urlSeeds = currentSeeds;
|
||||||
|
|
||||||
|
if (!removedUrlSeeds.isEmpty())
|
||||||
|
{
|
||||||
|
session->handleTorrentNeedSaveResumeData(thisTorrent);
|
||||||
|
session->handleTorrentUrlSeedsRemoved(thisTorrent, removedUrlSeeds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::clearPeers()
|
void TorrentImpl::clearPeers()
|
||||||
@@ -820,6 +867,11 @@ int TorrentImpl::seedingTimeLimit() const
|
|||||||
return m_seedingTimeLimit;
|
return m_seedingTimeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TorrentImpl::inactiveSeedingTimeLimit() const
|
||||||
|
{
|
||||||
|
return m_inactiveSeedingTimeLimit;
|
||||||
|
}
|
||||||
|
|
||||||
Path TorrentImpl::filePath(const int index) const
|
Path TorrentImpl::filePath(const int index) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(index >= 0);
|
Q_ASSERT(index >= 0);
|
||||||
@@ -973,7 +1025,7 @@ bool TorrentImpl::isErrored() const
|
|||||||
|| (m_state == TorrentState::Error));
|
|| (m_state == TorrentState::Error));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentImpl::isSeed() const
|
bool TorrentImpl::isFinished() const
|
||||||
{
|
{
|
||||||
return ((m_nativeStatus.state == lt::torrent_status::finished)
|
return ((m_nativeStatus.state == lt::torrent_status::finished)
|
||||||
|| (m_nativeStatus.state == lt::torrent_status::seeding));
|
|| (m_nativeStatus.state == lt::torrent_status::seeding));
|
||||||
@@ -1029,9 +1081,9 @@ void TorrentImpl::updateState()
|
|||||||
else if ((m_nativeStatus.state == lt::torrent_status::checking_files) && !isPaused())
|
else if ((m_nativeStatus.state == lt::torrent_status::checking_files) && !isPaused())
|
||||||
{
|
{
|
||||||
// If the torrent is not just in the "checking" state, but is being actually checked
|
// If the torrent is not just in the "checking" state, but is being actually checked
|
||||||
m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading;
|
m_state = m_hasFinishedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading;
|
||||||
}
|
}
|
||||||
else if (isSeed())
|
else if (isFinished())
|
||||||
{
|
{
|
||||||
if (isPaused())
|
if (isPaused())
|
||||||
m_state = TorrentState::PausedUploading;
|
m_state = TorrentState::PausedUploading;
|
||||||
@@ -1119,11 +1171,12 @@ qlonglong TorrentImpl::eta() const
|
|||||||
|
|
||||||
const SpeedSampleAvg speedAverage = m_payloadRateMonitor.average();
|
const SpeedSampleAvg speedAverage = m_payloadRateMonitor.average();
|
||||||
|
|
||||||
if (isSeed())
|
if (isFinished())
|
||||||
{
|
{
|
||||||
const qreal maxRatioValue = maxRatio();
|
const qreal maxRatioValue = maxRatio();
|
||||||
const int maxSeedingTimeValue = maxSeedingTime();
|
const int maxSeedingTimeValue = maxSeedingTime();
|
||||||
if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0)) return MAX_ETA;
|
const int maxInactiveSeedingTimeValue = maxInactiveSeedingTime();
|
||||||
|
if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0) && (maxInactiveSeedingTimeValue < 0)) return MAX_ETA;
|
||||||
|
|
||||||
qlonglong ratioEta = MAX_ETA;
|
qlonglong ratioEta = MAX_ETA;
|
||||||
|
|
||||||
@@ -1146,7 +1199,15 @@ qlonglong TorrentImpl::eta() const
|
|||||||
seedingTimeEta = 0;
|
seedingTimeEta = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::min(ratioEta, seedingTimeEta);
|
qlonglong inactiveSeedingTimeEta = MAX_ETA;
|
||||||
|
|
||||||
|
if (maxInactiveSeedingTimeValue >= 0)
|
||||||
|
{
|
||||||
|
inactiveSeedingTimeEta = (maxInactiveSeedingTimeValue * 60) - timeSinceActivity();
|
||||||
|
inactiveSeedingTimeEta = std::max<qlonglong>(inactiveSeedingTimeEta, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::min({ratioEta, seedingTimeEta, inactiveSeedingTimeEta});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!speedAverage.download) return MAX_ETA;
|
if (!speedAverage.download) return MAX_ETA;
|
||||||
@@ -1159,20 +1220,20 @@ QVector<qreal> TorrentImpl::filesProgress() const
|
|||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (m_completedFiles.count(true) == filesCount())
|
const int count = m_filesProgress.size();
|
||||||
return QVector<qreal>(filesCount(), 1);
|
Q_ASSERT(count == filesCount());
|
||||||
|
if (Q_UNLIKELY(count != filesCount()))
|
||||||
|
return {};
|
||||||
|
|
||||||
std::vector<int64_t> fp;
|
if (m_completedFiles.count(true) == count)
|
||||||
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
|
return QVector<qreal>(count, 1);
|
||||||
|
|
||||||
const int count = filesCount();
|
|
||||||
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
|
|
||||||
QVector<qreal> result;
|
QVector<qreal> result;
|
||||||
result.reserve(count);
|
result.reserve(count);
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])];
|
const int64_t progress = m_filesProgress.at(i);
|
||||||
const qlonglong size = fileSize(i);
|
const int64_t size = fileSize(i);
|
||||||
if ((size <= 0) || (progress == size))
|
if ((size <= 0) || (progress == size))
|
||||||
result << 1;
|
result << 1;
|
||||||
else
|
else
|
||||||
@@ -1291,15 +1352,13 @@ QVector<PeerInfo> TorrentImpl::peers() const
|
|||||||
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||||
|
|
||||||
for (const lt::peer_info &peer : nativePeers)
|
for (const lt::peer_info &peer : nativePeers)
|
||||||
peers << PeerInfo(this, peer);
|
peers.append(PeerInfo(peer, pieces()));
|
||||||
|
|
||||||
return peers;
|
return peers;
|
||||||
}
|
}
|
||||||
|
|
||||||
QBitArray TorrentImpl::pieces() const
|
QBitArray TorrentImpl::pieces() const
|
||||||
{
|
{
|
||||||
if (m_pieces.isEmpty())
|
|
||||||
m_pieces = LT::toQBitArray(m_nativeStatus.pieces);
|
|
||||||
return m_pieces;
|
return m_pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1345,6 +1404,14 @@ int TorrentImpl::maxSeedingTime() const
|
|||||||
return m_seedingTimeLimit;
|
return m_seedingTimeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TorrentImpl::maxInactiveSeedingTime() const
|
||||||
|
{
|
||||||
|
if (m_inactiveSeedingTimeLimit == USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
||||||
|
return m_session->globalMaxInactiveSeedingMinutes();
|
||||||
|
|
||||||
|
return m_inactiveSeedingTimeLimit;
|
||||||
|
}
|
||||||
|
|
||||||
qreal TorrentImpl::realRatio() const
|
qreal TorrentImpl::realRatio() const
|
||||||
{
|
{
|
||||||
const int64_t upload = m_nativeStatus.all_time_upload;
|
const int64_t upload = m_nativeStatus.all_time_upload;
|
||||||
@@ -1444,7 +1511,8 @@ void TorrentImpl::forceDHTAnnounce()
|
|||||||
|
|
||||||
void TorrentImpl::forceRecheck()
|
void TorrentImpl::forceRecheck()
|
||||||
{
|
{
|
||||||
if (!hasMetadata()) return;
|
if (!hasMetadata())
|
||||||
|
return;
|
||||||
|
|
||||||
m_nativeHandle.force_recheck();
|
m_nativeHandle.force_recheck();
|
||||||
// We have to force update the cached state, otherwise someone will be able to get
|
// We have to force update the cached state, otherwise someone will be able to get
|
||||||
@@ -1453,7 +1521,12 @@ void TorrentImpl::forceRecheck()
|
|||||||
|
|
||||||
m_hasMissingFiles = false;
|
m_hasMissingFiles = false;
|
||||||
m_unchecked = false;
|
m_unchecked = false;
|
||||||
|
|
||||||
m_completedFiles.fill(false);
|
m_completedFiles.fill(false);
|
||||||
|
m_filesProgress.fill(0);
|
||||||
|
m_pieces.fill(false);
|
||||||
|
m_nativeStatus.pieces.clear_all();
|
||||||
|
m_nativeStatus.num_pieces = 0;
|
||||||
|
|
||||||
if (isPaused())
|
if (isPaused())
|
||||||
{
|
{
|
||||||
@@ -1533,47 +1606,29 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled)
|
|||||||
|
|
||||||
void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames)
|
void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames)
|
||||||
{
|
{
|
||||||
endReceivedMetadataHandling(savePath, fileNames);
|
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
|
||||||
|
endReceivedMetadataHandling(savePath, fileNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, const int count)
|
TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceEntry, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
|
||||||
{
|
{
|
||||||
m_updatedTrackerEntries[trackerURL][endpoint] = count;
|
const auto it = std::find_if(m_trackerEntries.begin(), m_trackerEntries.end()
|
||||||
}
|
, [&announceEntry](const TrackerEntry &trackerEntry)
|
||||||
|
|
||||||
void TorrentImpl::invalidateTrackerEntry(const QString &trackerURL)
|
|
||||||
{
|
|
||||||
std::ignore = m_updatedTrackerEntries[trackerURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentImpl::refreshTrackerEntries() const
|
|
||||||
{
|
|
||||||
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
|
|
||||||
Q_ASSERT(nativeTrackers.size() == m_trackerEntries.size());
|
|
||||||
|
|
||||||
for (const lt::announce_entry &announceEntry : nativeTrackers)
|
|
||||||
{
|
{
|
||||||
const auto trackerURL = QString::fromStdString(announceEntry.url);
|
return (trackerEntry.url == QString::fromStdString(announceEntry.url));
|
||||||
const auto updatedTrackerIter = m_updatedTrackerEntries.find(trackerURL);
|
});
|
||||||
if (updatedTrackerIter == m_updatedTrackerEntries.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto trackerIter = std::find_if(m_trackerEntries.begin(), m_trackerEntries.end()
|
Q_ASSERT(it != m_trackerEntries.end());
|
||||||
, [&trackerURL](const TrackerEntry &trackerEntry)
|
// TODO: use [[unlikely]] in C++20
|
||||||
{
|
if (Q_UNLIKELY(it == m_trackerEntries.end()))
|
||||||
return (trackerEntry.url == trackerURL);
|
return {};
|
||||||
});
|
|
||||||
Q_ASSERT(trackerIter != m_trackerEntries.end());
|
|
||||||
|
|
||||||
TrackerEntry &trackerEntry = *trackerIter;
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
updateTrackerEntry(trackerEntry, announceEntry, m_nativeHandle.info_hashes(), updatedTrackerIter.value());
|
::updateTrackerEntry(*it, announceEntry, nativeHandle().info_hashes(), updateInfo);
|
||||||
#else
|
#else
|
||||||
updateTrackerEntry(trackerEntry, announceEntry, updatedTrackerIter.value());
|
::updateTrackerEntry(*it, announceEntry, updateInfo);
|
||||||
#endif
|
#endif
|
||||||
}
|
return *it;
|
||||||
|
|
||||||
m_updatedTrackerEntries.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const
|
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const
|
||||||
@@ -1585,7 +1640,13 @@ std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo()
|
|||||||
|
|
||||||
void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames)
|
void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(m_maintenanceJob == MaintenanceJob::HandleMetadata);
|
||||||
|
if (Q_UNLIKELY(m_maintenanceJob != MaintenanceJob::HandleMetadata))
|
||||||
|
return;
|
||||||
|
|
||||||
Q_ASSERT(m_filePaths.isEmpty());
|
Q_ASSERT(m_filePaths.isEmpty());
|
||||||
|
if (Q_UNLIKELY(!m_filePaths.isEmpty()))
|
||||||
|
m_filePaths.clear();
|
||||||
|
|
||||||
lt::add_torrent_params &p = m_ltAddTorrentParams;
|
lt::add_torrent_params &p = m_ltAddTorrentParams;
|
||||||
|
|
||||||
@@ -1594,15 +1655,17 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
|
|||||||
m_filePriorities.reserve(filesCount());
|
m_filePriorities.reserve(filesCount());
|
||||||
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
|
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
|
||||||
p.file_priorities = resized(p.file_priorities, metadata->files().num_files()
|
p.file_priorities = resized(p.file_priorities, metadata->files().num_files()
|
||||||
, LT::toNative(p.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
|
, LT::toNative(p.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
|
||||||
|
|
||||||
m_completedFiles.fill(static_cast<bool>(p.flags & lt::torrent_flags::seed_mode), filesCount());
|
m_completedFiles.fill(static_cast<bool>(p.flags & lt::torrent_flags::seed_mode), filesCount());
|
||||||
|
m_filesProgress.resize(filesCount());
|
||||||
|
updateProgress();
|
||||||
|
|
||||||
for (int i = 0; i < fileNames.size(); ++i)
|
for (int i = 0; i < fileNames.size(); ++i)
|
||||||
{
|
{
|
||||||
const auto nativeIndex = nativeIndexes.at(i);
|
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();
|
p.renamed_files[nativeIndex] = actualFilePath.toString().toStdString();
|
||||||
|
|
||||||
const Path filePath = actualFilePath.removedExtension(QB_EXT);
|
const Path filePath = actualFilePath.removedExtension(QB_EXT);
|
||||||
@@ -1642,9 +1705,13 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::reload()
|
void TorrentImpl::reload()
|
||||||
|
try
|
||||||
{
|
{
|
||||||
m_completedFiles.fill(false);
|
m_completedFiles.fill(false);
|
||||||
m_pieces.clear();
|
m_filesProgress.fill(0);
|
||||||
|
m_pieces.fill(false);
|
||||||
|
m_nativeStatus.pieces.clear_all();
|
||||||
|
m_nativeStatus.num_pieces = 0;
|
||||||
|
|
||||||
const auto queuePos = m_nativeHandle.queue_position();
|
const auto queuePos = m_nativeHandle.queue_position();
|
||||||
|
|
||||||
@@ -1681,6 +1748,11 @@ void TorrentImpl::reload()
|
|||||||
|
|
||||||
updateState();
|
updateState();
|
||||||
}
|
}
|
||||||
|
catch (const lt::system_error &err)
|
||||||
|
{
|
||||||
|
throw RuntimeError(tr("Failed to reload torrent. Torrent: %1. Reason: %2")
|
||||||
|
.arg(id().toString(), QString::fromLocal8Bit(err.what())));
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::pause()
|
void TorrentImpl::pause()
|
||||||
{
|
{
|
||||||
@@ -1735,7 +1807,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())
|
if (!hasMetadata())
|
||||||
{
|
{
|
||||||
@@ -1743,7 +1815,9 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode)
|
|||||||
return;
|
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))
|
||||||
{
|
{
|
||||||
if (!m_storageIsMoving)
|
if (!m_storageIsMoving)
|
||||||
{
|
{
|
||||||
@@ -1769,16 +1843,17 @@ void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
|||||||
updateStatus(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_storageIsMoving = hasOutstandingJob;
|
||||||
|
m_nativeStatus.save_path = path.toString().toStdString();
|
||||||
|
|
||||||
if (actualStorageLocation() != path)
|
m_session->handleTorrentSavePathChanged(this);
|
||||||
{
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
m_nativeStatus.save_path = path.toString().toStdString();
|
|
||||||
m_session->handleTorrentSavePathChanged(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_storageIsMoving)
|
if (!m_storageIsMoving)
|
||||||
{
|
{
|
||||||
@@ -1820,9 +1895,9 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
|
|||||||
if (!m_hasMissingFiles)
|
if (!m_hasMissingFiles)
|
||||||
{
|
{
|
||||||
if ((progress() < 1.0) && (wantedSize() > 0))
|
if ((progress() < 1.0) && (wantedSize() > 0))
|
||||||
m_hasSeedStatus = false;
|
m_hasFinishedStatus = false;
|
||||||
else if (progress() == 1.0)
|
else if (progress() == 1.0)
|
||||||
m_hasSeedStatus = true;
|
m_hasFinishedStatus = true;
|
||||||
|
|
||||||
adjustStorageLocation();
|
adjustStorageLocation();
|
||||||
manageIncompleteFiles();
|
manageIncompleteFiles();
|
||||||
@@ -1849,7 +1924,7 @@ void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p
|
|||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
|
|
||||||
m_hasMissingFiles = false;
|
m_hasMissingFiles = false;
|
||||||
if (m_hasSeedStatus)
|
if (m_hasFinishedStatus)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_statusUpdatedTriggers.enqueue([this]()
|
m_statusUpdatedTriggers.enqueue([this]()
|
||||||
@@ -1866,7 +1941,7 @@ void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_hasSeedStatus = true;
|
m_hasFinishedStatus = true;
|
||||||
|
|
||||||
if (isMoveInProgress() || (m_renameCount > 0))
|
if (isMoveInProgress() || (m_renameCount > 0))
|
||||||
m_moveFinishedTriggers.enqueue([this]() { m_session->handleTorrentFinished(this); });
|
m_moveFinishedTriggers.enqueue([this]() { m_session->handleTorrentFinished(this); });
|
||||||
@@ -1888,6 +1963,14 @@ void TorrentImpl::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p)
|
|||||||
|
|
||||||
void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
||||||
{
|
{
|
||||||
|
if (m_ltAddTorrentParams.url_seeds != p->params.url_seeds)
|
||||||
|
{
|
||||||
|
// URL seed list have been changed by libtorrent for some reason, so we need to update cached one.
|
||||||
|
// Unfortunately, URL seed list containing in "resume data" is generated according to different rules
|
||||||
|
// than the list we usually cache, so we have to request it from the appropriate source.
|
||||||
|
fetchURLSeeds([this](const QVector<QUrl> &urlSeeds) { m_urlSeeds = urlSeeds; });
|
||||||
|
}
|
||||||
|
|
||||||
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
|
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_indexMap.isEmpty());
|
Q_ASSERT(m_indexMap.isEmpty());
|
||||||
@@ -1971,8 +2054,9 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params ¶ms)
|
|||||||
resumeData.contentLayout = m_contentLayout;
|
resumeData.contentLayout = m_contentLayout;
|
||||||
resumeData.ratioLimit = m_ratioLimit;
|
resumeData.ratioLimit = m_ratioLimit;
|
||||||
resumeData.seedingTimeLimit = m_seedingTimeLimit;
|
resumeData.seedingTimeLimit = m_seedingTimeLimit;
|
||||||
|
resumeData.inactiveSeedingTimeLimit = m_inactiveSeedingTimeLimit;
|
||||||
resumeData.firstLastPiecePriority = m_hasFirstLastPiecePriority;
|
resumeData.firstLastPiecePriority = m_hasFirstLastPiecePriority;
|
||||||
resumeData.hasSeedStatus = m_hasSeedStatus;
|
resumeData.hasFinishedStatus = m_hasFinishedStatus;
|
||||||
resumeData.stopped = m_isStopped;
|
resumeData.stopped = m_isStopped;
|
||||||
resumeData.stopCondition = m_stopCondition;
|
resumeData.stopCondition = m_stopCondition;
|
||||||
resumeData.operatingMode = m_operatingMode;
|
resumeData.operatingMode = m_operatingMode;
|
||||||
@@ -2063,7 +2147,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
|||||||
const int fileIndex = m_indexMap.value(p->index, -1);
|
const int fileIndex = m_indexMap.value(p->index, -1);
|
||||||
Q_ASSERT(fileIndex >= 0);
|
Q_ASSERT(fileIndex >= 0);
|
||||||
|
|
||||||
m_completedFiles[fileIndex] = true;
|
m_completedFiles.setBit(fileIndex);
|
||||||
|
|
||||||
if (m_session->isAppendExtensionEnabled())
|
if (m_session->isAppendExtensionEnabled())
|
||||||
{
|
{
|
||||||
@@ -2107,7 +2191,7 @@ void TorrentImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert
|
|||||||
|
|
||||||
void TorrentImpl::handlePerformanceAlert(const lt::performance_alert *p) const
|
void TorrentImpl::handlePerformanceAlert(const lt::performance_alert *p) const
|
||||||
{
|
{
|
||||||
LogMsg((tr("Performance alert: %1. More info: %2").arg(QString::fromStdString(p->message()), u"https://libtorrent.org/reference-Alerts.html#enum-performance-warning-t"_qs))
|
LogMsg((tr("Performance alert: %1. More info: %2").arg(QString::fromStdString(p->message()), u"https://libtorrent.org/reference-Alerts.html#enum-performance-warning-t"_s))
|
||||||
, Log::INFO);
|
, Log::INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2198,11 +2282,10 @@ void TorrentImpl::manageIncompleteFiles()
|
|||||||
void TorrentImpl::adjustStorageLocation()
|
void TorrentImpl::adjustStorageLocation()
|
||||||
{
|
{
|
||||||
const Path downloadPath = this->downloadPath();
|
const Path downloadPath = this->downloadPath();
|
||||||
const bool isFinished = isSeed() || m_hasSeedStatus;
|
const Path targetPath = ((isFinished() || m_hasFinishedStatus || downloadPath.isEmpty()) ? savePath() : downloadPath);
|
||||||
const Path targetPath = ((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath);
|
|
||||||
|
|
||||||
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
||||||
moveStorage(targetPath, MoveStorageMode::Overwrite);
|
moveStorage(targetPath, MoveStorageContext::AdjustCurrentLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::doRenameFile(int index, const Path &path)
|
void TorrentImpl::doRenameFile(int index, const Path &path)
|
||||||
@@ -2223,17 +2306,24 @@ lt::torrent_handle TorrentImpl::nativeHandle() const
|
|||||||
return m_nativeHandle;
|
return m_nativeHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
|
void TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
|
||||||
{
|
{
|
||||||
if (hasMetadata())
|
if (hasMetadata())
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
|
m_session->invokeAsync([nativeHandle = m_nativeHandle, torrentInfo]
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
return m_nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section());
|
nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section());
|
||||||
#else
|
#else
|
||||||
const std::shared_ptr<lt::torrent_info> nativeInfo = torrentInfo.nativeInfo();
|
const std::shared_ptr<lt::torrent_info> nativeInfo = torrentInfo.nativeInfo();
|
||||||
return m_nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size()));
|
nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size()));
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Torrent::StopCondition TorrentImpl::stopCondition() const
|
Torrent::StopCondition TorrentImpl::stopCondition() const
|
||||||
@@ -2265,8 +2355,11 @@ bool TorrentImpl::isMoveInProgress() const
|
|||||||
|
|
||||||
void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus)
|
void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus)
|
||||||
{
|
{
|
||||||
m_pieces.clear();
|
const lt::torrent_status oldStatus = std::exchange(m_nativeStatus, nativeStatus);
|
||||||
m_nativeStatus = nativeStatus;
|
|
||||||
|
if (m_nativeStatus.num_pieces != oldStatus.num_pieces)
|
||||||
|
updateProgress();
|
||||||
|
|
||||||
updateState();
|
updateState();
|
||||||
|
|
||||||
m_payloadRateMonitor.addSample({nativeStatus.download_payload_rate
|
m_payloadRateMonitor.addSample({nativeStatus.download_payload_rate
|
||||||
@@ -2286,6 +2379,44 @@ void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus)
|
|||||||
std::invoke(m_statusUpdatedTriggers.dequeue());
|
std::invoke(m_statusUpdatedTriggers.dequeue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::updateProgress()
|
||||||
|
{
|
||||||
|
Q_ASSERT(hasMetadata());
|
||||||
|
if (Q_UNLIKELY(!hasMetadata()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Q_ASSERT(!m_filesProgress.isEmpty());
|
||||||
|
if (Q_UNLIKELY(m_filesProgress.isEmpty()))
|
||||||
|
m_filesProgress.resize(filesCount());
|
||||||
|
|
||||||
|
const QBitArray oldPieces = std::exchange(m_pieces, LT::toQBitArray(m_nativeStatus.pieces));
|
||||||
|
const QBitArray newPieces = m_pieces ^ oldPieces;
|
||||||
|
|
||||||
|
const int64_t pieceSize = m_torrentInfo.pieceLength();
|
||||||
|
for (qsizetype index = 0; index < newPieces.size(); ++index)
|
||||||
|
{
|
||||||
|
if (!newPieces.at(index))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int64_t size = m_torrentInfo.pieceLength(index);
|
||||||
|
int64_t pieceOffset = index * pieceSize;
|
||||||
|
|
||||||
|
for (const int fileIndex : asConst(m_torrentInfo.fileIndicesForPiece(index)))
|
||||||
|
{
|
||||||
|
const int64_t fileOffsetInPiece = pieceOffset - m_torrentInfo.fileOffset(fileIndex);
|
||||||
|
const int64_t add = std::min<int64_t>((m_torrentInfo.fileSize(fileIndex) - fileOffsetInPiece), size);
|
||||||
|
|
||||||
|
m_filesProgress[fileIndex] += add;
|
||||||
|
|
||||||
|
size -= add;
|
||||||
|
if (size <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pieceOffset += add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::setRatioLimit(qreal limit)
|
void TorrentImpl::setRatioLimit(qreal limit)
|
||||||
{
|
{
|
||||||
if (limit < USE_GLOBAL_RATIO)
|
if (limit < USE_GLOBAL_RATIO)
|
||||||
@@ -2316,6 +2447,21 @@ void TorrentImpl::setSeedingTimeLimit(int limit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::setInactiveSeedingTimeLimit(int limit)
|
||||||
|
{
|
||||||
|
if (limit < USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
||||||
|
limit = NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||||
|
else if (limit > MAX_INACTIVE_SEEDING_TIME)
|
||||||
|
limit = MAX_SEEDING_TIME;
|
||||||
|
|
||||||
|
if (m_inactiveSeedingTimeLimit != limit)
|
||||||
|
{
|
||||||
|
m_inactiveSeedingTimeLimit = limit;
|
||||||
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
|
m_session->handleTorrentShareLimitChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::setUploadLimit(const int limit)
|
void TorrentImpl::setUploadLimit(const int limit)
|
||||||
{
|
{
|
||||||
const int cleanValue = cleanLimitValue(limit);
|
const int cleanValue = cleanLimitValue(limit);
|
||||||
@@ -2397,7 +2543,39 @@ void TorrentImpl::flushCache() const
|
|||||||
|
|
||||||
QString TorrentImpl::createMagnetURI() const
|
QString TorrentImpl::createMagnetURI() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(lt::make_magnet_uri(m_nativeHandle));
|
QString ret = u"magnet:?"_s;
|
||||||
|
|
||||||
|
const SHA1Hash infoHash1 = infoHash().v1();
|
||||||
|
if (infoHash1.isValid())
|
||||||
|
{
|
||||||
|
ret += u"xt=urn:btih:" + infoHash1.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHA256Hash infoHash2 = infoHash().v2();
|
||||||
|
if (infoHash2.isValid())
|
||||||
|
{
|
||||||
|
if (infoHash1.isValid())
|
||||||
|
ret += u'&';
|
||||||
|
ret += u"xt=urn:btmh:1220" + infoHash2.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString displayName = name();
|
||||||
|
if (displayName != id().toString())
|
||||||
|
{
|
||||||
|
ret += u"&dn=" + QString::fromLatin1(QUrl::toPercentEncoding(displayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const TrackerEntry &tracker : asConst(trackers()))
|
||||||
|
{
|
||||||
|
ret += u"&tr=" + QString::fromLatin1(QUrl::toPercentEncoding(tracker.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QUrl &urlSeed : asConst(urlSeeds()))
|
||||||
|
{
|
||||||
|
ret += u"&ws=" + QString::fromLatin1(urlSeed.toEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
nonstd::expected<lt::entry, QString> TorrentImpl::exportTorrent() const
|
nonstd::expected<lt::entry, QString> TorrentImpl::exportTorrent() const
|
||||||
@@ -2453,6 +2631,129 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QVector<PeerInfo>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<lt::peer_info> nativePeers;
|
||||||
|
nativeHandle.get_peer_info(nativePeers);
|
||||||
|
QVector<PeerInfo> peers;
|
||||||
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||||
|
for (const lt::peer_info &peer : nativePeers)
|
||||||
|
peers.append(PeerInfo(peer, allPieces));
|
||||||
|
return peers;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<QUrl>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const std::set<std::string> currentSeeds = nativeHandle.url_seeds();
|
||||||
|
QVector<QUrl> urlSeeds;
|
||||||
|
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
|
||||||
|
for (const std::string &urlSeed : currentSeeds)
|
||||||
|
urlSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
return urlSeeds;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<int>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<int> piecesAvailability;
|
||||||
|
nativeHandle.piece_availability(piecesAvailability);
|
||||||
|
return QVector<int>(piecesAvailability.cbegin(), piecesAvailability.cend());
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
const std::vector<lt::partial_piece_info> queue = nativeHandle.get_download_queue();
|
||||||
|
#else
|
||||||
|
std::vector<lt::partial_piece_info> queue;
|
||||||
|
nativeHandle.get_download_queue(queue);
|
||||||
|
#endif
|
||||||
|
QBitArray result;
|
||||||
|
result.resize(torrentInfo.piecesCount());
|
||||||
|
for (const lt::partial_piece_info &info : queue)
|
||||||
|
result.setBit(LT::toUnderlyingType(info.piece_index));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QVector<qreal>
|
||||||
|
{
|
||||||
|
if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<int> piecesAvailability;
|
||||||
|
nativeHandle.piece_availability(piecesAvailability);
|
||||||
|
const int filesCount = torrentInfo.filesCount();
|
||||||
|
// libtorrent returns empty array for seeding only torrents
|
||||||
|
if (piecesAvailability.empty())
|
||||||
|
return QVector<qreal>(filesCount, -1);
|
||||||
|
|
||||||
|
QVector<qreal> result;
|
||||||
|
result.reserve(filesCount);
|
||||||
|
for (int i = 0; i < filesCount; ++i)
|
||||||
|
{
|
||||||
|
const TorrentInfo::PieceRange filePieces = torrentInfo.filePieces(i);
|
||||||
|
|
||||||
|
int availablePieces = 0;
|
||||||
|
for (const int piece : filePieces)
|
||||||
|
availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0;
|
||||||
|
|
||||||
|
const qreal availability = filePieces.isEmpty()
|
||||||
|
? 1 // the file has no pieces, so it is available by default
|
||||||
|
: static_cast<qreal>(availablePieces) / filePieces.size();
|
||||||
|
result.append(availability);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
||||||
{
|
{
|
||||||
if (!hasMetadata()) return;
|
if (!hasMetadata()) return;
|
||||||
@@ -2468,7 +2769,7 @@ void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
|||||||
&& (priorities[i] > DownloadPriority::Ignored)
|
&& (priorities[i] > DownloadPriority::Ignored)
|
||||||
&& !m_completedFiles.at(i))
|
&& !m_completedFiles.at(i))
|
||||||
{
|
{
|
||||||
m_hasSeedStatus = false;
|
m_hasFinishedStatus = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2516,3 +2817,19 @@ QVector<qreal> TorrentImpl::availableFileFractions() const
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename Callback>
|
||||||
|
void TorrentImpl::invokeAsync(Func func, Callback resultHandler) const
|
||||||
|
{
|
||||||
|
m_session->invokeAsync([session = m_session
|
||||||
|
, func = std::move(func)
|
||||||
|
, resultHandler = std::move(resultHandler)
|
||||||
|
, thisTorrent = QPointer<const TorrentImpl>(this)]() mutable
|
||||||
|
{
|
||||||
|
session->invoke([result = func(), thisTorrent, resultHandler = std::move(resultHandler)]
|
||||||
|
{
|
||||||
|
if (thisTorrent)
|
||||||
|
resultHandler(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* 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>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -68,6 +68,13 @@ namespace BitTorrent
|
|||||||
Overwrite
|
Overwrite
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MoveStorageContext
|
||||||
|
{
|
||||||
|
AdjustCurrentLocation,
|
||||||
|
ChangeSavePath,
|
||||||
|
ChangeDownloadPath
|
||||||
|
};
|
||||||
|
|
||||||
enum class MaintenanceJob
|
enum class MaintenanceJob
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@@ -77,13 +84,13 @@ namespace BitTorrent
|
|||||||
struct FileErrorInfo
|
struct FileErrorInfo
|
||||||
{
|
{
|
||||||
lt::error_code error;
|
lt::error_code error;
|
||||||
lt::operation_t operation;
|
lt::operation_t operation = lt::operation_t::unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TorrentImpl final : public QObject, public Torrent
|
class TorrentImpl final : public Torrent
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
||||||
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentImpl)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||||
@@ -131,6 +138,7 @@ namespace BitTorrent
|
|||||||
QDateTime addedTime() const override;
|
QDateTime addedTime() const override;
|
||||||
qreal ratioLimit() const override;
|
qreal ratioLimit() const override;
|
||||||
int seedingTimeLimit() const override;
|
int seedingTimeLimit() const override;
|
||||||
|
int inactiveSeedingTimeLimit() const override;
|
||||||
|
|
||||||
Path filePath(int index) const override;
|
Path filePath(int index) const override;
|
||||||
Path actualFilePath(int index) const override;
|
Path actualFilePath(int index) const override;
|
||||||
@@ -139,7 +147,7 @@ namespace BitTorrent
|
|||||||
QVector<DownloadPriority> filePriorities() const override;
|
QVector<DownloadPriority> filePriorities() const override;
|
||||||
|
|
||||||
TorrentInfo info() const override;
|
TorrentInfo info() const override;
|
||||||
bool isSeed() const override;
|
bool isFinished() const override;
|
||||||
bool isPaused() const override;
|
bool isPaused() const override;
|
||||||
bool isQueued() const override;
|
bool isQueued() const override;
|
||||||
bool isForced() const override;
|
bool isForced() const override;
|
||||||
@@ -191,6 +199,7 @@ namespace BitTorrent
|
|||||||
qreal distributedCopies() const override;
|
qreal distributedCopies() const override;
|
||||||
qreal maxRatio() const override;
|
qreal maxRatio() const override;
|
||||||
int maxSeedingTime() const override;
|
int maxSeedingTime() const override;
|
||||||
|
int maxInactiveSeedingTime() const override;
|
||||||
qreal realRatio() const override;
|
qreal realRatio() const override;
|
||||||
int uploadPayloadRate() const override;
|
int uploadPayloadRate() const override;
|
||||||
int downloadPayloadRate() const override;
|
int downloadPayloadRate() const override;
|
||||||
@@ -213,6 +222,7 @@ namespace BitTorrent
|
|||||||
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
||||||
void setRatioLimit(qreal limit) override;
|
void setRatioLimit(qreal limit) override;
|
||||||
void setSeedingTimeLimit(int limit) override;
|
void setSeedingTimeLimit(int limit) override;
|
||||||
|
void setInactiveSeedingTimeLimit(int limit) override;
|
||||||
void setUploadLimit(int limit) override;
|
void setUploadLimit(int limit) override;
|
||||||
void setDownloadLimit(int limit) override;
|
void setDownloadLimit(int limit) override;
|
||||||
void setSuperSeeding(bool enable) override;
|
void setSuperSeeding(bool enable) override;
|
||||||
@@ -227,7 +237,7 @@ namespace BitTorrent
|
|||||||
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
||||||
bool connectPeer(const PeerAddress &peerAddress) override;
|
bool connectPeer(const PeerAddress &peerAddress) override;
|
||||||
void clearPeers() override;
|
void clearPeers() override;
|
||||||
bool setMetadata(const TorrentInfo &torrentInfo) override;
|
void setMetadata(const TorrentInfo &torrentInfo) override;
|
||||||
|
|
||||||
StopCondition stopCondition() const override;
|
StopCondition stopCondition() const override;
|
||||||
void setStopCondition(StopCondition stopCondition) override;
|
void setStopCondition(StopCondition stopCondition) override;
|
||||||
@@ -236,6 +246,12 @@ namespace BitTorrent
|
|||||||
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
||||||
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
||||||
|
|
||||||
|
void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const override;
|
||||||
|
void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const override;
|
||||||
|
void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const override;
|
||||||
|
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
|
||||||
|
void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const override;
|
||||||
|
|
||||||
bool needSaveResumeData() const;
|
bool needSaveResumeData() const;
|
||||||
|
|
||||||
// Session interface
|
// Session interface
|
||||||
@@ -246,18 +262,17 @@ namespace BitTorrent
|
|||||||
void handleCategoryOptionsChanged();
|
void handleCategoryOptionsChanged();
|
||||||
void handleAppendExtensionToggled();
|
void handleAppendExtensionToggled();
|
||||||
void saveResumeData(lt::resume_data_flags_t flags = {});
|
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);
|
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||||
void updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, int count);
|
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QMap<TrackerEntry::Endpoint, int> &updateInfo);
|
||||||
void invalidateTrackerEntry(const QString &trackerURL);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using EventTrigger = std::function<void ()>;
|
using EventTrigger = std::function<void ()>;
|
||||||
|
|
||||||
std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const;
|
std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const;
|
||||||
|
|
||||||
void refreshTrackerEntries() const;
|
|
||||||
void updateStatus(const lt::torrent_status &nativeStatus);
|
void updateStatus(const lt::torrent_status &nativeStatus);
|
||||||
|
void updateProgress();
|
||||||
void updateState();
|
void updateState();
|
||||||
|
|
||||||
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
|
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
|
||||||
@@ -284,7 +299,7 @@ namespace BitTorrent
|
|||||||
Path wantedActualPath(int index, const Path &path) const;
|
Path wantedActualPath(int index, const Path &path) const;
|
||||||
void adjustStorageLocation();
|
void adjustStorageLocation();
|
||||||
void doRenameFile(int index, const Path &path);
|
void doRenameFile(int index, const Path &path);
|
||||||
void moveStorage(const Path &newPath, MoveStorageMode mode);
|
void moveStorage(const Path &newPath, MoveStorageContext context);
|
||||||
void manageIncompleteFiles();
|
void manageIncompleteFiles();
|
||||||
void applyFirstLastPiecePriority(bool enabled);
|
void applyFirstLastPiecePriority(bool enabled);
|
||||||
|
|
||||||
@@ -294,6 +309,9 @@ namespace BitTorrent
|
|||||||
|
|
||||||
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
||||||
|
|
||||||
|
template <typename Func, typename Callback>
|
||||||
|
void invokeAsync(Func func, Callback resultHandler) const;
|
||||||
|
|
||||||
SessionImpl *const m_session = nullptr;
|
SessionImpl *const m_session = nullptr;
|
||||||
lt::session *m_nativeSession = nullptr;
|
lt::session *m_nativeSession = nullptr;
|
||||||
lt::torrent_handle m_nativeHandle;
|
lt::torrent_handle m_nativeHandle;
|
||||||
@@ -318,10 +336,8 @@ namespace BitTorrent
|
|||||||
|
|
||||||
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
|
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
|
||||||
|
|
||||||
// TODO: Use QHash<TrackerEntry::Endpoint, int> once Qt5 is dropped.
|
QVector<TrackerEntry> m_trackerEntries;
|
||||||
using TrackerEntryUpdateInfo = QMap<TrackerEntry::Endpoint, int>;
|
QVector<QUrl> m_urlSeeds;
|
||||||
mutable QHash<QString, TrackerEntryUpdateInfo> m_updatedTrackerEntries;
|
|
||||||
mutable QVector<TrackerEntry> m_trackerEntries;
|
|
||||||
FileErrorInfo m_lastFileError;
|
FileErrorInfo m_lastFileError;
|
||||||
|
|
||||||
// Persistent data
|
// Persistent data
|
||||||
@@ -332,9 +348,10 @@ namespace BitTorrent
|
|||||||
TagSet m_tags;
|
TagSet m_tags;
|
||||||
qreal m_ratioLimit;
|
qreal m_ratioLimit;
|
||||||
int m_seedingTimeLimit;
|
int m_seedingTimeLimit;
|
||||||
|
int m_inactiveSeedingTimeLimit;
|
||||||
TorrentOperatingMode m_operatingMode;
|
TorrentOperatingMode m_operatingMode;
|
||||||
TorrentContentLayout m_contentLayout;
|
TorrentContentLayout m_contentLayout;
|
||||||
bool m_hasSeedStatus;
|
bool m_hasFinishedStatus;
|
||||||
bool m_hasMissingFiles = false;
|
bool m_hasMissingFiles = false;
|
||||||
bool m_hasFirstLastPiecePriority = false;
|
bool m_hasFirstLastPiecePriority = false;
|
||||||
bool m_useAutoTMM;
|
bool m_useAutoTMM;
|
||||||
@@ -348,6 +365,7 @@ namespace BitTorrent
|
|||||||
int m_downloadLimit = 0;
|
int m_downloadLimit = 0;
|
||||||
int m_uploadLimit = 0;
|
int m_uploadLimit = 0;
|
||||||
|
|
||||||
mutable QBitArray m_pieces;
|
QBitArray m_pieces;
|
||||||
|
QVector<std::int64_t> m_filesProgress;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,10 @@
|
|||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "common.h"
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
|
|
||||||
@@ -67,12 +67,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)
|
TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
|
||||||
{
|
{
|
||||||
if (this != &other)
|
if (this != &other)
|
||||||
@@ -92,9 +86,11 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)
|
|||||||
{
|
{
|
||||||
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
|
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
|
||||||
// used in `torrent_info()` constructor
|
// used in `torrent_info()` constructor
|
||||||
|
const auto *pref = Preferences::instance();
|
||||||
|
|
||||||
lt::error_code ec;
|
lt::error_code ec;
|
||||||
const lt::bdecode_node node = lt::bdecode(data, ec
|
const lt::bdecode_node node = lt::bdecode(data, ec
|
||||||
, nullptr, BENCODE_DEPTH_LIMIT, BENCODE_TOKEN_LIMIT);
|
, nullptr, pref->getBdecodeDepthLimit(), pref->getBdecodeTokenLimit());
|
||||||
if (ec)
|
if (ec)
|
||||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
||||||
|
|
||||||
@@ -107,28 +103,21 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)
|
|||||||
|
|
||||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const Path &path) noexcept
|
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;
|
QByteArray data;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data = file.readAll();
|
const qint64 torrentSizeLimit = Preferences::instance()->getTorrentFileSizeLimit();
|
||||||
|
const auto readResult = Utils::IO::readFile(path, torrentSizeLimit);
|
||||||
|
if (!readResult)
|
||||||
|
return nonstd::make_unexpected(readResult.error().message);
|
||||||
|
data = readResult.value();
|
||||||
}
|
}
|
||||||
catch (const std::bad_alloc &e)
|
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);
|
return load(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentInfo() = default;
|
TorrentInfo() = default;
|
||||||
TorrentInfo(const TorrentInfo &other);
|
TorrentInfo(const TorrentInfo &other) = default;
|
||||||
|
|
||||||
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
||||||
|
|
||||||
|
|||||||
@@ -53,23 +53,23 @@ namespace
|
|||||||
// constants
|
// constants
|
||||||
const int PEER_ID_SIZE = 20;
|
const int PEER_ID_SIZE = 20;
|
||||||
|
|
||||||
const QString ANNOUNCE_REQUEST_PATH = u"/announce"_qs;
|
const QString ANNOUNCE_REQUEST_PATH = u"/announce"_s;
|
||||||
|
|
||||||
const QString ANNOUNCE_REQUEST_COMPACT = u"compact"_qs;
|
const QString ANNOUNCE_REQUEST_COMPACT = u"compact"_s;
|
||||||
const QString ANNOUNCE_REQUEST_INFO_HASH = u"info_hash"_qs;
|
const QString ANNOUNCE_REQUEST_INFO_HASH = u"info_hash"_s;
|
||||||
const QString ANNOUNCE_REQUEST_IP = u"ip"_qs;
|
const QString ANNOUNCE_REQUEST_IP = u"ip"_s;
|
||||||
const QString ANNOUNCE_REQUEST_LEFT = u"left"_qs;
|
const QString ANNOUNCE_REQUEST_LEFT = u"left"_s;
|
||||||
const QString ANNOUNCE_REQUEST_NO_PEER_ID = u"no_peer_id"_qs;
|
const QString ANNOUNCE_REQUEST_NO_PEER_ID = u"no_peer_id"_s;
|
||||||
const QString ANNOUNCE_REQUEST_NUM_WANT = u"numwant"_qs;
|
const QString ANNOUNCE_REQUEST_NUM_WANT = u"numwant"_s;
|
||||||
const QString ANNOUNCE_REQUEST_PEER_ID = u"peer_id"_qs;
|
const QString ANNOUNCE_REQUEST_PEER_ID = u"peer_id"_s;
|
||||||
const QString ANNOUNCE_REQUEST_PORT = u"port"_qs;
|
const QString ANNOUNCE_REQUEST_PORT = u"port"_s;
|
||||||
|
|
||||||
const QString ANNOUNCE_REQUEST_EVENT = u"event"_qs;
|
const QString ANNOUNCE_REQUEST_EVENT = u"event"_s;
|
||||||
const QString ANNOUNCE_REQUEST_EVENT_COMPLETED = u"completed"_qs;
|
const QString ANNOUNCE_REQUEST_EVENT_COMPLETED = u"completed"_s;
|
||||||
const QString ANNOUNCE_REQUEST_EVENT_EMPTY = u"empty"_qs;
|
const QString ANNOUNCE_REQUEST_EVENT_EMPTY = u"empty"_s;
|
||||||
const QString ANNOUNCE_REQUEST_EVENT_STARTED = u"started"_qs;
|
const QString ANNOUNCE_REQUEST_EVENT_STARTED = u"started"_s;
|
||||||
const QString ANNOUNCE_REQUEST_EVENT_STOPPED = u"stopped"_qs;
|
const QString ANNOUNCE_REQUEST_EVENT_STOPPED = u"stopped"_s;
|
||||||
const QString ANNOUNCE_REQUEST_EVENT_PAUSED = u"paused"_qs;
|
const QString ANNOUNCE_REQUEST_EVENT_PAUSED = u"paused"_s;
|
||||||
|
|
||||||
const char ANNOUNCE_RESPONSE_COMPLETE[] = "complete";
|
const char ANNOUNCE_RESPONSE_COMPLETE[] = "complete";
|
||||||
const char ANNOUNCE_RESPONSE_EXTERNAL_IP[] = "external ip";
|
const char ANNOUNCE_RESPONSE_EXTERNAL_IP[] = "external ip";
|
||||||
@@ -297,32 +297,32 @@ void Tracker::processAnnounceRequest()
|
|||||||
// 1. info_hash
|
// 1. info_hash
|
||||||
const auto infoHashIter = queryParams.find(ANNOUNCE_REQUEST_INFO_HASH);
|
const auto infoHashIter = queryParams.find(ANNOUNCE_REQUEST_INFO_HASH);
|
||||||
if (infoHashIter == queryParams.end())
|
if (infoHashIter == queryParams.end())
|
||||||
throw TrackerError(u"Missing \"info_hash\" parameter"_qs);
|
throw TrackerError(u"Missing \"info_hash\" parameter"_s);
|
||||||
|
|
||||||
const auto torrentID = TorrentID::fromString(QString::fromLatin1(infoHashIter->toHex()));
|
const auto torrentID = TorrentID::fromString(QString::fromLatin1(infoHashIter->toHex()));
|
||||||
if (!torrentID.isValid())
|
if (!torrentID.isValid())
|
||||||
throw TrackerError(u"Invalid \"info_hash\" parameter"_qs);
|
throw TrackerError(u"Invalid \"info_hash\" parameter"_s);
|
||||||
|
|
||||||
announceReq.torrentID = torrentID;
|
announceReq.torrentID = torrentID;
|
||||||
|
|
||||||
// 2. peer_id
|
// 2. peer_id
|
||||||
const auto peerIdIter = queryParams.find(ANNOUNCE_REQUEST_PEER_ID);
|
const auto peerIdIter = queryParams.find(ANNOUNCE_REQUEST_PEER_ID);
|
||||||
if (peerIdIter == queryParams.end())
|
if (peerIdIter == queryParams.end())
|
||||||
throw TrackerError(u"Missing \"peer_id\" parameter"_qs);
|
throw TrackerError(u"Missing \"peer_id\" parameter"_s);
|
||||||
|
|
||||||
if (peerIdIter->size() > PEER_ID_SIZE)
|
if (peerIdIter->size() > PEER_ID_SIZE)
|
||||||
throw TrackerError(u"Invalid \"peer_id\" parameter"_qs);
|
throw TrackerError(u"Invalid \"peer_id\" parameter"_s);
|
||||||
|
|
||||||
announceReq.peer.peerId = *peerIdIter;
|
announceReq.peer.peerId = *peerIdIter;
|
||||||
|
|
||||||
// 3. port
|
// 3. port
|
||||||
const auto portIter = queryParams.find(ANNOUNCE_REQUEST_PORT);
|
const auto portIter = queryParams.find(ANNOUNCE_REQUEST_PORT);
|
||||||
if (portIter == queryParams.end())
|
if (portIter == queryParams.end())
|
||||||
throw TrackerError(u"Missing \"port\" parameter"_qs);
|
throw TrackerError(u"Missing \"port\" parameter"_s);
|
||||||
|
|
||||||
const ushort portNum = portIter->toUShort();
|
const ushort portNum = portIter->toUShort();
|
||||||
if (portNum == 0)
|
if (portNum == 0)
|
||||||
throw TrackerError(u"Invalid \"port\" parameter"_qs);
|
throw TrackerError(u"Invalid \"port\" parameter"_s);
|
||||||
|
|
||||||
announceReq.peer.port = portNum;
|
announceReq.peer.port = portNum;
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ void Tracker::processAnnounceRequest()
|
|||||||
{
|
{
|
||||||
const int num = numWantIter->toInt();
|
const int num = numWantIter->toInt();
|
||||||
if (num < 0)
|
if (num < 0)
|
||||||
throw TrackerError(u"Invalid \"numwant\" parameter"_qs);
|
throw TrackerError(u"Invalid \"numwant\" parameter"_s);
|
||||||
announceReq.numwant = num;
|
announceReq.numwant = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +377,7 @@ void Tracker::processAnnounceRequest()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw TrackerError(u"Invalid \"event\" parameter"_qs);
|
throw TrackerError(u"Invalid \"event\" parameter"_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareAnnounceResponse(announceReq);
|
prepareAnnounceResponse(announceReq);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
|
|
||||||
Digest32() = default;
|
Digest32() = default;
|
||||||
Digest32(const Digest32 &other) = default;
|
Digest32(const Digest32 &other) = default;
|
||||||
Digest32(Digest32 &&other) = default;
|
Digest32(Digest32 &&other) noexcept = default;
|
||||||
|
|
||||||
Digest32(const UnderlyingType &nativeDigest)
|
Digest32(const UnderlyingType &nativeDigest)
|
||||||
: m_dataPtr {new Data(nativeDigest)}
|
: m_dataPtr {new Data(nativeDigest)}
|
||||||
@@ -63,7 +63,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Digest32 &operator=(const Digest32 &other) = default;
|
Digest32 &operator=(const Digest32 &other) = default;
|
||||||
Digest32 &operator=(Digest32 &&other) = default;
|
Digest32 &operator=(Digest32 &&other) noexcept = default;
|
||||||
|
|
||||||
operator UnderlyingType() const
|
operator UnderlyingType() const
|
||||||
{
|
{
|
||||||
@@ -84,7 +84,7 @@ private:
|
|||||||
class Data;
|
class Data;
|
||||||
|
|
||||||
explicit Digest32(QSharedDataPointer<Data> dataPtr)
|
explicit Digest32(QSharedDataPointer<Data> dataPtr)
|
||||||
: m_dataPtr {dataPtr}
|
: m_dataPtr {std::move(dataPtr)}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +167,6 @@ std::size_t qHash(const Digest32<N> &key, const std::size_t seed = 0)
|
|||||||
template <int N>
|
template <int N>
|
||||||
uint qHash(const Digest32<N> &key, const uint seed = 0)
|
uint qHash(const Digest32<N> &key, const uint seed = 0)
|
||||||
{
|
{
|
||||||
return static_cast<uint>((std::hash<typename Digest32<N>::UnderlyingType> {})(key)) ^ seed;
|
return ::qHash(std::hash<typename Digest32<N>::UnderlyingType> {}(key), seed);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Mike Tzou
|
* Copyright (C) 2016-2023 Mike Tzou (Chocobo1)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -40,18 +40,24 @@ constexpr typename std::add_const_t<T> &asConst(T &t) noexcept { return t; }
|
|||||||
|
|
||||||
// Forward rvalue as const
|
// Forward rvalue as const
|
||||||
template <typename T>
|
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
|
// Prevent const rvalue arguments
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void asConst(const T &&) = delete;
|
void asConst(const T &&) = delete;
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 4, 0))
|
||||||
// https://doc.qt.io/qt-6/qstring.html#operator-22-22_qs
|
// https://doc.qt.io/qt-6/qstring.html#operator-22-22_s
|
||||||
inline QString operator"" _qs(const char16_t *str, const std::size_t size)
|
inline QString operator"" _s(const char16_t *str, const std::size_t size)
|
||||||
{
|
{
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
return QString::fromRawData(reinterpret_cast<const QChar *>(str), static_cast<int>(size));
|
return QString::fromRawData(reinterpret_cast<const QChar *>(str), static_cast<int>(size));
|
||||||
|
#else
|
||||||
|
return operator""_qs(str, size);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
using namespace Qt::Literals::StringLiterals;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline const QString TORRENT_FILE_EXTENSION = u".torrent"_qs;
|
inline const QString TORRENT_FILE_EXTENSION = u".torrent"_s;
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
|
||||||
#include "base/logger.h"
|
|
||||||
#include "irequesthandler.h"
|
#include "irequesthandler.h"
|
||||||
#include "requestparser.h"
|
#include "requestparser.h"
|
||||||
#include "responsegenerator.h"
|
#include "responsegenerator.h"
|
||||||
@@ -46,6 +45,10 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
|
|||||||
{
|
{
|
||||||
m_socket->setParent(this);
|
m_socket->setParent(this);
|
||||||
|
|
||||||
|
// reserve common size for requests, don't use the max allowed size which is too big for
|
||||||
|
// memory constrained platforms
|
||||||
|
m_receivedData.reserve(1024 * 1024);
|
||||||
|
|
||||||
// reset timer when there are activity
|
// reset timer when there are activity
|
||||||
m_idleTimer.start();
|
m_idleTimer.start();
|
||||||
connect(m_socket, &QIODevice::readyRead, this, [this]()
|
connect(m_socket, &QIODevice::readyRead, this, [this]()
|
||||||
@@ -66,7 +69,18 @@ Connection::~Connection()
|
|||||||
|
|
||||||
void Connection::read()
|
void Connection::read()
|
||||||
{
|
{
|
||||||
m_receivedData.append(m_socket->readAll());
|
// reuse existing buffer and avoid unnecessary memory allocation/relocation
|
||||||
|
const qsizetype previousSize = m_receivedData.size();
|
||||||
|
const qint64 bytesAvailable = m_socket->bytesAvailable();
|
||||||
|
m_receivedData.resize(previousSize + bytesAvailable);
|
||||||
|
const qint64 bytesRead = m_socket->read((m_receivedData.data() + previousSize), bytesAvailable);
|
||||||
|
if (Q_UNLIKELY(bytesRead < 0))
|
||||||
|
{
|
||||||
|
m_socket->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Q_UNLIKELY(bytesRead < bytesAvailable))
|
||||||
|
m_receivedData.chop(bytesAvailable - bytesRead);
|
||||||
|
|
||||||
while (!m_receivedData.isEmpty())
|
while (!m_receivedData.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -79,11 +93,11 @@ void Connection::read()
|
|||||||
const long bufferLimit = RequestParser::MAX_CONTENT_SIZE * 1.1; // some margin for headers
|
const long bufferLimit = RequestParser::MAX_CONTENT_SIZE * 1.1; // some margin for headers
|
||||||
if (m_receivedData.size() > bufferLimit)
|
if (m_receivedData.size() > bufferLimit)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Http request size exceeds limitation, closing socket. Limit: %1, IP: %2")
|
qWarning("%s", qUtf8Printable(tr("Http request size exceeds limitation, closing socket. Limit: %1, IP: %2")
|
||||||
.arg(bufferLimit).arg(m_socket->peerAddress().toString()), Log::WARNING);
|
.arg(QString::number(bufferLimit), m_socket->peerAddress().toString())));
|
||||||
|
|
||||||
Response resp(413, u"Payload Too Large"_qs);
|
Response resp(413, u"Payload Too Large"_s);
|
||||||
resp.headers[HEADER_CONNECTION] = u"close"_qs;
|
resp.headers[HEADER_CONNECTION] = u"close"_s;
|
||||||
|
|
||||||
sendResponse(resp);
|
sendResponse(resp);
|
||||||
m_socket->close();
|
m_socket->close();
|
||||||
@@ -91,13 +105,26 @@ void Connection::read()
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case RequestParser::ParseStatus::BadMethod:
|
||||||
|
{
|
||||||
|
qWarning("%s", qUtf8Printable(tr("Bad Http request method, closing socket. IP: %1. Method: \"%2\"")
|
||||||
|
.arg(m_socket->peerAddress().toString(), result.request.method)));
|
||||||
|
|
||||||
|
Response resp(501, u"Not Implemented"_s);
|
||||||
|
resp.headers[HEADER_CONNECTION] = u"close"_s;
|
||||||
|
|
||||||
|
sendResponse(resp);
|
||||||
|
m_socket->close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
case RequestParser::ParseStatus::BadRequest:
|
case RequestParser::ParseStatus::BadRequest:
|
||||||
{
|
{
|
||||||
LogMsg(tr("Bad Http request, closing socket. IP: %1")
|
qWarning("%s", qUtf8Printable(tr("Bad Http request, closing socket. IP: %1")
|
||||||
.arg(m_socket->peerAddress().toString()), Log::WARNING);
|
.arg(m_socket->peerAddress().toString())));
|
||||||
|
|
||||||
Response resp(400, u"Bad Request"_qs);
|
Response resp(400, u"Bad Request"_s);
|
||||||
resp.headers[HEADER_CONNECTION] = u"close"_qs;
|
resp.headers[HEADER_CONNECTION] = u"close"_s;
|
||||||
|
|
||||||
sendResponse(resp);
|
sendResponse(resp);
|
||||||
m_socket->close();
|
m_socket->close();
|
||||||
@@ -108,15 +135,31 @@ void Connection::read()
|
|||||||
{
|
{
|
||||||
const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()};
|
const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()};
|
||||||
|
|
||||||
Response resp = m_requestHandler->processRequest(result.request, env);
|
if (result.request.method == HEADER_REQUEST_METHOD_HEAD)
|
||||||
|
{
|
||||||
|
Request getRequest = result.request;
|
||||||
|
getRequest.method = HEADER_REQUEST_METHOD_GET;
|
||||||
|
|
||||||
if (acceptsGzipEncoding(result.request.headers[u"accept-encoding"_qs]))
|
Response resp = m_requestHandler->processRequest(getRequest, env);
|
||||||
resp.headers[HEADER_CONTENT_ENCODING] = u"gzip"_qs;
|
|
||||||
|
|
||||||
resp.headers[HEADER_CONNECTION] = u"keep-alive"_qs;
|
resp.headers[HEADER_CONNECTION] = u"keep-alive"_s;
|
||||||
|
resp.headers[HEADER_CONTENT_LENGTH] = QString::number(resp.content.length());
|
||||||
|
resp.content.clear();
|
||||||
|
|
||||||
sendResponse(resp);
|
sendResponse(resp);
|
||||||
m_receivedData = m_receivedData.mid(result.frameSize);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Response resp = m_requestHandler->processRequest(result.request, env);
|
||||||
|
|
||||||
|
if (acceptsGzipEncoding(result.request.headers.value(u"accept-encoding"_s)))
|
||||||
|
resp.headers[HEADER_CONTENT_ENCODING] = u"gzip"_s;
|
||||||
|
resp.headers[HEADER_CONNECTION] = u"keep-alive"_s;
|
||||||
|
|
||||||
|
sendResponse(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_receivedData.remove(0, result.frameSize);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -176,11 +219,11 @@ bool Connection::acceptsGzipEncoding(QString codings)
|
|||||||
if (list.isEmpty())
|
if (list.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const bool canGzip = isCodingAvailable(list, u"gzip"_qs);
|
const bool canGzip = isCodingAvailable(list, u"gzip"_s);
|
||||||
if (canGzip)
|
if (canGzip)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const bool canAny = isCodingAvailable(list, u"*"_qs);
|
const bool canAny = isCodingAvailable(list, u"*"_s);
|
||||||
if (canAny)
|
if (canAny)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -48,41 +48,41 @@ QString HTTPError::statusText() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
BadRequestHTTPError::BadRequestHTTPError(const QString &message)
|
BadRequestHTTPError::BadRequestHTTPError(const QString &message)
|
||||||
: HTTPError(400, u"Bad Request"_qs, message)
|
: HTTPError(400, u"Bad Request"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
UnauthorizedHTTPError::UnauthorizedHTTPError(const QString &message)
|
UnauthorizedHTTPError::UnauthorizedHTTPError(const QString &message)
|
||||||
: HTTPError(401, u"Unauthorized"_qs, message)
|
: HTTPError(401, u"Unauthorized"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ForbiddenHTTPError::ForbiddenHTTPError(const QString &message)
|
ForbiddenHTTPError::ForbiddenHTTPError(const QString &message)
|
||||||
: HTTPError(403, u"Forbidden"_qs, message)
|
: HTTPError(403, u"Forbidden"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NotFoundHTTPError::NotFoundHTTPError(const QString &message)
|
NotFoundHTTPError::NotFoundHTTPError(const QString &message)
|
||||||
: HTTPError(404, u"Not Found"_qs, message)
|
: HTTPError(404, u"Not Found"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodNotAllowedHTTPError::MethodNotAllowedHTTPError(const QString &message)
|
MethodNotAllowedHTTPError::MethodNotAllowedHTTPError(const QString &message)
|
||||||
: HTTPError(405, u"Method Not Allowed"_qs, message)
|
: HTTPError(405, u"Method Not Allowed"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ConflictHTTPError::ConflictHTTPError(const QString &message)
|
ConflictHTTPError::ConflictHTTPError(const QString &message)
|
||||||
: HTTPError(409, u"Conflict"_qs, message)
|
: HTTPError(409, u"Conflict"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsupportedMediaTypeHTTPError::UnsupportedMediaTypeHTTPError(const QString &message)
|
UnsupportedMediaTypeHTTPError::UnsupportedMediaTypeHTTPError(const QString &message)
|
||||||
: HTTPError(415, u"Unsupported Media Type"_qs, message)
|
: HTTPError(415, u"Unsupported Media Type"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalServerErrorHTTPError::InternalServerErrorHTTPError(const QString &message)
|
InternalServerErrorHTTPError::InternalServerErrorHTTPError(const QString &message)
|
||||||
: HTTPError(500, u"Internal Server Error"_qs, message)
|
: HTTPError(500, u"Internal Server Error"_s, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace Http
|
|||||||
class IRequestHandler
|
class IRequestHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IRequestHandler() {}
|
virtual ~IRequestHandler() = default;
|
||||||
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,10 +75,6 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestParser::RequestParser()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestParser::ParseResult RequestParser::parse(const QByteArray &data)
|
RequestParser::ParseResult RequestParser::parse(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// Warning! Header names are converted to lowercase
|
// Warning! Header names are converted to lowercase
|
||||||
@@ -107,6 +103,7 @@ RequestParser::ParseResult RequestParser::doParse(const QByteArray &data)
|
|||||||
// handle supported methods
|
// handle supported methods
|
||||||
if ((m_request.method == HEADER_REQUEST_METHOD_GET) || (m_request.method == HEADER_REQUEST_METHOD_HEAD))
|
if ((m_request.method == HEADER_REQUEST_METHOD_GET) || (m_request.method == HEADER_REQUEST_METHOD_HEAD))
|
||||||
return {ParseStatus::OK, m_request, headerLength};
|
return {ParseStatus::OK, m_request, headerLength};
|
||||||
|
|
||||||
if (m_request.method == HEADER_REQUEST_METHOD_POST)
|
if (m_request.method == HEADER_REQUEST_METHOD_POST)
|
||||||
{
|
{
|
||||||
const auto parseContentLength = [this]() -> int
|
const auto parseContentLength = [this]() -> int
|
||||||
@@ -150,8 +147,7 @@ RequestParser::ParseResult RequestParser::doParse(const QByteArray &data)
|
|||||||
return {ParseStatus::OK, m_request, (headerLength + contentLength)};
|
return {ParseStatus::OK, m_request, (headerLength + contentLength)};
|
||||||
}
|
}
|
||||||
|
|
||||||
qWarning() << Q_FUNC_INFO << "unsupported request method: " << m_request.method;
|
return {ParseStatus::BadMethod, m_request, 0};
|
||||||
return {ParseStatus::BadRequest, Request(), 0}; // TODO: SHOULD respond "501 Not Implemented"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequestParser::parseStartLines(const QStringView data)
|
bool RequestParser::parseStartLines(const QStringView data)
|
||||||
@@ -193,7 +189,7 @@ bool RequestParser::parseRequestLine(const QString &line)
|
|||||||
{
|
{
|
||||||
// [rfc7230] 3.1.1. Request Line
|
// [rfc7230] 3.1.1. Request Line
|
||||||
|
|
||||||
const QRegularExpression re(u"^([A-Z]+)\\s+(\\S+)\\s+HTTP\\/(\\d\\.\\d)$"_qs);
|
static const QRegularExpression re(u"^([A-Z]+)\\s+(\\S+)\\s+HTTP\\/(\\d\\.\\d)$"_s);
|
||||||
const QRegularExpressionMatch match = re.match(line);
|
const QRegularExpressionMatch match = re.match(line);
|
||||||
|
|
||||||
if (!match.hasMatch())
|
if (!match.hasMatch())
|
||||||
@@ -268,7 +264,7 @@ bool RequestParser::parsePostMessage(const QByteArray &data)
|
|||||||
// [rfc2046] 5.1.1. Common Syntax
|
// [rfc2046] 5.1.1. Common Syntax
|
||||||
|
|
||||||
// find boundary delimiter
|
// find boundary delimiter
|
||||||
const QString boundaryFieldName = u"boundary="_qs;
|
const QString boundaryFieldName = u"boundary="_s;
|
||||||
const int idx = contentType.indexOf(boundaryFieldName);
|
const int idx = contentType.indexOf(boundaryFieldName);
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
{
|
{
|
||||||
@@ -347,8 +343,8 @@ bool RequestParser::parseFormData(const QByteArray &data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pick data
|
// pick data
|
||||||
const QString filename = u"filename"_qs;
|
const QString filename = u"filename"_s;
|
||||||
const QString name = u"name"_qs;
|
const QString name = u"name"_s;
|
||||||
|
|
||||||
if (headersMap.contains(filename))
|
if (headersMap.contains(filename))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,15 +41,16 @@ namespace Http
|
|||||||
{
|
{
|
||||||
OK,
|
OK,
|
||||||
Incomplete,
|
Incomplete,
|
||||||
|
BadMethod,
|
||||||
BadRequest
|
BadRequest
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParseResult
|
struct ParseResult
|
||||||
{
|
{
|
||||||
// when `status != ParseStatus::OK`, `request` & `frameSize` are undefined
|
// when `status != ParseStatus::OK`, `request` & `frameSize` are undefined
|
||||||
ParseStatus status;
|
ParseStatus status = ParseStatus::BadRequest;
|
||||||
Request request;
|
Request request;
|
||||||
long frameSize; // http request frame size (bytes)
|
long frameSize = 0; // http request frame size (bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
static ParseResult parse(const QByteArray &data);
|
static ParseResult parse(const QByteArray &data);
|
||||||
@@ -57,7 +58,7 @@ namespace Http
|
|||||||
static const long MAX_CONTENT_SIZE = 64 * 1024 * 1024; // 64 MB
|
static const long MAX_CONTENT_SIZE = 64 * 1024 * 1024; // 64 MB
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RequestParser();
|
RequestParser() = default;
|
||||||
|
|
||||||
ParseResult doParse(const QByteArray &data);
|
ParseResult doParse(const QByteArray &data);
|
||||||
bool parseStartLines(QStringView data);
|
bool parseStartLines(QStringView data);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Http
|
|||||||
class ResponseBuilder
|
class ResponseBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void status(uint code = 200, const QString &text = u"OK"_qs);
|
void status(uint code = 200, const QString &text = u"OK"_s);
|
||||||
void setHeader(const Header &header);
|
void setHeader(const Header &header);
|
||||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||||
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ QByteArray Http::toByteArray(Response response)
|
|||||||
{
|
{
|
||||||
compressContent(response);
|
compressContent(response);
|
||||||
|
|
||||||
response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length());
|
|
||||||
response.headers[HEADER_DATE] = httpDate();
|
response.headers[HEADER_DATE] = httpDate();
|
||||||
|
if (QString &value = response.headers[HEADER_CONTENT_LENGTH]; value.isEmpty())
|
||||||
|
value = QString::number(response.content.length());
|
||||||
|
|
||||||
QByteArray buf;
|
QByteArray buf;
|
||||||
buf.reserve(1024 + response.content.length());
|
buf.reserve(1024 + response.content.length());
|
||||||
@@ -63,7 +64,7 @@ QByteArray Http::toByteArray(Response response)
|
|||||||
// the first empty line
|
// the first empty line
|
||||||
buf += CRLF;
|
buf += CRLF;
|
||||||
|
|
||||||
// message body // TODO: support HEAD request
|
// message body
|
||||||
buf += response.content;
|
buf += response.content;
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
@@ -74,7 +75,7 @@ QString Http::httpDate()
|
|||||||
// [RFC 7231] 7.1.1.1. Date/Time Formats
|
// [RFC 7231] 7.1.1.1. Date/Time Formats
|
||||||
// example: "Sun, 06 Nov 1994 08:49:37 GMT"
|
// example: "Sun, 06 Nov 1994 08:49:37 GMT"
|
||||||
|
|
||||||
return QLocale::c().toString(QDateTime::currentDateTimeUtc(), u"ddd, dd MMM yyyy HH:mm:ss"_qs)
|
return QLocale::c().toString(QDateTime::currentDateTimeUtc(), u"ddd, dd MMM yyyy HH:mm:ss"_s)
|
||||||
.append(u" GMT");
|
.append(u" GMT");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,5 +107,5 @@ void Http::compressContent(Response &response)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
response.content = compressedData;
|
response.content = compressedData;
|
||||||
response.headers[HEADER_CONTENT_ENCODING] = u"gzip"_qs;
|
response.headers[HEADER_CONTENT_ENCODING] = u"gzip"_s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,28 +55,28 @@ namespace
|
|||||||
|
|
||||||
QList<QSslCipher> safeCipherList()
|
QList<QSslCipher> safeCipherList()
|
||||||
{
|
{
|
||||||
const QStringList badCiphers {u"idea"_qs, u"rc4"_qs};
|
const QStringList badCiphers {u"idea"_s, u"rc4"_s};
|
||||||
// Contains Ciphersuites that use RSA for the Key Exchange but they don't mention it in their name
|
// Contains Ciphersuites that use RSA for the Key Exchange but they don't mention it in their name
|
||||||
const QStringList badRSAShorthandSuites {
|
const QStringList badRSAShorthandSuites {
|
||||||
u"AES256-GCM-SHA384"_qs, u"AES128-GCM-SHA256"_qs, u"AES256-SHA256"_qs,
|
u"AES256-GCM-SHA384"_s, u"AES128-GCM-SHA256"_s, u"AES256-SHA256"_s,
|
||||||
u"AES128-SHA256"_qs, u"AES256-SHA"_qs, u"AES128-SHA"_qs};
|
u"AES128-SHA256"_s, u"AES256-SHA"_s, u"AES128-SHA"_s};
|
||||||
// Contains Ciphersuites that use AES CBC mode but they don't mention it in their name
|
// Contains Ciphersuites that use AES CBC mode but they don't mention it in their name
|
||||||
const QStringList badAESShorthandSuites {
|
const QStringList badAESShorthandSuites {
|
||||||
u"ECDHE-ECDSA-AES256-SHA384"_qs, u"ECDHE-RSA-AES256-SHA384"_qs, u"DHE-RSA-AES256-SHA256"_qs,
|
u"ECDHE-ECDSA-AES256-SHA384"_s, u"ECDHE-RSA-AES256-SHA384"_s, u"DHE-RSA-AES256-SHA256"_s,
|
||||||
u"ECDHE-ECDSA-AES128-SHA256"_qs, u"ECDHE-RSA-AES128-SHA256"_qs, u"DHE-RSA-AES128-SHA256"_qs,
|
u"ECDHE-ECDSA-AES128-SHA256"_s, u"ECDHE-RSA-AES128-SHA256"_s, u"DHE-RSA-AES128-SHA256"_s,
|
||||||
u"ECDHE-ECDSA-AES256-SHA"_qs, u"ECDHE-RSA-AES256-SHA"_qs, u"DHE-RSA-AES256-SHA"_qs,
|
u"ECDHE-ECDSA-AES256-SHA"_s, u"ECDHE-RSA-AES256-SHA"_s, u"DHE-RSA-AES256-SHA"_s,
|
||||||
u"ECDHE-ECDSA-AES128-SHA"_qs, u"ECDHE-RSA-AES128-SHA"_qs, u"DHE-RSA-AES128-SHA"_qs};
|
u"ECDHE-ECDSA-AES128-SHA"_s, u"ECDHE-RSA-AES128-SHA"_s, u"DHE-RSA-AES128-SHA"_s};
|
||||||
const QList<QSslCipher> allCiphers {QSslConfiguration::supportedCiphers()};
|
const QList<QSslCipher> allCiphers {QSslConfiguration::supportedCiphers()};
|
||||||
QList<QSslCipher> safeCiphers;
|
QList<QSslCipher> safeCiphers;
|
||||||
std::copy_if(allCiphers.cbegin(), allCiphers.cend(), std::back_inserter(safeCiphers),
|
std::copy_if(allCiphers.cbegin(), allCiphers.cend(), std::back_inserter(safeCiphers),
|
||||||
[&badCiphers, &badRSAShorthandSuites, &badAESShorthandSuites](const QSslCipher &cipher)
|
[&badCiphers, &badRSAShorthandSuites, &badAESShorthandSuites](const QSslCipher &cipher)
|
||||||
{
|
{
|
||||||
const QString name = cipher.name();
|
const QString name = cipher.name();
|
||||||
if (name.contains(u"-cbc-"_qs, Qt::CaseInsensitive) // AES CBC mode is considered vulnerable to BEAST attack
|
if (name.contains(u"-cbc-"_s, Qt::CaseInsensitive) // AES CBC mode is considered vulnerable to BEAST attack
|
||||||
|| name.startsWith(u"adh-"_qs, Qt::CaseInsensitive) // Key Exchange: Diffie-Hellman, doesn't support Perfect Forward Secrecy
|
|| name.startsWith(u"adh-"_s, Qt::CaseInsensitive) // Key Exchange: Diffie-Hellman, doesn't support Perfect Forward Secrecy
|
||||||
|| name.startsWith(u"aecdh-"_qs, Qt::CaseInsensitive) // Key Exchange: Elliptic Curve Diffie-Hellman, doesn't support Perfect Forward Secrecy
|
|| name.startsWith(u"aecdh-"_s, Qt::CaseInsensitive) // Key Exchange: Elliptic Curve Diffie-Hellman, doesn't support Perfect Forward Secrecy
|
||||||
|| name.startsWith(u"psk-"_qs, Qt::CaseInsensitive) // Key Exchange: Pre-Shared Key, doesn't support Perfect Forward Secrecy
|
|| name.startsWith(u"psk-"_s, Qt::CaseInsensitive) // Key Exchange: Pre-Shared Key, doesn't support Perfect Forward Secrecy
|
||||||
|| name.startsWith(u"rsa-"_qs, Qt::CaseInsensitive) // Key Exchange: Rivest Shamir Adleman (RSA), doesn't support Perfect Forward Secrecy
|
|| name.startsWith(u"rsa-"_s, Qt::CaseInsensitive) // Key Exchange: Rivest Shamir Adleman (RSA), doesn't support Perfect Forward Secrecy
|
||||||
|| badRSAShorthandSuites.contains(name, Qt::CaseInsensitive)
|
|| badRSAShorthandSuites.contains(name, Qt::CaseInsensitive)
|
||||||
|| badAESShorthandSuites.contains(name, Qt::CaseInsensitive))
|
|| badAESShorthandSuites.contains(name, Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,41 +37,42 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
inline const QString METHOD_GET = u"GET"_qs;
|
inline const QString METHOD_GET = u"GET"_s;
|
||||||
inline const QString METHOD_POST = u"POST"_qs;
|
inline const QString METHOD_POST = u"POST"_s;
|
||||||
|
|
||||||
inline const QString HEADER_CACHE_CONTROL = u"cache-control"_qs;
|
inline const QString HEADER_CACHE_CONTROL = u"cache-control"_s;
|
||||||
inline const QString HEADER_CONNECTION = u"connection"_qs;
|
inline const QString HEADER_CONNECTION = u"connection"_s;
|
||||||
inline const QString HEADER_CONTENT_DISPOSITION = u"content-disposition"_qs;
|
inline const QString HEADER_CONTENT_DISPOSITION = u"content-disposition"_s;
|
||||||
inline const QString HEADER_CONTENT_ENCODING = u"content-encoding"_qs;
|
inline const QString HEADER_CONTENT_ENCODING = u"content-encoding"_s;
|
||||||
inline const QString HEADER_CONTENT_LENGTH = u"content-length"_qs;
|
inline const QString HEADER_CONTENT_LENGTH = u"content-length"_s;
|
||||||
inline const QString HEADER_CONTENT_SECURITY_POLICY = u"content-security-policy"_qs;
|
inline const QString HEADER_CONTENT_SECURITY_POLICY = u"content-security-policy"_s;
|
||||||
inline const QString HEADER_CONTENT_TYPE = u"content-type"_qs;
|
inline const QString HEADER_CONTENT_TYPE = u"content-type"_s;
|
||||||
inline const QString HEADER_DATE = u"date"_qs;
|
inline const QString HEADER_CROSS_ORIGIN_OPENER_POLICY = u"cross-origin-opener-policy"_s;
|
||||||
inline const QString HEADER_HOST = u"host"_qs;
|
inline const QString HEADER_DATE = u"date"_s;
|
||||||
inline const QString HEADER_ORIGIN = u"origin"_qs;
|
inline const QString HEADER_HOST = u"host"_s;
|
||||||
inline const QString HEADER_REFERER = u"referer"_qs;
|
inline const QString HEADER_ORIGIN = u"origin"_s;
|
||||||
inline const QString HEADER_REFERRER_POLICY = u"referrer-policy"_qs;
|
inline const QString HEADER_REFERER = u"referer"_s;
|
||||||
inline const QString HEADER_SET_COOKIE = u"set-cookie"_qs;
|
inline const QString HEADER_REFERRER_POLICY = u"referrer-policy"_s;
|
||||||
inline const QString HEADER_X_CONTENT_TYPE_OPTIONS = u"x-content-type-options"_qs;
|
inline const QString HEADER_SET_COOKIE = u"set-cookie"_s;
|
||||||
inline const QString HEADER_X_FORWARDED_FOR = u"x-forwarded-for"_qs;
|
inline const QString HEADER_X_CONTENT_TYPE_OPTIONS = u"x-content-type-options"_s;
|
||||||
inline const QString HEADER_X_FORWARDED_HOST = u"x-forwarded-host"_qs;
|
inline const QString HEADER_X_FORWARDED_FOR = u"x-forwarded-for"_s;
|
||||||
inline const QString HEADER_X_FRAME_OPTIONS = u"x-frame-options"_qs;
|
inline const QString HEADER_X_FORWARDED_HOST = u"x-forwarded-host"_s;
|
||||||
inline const QString HEADER_X_XSS_PROTECTION = u"x-xss-protection"_qs;
|
inline const QString HEADER_X_FRAME_OPTIONS = u"x-frame-options"_s;
|
||||||
|
inline const QString HEADER_X_XSS_PROTECTION = u"x-xss-protection"_s;
|
||||||
|
|
||||||
inline const QString HEADER_REQUEST_METHOD_GET = u"GET"_qs;
|
inline const QString HEADER_REQUEST_METHOD_GET = u"GET"_s;
|
||||||
inline const QString HEADER_REQUEST_METHOD_HEAD = u"HEAD"_qs;
|
inline const QString HEADER_REQUEST_METHOD_HEAD = u"HEAD"_s;
|
||||||
inline const QString HEADER_REQUEST_METHOD_POST = u"POST"_qs;
|
inline const QString HEADER_REQUEST_METHOD_POST = u"POST"_s;
|
||||||
|
|
||||||
inline const QString CONTENT_TYPE_HTML = u"text/html"_qs;
|
inline const QString CONTENT_TYPE_HTML = u"text/html"_s;
|
||||||
inline const QString CONTENT_TYPE_CSS = u"text/css"_qs;
|
inline const QString CONTENT_TYPE_CSS = u"text/css"_s;
|
||||||
inline const QString CONTENT_TYPE_TXT = u"text/plain; charset=UTF-8"_qs;
|
inline const QString CONTENT_TYPE_TXT = u"text/plain; charset=UTF-8"_s;
|
||||||
inline const QString CONTENT_TYPE_JS = u"application/javascript"_qs;
|
inline const QString CONTENT_TYPE_JS = u"application/javascript"_s;
|
||||||
inline const QString CONTENT_TYPE_JSON = u"application/json"_qs;
|
inline const QString CONTENT_TYPE_JSON = u"application/json"_s;
|
||||||
inline const QString CONTENT_TYPE_GIF = u"image/gif"_qs;
|
inline const QString CONTENT_TYPE_GIF = u"image/gif"_s;
|
||||||
inline const QString CONTENT_TYPE_PNG = u"image/png"_qs;
|
inline const QString CONTENT_TYPE_PNG = u"image/png"_s;
|
||||||
inline const QString CONTENT_TYPE_FORM_ENCODED = u"application/x-www-form-urlencoded"_qs;
|
inline const QString CONTENT_TYPE_FORM_ENCODED = u"application/x-www-form-urlencoded"_s;
|
||||||
inline const QString CONTENT_TYPE_FORM_DATA = u"multipart/form-data"_qs;
|
inline const QString CONTENT_TYPE_FORM_DATA = u"multipart/form-data"_s;
|
||||||
|
|
||||||
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
|
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
|
||||||
inline const char CRLF[] = {0x0D, 0x0A, '\0'};
|
inline const char CRLF[] = {0x0D, 0x0A, '\0'};
|
||||||
@@ -79,10 +80,10 @@ namespace Http
|
|||||||
struct Environment
|
struct Environment
|
||||||
{
|
{
|
||||||
QHostAddress localAddress;
|
QHostAddress localAddress;
|
||||||
quint16 localPort;
|
quint16 localPort = 0;
|
||||||
|
|
||||||
QHostAddress clientAddress;
|
QHostAddress clientAddress;
|
||||||
quint16 clientPort;
|
quint16 clientPort = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UploadedFile
|
struct UploadedFile
|
||||||
@@ -123,7 +124,7 @@ namespace Http
|
|||||||
HeaderMap headers;
|
HeaderMap headers;
|
||||||
QByteArray content;
|
QByteArray content;
|
||||||
|
|
||||||
Response(uint code = 200, const QString &text = u"OK"_qs)
|
Response(uint code = 200, const QString &text = u"OK"_s)
|
||||||
: status {code, text}
|
: status {code, text}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ IconProvider::IconProvider(QObject *parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
IconProvider::~IconProvider() {}
|
|
||||||
|
|
||||||
void IconProvider::initInstance()
|
void IconProvider::initInstance()
|
||||||
{
|
{
|
||||||
if (!m_instance)
|
if (!m_instance)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
class IconProvider : public QObject
|
class IconProvider final : public QObject
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(IconProvider)
|
Q_DISABLE_COPY_MOVE(IconProvider)
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit IconProvider(QObject *parent = nullptr);
|
explicit IconProvider(QObject *parent = nullptr);
|
||||||
~IconProvider();
|
~IconProvider() = default;
|
||||||
|
|
||||||
static IconProvider *m_instance;
|
static IconProvider *m_instance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
|
|
||||||
const int MAX_LOG_MESSAGES = 20000;
|
inline const int MAX_LOG_MESSAGES = 20000;
|
||||||
|
|
||||||
namespace Log
|
namespace Log
|
||||||
{
|
{
|
||||||
@@ -51,17 +51,17 @@ namespace Log
|
|||||||
|
|
||||||
struct Msg
|
struct Msg
|
||||||
{
|
{
|
||||||
int id;
|
int id = -1;
|
||||||
MsgType type;
|
MsgType type = ALL;
|
||||||
qint64 timestamp;
|
qint64 timestamp = -1;
|
||||||
QString message;
|
QString message;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Peer
|
struct Peer
|
||||||
{
|
{
|
||||||
int id;
|
int id = -1;
|
||||||
bool blocked;
|
bool blocked = false;
|
||||||
qint64 timestamp;
|
qint64 timestamp = -1;
|
||||||
QString ip;
|
QString ip;
|
||||||
QString reason;
|
QString reason;
|
||||||
};
|
};
|
||||||
@@ -69,7 +69,7 @@ namespace Log
|
|||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Log::MsgTypes)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(Log::MsgTypes)
|
||||||
|
|
||||||
class Logger : public QObject
|
class Logger final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(Logger)
|
Q_DISABLE_COPY_MOVE(Logger)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user