mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-24 01:08:06 -06:00
Compare commits
580 Commits
release-4.
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ffd25f9e1 | ||
|
|
665ab34f25 | ||
|
|
37f227ae74 | ||
|
|
f6eb29d800 | ||
|
|
f5315d9ba7 | ||
|
|
124cc9621d | ||
|
|
3faa7226e7 | ||
|
|
6070b41c9b | ||
|
|
50ddfea617 | ||
|
|
e74ad86f14 | ||
|
|
2bd2490539 | ||
|
|
5c0378a684 | ||
|
|
2bd5aca3a4 | ||
|
|
ccb59fbad3 | ||
|
|
6c66d02aff | ||
|
|
9f03598259 | ||
|
|
8191246c19 | ||
|
|
437769ac0b | ||
|
|
f97a1103b1 | ||
|
|
9b0c9be7dd | ||
|
|
97c7f3bc67 | ||
|
|
2503271a45 | ||
|
|
d335f263f3 | ||
|
|
1c34635016 | ||
|
|
b6a35e9477 | ||
|
|
6aa8251b98 | ||
|
|
3b4bf90a13 | ||
|
|
0c71756009 | ||
|
|
2f6abb8aa1 | ||
|
|
42582e21f7 | ||
|
|
af49a4dd5a | ||
|
|
b2081faf87 | ||
|
|
f960934eb9 | ||
|
|
5522725f5d | ||
|
|
9f3d36bab0 | ||
|
|
87e7085c22 | ||
|
|
d8eac56f5e | ||
|
|
c20557f690 | ||
|
|
65bdc4cf43 | ||
|
|
ee79c196df | ||
|
|
41682bfcbb | ||
|
|
34be8c9213 | ||
|
|
aebb9f89d1 | ||
|
|
40f6201509 | ||
|
|
2e8e2b04a1 | ||
|
|
015780fc72 | ||
|
|
c64e433a69 | ||
|
|
e21f46d824 | ||
|
|
ef79546508 | ||
|
|
2993fdb169 | ||
|
|
365b1c6299 | ||
|
|
9565b695ef | ||
|
|
89cedd411e | ||
|
|
a51742b47c | ||
|
|
ea3b897d5d | ||
|
|
e432d67b3b | ||
|
|
bd7dc8b5e7 | ||
|
|
a7ac700fe1 | ||
|
|
ff87958188 | ||
|
|
f308cd392b | ||
|
|
e4bea17fb8 | ||
|
|
2c47cfbe25 | ||
|
|
b0685541d9 | ||
|
|
f097c15c61 | ||
|
|
511fa37c84 | ||
|
|
a82ca6adb2 | ||
|
|
05e3e46f5a | ||
|
|
6d399f0303 | ||
|
|
ecebfc34fa | ||
|
|
59b53f3db4 | ||
|
|
e86916a7f9 | ||
|
|
11ae073c12 | ||
|
|
aec53b2849 | ||
|
|
0f42ab230e | ||
|
|
211c92c387 | ||
|
|
2f1ec09aef | ||
|
|
4561d844e4 | ||
|
|
3caa57358f | ||
|
|
94195d5339 | ||
|
|
c9a552c89c | ||
|
|
6b123921a4 | ||
|
|
383eaf44ac | ||
|
|
223d15802e | ||
|
|
bdf2f6c3e1 | ||
|
|
fdc186c92f | ||
|
|
ab6141edb7 | ||
|
|
d0dcf53575 | ||
|
|
ab906f17de | ||
|
|
28ef33b0a0 | ||
|
|
376dedebb1 | ||
|
|
137c6458f7 | ||
|
|
75e0990eb3 | ||
|
|
62a6c725d6 | ||
|
|
d4554c2e5c | ||
|
|
d6a398cf2c | ||
|
|
68e3bcbcda | ||
|
|
6864e13e6f | ||
|
|
942fad1d6f | ||
|
|
29e6b229ac | ||
|
|
a4ce5d1687 | ||
|
|
cb8d6a0939 | ||
|
|
e38128119c | ||
|
|
c7c7924d37 | ||
|
|
b5a24fd877 | ||
|
|
e1ed5b73d0 | ||
|
|
3e47d26e44 | ||
|
|
e0d17e496a | ||
|
|
6cf99cd0f2 | ||
|
|
31d84100cd | ||
|
|
0eb39aa9e3 | ||
|
|
f457069881 | ||
|
|
47eef78365 | ||
|
|
eaaadf40c9 | ||
|
|
c9e2da5f53 | ||
|
|
53b7956968 | ||
|
|
d13be829c3 | ||
|
|
e18b2ab437 | ||
|
|
6777a615d4 | ||
|
|
41f2375053 | ||
|
|
f385bd2236 | ||
|
|
35032d7eed | ||
|
|
bfd48f187a | ||
|
|
7662da52e6 | ||
|
|
7722916fad | ||
|
|
d82a1d7198 | ||
|
|
45925efe32 | ||
|
|
a64bb1a990 | ||
|
|
650bf15db8 | ||
|
|
2b8e50b296 | ||
|
|
2731eb1c05 | ||
|
|
f97aa05bdb | ||
|
|
4a68df084e | ||
|
|
83530b7adb | ||
|
|
a2ef09466f | ||
|
|
7c9488105e | ||
|
|
3f32b040dc | ||
|
|
21b3c61162 | ||
|
|
5045fa6dcd | ||
|
|
c4a3d70500 | ||
|
|
435bb34435 | ||
|
|
bb9ca7f418 | ||
|
|
2e30ed17bd | ||
|
|
0b3d088782 | ||
|
|
6514eaf565 | ||
|
|
f8e7602d96 | ||
|
|
565aef9637 | ||
|
|
82efb83c43 | ||
|
|
f29ff67585 | ||
|
|
825bf8d61e | ||
|
|
1344b31535 | ||
|
|
9b8bddf7b2 | ||
|
|
8a087a876e | ||
|
|
f8067aa592 | ||
|
|
2b837381f3 | ||
|
|
cd6959b712 | ||
|
|
3e5f8c64d4 | ||
|
|
2d1c34d8e0 | ||
|
|
48d532777a | ||
|
|
f6336a6056 | ||
|
|
cd7bdc8998 | ||
|
|
2b7b3c65cc | ||
|
|
98e02a8fed | ||
|
|
a93391e247 | ||
|
|
102cc684dd | ||
|
|
a230228441 | ||
|
|
135cad576c | ||
|
|
561fbf2cca | ||
|
|
a8127d6102 | ||
|
|
2f28d3c7b6 | ||
|
|
e59c735331 | ||
|
|
221014a2e3 | ||
|
|
e2dbfa9ace | ||
|
|
121ff2b7be | ||
|
|
45465e994e | ||
|
|
af85a8a340 | ||
|
|
5fe0e9395f | ||
|
|
1f1cabd144 | ||
|
|
c3bab70434 | ||
|
|
1d6af22813 | ||
|
|
a50798c78f | ||
|
|
379d41b6fb | ||
|
|
09da6828b8 | ||
|
|
9f386afe9c | ||
|
|
35dedd3d83 | ||
|
|
45c0d5a823 | ||
|
|
7bc5bfa140 | ||
|
|
fda6def384 | ||
|
|
4bf4d45389 | ||
|
|
4e8eb2c996 | ||
|
|
e43a9de2f3 | ||
|
|
1a9e97ee3b | ||
|
|
901fca2d12 | ||
|
|
c23f8542b2 | ||
|
|
0189606445 | ||
|
|
9b31496b22 | ||
|
|
b9676ac3eb | ||
|
|
764aabc459 | ||
|
|
61d2ff359b | ||
|
|
87ad8a1495 | ||
|
|
5d889e4a8f | ||
|
|
b65a714d17 | ||
|
|
ea1c4a8fc8 | ||
|
|
5beb1b2cd0 | ||
|
|
f55e0b6775 | ||
|
|
d124041726 | ||
|
|
7a26a92edd | ||
|
|
43c8ac0aa1 | ||
|
|
46e6ed480a | ||
|
|
9f30aba2b3 | ||
|
|
560ecbc6c3 | ||
|
|
8d9b6cca63 | ||
|
|
3972597163 | ||
|
|
b0d17221f2 | ||
|
|
be2cdca1e9 | ||
|
|
f6ab1d63e8 | ||
|
|
cf5e833898 | ||
|
|
6ed2e2694f | ||
|
|
04827188f2 | ||
|
|
560239c918 | ||
|
|
a78929dadf | ||
|
|
349cc54c69 | ||
|
|
a8ae97ba91 | ||
|
|
aad6b69f00 | ||
|
|
d74df935f4 | ||
|
|
5c8806b307 | ||
|
|
a078633a32 | ||
|
|
f022458383 | ||
|
|
6139d0d65a | ||
|
|
86a283b4ae | ||
|
|
bb39a41e9e | ||
|
|
1568e98d43 | ||
|
|
0c9ecd1d76 | ||
|
|
799e67bbca | ||
|
|
561b597031 | ||
|
|
6d6908e625 | ||
|
|
4da4fb0676 | ||
|
|
7a8c05dc7c | ||
|
|
d7bacdcbff | ||
|
|
73e927ff19 | ||
|
|
e2c785b2d5 | ||
|
|
20e9952d98 | ||
|
|
0bf36ad031 | ||
|
|
f3435c5e35 | ||
|
|
166ec74ff9 | ||
|
|
6b3c6c12ff | ||
|
|
5161758193 | ||
|
|
bea32cfe38 | ||
|
|
8cfd803222 | ||
|
|
9a567721a8 | ||
|
|
3a49c8f2da | ||
|
|
12938799a6 | ||
|
|
89807fb55f | ||
|
|
891a24c6ba | ||
|
|
752b45083c | ||
|
|
f00f552369 | ||
|
|
5c2b81d78f | ||
|
|
a2b0531d5f | ||
|
|
752eb58ec0 | ||
|
|
7373b60522 | ||
|
|
59f0961594 | ||
|
|
b776f98df8 | ||
|
|
7400284cff | ||
|
|
da87eb7b4c | ||
|
|
e1f9083c81 | ||
|
|
614376ed64 | ||
|
|
529dd6e3a8 | ||
|
|
acb1bc0c0e | ||
|
|
8233f60569 | ||
|
|
4ae2160372 | ||
|
|
1df2dd9593 | ||
|
|
d53a1d1412 | ||
|
|
de9b43984a | ||
|
|
4a0b36a50b | ||
|
|
fffe5e7003 | ||
|
|
0bac639a04 | ||
|
|
0d0d0a7c23 | ||
|
|
c8e8a44747 | ||
|
|
173f8b093f | ||
|
|
e46c88580a | ||
|
|
e6033c952e | ||
|
|
3dfd0ff3b3 | ||
|
|
dd65f35e5a | ||
|
|
0d550c9bce | ||
|
|
2aeb8b9390 | ||
|
|
afa2fc0ba9 | ||
|
|
6a45919b25 | ||
|
|
e4f7d607e1 | ||
|
|
0690ef31d1 | ||
|
|
c3f02d833c | ||
|
|
e273ac3a0d | ||
|
|
459d02abc8 | ||
|
|
f5e8b26a55 | ||
|
|
2d27083509 | ||
|
|
ce482c20cb | ||
|
|
c2f149cca3 | ||
|
|
ab0e1ec6e8 | ||
|
|
5e4f548321 | ||
|
|
e53634ecef | ||
|
|
03e7019182 | ||
|
|
82b0bc63a3 | ||
|
|
04275e7d5d | ||
|
|
7a471ea6b2 | ||
|
|
45874fa333 | ||
|
|
74a5c6e745 | ||
|
|
cfafe90fe0 | ||
|
|
12d396ffc5 | ||
|
|
dc39b9e643 | ||
|
|
e6a8c02745 | ||
|
|
88d695f7af | ||
|
|
b673e0c219 | ||
|
|
5c819f7242 | ||
|
|
40bd2039d4 | ||
|
|
53f29613c2 | ||
|
|
3371709472 | ||
|
|
50c009265e | ||
|
|
e169c0ce5e | ||
|
|
f04d4b10e4 | ||
|
|
f3e4338efc | ||
|
|
c8979a6a49 | ||
|
|
5b495e2f51 | ||
|
|
42637a642b | ||
|
|
2375e7c100 | ||
|
|
ae1b852821 | ||
|
|
44e4dd3700 | ||
|
|
24d7d599f1 | ||
|
|
d85a41ad75 | ||
|
|
478ddfe102 | ||
|
|
198f832c3d | ||
|
|
f0b78ffc04 | ||
|
|
a445311705 | ||
|
|
1fe1fa9eac | ||
|
|
6258652c7b | ||
|
|
28d31b9d5b | ||
|
|
75426cc498 | ||
|
|
ae29e8bbab | ||
|
|
2b9c7e04a4 | ||
|
|
d1aba56096 | ||
|
|
3985d58d3c | ||
|
|
1479b61214 | ||
|
|
15a249eb54 | ||
|
|
08b3d6bbb0 | ||
|
|
710c5e2c31 | ||
|
|
17fa615bd3 | ||
|
|
6bfed97710 | ||
|
|
f9e286123f | ||
|
|
613fd1bcf0 | ||
|
|
52ce52d466 | ||
|
|
cede5ac9d2 | ||
|
|
89559eae2b | ||
|
|
d7fb2e6403 | ||
|
|
e3119b457c | ||
|
|
ae27a5b7b7 | ||
|
|
302cb27e98 | ||
|
|
2d3481b9a9 | ||
|
|
9b67e988db | ||
|
|
15f1fdddd9 | ||
|
|
24fa9e32b0 | ||
|
|
0b4fef19f6 | ||
|
|
35731b96dc | ||
|
|
b79a1b5755 | ||
|
|
1561f6f09f | ||
|
|
36d7fce909 | ||
|
|
b8d6058b28 | ||
|
|
8771e1a339 | ||
|
|
7b657c942d | ||
|
|
531ae501ad | ||
|
|
d0cac421bb | ||
|
|
4429a16ca8 | ||
|
|
c669401767 | ||
|
|
4d349f5f81 | ||
|
|
08e0349ca3 | ||
|
|
ee5fe424e8 | ||
|
|
552ff0489d | ||
|
|
04a9ce6e81 | ||
|
|
586bdc0567 | ||
|
|
4bb3d13921 | ||
|
|
7c02630186 | ||
|
|
ff63ad8b97 | ||
|
|
bdf1fb6db8 | ||
|
|
d21fdb7546 | ||
|
|
be5af2796d | ||
|
|
b1020c599f | ||
|
|
b2199202ab | ||
|
|
06105072f9 | ||
|
|
b676ca7d96 | ||
|
|
90f355cfaf | ||
|
|
757ab3dc92 | ||
|
|
e022c371ff | ||
|
|
50a2cc9917 | ||
|
|
5209b0172b | ||
|
|
cfb55d9d77 | ||
|
|
df2bbe129d | ||
|
|
f7cae610a4 | ||
|
|
bb698d682c | ||
|
|
5bd6ff2285 | ||
|
|
e6cf186c23 | ||
|
|
e8b5508463 | ||
|
|
d70b893852 | ||
|
|
a579b4a519 | ||
|
|
fffa2f097e | ||
|
|
cabb2198b0 | ||
|
|
3af2168b02 | ||
|
|
f1337524f6 | ||
|
|
348109a1f9 | ||
|
|
0b0597be0c | ||
|
|
690dbc4725 | ||
|
|
cc9b1ea8a1 | ||
|
|
ffebe82586 | ||
|
|
4453e7fcdd | ||
|
|
60d65d8137 | ||
|
|
c8eefe749f | ||
|
|
162421a59a | ||
|
|
79048812e9 | ||
|
|
70b242f190 | ||
|
|
8db4bde15d | ||
|
|
fbb8f0cbf5 | ||
|
|
aafa12eb6e | ||
|
|
76f285f19c | ||
|
|
e1073de36f | ||
|
|
ae48e49cba | ||
|
|
4180db601d | ||
|
|
423983e023 | ||
|
|
90a1ea4281 | ||
|
|
0ebd864db9 | ||
|
|
02e85913da | ||
|
|
1d5dc283fe | ||
|
|
769f0a78d4 | ||
|
|
4029f86c60 | ||
|
|
f6d1fea9b7 | ||
|
|
fa6524d377 | ||
|
|
c56cb8adb6 | ||
|
|
78638a15be | ||
|
|
d4a51979bb | ||
|
|
77555cd5c2 | ||
|
|
7c48ba2f19 | ||
|
|
4dbe0a0d0e | ||
|
|
ca92a74a39 | ||
|
|
c01d28a471 | ||
|
|
98ff09931d | ||
|
|
20ae89c2a1 | ||
|
|
0baa23f553 | ||
|
|
10fbb6a2a8 | ||
|
|
6152b83405 | ||
|
|
aed25ff87c | ||
|
|
5f94238d23 | ||
|
|
4d1d5d6b20 | ||
|
|
5ba6a5fca1 | ||
|
|
cba8d83b21 | ||
|
|
aaeffe3846 | ||
|
|
6881e8fbe3 | ||
|
|
137e455f03 | ||
|
|
ff3d0346eb | ||
|
|
498da509db | ||
|
|
d484c0e7ce | ||
|
|
5c1c561d7d | ||
|
|
9f0429ca6f | ||
|
|
3485ad39d9 | ||
|
|
d899923876 | ||
|
|
4e04cd27c9 | ||
|
|
7e4b428a3e | ||
|
|
9f65a318da | ||
|
|
2f6ed86c78 | ||
|
|
6590915b15 | ||
|
|
0c3fe54b0b | ||
|
|
a93b675cb8 | ||
|
|
0f2df23800 | ||
|
|
e4e0a24416 | ||
|
|
6aa5abf298 | ||
|
|
bd672c4c4e | ||
|
|
8d768bda31 | ||
|
|
5110994f81 | ||
|
|
83d17b5c0e | ||
|
|
e6c174c33b | ||
|
|
9497300a4a | ||
|
|
acab62e345 | ||
|
|
0e8feed2f2 | ||
|
|
82716d8014 | ||
|
|
af262e9a14 | ||
|
|
d0d5af8c66 | ||
|
|
86f9b1f6db | ||
|
|
c4485d0af7 | ||
|
|
7c5d0a0e00 | ||
|
|
5f014a2056 | ||
|
|
eaaacd71a8 | ||
|
|
75cead9266 | ||
|
|
d79c5824b8 | ||
|
|
4381739b6d | ||
|
|
8f2cdcef0e | ||
|
|
990cc41e80 | ||
|
|
9721acbf63 | ||
|
|
be5cb1683b | ||
|
|
a4e7e546ff | ||
|
|
4259b4571c | ||
|
|
2414a79578 | ||
|
|
84623ac1f6 | ||
|
|
d46343fb9c | ||
|
|
5c788a6130 | ||
|
|
281cf584ec | ||
|
|
13c4581c86 | ||
|
|
9c938b91b7 | ||
|
|
f71f7a0b63 | ||
|
|
b8c03a1905 | ||
|
|
9515b40ca5 | ||
|
|
df3d3db776 | ||
|
|
e15df81351 | ||
|
|
77ec423510 | ||
|
|
c41df9ffbd | ||
|
|
acad35c5bc | ||
|
|
6b3fe68a17 | ||
|
|
6ec07d744e | ||
|
|
989fdb5895 | ||
|
|
e60b91b5e2 | ||
|
|
0be2567d97 | ||
|
|
61770ad69e | ||
|
|
85bd0feee0 | ||
|
|
b249216db6 | ||
|
|
2a2a80b0bf | ||
|
|
7ddd5e9bc3 | ||
|
|
6b4a4517ec | ||
|
|
7c1c91ac43 | ||
|
|
6b56768e9c | ||
|
|
b2b7d02c01 | ||
|
|
8bb097fd10 | ||
|
|
a5f8f1f0f5 | ||
|
|
424e2c76fa | ||
|
|
41ae2bfb84 | ||
|
|
4bdf9eda41 | ||
|
|
ef1c7eec74 | ||
|
|
87527fccc6 | ||
|
|
f23234d0df | ||
|
|
fdbc3a692f | ||
|
|
571aaea3e0 | ||
|
|
d0986297ca | ||
|
|
188e679ea3 | ||
|
|
4209a5699a | ||
|
|
80ca7796ca | ||
|
|
02f19bfbee | ||
|
|
49d5591f48 | ||
|
|
816bc45707 | ||
|
|
73e9dce143 | ||
|
|
9f0edde12b | ||
|
|
cf55b67cee | ||
|
|
9ca415c665 | ||
|
|
af029e6c3f | ||
|
|
6d514c97f8 | ||
|
|
e812ac2c0b | ||
|
|
480832318c | ||
|
|
7a3e397949 | ||
|
|
c95e450b8d | ||
|
|
b63a34110e | ||
|
|
0a2a71e83b | ||
|
|
831bf71ce6 | ||
|
|
172eda5471 | ||
|
|
aa899f0693 | ||
|
|
82602b51bf | ||
|
|
3971a12f0e | ||
|
|
70c4eb44fd | ||
|
|
9ce54162c2 | ||
|
|
92c6fc04a8 | ||
|
|
46ebf9b7e5 | ||
|
|
799ad0feff | ||
|
|
1a56385cf9 | ||
|
|
71827fe4e6 | ||
|
|
8568f7a0fb | ||
|
|
39c61327b5 | ||
|
|
81d2130b06 | ||
|
|
536f5ff091 | ||
|
|
7e258bfe38 | ||
|
|
66f0e6a8f2 | ||
|
|
04306a544f | ||
|
|
e4fe3bfe35 | ||
|
|
b636bf4b2a | ||
|
|
05c7796909 | ||
|
|
e2b3463c05 | ||
|
|
6c016cf443 | ||
|
|
fc48168153 | ||
|
|
096e6f2f80 | ||
|
|
4a00bfcc55 | ||
|
|
6ebc19fccb | ||
|
|
0fa40c9ac3 |
@@ -13,8 +13,8 @@ environment:
|
||||
REPO_DIR: &REPO_DIR c:\qbittorrent
|
||||
CACHE_DIR: &CACHE_DIR c:\qbt_cache
|
||||
|
||||
QBT_VER_URL: https://builds.shiki.hu/appveyor/version
|
||||
QBT_LIB_URL: https://builds.shiki.hu/appveyor/qbt_libraries.7z
|
||||
QBT_VER_URL: https://builds.shiki.hu/appveyor/version_64
|
||||
QBT_LIB_URL: https://builds.shiki.hu/appveyor/qbt_libraries_64.7z
|
||||
|
||||
# project directory
|
||||
clone_folder: *REPO_DIR
|
||||
@@ -38,12 +38,12 @@ install:
|
||||
appveyor DownloadFile "%QBT_LIB_URL%" -FileName "c:\qbt_lib.7z" && 7z x "c:\qbt_lib.7z" -o"%CACHE_DIR%" > nul &&
|
||||
COPY "c:\version_new" "%CACHE_DIR%\version")
|
||||
# Qt stay compressed in cache
|
||||
- 7z x "%CACHE_DIR%\qt5_32.7z" -o"c:\qbt" > nul
|
||||
- 7z x "%CACHE_DIR%\qt5_64.7z" -o"c:\qbt" > nul
|
||||
|
||||
before_build:
|
||||
# setup env
|
||||
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"
|
||||
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
|
||||
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
- SET PATH=%PATH%;c:\qbt\qt5_64\bin;%CACHE_DIR%\jom;
|
||||
# setup project
|
||||
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
|
||||
# workarounds
|
||||
|
||||
19
.github/ISSUE_TEMPLATE.md
vendored
19
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,19 +0,0 @@
|
||||
**Please provide the following information**
|
||||
|
||||
### qBittorrent version and Operating System
|
||||
(type here)
|
||||
|
||||
### If on linux, libtorrent-rasterbar and Qt version
|
||||
(type here)
|
||||
|
||||
### What is the problem
|
||||
(type here)
|
||||
|
||||
### What is the expected behavior
|
||||
(type here)
|
||||
|
||||
### Steps to reproduce
|
||||
(type here)
|
||||
|
||||
### Extra info(if any)
|
||||
(type here)
|
||||
89
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
89
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a problem with qBittorrent to help us resolve it.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Bug report
|
||||
|
||||
<!--
|
||||
###############################################################################
|
||||
WARNING!
|
||||
IGNORING THE INSTRUCTIONS IN THIS TEMPLATE WILL RESULT IN THE ISSUE BEING
|
||||
CLOSED AS INCOMPLETE/INVALID
|
||||
###############################################################################
|
||||
-->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--
|
||||
################################## IMPORTANT ##################################
|
||||
|
||||
As you read and fulfill each of the following requirements below,
|
||||
put an "x" between the square brackets to mark each task as done, like so: [x]
|
||||
-->
|
||||
|
||||
- [ ] I have read the **issue reporting section** in the [contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md), so I know how to submit a good bug report with the required information
|
||||
- [ ] I have verified that the **issue is not fixed and is reproducible** in the **[latest version](https://www.qbittorrent.org/download.php)**
|
||||
- [ ] (optional but recommended) I have verified that the **issue is not fixed and is reproducible** in the **[latest CI build](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci.yaml?query=branch%3Amaster+event%3Apush)**
|
||||
- [ ] I have **checked the [frequent/common issues list](https://github.com/qbittorrent/qBittorrent/projects/2)** and **searched** the issue tracker for similar bug reports (including closed ones) **to avoid posting a duplicate**
|
||||
- [ ] This report is **not a support request or question**, both of which are better suited for either the [discussions section](https://github.com/qbittorrent/qBittorrent/discussions), [forum](https://qbforums.shiki.hu/), or [subreddit](https://www.reddit.com/r/qBittorrent/). The [wiki](https://github.com/qbittorrent/qBittorrent/wiki) did not contain a suitable solution either
|
||||
- [ ] I have **pasted/attached the settings file and relevant log(s)** in the **Attachments** section at the bottom of the report. Mandatory: the settings file and at least the most recent log. See [this wiki page](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings) if you're not sure where to find them.
|
||||
|
||||
## Description
|
||||
|
||||
<!--
|
||||
################################## IMPORTANT ##################################
|
||||
|
||||
Delete each "(type here)" indicator and type your text in their place in the subsections below.
|
||||
You MUST fill in ALL subsections marked with "(type here)" with the appropriate information.
|
||||
|
||||
Please make sure the description is worded well enough to be understood.
|
||||
Provide steps to reproduce the issue, any additional relevant information, suggested solution (if applicable) and as much context and examples as possible.
|
||||
For more information consult the Contributing Guidelines at https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md.
|
||||
|
||||
Do not forget about the mandatory attachments!
|
||||
|
||||
Use the Preview tab before posting to make sure your report looks like it is formatted properly.
|
||||
You don't need to delete these comments, they won't show up in the final post.
|
||||
-->
|
||||
|
||||
### qBittorrent info and operating system(s)
|
||||
|
||||
<!--
|
||||
IMPORTANT:
|
||||
if you did not get the qBittorrent installer from the links in the official website
|
||||
or if you did not install it from the PPA, please mention that after the version
|
||||
-->
|
||||
|
||||
- qBittorrent version: (type here)
|
||||
- Operating system(s) where the issue occurs: (type here)
|
||||
|
||||
### If on Linux, `libtorrent-rasterbar` and `Qt` versions
|
||||
|
||||
- Qt: (type here)
|
||||
- libtorrent-rasterbar: (type here)
|
||||
|
||||
### What is the problem
|
||||
|
||||
(type here)
|
||||
|
||||
### Detailed steps to reproduce the problem
|
||||
|
||||
1. (type here)
|
||||
2. (type here)
|
||||
3. (etc.)
|
||||
|
||||
### What is the expected behavior
|
||||
|
||||
(type here)
|
||||
|
||||
### Extra info (if any)
|
||||
|
||||
(type here)
|
||||
|
||||
## Attachments
|
||||
|
||||
<!-- paste file contents here (or attach the files if they are big), do NOT link to external sites -->
|
||||
20
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
20
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
-
|
||||
about: "Consult the wiki first (especially the FAQ), it might already contain the information you are looking for"
|
||||
name: Wiki
|
||||
url: "https://github.com/qbittorrent/qBittorrent/wiki/"
|
||||
|
||||
-
|
||||
about: "Please ask questions related to usage/setup/support/non-issue development discussion in the Discussions section"
|
||||
name: Question
|
||||
url: "https://github.com/qbittorrent/qBittorrent/discussions"
|
||||
|
||||
-
|
||||
about: "Alternatively, ask on the official forum"
|
||||
name: Question
|
||||
url: "http://forum.qbittorrent.org/"
|
||||
-
|
||||
about: "Alternatively, use the subreddit"
|
||||
name: Question
|
||||
url: "https://www.reddit.com/r/qBittorrent/"
|
||||
61
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
61
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest a new feature or enhancement for qBittorrent.
|
||||
title: ''
|
||||
labels: 'Feature request'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Feature request
|
||||
|
||||
<!--
|
||||
###############################################################################
|
||||
WARNING!
|
||||
IGNORING THE INSTRUCTIONS IN THIS TEMPLATE WILL RESULT IN THE ISSUE BEING
|
||||
CLOSED AS INCOMPLETE/INVALID
|
||||
###############################################################################
|
||||
-->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--
|
||||
################################## IMPORTANT ##################################
|
||||
|
||||
As you read and fulfill each of the following requirements below,
|
||||
put an "x" between the square brackets to mark each task as done, like so: [x]
|
||||
-->
|
||||
|
||||
- [ ] I have read the **feature request section** in the [contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md), so I know how to submit a good feature request with the required information
|
||||
- [ ] I have verified that the **feature** I am requesting is **not available** in the **[latest version](https://www.qbittorrent.org/download.php)**
|
||||
- [ ] (optional but recommended) I have verified that the **feature** I am requesting is **not available** in the **[latest CI build](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci.yaml?query=branch%3Amaster+event%3Apush)**
|
||||
- [ ] I have **checked the [frequent/common issues list](https://github.com/qbittorrent/qBittorrent/projects/2)** and **searched** the issue tracker for similar feature requests (including closed ones) **to avoid posting a duplicate**
|
||||
- [ ] This request is **not a support request or question**, both of which are better suited for either the [discussions section](https://github.com/qbittorrent/qBittorrent/discussions), [forum](https://qbforums.shiki.hu/), or [subreddit](https://www.reddit.com/r/qBittorrent/). The [wiki](https://github.com/qbittorrent/qBittorrent/wiki) did not contain a suitable solution either
|
||||
|
||||
## Description
|
||||
|
||||
<!--
|
||||
################################## IMPORTANT ##################################
|
||||
|
||||
Delete each "(type here)" indicator and type your text in their place in the subsections below.
|
||||
You MUST fill in ALL subsections marked with "(type here)" with the appropriate information.
|
||||
|
||||
Please make sure the description is worded well enough to be understood.
|
||||
Provide a detailed description of the feature and as much context and examples as necessary.
|
||||
If the feature request has to do with visual elements and the GUI, images/screenshots are always helpful.
|
||||
For more information consult the Contributing Guidelines at https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md.
|
||||
|
||||
Use the Preview tab before posting to make sure your report looks like it is formatted properly.
|
||||
You don't need to delete these comments, they won't show up in the final post.
|
||||
-->
|
||||
|
||||
### Suggestion
|
||||
|
||||
(type here)
|
||||
|
||||
### Use case
|
||||
|
||||
(type here)
|
||||
|
||||
### Extra info/examples/attachments
|
||||
|
||||
<!-- optional -->
|
||||
16
.github/PULL_REQUEST_TEMPLATE/pull_request.md
vendored
Normal file
16
.github/PULL_REQUEST_TEMPLATE/pull_request.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Changes proposed in this pull request
|
||||
|
||||
<!--
|
||||
IMPORTANT: an image is worth a thousand words.
|
||||
It is often a good idea to post screenshots showing the "before" and "after" your PR's changes,
|
||||
especially with changes related to the GUI, along with the textual description.
|
||||
Images makes it immediately clearer for others what your proposed changes are all about.
|
||||
-->
|
||||
|
||||
(type here)
|
||||
|
||||
<!--
|
||||
OPTIONAL: if this PR directly addresses an issue, make sure to include a "Closes #XXXXX" statement at the end.
|
||||
-->
|
||||
|
||||
<!-- You don't need to delete these comments before posting, they won't show up in the post :) -->
|
||||
18
.github/SUPPORT.md
vendored
Normal file
18
.github/SUPPORT.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Support Resources
|
||||
|
||||
The issue tracker is only for bug reports/feature requests related to the project itself.
|
||||
|
||||
Please do not use the issue tracker for questions about general program usage,
|
||||
how BitTorrent (the protocol) works in general, etc.
|
||||
|
||||
For such questions, use one of the following community support resources:
|
||||
|
||||
* The [discussions section][discussions-url]
|
||||
|
||||
* The official forum [official forum][forum-url]
|
||||
|
||||
* The [qBittorrent subreddit][subreddit-url]
|
||||
|
||||
[discussions-url]: https://github.com/qbittorrent/qBittorrent/discussions
|
||||
[forum-url]: http://forum.qbittorrent.org/
|
||||
[subreddit-url]: https://www.reddit.com/r/qBittorrent/
|
||||
254
.github/workflows/ci.yaml
vendored
Normal file
254
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
name: GitHub Actions CI
|
||||
|
||||
# Cache is used for all Windows and macOS dependencies (size approx. 1230 * 2 + 1850 = 4310 MiB)
|
||||
# Cache is not used for Ubuntu builds, because it already has all dependencies except
|
||||
# the appropriate libtorrent version, which only takes 3-5 minutes to build from source anyway
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
env:
|
||||
VCPKG_COMMIT: 8f03e2264da6b95fa5b01dd89cdd5b499458d428
|
||||
VCPKG_DEST_MACOS: /Users/runner/qbt_tools/vcpkg
|
||||
VCPKG_DEST_WIN: C:\qbt_tools\vcpkg
|
||||
LIBTORRENT_VERSION_TAG: v1.2.13
|
||||
|
||||
jobs:
|
||||
|
||||
ci_ubuntu:
|
||||
name: Ubuntu
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: install all build dependencies except libtorrent from Ubuntu repos
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install \
|
||||
build-essential cmake git ninja-build pkg-config \
|
||||
libssl-dev libgeoip-dev zlib1g-dev \
|
||||
libboost-dev libboost-chrono-dev libboost-random-dev libboost-system-dev
|
||||
# sudo apt install libqt5svg5-dev qtbase5-dev qttools5-dev # the Qt version in the standard repositories is too old...
|
||||
|
||||
# this will be installed under /opt/qt514. CMake will still find it automatically without additional hints
|
||||
# to speed up the process, only the required components are installed rather than the full qt514-meta-full metapackage
|
||||
- name: install Qt 5.14.2 from an external PPA
|
||||
run: |
|
||||
sudo add-apt-repository ppa:beineri/opt-qt-5.14.2-focal
|
||||
sudo apt install qt514base qt514svg qt514tools
|
||||
|
||||
- name: install libtorrent from source
|
||||
run: |
|
||||
git clone https://github.com/arvidn/libtorrent && cd libtorrent
|
||||
git checkout ${{ env.LIBTORRENT_VERSION_TAG }}
|
||||
cmake -B cmake-build-dir -G "Ninja" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-Ddeprecated-functions=OFF \
|
||||
--graphviz=cmake-build-dir/target_graph.dot
|
||||
cmake --build cmake-build-dir
|
||||
sudo cmake --install cmake-build-dir --prefix /usr/local
|
||||
|
||||
- name: build qBittorrent
|
||||
run: |
|
||||
cmake -B build -G "Ninja" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-D${{ matrix.qbt_gui }} \
|
||||
-DVERBOSE_CONFIGURE=ON \
|
||||
--graphviz=build/target_graph.dot
|
||||
cmake --build build
|
||||
|
||||
- name: install qBittorrent
|
||||
run: sudo cmake --install build --prefix /usr/local
|
||||
|
||||
- name: upload artifact as zip
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qBittorrent-CI_${{ matrix.os }}-x64_${{ matrix.qbt_gui }}
|
||||
path: |
|
||||
build/compile_commands.json
|
||||
build/install_manifest.txt
|
||||
build/target_graph.dot
|
||||
build/qbittorrent
|
||||
build/qbittorrent-nox
|
||||
libtorrent/cmake-build-dir/compile_commands.json
|
||||
libtorrent/cmake-build-dir/target_graph.dot
|
||||
|
||||
ci_windows:
|
||||
name: Windows + vcpkg
|
||||
|
||||
runs-on: windows-2019
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# - ninja is needed for building qBittorrent (because it's preferable, not a hard requirement)
|
||||
- name: install additional required packages with chocolatey
|
||||
run: |
|
||||
choco install ninja
|
||||
|
||||
- name: setup vcpkg (cached, if possible)
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
vcpkgDirectory: ${{ env.VCPKG_DEST_WIN }}
|
||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||
setupOnly: true
|
||||
|
||||
# Tell vcpkg to only build Release variants of the dependencies
|
||||
- name: configure vcpkg triplet overlay for release builds only
|
||||
run: |
|
||||
New-Item -Path ${{ github.workspace }} -Name "triplets_overlay" -ItemType Directory
|
||||
Copy-Item ${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-windows-static.cmake `
|
||||
${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake
|
||||
Add-Content ${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake `
|
||||
-Value "set(VCPKG_BUILD_TYPE release)"
|
||||
|
||||
# clear buildtrees after each package installation to reduce disk space requirements
|
||||
- name: install dependencies via vcpkg
|
||||
run: |
|
||||
$packages = `
|
||||
"boost-circular-buffer:x64-windows-static-release",
|
||||
"libtorrent:x64-windows-static-release",
|
||||
"qt5-base:x64-windows-static-release",
|
||||
"qt5-svg:x64-windows-static-release",
|
||||
"qt5-tools:x64-windows-static-release",
|
||||
"qt5-winextras:x64-windows-static-release"
|
||||
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
|
||||
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
|
||||
--no-dry-run
|
||||
foreach($package in $packages)
|
||||
{
|
||||
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install $package `
|
||||
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
|
||||
--clean-after-build
|
||||
}
|
||||
|
||||
# NOTE: this is necessary to correctly find and use cl.exe with the Ninja generator for now
|
||||
- name: setup devcmd
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: build qBittorrent
|
||||
shell: cmd
|
||||
run: |
|
||||
cmake -B build -G "Ninja" ^
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo ^
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DEST_WIN }}\scripts\buildsystems\vcpkg.cmake ^
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release ^
|
||||
-DVERBOSE_CONFIGURE=ON ^
|
||||
-DMSVC_RUNTIME_DYNAMIC=OFF ^
|
||||
--graphviz=build\target_graph.dot
|
||||
cmake --build build
|
||||
|
||||
- name: upload artifact as zip
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qBittorrent-CI_Windows-x64
|
||||
path: |
|
||||
build/compile_commands.json
|
||||
build/target_graph.dot
|
||||
build/qbittorrent.exe
|
||||
build/qbittorrent.pdb
|
||||
dist/windows/qt.conf
|
||||
|
||||
ci_macos:
|
||||
name: macOS + vcpkg
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
fail-fast: false
|
||||
|
||||
runs-on: macos-10.15
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# - ninja is needed for building qBittorrent (because it's preferable, not a hard requirement)
|
||||
# - automake is needed for the installation the vcpkg installation of fontconfig, a dependency of qt5-base
|
||||
- name: install additional required packages with homebrew
|
||||
shell: bash
|
||||
run: |
|
||||
brew install automake ninja
|
||||
|
||||
- name: setup vcpkg (cached, if possible)
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
vcpkgDirectory: ${{ env.VCPKG_DEST_MACOS }}
|
||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||
setupOnly: true
|
||||
|
||||
- name: configure vcpkg triplet overlay for release builds only
|
||||
run: |
|
||||
New-Item -Path ${{ github.workspace }} -Name "triplets_overlay" -ItemType Directory
|
||||
Copy-Item ${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-osx.cmake `
|
||||
${{ github.workspace }}/triplets_overlay/x64-osx-release.cmake
|
||||
Add-Content ${{ github.workspace }}/triplets_overlay/x64-osx-release.cmake `
|
||||
-Value "set(VCPKG_BUILD_TYPE release)","set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)"
|
||||
|
||||
# NOTE: Avoids a libtorrent ABI issue. See https://github.com/arvidn/libtorrent/issues/4965
|
||||
- name: force AppleClang to compile libtorrent with the same C++ standard as qBittorrent
|
||||
run: |
|
||||
(Get-Content -path ${{ env.RUNVCPKG_VCPKG_ROOT }}/ports/libtorrent/portfile.cmake).Replace( `
|
||||
'${FEATURE_OPTIONS}', '${FEATURE_OPTIONS} -DCMAKE_CXX_STANDARD=17') `
|
||||
| Set-Content -Path ${{ env.RUNVCPKG_VCPKG_ROOT }}/ports/libtorrent/portfile.cmake
|
||||
|
||||
- name: install dependencies via vcpkg
|
||||
run: |
|
||||
$packages = `
|
||||
"boost-circular-buffer:x64-osx-release",
|
||||
"libtorrent:x64-osx-release",
|
||||
"qt5-base:x64-osx-release",
|
||||
"qt5-svg:x64-osx-release",
|
||||
"qt5-tools:x64-osx-release",
|
||||
"qt5-macextras:x64-osx-release"
|
||||
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg upgrade `
|
||||
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
|
||||
--no-dry-run
|
||||
foreach($package in $packages)
|
||||
{
|
||||
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg install $package `
|
||||
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
|
||||
--clean-after-build
|
||||
}
|
||||
|
||||
- name: build qBittorrent
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -B build -G "Ninja" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DEST_MACOS }}/scripts/buildsystems/vcpkg.cmake \
|
||||
-DVCPKG_TARGET_TRIPLET=x64-osx-release \
|
||||
-D${{ matrix.qbt_gui }} \
|
||||
-DVERBOSE_CONFIGURE=ON \
|
||||
--graphviz=build/target_graph.dot
|
||||
cmake --build build
|
||||
|
||||
- name: upload artifact as zip
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}
|
||||
path: |
|
||||
build/compile_commands.json
|
||||
build/target_graph.dot
|
||||
build/qbittorrent.app
|
||||
build/qbittorrent-nox.app
|
||||
20
.github/workflows/ci_file_health.yaml
vendored
20
.github/workflows/ci_file_health.yaml
vendored
@@ -1,20 +0,0 @@
|
||||
name: CI - File health
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install zsh
|
||||
|
||||
- name: Check files
|
||||
run: |
|
||||
./.github/workflows/file_health.sh
|
||||
73
.github/workflows/ci_macos.yaml
vendored
73
.github/workflows/ci_macos.yaml
vendored
@@ -1,73 +0,0 @@
|
||||
name: CI - macOS
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: Build
|
||||
runs-on: macos-10.15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
libt_version: ["v1.2.12"]
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
openssl_root: /usr/local/opt/openssl@1.1
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew update > /dev/null
|
||||
brew install \
|
||||
cmake ninja \
|
||||
boost openssl@1.1 qt@5 zlib
|
||||
brew link --force \
|
||||
qt@5
|
||||
# workaround for cmake + Qt
|
||||
sudo ln -s /usr/local/opt/qt@5/mkspecs /usr/local/mkspecs
|
||||
sudo ln -s /usr/local/opt/qt@5/plugins /usr/local/plugins
|
||||
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone --branch ${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
|
||||
cd libtorrent
|
||||
git submodule update --init --recursive
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Ddeprecated-functions=OFF \
|
||||
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}"
|
||||
cmake --build build
|
||||
sudo cmake --install build
|
||||
|
||||
- name: Build qBittorrent
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-D${{ matrix.qbt_gui }} \
|
||||
-DVERBOSE_CONFIGURE=ON \
|
||||
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}"
|
||||
cmake --build build
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}
|
||||
path: |
|
||||
build/qbittorrent.app
|
||||
build/qbittorrent-nox.app
|
||||
84
.github/workflows/ci_ubuntu.yaml
vendored
84
.github/workflows/ci_ubuntu.yaml
vendored
@@ -1,84 +0,0 @@
|
||||
name: CI - Ubuntu
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: Build
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
libt_version: ["v1.2.12"]
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install \
|
||||
build-essential cmake git ninja-build pkg-config \
|
||||
libssl-dev libgeoip-dev zlib1g-dev \
|
||||
libboost-dev libboost-chrono-dev libboost-random-dev libboost-system-dev
|
||||
# sudo apt install libqt5svg5-dev qtbase5-dev qttools5-dev # the Qt version in the standard repositories is too old...
|
||||
|
||||
# this will be installed under /opt/qt515. CMake will still find it automatically without additional hints
|
||||
# to speed up the process, only the required components are installed rather than the full qt515-meta-full metapackage
|
||||
- name: Install Qt
|
||||
run: |
|
||||
sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-focal
|
||||
sudo apt install \
|
||||
qt515base qt515svg qt515tools
|
||||
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone --branch ${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
|
||||
cd libtorrent
|
||||
git submodule update --init --recursive
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-Ddeprecated-functions=OFF \
|
||||
--graphviz=cmake-build-dir/target_graph.dot
|
||||
cmake --build build
|
||||
sudo cmake --install build
|
||||
|
||||
- name: Build qBittorrent
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-D${{ matrix.qbt_gui }} \
|
||||
-DVERBOSE_CONFIGURE=ON \
|
||||
--graphviz=build/target_graph.dot
|
||||
cmake --build build
|
||||
|
||||
- name: Install qBittorrent
|
||||
run: sudo cmake --install build
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qBittorrent-CI_ubuntu-20.04-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}
|
||||
path: |
|
||||
build/compile_commands.json
|
||||
build/install_manifest.txt
|
||||
build/target_graph.dot
|
||||
build/qbittorrent
|
||||
build/qbittorrent-nox
|
||||
libtorrent/cmake-build-dir/compile_commands.json
|
||||
libtorrent/cmake-build-dir/target_graph.dot
|
||||
32
.github/workflows/ci_webui.yaml
vendored
32
.github/workflows/ci_webui.yaml
vendored
@@ -1,32 +0,0 @@
|
||||
name: CI - WebUI
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src/webui/www
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup nodejs
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- name: Install tools
|
||||
run: npm install
|
||||
|
||||
- name: Lint code
|
||||
run: npm run lint
|
||||
|
||||
- name: Format code
|
||||
run: |
|
||||
npm run format
|
||||
git diff --exit-code
|
||||
85
.github/workflows/ci_windows.yaml
vendored
85
.github/workflows/ci_windows.yaml
vendored
@@ -1,85 +0,0 @@
|
||||
name: CI - Windows
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: Build
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
choco install ninja
|
||||
|
||||
# use the preinstalled vcpkg from image
|
||||
# https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#package-management
|
||||
- name: Setup vcpkg
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
vcpkgDirectory: C:/vcpkg
|
||||
doNotUpdateVcpkg: true # the preinstalled vcpkg is updated regularly
|
||||
setupOnly: true
|
||||
|
||||
# tell vcpkg to only build Release variants of the dependencies
|
||||
- name: Configure vcpkg triplet overlay
|
||||
run: |
|
||||
New-Item `
|
||||
-Path "${{ github.workspace }}" `
|
||||
-Name "triplets_overlay" `
|
||||
-ItemType Directory
|
||||
Copy-Item `
|
||||
"${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-windows-static.cmake" `
|
||||
"${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake"
|
||||
Add-Content `
|
||||
"${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake" `
|
||||
-Value "set(VCPKG_BUILD_TYPE release)"
|
||||
|
||||
# clear buildtrees after each package installation to reduce disk space requirements
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
$packages = `
|
||||
"boost-circular-buffer:x64-windows-static-release",
|
||||
"libtorrent:x64-windows-static-release",
|
||||
"qt5-base:x64-windows-static-release",
|
||||
"qt5-svg:x64-windows-static-release",
|
||||
"qt5-tools:x64-windows-static-release",
|
||||
"qt5-winextras:x64-windows-static-release"
|
||||
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
|
||||
--overlay-triplets="${{ github.workspace }}/triplets_overlay" `
|
||||
--no-dry-run
|
||||
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install `
|
||||
--overlay-triplets="${{ github.workspace }}/triplets_overlay" `
|
||||
--clean-after-build `
|
||||
$packages
|
||||
|
||||
# this is necessary to correctly find and use cl.exe with the Ninja generator for now
|
||||
- name: Setup devcmd
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build qBittorrent
|
||||
run: |
|
||||
cmake `
|
||||
-B build `
|
||||
-G "Ninja" `
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
|
||||
-DCMAKE_TOOLCHAIN_FILE="${{ env.RUNVCPKG_VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" `
|
||||
-DMSVC_RUNTIME_DYNAMIC=OFF `
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release `
|
||||
-DVERBOSE_CONFIGURE=ON `
|
||||
--graphviz=build/target_graph.dot
|
||||
cmake --build build
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qBittorrent-CI_Windows-x64
|
||||
path: |
|
||||
build/compile_commands.json
|
||||
build/qbittorrent.exe
|
||||
build/qbittorrent.pdb
|
||||
build/target_graph.dot
|
||||
dist/windows/qt.conf
|
||||
4
.github/workflows/file_health.sh
vendored
4
.github/workflows/file_health.sh
vendored
@@ -11,9 +11,9 @@ regressions=0
|
||||
# exclusions (these are just grep extended regular expressions to match against paths relative to the root of the repository)
|
||||
exclusions_nonutf8='(.*(7z|gif|ic(ns|o)|png|qm|zip))'
|
||||
exclusions_bom='src/base/unicodestrings.h'
|
||||
exclusions_tw='(*.ts)|src/webui/www/private/scripts/lib/*'
|
||||
exclusions_tw='(*.ts)|src/webui/www/private/scripts/lib/mootools-1.2-more.js'
|
||||
exclusions_trailing_newline='configure'
|
||||
exclusions_no_lf='(*.ts)|(.*svg)|compile_commands.json|src/webui/www/private/scripts/lib/*'
|
||||
exclusions_no_lf='(*.ts)|(.*svg)|compile_commands.json|src/webui/www/private/scripts/lib/mootools-1.2-(core-yc.js|more.js)'
|
||||
|
||||
echo -e "\n*** Detect files not encoded in UTF-8 ***\n"
|
||||
|
||||
|
||||
22
.github/workflows/file_health.yaml
vendored
Normal file
22
.github/workflows/file_health.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: GitHub Actions file health check
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
check_file_health:
|
||||
name: Check file health
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: install zsh
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install zsh
|
||||
|
||||
- name: run check file health script
|
||||
run: |
|
||||
./.github/workflows/file_health.sh
|
||||
31
.github/workflows/webui_ci.yaml
vendored
Normal file
31
.github/workflows/webui_ci.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: WebUI CI
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
check_webui:
|
||||
name: Check WebUI
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src/webui/www
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: setup nodejs
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
|
||||
- name: install tools
|
||||
run: npm install
|
||||
|
||||
- name: lint code
|
||||
run: npm run lint
|
||||
|
||||
- name: format code
|
||||
run: |
|
||||
npm run format
|
||||
git diff --exit-code
|
||||
194
.travis.yml
Normal file
194
.travis.yml
Normal file
@@ -0,0 +1,194 @@
|
||||
language: cpp
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: focal
|
||||
osx_image: xcode12.2
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- libt_branch=RC_1_2 gui=true build_system=qmake
|
||||
- libt_branch=RC_1_2 gui=false build_system=qmake
|
||||
- libt_branch=RC_1_2 gui=true build_system=cmake
|
||||
- libt_branch=RC_1_2 gui=false build_system=cmake
|
||||
global:
|
||||
- secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
|
||||
- coverity_branch: coverity_scan
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- env: libt_branch=RC_2_0 gui=true build_system=qmake
|
||||
os: linux
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/travis/deb
|
||||
- $HOME/travis/brew
|
||||
|
||||
addons:
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "qbittorrent/qBittorrent"
|
||||
description: "Build submitted via Travis CI"
|
||||
build_command_prepend: "./bootstrap.sh && ./configure $qmake_conf"
|
||||
build_command: "make -j2"
|
||||
branch_pattern: $coverity_branch
|
||||
notification_email: sledgehammer999@qbittorrent.org
|
||||
apt:
|
||||
sources:
|
||||
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
|
||||
- sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main'
|
||||
key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc'
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.14.2-focal'
|
||||
packages:
|
||||
# packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty
|
||||
- [autoconf, automake, cmake, colormake]
|
||||
- [libboost-dev, libboost-system-dev]
|
||||
- libssl-dev
|
||||
- [qt514base, qt514svg, qt514tools]
|
||||
- zlib1g-dev
|
||||
|
||||
before_install:
|
||||
# only allow specific build for coverity scan, others will stop
|
||||
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$libt_branch" = "RC_1_2" -a "$gui" = "true" -a "$build_system" = "qmake" ]; then exit ; fi
|
||||
|
||||
- shopt -s expand_aliases
|
||||
- alias make="colormake -j2" # Using nprocs/2 sometimes may fail (gcc is killed by system)
|
||||
- qbt_path="$HOME/qbt_install"
|
||||
- qmake_conf="$qmake_conf --prefix=$qbt_path"
|
||||
- cmake_conf="$cmake_conf -DCMAKE_INSTALL_PREFIX=$qbt_path"
|
||||
|
||||
# options for specific branches
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
# setup virtual display for after_success target
|
||||
if [ "$gui" = "true" ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ;
|
||||
# CMake from Kitware is installed in /usr/bin
|
||||
# TravisCI installs its own cmake to another location which ovverides other installations
|
||||
# if they don't call the new binary directly
|
||||
alias cmake="/usr/bin/cmake"
|
||||
|
||||
# Qt 5.14.2
|
||||
PATH="/opt/qt514/bin:$PATH"
|
||||
qmake_conf="$qmake_conf PKG_CONFIG_PATH=/opt/qt514/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
cmake_conf="$cmake_conf PKG_CONFIG_PATH=/opt/qt514/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
||||
fi
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedefs"
|
||||
|
||||
openssl_root_path="/usr/local/opt/openssl"
|
||||
qmake_conf="$qmake_conf PKG_CONFIG_PATH=$openssl_root_path/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
cmake_conf="$cmake_conf -DOPENSSL_ROOT_DIR=$openssl_root_path"
|
||||
fi
|
||||
- |
|
||||
if [ "$gui" = "false" ]; then
|
||||
qmake_conf="$qmake_conf --disable-gui"
|
||||
cmake_conf="$cmake_conf -DGUI=OFF"
|
||||
fi
|
||||
|
||||
# print settings
|
||||
- echo $libt_branch
|
||||
- echo $gui
|
||||
- echo $build_system
|
||||
- echo $qmake_conf
|
||||
- echo $cmake_conf
|
||||
|
||||
install:
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
# dependencies
|
||||
PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
brew update > /dev/null
|
||||
brew upgrade cmake
|
||||
brew install ccache colormake boost openssl qt@5 zlib
|
||||
brew link --force qt@5 zlib
|
||||
|
||||
if [ "$build_system" = "cmake" ]; then
|
||||
sudo ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
|
||||
sudo ln -s /usr/local/opt/qt/plugins /usr/local/plugins
|
||||
fi
|
||||
fi
|
||||
- |
|
||||
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
|
||||
export use_ccache=true
|
||||
ccache -M 512M
|
||||
ccache -V && ccache --show-stats && ccache --zero-stats
|
||||
fi
|
||||
- |
|
||||
if [ "$libt_branch" = "RC_1_2" ]; then
|
||||
pushd "$HOME"
|
||||
git clone --single-branch --branch RC_1_2 https://github.com/arvidn/libtorrent.git
|
||||
cd libtorrent
|
||||
git checkout tags/v1.2.13
|
||||
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Ddeprecated-functions=OFF \
|
||||
-DOPENSSL_ROOT_DIR="$openssl_root_path" \
|
||||
./
|
||||
make
|
||||
sudo make install
|
||||
popd
|
||||
elif [ "$libt_branch" = "RC_2_0" ]; then
|
||||
pushd "$HOME"
|
||||
git clone --single-branch --branch RC_2_0 https://github.com/arvidn/libtorrent.git
|
||||
cd libtorrent
|
||||
git checkout tags/v2.0.3
|
||||
git submodule update --init --recursive
|
||||
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Ddeprecated-functions=OFF \
|
||||
-DOPENSSL_ROOT_DIR="$openssl_root_path" \
|
||||
./
|
||||
make
|
||||
sudo make install
|
||||
popd
|
||||
fi
|
||||
|
||||
script:
|
||||
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # skip usual build when running coverity scan
|
||||
- |
|
||||
cd "$TRAVIS_BUILD_DIR"
|
||||
if [ "$build_system" = "qmake" ]; then
|
||||
# scan only as lupdate is prone to hang
|
||||
lupdate -extensions c,cpp,h,hpp,ui ./
|
||||
./bootstrap.sh
|
||||
./configure $qmake_conf CXXFLAGS="$CXXFLAGS"
|
||||
else
|
||||
mkdir build && cd build
|
||||
cmake $cmake_conf ../
|
||||
fi
|
||||
- make
|
||||
- make install
|
||||
|
||||
after_success:
|
||||
- if [ "$gui" = "true" ]; then qbt_exe="qbittorrent" ; else qbt_exe="qbittorrent-nox" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; fi
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$build_system" = "qmake" ]; then
|
||||
macdeployqt "$TRAVIS_BUILD_DIR/src/$qbt_exe.app"
|
||||
cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS"
|
||||
else
|
||||
cd "$qbt_path/$qbt_exe.app/Contents/MacOS"
|
||||
fi
|
||||
fi
|
||||
- ./$qbt_exe --version
|
||||
|
||||
after_script:
|
||||
- if [ "$use_ccache" = true ]; then ccache --show-stats ; fi
|
||||
@@ -10,11 +10,11 @@ project(qBittorrent
|
||||
|
||||
# use CONFIG mode first in find_package
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
|
||||
# version requirements - older versions may work, but you are on your own
|
||||
# version requirements - older vesions may work, but you are on your own
|
||||
set(minBoostVersion 1.65)
|
||||
set(minQtVersion 5.11)
|
||||
set(minQtVersion 5.14)
|
||||
set(minOpenSSLVersion 1.1.1)
|
||||
set(minLibtorrentVersion 1.2.12)
|
||||
set(minLibtorrentVersion 1.2.13)
|
||||
set(minZlibVersion 1.2.11)
|
||||
|
||||
# features (some are platform-specific)
|
||||
|
||||
65
COPYING
65
COPYING
@@ -18,8 +18,8 @@ See also the AUTHORS file
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@@ -32,7 +32,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -295,62 +295,3 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
160
Changelog
160
Changelog
@@ -1,160 +1,6 @@
|
||||
Sun Aug 29 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.8
|
||||
- BUGFIX: Delay processing of watched folders (#15282) (glassez)
|
||||
- BUGFIX: Use the same icon for selecting folders/files (Chocobo1)
|
||||
- BUGFIX: Use default upper limits for ddns entries (Chocobo1)
|
||||
- WEBUI: Expose SSRF mitigation (#15247) (Sylvain Finot)
|
||||
- WEBUI: Update webui libraries (Chocobo1)
|
||||
- WEBUI: Group trackers by hostname (#15264) (Mengyang Li)
|
||||
- WEBUI: Improve "last activity" calculation in WebAPI (#15339) (Chocobo1)
|
||||
- WINDOWS: NSIS: Add Polish translation (#15262) (Matthaiks)
|
||||
|
||||
Tue Aug 03 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.7
|
||||
- BUGFIX: Don't forget to start Watched folders timer (glassez)
|
||||
- BUGFIX: Don't close tags menu when toggling items (tgregerson)
|
||||
- BUGFIX: Don't overwrite tracker message (glassez)
|
||||
- BUGFIX: Bump file pool size (#14966) (An0n)
|
||||
- BUGFIX: Properly create "clean path" for watched folder (glassez)
|
||||
- WEBUI: Disconnect comment links (Daniel Aleksandersen)
|
||||
- WINDOWS: NSIS: Update Danish translation (scootergrisen)
|
||||
|
||||
Sat Jun 26 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.6
|
||||
- FEATURE: New languages: Mongolian, Persian, Thai
|
||||
- BUGFIX: Provide correct error description in "upload mode" (glassez)
|
||||
- BUGFIX: Allow adding torrents with relative save path (glassez)
|
||||
- BUGFIX: Fix main window turns blank after restoring from tray (#15031) (Chocobo1)
|
||||
- BUGFIX: Remove the lockfile on exit (#14997) (brvphoenix)
|
||||
- BUGFIX: Improve "Watched folders" feature (glassez)
|
||||
- BUGFIX: Keep sub-sorting order (#15074) (Dmitry Khlestkov)
|
||||
- BUGFIX: Properly add torrent with new tags (glassez)
|
||||
- WINDOWS: NSIS: Update Japanese, Turkish, Hungarian, Swedish translation (maboroshin, Burak Yavuz, xkrstudio, nonew-star)
|
||||
|
||||
Sun May 02 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.5
|
||||
- BUGFIX: Move cursor to the end when autofilling URL/hash in "Download from URLs" dialog (Chocobo1)
|
||||
- BUGFIX: Sort invalid QDateTime values after valid values (Chocobo1)
|
||||
- BUGFIX: Fix tabChangesFocus attribute in "Edit trackers" dialog (Christoph Rackwitz)
|
||||
- BUGFIX: Update DynDNS register url (zhuangzi926)
|
||||
- BUGFIX: Handle "not enough disk space" error more graciously (glassez)
|
||||
- BUGFIX: Correctly draw progress background with stylesheet (jagannatharjun)
|
||||
- WEBUI: Fix magnet url from the search facility (Chocobo1)
|
||||
- WEBUI: Revise folder monitoring functions (Chocobo1)
|
||||
- WEBUI: Fix magnet url from the browser (brvphoenix)
|
||||
- WEBUI: Allow to specify file indexes in torrents/files API (glassez)
|
||||
- WINDOWS: NSIS: Allow more strings to translated (bovirus, Chocobo1)
|
||||
- WINDOWS: NSIS: Update Italian, German, Estonian, Russian, PortugueseBR translations (bovirus, Henry Water, PriitUring, Долматов Алексей, Felipe)
|
||||
- LINUX: Fix D-Bus Notification `desktop-entry` field (Chocobo1)
|
||||
- MACOS: Don't use executable name as CFBundleName value (Nick Korotysh)
|
||||
- OTHER: Lower Qt requirement to 5.11 (sledgehammer999)
|
||||
- OTHER: Clarify that the license is GPLv2+ (sledgehammer999)
|
||||
|
||||
Wed Mar 24 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.4.1
|
||||
- BUGFIX: Correctly draw progress bar (glassez)
|
||||
- WEBUI: Fix javascript code which broke the UI (Chocobo1)
|
||||
|
||||
Tue Mar 23 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.4
|
||||
- FEATURE: Add ability to prioritize selected items by shown file order (Chocobo1)
|
||||
- FEATURE: Allow tab to escape the text box in "Edit trackers" dialog (Christoph Rackwitz)
|
||||
- FEATURE: Support sub-sorting in Transferlist (jagannatharjun)
|
||||
- FEATURE: Expose ToS setting from libtorrent (Chocobo1)
|
||||
- FEATURE: Improve tracker entries handling (glassez)
|
||||
- BUGFIX: Drop extension from generated content folder name (glassez)
|
||||
- BUGFIX: Change qBittorrent Updater window title (xavier2k6)
|
||||
- BUGFIX: Validate HTTPS Tracker Certificate by default (an0n666)
|
||||
- BUGFIX: Don't let "program update" dialog steal focus (Chocobo1)
|
||||
- BUGFIX: Disable expand on double click in TorrentContentTreeView (jagannatharjun)
|
||||
- BUGFIX: Add hyperlink to Transifex on translator list (Si Yong Kim)
|
||||
- BUGFIX: Enlarge "speed limit" icon slightly (Chocobo1)
|
||||
- BUGFIX: Don't prevent system sleep due to errored torrents (dyumin)
|
||||
- BUGFIX: Use stable sorting in transfer list (Chocobo1)
|
||||
- BUGFIX: Allow "missing files" torrents to save more resume data (glassez)
|
||||
- BUGFIX: Restart "missing files" torrents after changing location (glassez)
|
||||
- BUGFIX: Show proper string when torrent availability is not available (Chocobo1)
|
||||
- BUGFIX: Apply "Hide zero/infinity values" to "Time Active", "Down/Up Limit" and ETA columns (Chocobo1)
|
||||
- BUGFIX: Fix potential out-of-bounds access (Chocobo1)
|
||||
- BUGFIX: Make SpeedPlotView averager time aware (jagannatharjun)
|
||||
- BUGFIX: Add a 3-Hour graph (jagannatharjun)
|
||||
- BUGFIX: Add an option to disable icons in menus (always disabled on MacOS) (Michał Kopeć)
|
||||
- BUGFIX: Improve detection of filename extension of audio/video files (Chocobo1)
|
||||
- BUGFIX: Various drawing improvements of progress bar (Chocobo1)
|
||||
- BUGFIX: Properly stop torrent creation if aborted (Chocobo1)
|
||||
- BUGFIX: Replace external program parameters in one step (Chocobo1)
|
||||
- BUGFIX: Improve "save resume data" handling (glassez)
|
||||
- BUGFIX: Fix bad IPv6 address format for outgoingInterfaces (treysis)
|
||||
- WEBUI: Properly decode strings (brvphoenix)
|
||||
- WEBUI: Accept "share limits" when adding torrent using WebAPI (glassez)
|
||||
- WEBUI: Add seeding time to the active time column (thalieht)
|
||||
- WEBUI: Fix incorrect seeding time string in General tab (thalieht)
|
||||
- WEBUI: Allow >100 days in WebUI function "friendlyDuration" (thalieht)
|
||||
- WEBUI: Avoid decoding strings repeatedly (brvphoenix)
|
||||
- RSS: Add category button on AutomatedRSSDownloader on GUI (Si Yong Kim)
|
||||
- WINDOWS: NSIS: Update Czech translation (slrslr)
|
||||
- WINDOWS: NSIS: Update Portuguese BR translation (Alex)
|
||||
- WINDOWS: NSIS: Add Estonian translation (PriitUring)
|
||||
- WINDOWS: Allow change-case-only file renaming (glassez)
|
||||
- LINUX: Systemd: wait for mounting of local filesystems (Juraj Oršulić)
|
||||
- OTHER: Raise minimum libtorrent version to 1.2.12 (glassez)
|
||||
- OTHER: Raise minimum Qt version to 5.12 (glassez)
|
||||
|
||||
Tue Jan 19 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.3
|
||||
- FEATURE: New languages: Azerbaijani, Estonian
|
||||
- BUGFIX: Unify global speed dialogs for normal/alternative speeds (thalieht)
|
||||
- BUGFIX: Increase maximum global speed limits ~2 GiB/s (thalieht)
|
||||
- BUGFIX: Save fastresume when setting torrent speed limits (thalieht)
|
||||
- BUGFIX: Group several torrent options into one dialog (thalieht)
|
||||
- BUGFIX: Capitalize locale names (Chocobo1)
|
||||
- BUGFIX: Improve content file/folder names handling (glassez)
|
||||
- BUGFIX: Drop notification about move storage finished or failed (glassez)
|
||||
- BUGFIX: Reload "missing files" torrent instead of re-checking (glassez)
|
||||
- BUGFIX: Remember dialog sizes (Chocobo1)
|
||||
- BUGFIX: Improve detection of file extension string (Chocobo1)
|
||||
- WEBUI: Don't call non-existent elements (glassez)
|
||||
- WEBUI: Update "Keep top-level folder" in WebUI options (thalieht)
|
||||
- MACOS: QMake: Raise minimal macOS target version to 10.14 (glassez)
|
||||
- LINUX: Use legacy 'data' directory only as a fallback (lbilli)
|
||||
- OTHER: Bump project requirement to C++17 (Chocobo1)
|
||||
|
||||
Sun Dec 27 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.2
|
||||
- FEATURE: Allow to add root folder to torrent content (glassez)
|
||||
- FEATURE: "HTTPS tracker validation" option is available on all platforms with latest libtorrent (Chocobo1)
|
||||
- FEATURE: Option for supporting internationalized domain names (IDNs) (Chocobo1)
|
||||
- BUGFIX: Fix broken sorting on some columns (Chocobo1)
|
||||
- BUGFIX: Fix availability per file value (Chocobo1)
|
||||
- BUGFIX: Fix status of torrents without metadata (sledgehammer999)
|
||||
- BUGFIX: Don't try to remove folders for a torrent without metadata (sledgehammer999)
|
||||
- BUGFIX: Lift upper limit of "Max concurrent HTTP announces" option (Chocobo1)
|
||||
- BUGFIX: Add links to libtorrent documentation (Chocobo1)
|
||||
- BUGFIX: Move "embedded tracker" options to qbt section (Chocobo1)
|
||||
- BUGFIX: Properly handle "Append extension" option changing (glassez)
|
||||
- BUGFIX: Correctly save paused torrent state (glassez)
|
||||
- BUGFIX: Fix bug of "move storage job" can be performed multiple times (glassez)
|
||||
- WEBUI: Add ability to use 'shift+delete' to delete torrents (Chocobo1)
|
||||
- WEBUI: Allow to attach tags while adding torrents (Jesse Chan)
|
||||
- WEBUI: Bump version to 2.6.2 (Jesse Chan)
|
||||
- WEBUI: Remove unnecessary restriction on input length (Chocobo1)
|
||||
- WINDOWS: NSIS: Update Russian translation (Andrei Stepanov)
|
||||
- WINDOWS: NSIS: Update Italian translation (Alessandro Simonelli)
|
||||
- OTHER: Drop support for building with libtorrent < 1.2.11 (Vladimir Golovnev)
|
||||
|
||||
Wed Nov 25 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.1
|
||||
- FEATURE: Allow progress bar styling from custom themes (jagannatharjun)
|
||||
- FEATURE: Allow adding torrents using "Paste" key sequence (Chocobo1)
|
||||
- FEATURE: Add Latgalian translation (sledgehammer999)
|
||||
- BUGFIX: Prevent resume data to be saved for removed torrent (glassez)
|
||||
- BUGFIX: Clarify connection protocol choice label (FranciscoPombal)
|
||||
- BUGFIX: Fix crash when clicked outside the table of torrent content view (jagannatharjun)
|
||||
- BUGFIX: Don't resume "paused" torrents when put into "checking" state by libtorrent (glassez)
|
||||
- BUGFIX: Fix torrent state calculation (glassez)
|
||||
- BUGFIX: Align integer data to right in torrent content view (jagannatharjun)
|
||||
- WEBUI: Place WebUI RSS description in sandboxed iframe (Sepro)
|
||||
- WEBUI: Avoid settings being reset via WebAPI (Chocobo1)
|
||||
- WEBUI: Fix toggling advanced option in WebUI (thalieht)
|
||||
- WEBUI: Expose contentPath in WebAPI torrents/info (FranciscoPombal)
|
||||
- WEBUI: Fix the issue that IPv6 address can't be banned (brvphoenix)
|
||||
- RSS: Fix confusion in date format description (Thomas De Rocker)
|
||||
- WINDOWS: Update dutch.nsi (Thomas De Rocker)
|
||||
- LINUX: Update .desktop file translations (sledgehammer999)
|
||||
|
||||
Thu Oct 22 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.0.1
|
||||
- WINDOWS: NSIS: Update Italian translation (bovirus)
|
||||
Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.4.0
|
||||
- FEATURE: Add support for creating v2 torrents(requires libtorrent 2.0.x) (Chocobo1)
|
||||
- FEATURE: Expose libtorrent hashing_threads settings (Anton Bershanskiy)
|
||||
|
||||
Sun Oct 18 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.0
|
||||
- FEATURE: Many UI elements colors are themeable now (jagannatharjun)
|
||||
|
||||
4
INSTALL
4
INSTALL
@@ -5,13 +5,13 @@ qBittorrent - A BitTorrent client in C++ / Qt
|
||||
|
||||
- Boost >= 1.65
|
||||
|
||||
- libtorrent-rasterbar >= 1.2.12 (by Arvid Norberg)
|
||||
- libtorrent-rasterbar >= 1.2.13 (by Arvid Norberg)
|
||||
* https://www.libtorrent.org/
|
||||
* Be careful: another library (the one used by rTorrent) uses a similar name
|
||||
|
||||
- OpenSSL >= 1.1.1
|
||||
|
||||
- Qt >= 5.12
|
||||
- Qt >= 5.14
|
||||
|
||||
- zlib >= 1.2.11
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
qBittorrent - A BitTorrent client in Qt
|
||||
------------------------------------------
|
||||
|
||||
[](https://travis-ci.org/qbittorrent/qBittorrent)
|
||||
[](https://ci.appveyor.com/project/qbittorrent/qBittorrent)
|
||||
[](https://github.com/qbittorrent/qBittorrent/actions)
|
||||
[](https://scan.coverity.com/projects/5494)
|
||||
@@ -45,8 +46,8 @@ http://forum.qbittorrent.org
|
||||
Please report any bug (or feature request) to:
|
||||
http://bugs.qbittorrent.org
|
||||
|
||||
Official IRC channel:
|
||||
`#qbittorrent on irc.libera.chat`
|
||||
You can also meet me (sledgehammer_999) on IRC:
|
||||
`#qbittorrent on irc.freenode.net`
|
||||
|
||||
------------------------------------------
|
||||
sledgehammer999 <sledgehammer999@qbittorrent.org>
|
||||
|
||||
@@ -17,7 +17,7 @@ macro(qbt_common_config)
|
||||
)
|
||||
|
||||
target_compile_definitions(qbt_common_cfg INTERFACE
|
||||
QT_DEPRECATED_WARNINGS
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x050e00
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_CAST_FROM_BYTEARRAY
|
||||
QT_USE_QSTRINGBUILDER
|
||||
@@ -37,7 +37,7 @@ macro(qbt_common_config)
|
||||
)
|
||||
endif()
|
||||
|
||||
if ((CXX_COMPILER_ID STREQUAL "GNU") OR (CXX_COMPILER_ID STREQUAL "Clang") OR (CXX_COMPILER_ID STREQUAL "AppleClang"))
|
||||
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
|
||||
target_compile_options(qbt_common_cfg INTERFACE
|
||||
-Wall
|
||||
-Wextra
|
||||
@@ -59,7 +59,7 @@ macro(qbt_common_config)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if ((CXX_COMPILER_ID STREQUAL "Clang") OR (CXX_COMPILER_ID STREQUAL "AppleClang"))
|
||||
if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
|
||||
target_compile_options(qbt_common_cfg INTERFACE
|
||||
-Wno-range-loop-analysis
|
||||
)
|
||||
@@ -79,6 +79,8 @@ macro(qbt_common_config)
|
||||
target_compile_options(qbt_common_cfg INTERFACE
|
||||
/guard:cf
|
||||
/utf-8
|
||||
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
|
||||
/Zc:__cplusplus
|
||||
)
|
||||
target_link_options(qbt_common_cfg INTERFACE
|
||||
/guard:cf
|
||||
|
||||
619
configure
vendored
619
configure
vendored
@@ -1,11 +1,12 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.70 for qbittorrent v4.3.8.
|
||||
# Generated by GNU Autoconf 2.71 for qbittorrent v4.4.0alpha.
|
||||
#
|
||||
# Report bugs to <bugs.qbittorrent.org>.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
|
||||
# Inc.
|
||||
#
|
||||
#
|
||||
# This configure script is free software; the Free Software Foundation
|
||||
@@ -610,8 +611,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='qbittorrent'
|
||||
PACKAGE_TARNAME='qbittorrent'
|
||||
PACKAGE_VERSION='v4.3.8'
|
||||
PACKAGE_STRING='qbittorrent v4.3.8'
|
||||
PACKAGE_VERSION='v4.4.0alpha'
|
||||
PACKAGE_STRING='qbittorrent v4.4.0alpha'
|
||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||
|
||||
@@ -637,7 +638,6 @@ openssl_LIBS
|
||||
openssl_CFLAGS
|
||||
libtorrent_LIBS
|
||||
libtorrent_CFLAGS
|
||||
BOOST_SYSTEM_LIB
|
||||
BOOST_LDFLAGS
|
||||
BOOST_CPPFLAGS
|
||||
Qt5Svg_LIBS
|
||||
@@ -757,7 +757,6 @@ enable_webui
|
||||
enable_qt_dbus
|
||||
with_boost
|
||||
with_boost_libdir
|
||||
with_boost_system
|
||||
'
|
||||
ac_precious_vars='build_alias
|
||||
host_alias
|
||||
@@ -1330,7 +1329,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures qbittorrent v4.3.8 to adapt to many kinds of systems.
|
||||
\`configure' configures qbittorrent v4.4.0alpha to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1401,7 +1400,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.3.8:";;
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.4.0alpha:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1435,10 +1434,6 @@ Optional Packages:
|
||||
this parameter only if default library detection
|
||||
fails and you know exactly where your boost
|
||||
libraries are located.
|
||||
--with-boost-system[=special-lib]
|
||||
use the System library from boost - it is possible
|
||||
to specify a certain library for the linker e.g.
|
||||
--with-boost-system=boost_system-gcc-mt
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
@@ -1455,7 +1450,7 @@ Some influential environment variables:
|
||||
directories to add to pkg-config's search path
|
||||
PKG_CONFIG_LIBDIR
|
||||
path overriding pkg-config's built-in search path
|
||||
QT_QMAKE value of host_bins for Qt5Core >= 5.11, overriding pkg-config
|
||||
QT_QMAKE value of host_bins for Qt5Core >= 5.14, overriding pkg-config
|
||||
Qt5Svg_CFLAGS
|
||||
C compiler flags for Qt5Svg, overriding pkg-config
|
||||
Qt5Svg_LIBS linker flags for Qt5Svg, overriding pkg-config
|
||||
@@ -1538,10 +1533,10 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
qbittorrent configure v4.3.8
|
||||
generated by GNU Autoconf 2.70
|
||||
qbittorrent configure v4.4.0alpha
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
This configure script is free software; the Free Software Foundation
|
||||
gives unlimited permission to copy, distribute and modify it.
|
||||
_ACEOF
|
||||
@@ -1629,53 +1624,6 @@ fi
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_cxx_try_compile
|
||||
|
||||
# ac_fn_cxx_try_link LINENO
|
||||
# -------------------------
|
||||
# Try to link conftest.$ac_ext, and return whether this succeeded.
|
||||
ac_fn_cxx_try_link ()
|
||||
{
|
||||
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
||||
rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
|
||||
if { { ac_try="$ac_link"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
|
||||
printf "%s\n" "$ac_try_echo"; } >&5
|
||||
(eval "$ac_link") 2>conftest.err
|
||||
ac_status=$?
|
||||
if test -s conftest.err; then
|
||||
grep -v '^ *+' conftest.err >conftest.er1
|
||||
cat conftest.er1 >&5
|
||||
mv -f conftest.er1 conftest.err
|
||||
fi
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; } && {
|
||||
test -z "$ac_cxx_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
} && test -s conftest$ac_exeext && {
|
||||
test "$cross_compiling" = yes ||
|
||||
test -x conftest$ac_exeext
|
||||
}
|
||||
then :
|
||||
ac_retval=0
|
||||
else $as_nop
|
||||
printf "%s\n" "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
ac_retval=1
|
||||
fi
|
||||
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
|
||||
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
|
||||
# interfere with the next link command; also delete a directory that is
|
||||
# left behind by Apple's compiler. We do this before executing the actions.
|
||||
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
|
||||
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_cxx_try_link
|
||||
ac_configure_args_raw=
|
||||
for ac_arg
|
||||
do
|
||||
@@ -1700,8 +1648,8 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by qbittorrent $as_me v4.3.8, which was
|
||||
generated by GNU Autoconf 2.70. Invocation command line was
|
||||
It was created by qbittorrent $as_me v4.4.0alpha, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
|
||||
@@ -3515,7 +3463,10 @@ else
|
||||
CFLAGS=
|
||||
fi
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
|
||||
ac_prog_cc_stdc=no
|
||||
if test x$ac_prog_cc_stdc = xno
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
|
||||
printf %s "checking for $CC option to enable C11 features... " >&6; }
|
||||
if test ${ac_cv_prog_cc_c11+y}
|
||||
then :
|
||||
@@ -3539,28 +3490,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
|
||||
done
|
||||
rm -f conftest.$ac_ext
|
||||
CC=$ac_save_CC
|
||||
|
||||
fi
|
||||
# AC_CACHE_VAL
|
||||
ac_prog_cc_stdc_options=
|
||||
case "x$ac_cv_prog_cc_c11" in #(
|
||||
x) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; } ;; #(
|
||||
xno) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; } ;; #(
|
||||
*) :
|
||||
ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c11"
|
||||
CC="$CC$ac_prog_cc_stdc_options"
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } ;;
|
||||
esac
|
||||
if test "x$ac_cv_prog_cc_c11" != xno
|
||||
|
||||
if test "x$ac_cv_prog_cc_c11" = xno
|
||||
then :
|
||||
ac_prog_cc_stdc=c11
|
||||
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; }
|
||||
else $as_nop
|
||||
if test "x$ac_cv_prog_cc_c11" = x
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; }
|
||||
else $as_nop
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
|
||||
CC="$CC $ac_cv_prog_cc_c11"
|
||||
fi
|
||||
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
|
||||
ac_prog_cc_stdc=c11
|
||||
fi
|
||||
fi
|
||||
if test x$ac_prog_cc_stdc = xno
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
|
||||
printf %s "checking for $CC option to enable C99 features... " >&6; }
|
||||
if test ${ac_cv_prog_cc_c99+y}
|
||||
@@ -3571,9 +3522,9 @@ else $as_nop
|
||||
ac_save_CC=$CC
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
$ac_c_conftest_c89_program
|
||||
$ac_c_conftest_c99_program
|
||||
_ACEOF
|
||||
for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc1x -qlanglvl=extc99
|
||||
for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
|
||||
do
|
||||
CC="$ac_save_CC $ac_arg"
|
||||
if ac_fn_c_try_compile "$LINENO"
|
||||
@@ -3585,28 +3536,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
|
||||
done
|
||||
rm -f conftest.$ac_ext
|
||||
CC=$ac_save_CC
|
||||
|
||||
fi
|
||||
# AC_CACHE_VAL
|
||||
ac_prog_cc_stdc_options=
|
||||
case "x$ac_cv_prog_cc_c99" in #(
|
||||
x) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; } ;; #(
|
||||
xno) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; } ;; #(
|
||||
*) :
|
||||
ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c99"
|
||||
CC="$CC$ac_prog_cc_stdc_options"
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } ;;
|
||||
esac
|
||||
if test "x$ac_cv_prog_cc_c99" != xno
|
||||
|
||||
if test "x$ac_cv_prog_cc_c99" = xno
|
||||
then :
|
||||
ac_prog_cc_stdc=c99
|
||||
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; }
|
||||
else $as_nop
|
||||
if test "x$ac_cv_prog_cc_c99" = x
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; }
|
||||
else $as_nop
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
|
||||
CC="$CC $ac_cv_prog_cc_c99"
|
||||
fi
|
||||
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
|
||||
ac_prog_cc_stdc=c99
|
||||
fi
|
||||
fi
|
||||
if test x$ac_prog_cc_stdc = xno
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
|
||||
printf %s "checking for $CC option to enable C89 features... " >&6; }
|
||||
if test ${ac_cv_prog_cc_c89+y}
|
||||
@@ -3619,8 +3570,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
$ac_c_conftest_c89_program
|
||||
_ACEOF
|
||||
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
|
||||
-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
|
||||
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
|
||||
do
|
||||
CC="$ac_save_CC $ac_arg"
|
||||
if ac_fn_c_try_compile "$LINENO"
|
||||
@@ -3632,34 +3582,25 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
|
||||
done
|
||||
rm -f conftest.$ac_ext
|
||||
CC=$ac_save_CC
|
||||
|
||||
fi
|
||||
# AC_CACHE_VAL
|
||||
ac_prog_cc_stdc_options=
|
||||
case "x$ac_cv_prog_cc_c89" in #(
|
||||
x) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; } ;; #(
|
||||
xno) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; } ;; #(
|
||||
*) :
|
||||
ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c89"
|
||||
CC="$CC$ac_prog_cc_stdc_options"
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } ;;
|
||||
esac
|
||||
if test "x$ac_cv_prog_cc_c89" != xno
|
||||
|
||||
if test "x$ac_cv_prog_cc_c89" = xno
|
||||
then :
|
||||
ac_prog_cc_stdc=c89
|
||||
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; }
|
||||
else $as_nop
|
||||
ac_prog_cc_stdc=no
|
||||
ac_cv_prog_cc_stdc=no
|
||||
if test "x$ac_cv_prog_cc_c89" = x
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; }
|
||||
else $as_nop
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
|
||||
CC="$CC $ac_cv_prog_cc_c89"
|
||||
fi
|
||||
|
||||
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
|
||||
ac_prog_cc_stdc=c89
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
ac_ext=c
|
||||
@@ -4009,19 +3950,22 @@ else
|
||||
CXXFLAGS=
|
||||
fi
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
|
||||
ac_prog_cxx_stdcxx=no
|
||||
if test x$ac_prog_cxx_stdcxx = xno
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
|
||||
printf %s "checking for $CXX option to enable C++11 features... " >&6; }
|
||||
if test ${ac_cv_prog_cxx_cxx11+y}
|
||||
if test ${ac_cv_prog_cxx_11+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_cv_prog_cxx_cxx11=no
|
||||
ac_cv_prog_cxx_11=no
|
||||
ac_save_CXX=$CXX
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
$ac_cxx_conftest_cxx11_program
|
||||
_ACEOF
|
||||
for ac_arg in '' -std=gnu++11 -std=c++11 -std=gnu++0x -std=c++0x -qlanglvl=extended0x -AA
|
||||
for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA
|
||||
do
|
||||
CXX="$ac_save_CXX $ac_arg"
|
||||
if ac_fn_cxx_try_compile "$LINENO"
|
||||
@@ -4033,36 +3977,35 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
|
||||
done
|
||||
rm -f conftest.$ac_ext
|
||||
CXX=$ac_save_CXX
|
||||
|
||||
fi
|
||||
# AC_CACHE_VAL
|
||||
ac_prog_cxx_stdcxx_options=
|
||||
case "x$ac_cv_prog_cxx_cxx11" in #(
|
||||
x) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; } ;; #(
|
||||
xno) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; } ;; #(
|
||||
*) :
|
||||
ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_cxx11"
|
||||
CXX=$CXX$ac_prog_cxx_stdcxx_options
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } ;;
|
||||
esac
|
||||
if test "x$ac_cv_prog_cxx_cxx11" != xno
|
||||
|
||||
if test "x$ac_cv_prog_cxx_cxx11" = xno
|
||||
then :
|
||||
ac_prog_cxx_stdcxx=cxx11
|
||||
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
|
||||
ac_cv_prog_cxx_cxx98=$ac_cv_prog_cxx_cxx11
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; }
|
||||
else $as_nop
|
||||
if test "x$ac_cv_prog_cxx_cxx11" = x
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; }
|
||||
else $as_nop
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; }
|
||||
CXX="$CXX $ac_cv_prog_cxx_cxx11"
|
||||
fi
|
||||
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
|
||||
ac_prog_cxx_stdcxx=cxx11
|
||||
fi
|
||||
fi
|
||||
if test x$ac_prog_cxx_stdcxx = xno
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
|
||||
printf %s "checking for $CXX option to enable C++98 features... " >&6; }
|
||||
if test ${ac_cv_prog_cxx_cxx98+y}
|
||||
if test ${ac_cv_prog_cxx_98+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_cv_prog_cxx_cxx98=no
|
||||
ac_cv_prog_cxx_98=no
|
||||
ac_save_CXX=$CXX
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
@@ -4080,32 +4023,25 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
|
||||
done
|
||||
rm -f conftest.$ac_ext
|
||||
CXX=$ac_save_CXX
|
||||
|
||||
fi
|
||||
# AC_CACHE_VAL
|
||||
ac_prog_cxx_stdcxx_options=
|
||||
case "x$ac_cv_prog_cxx_cxx98" in #(
|
||||
x) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; } ;; #(
|
||||
xno) :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; } ;; #(
|
||||
*) :
|
||||
ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_cxx98"
|
||||
CXX=$CXX$ac_prog_cxx_stdcxx_options
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } ;;
|
||||
esac
|
||||
if test "x$ac_cv_prog_cxx_cxx98" != xno
|
||||
|
||||
if test "x$ac_cv_prog_cxx_cxx98" = xno
|
||||
then :
|
||||
ac_prog_cxx_stdcxx=cxx98
|
||||
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
|
||||
printf "%s\n" "unsupported" >&6; }
|
||||
else $as_nop
|
||||
ac_prog_cxx_stdcxx=no
|
||||
ac_cv_prog_cxx_stdcxx=no
|
||||
if test "x$ac_cv_prog_cxx_cxx98" = x
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
|
||||
printf "%s\n" "none needed" >&6; }
|
||||
else $as_nop
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
|
||||
printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; }
|
||||
CXX="$CXX $ac_cv_prog_cxx_cxx98"
|
||||
fi
|
||||
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
|
||||
ac_prog_cxx_stdcxx=cxx98
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
ac_ext=c
|
||||
@@ -4450,12 +4386,7 @@ program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_scrip
|
||||
|
||||
|
||||
if test x"${MISSING+set}" != xset; then
|
||||
case $am_aux_dir in
|
||||
*\ * | *\ *)
|
||||
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
|
||||
*)
|
||||
MISSING="\${SHELL} $am_aux_dir/missing" ;;
|
||||
esac
|
||||
MISSING="\${SHELL} '$am_aux_dir/missing'"
|
||||
fi
|
||||
# Use eval to expand $SHELL
|
||||
if eval "$MISSING --is-lightweight"; then
|
||||
@@ -4848,7 +4779,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='qbittorrent'
|
||||
VERSION='v4.3.8'
|
||||
VERSION='v4.4.0alpha'
|
||||
|
||||
|
||||
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||
@@ -5525,8 +5456,8 @@ printf "%s\n" "$enable_webui" >&6; }
|
||||
esac
|
||||
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.11\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.11") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.14\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.14") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
@@ -5535,12 +5466,12 @@ if test -n "$QT_QMAKE"; then
|
||||
pkg_cv_QT_QMAKE="$QT_QMAKE"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.11\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.11") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.14\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.14") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_QT_QMAKE=`$PKG_CONFIG --variable="host_bins" "Qt5Core >= 5.11" 2>/dev/null`
|
||||
pkg_cv_QT_QMAKE=`$PKG_CONFIG --variable="host_bins" "Qt5Core >= 5.14" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -5570,8 +5501,8 @@ fi
|
||||
|
||||
fi
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5 qmake >= 5.11" >&5
|
||||
printf %s "checking for Qt5 qmake >= 5.11... " >&6; }
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5 qmake >= 5.14" >&5
|
||||
printf %s "checking for Qt5 qmake >= 5.14... " >&6; }
|
||||
if test "x$QT_QMAKE" != "x"
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $QT_QMAKE" >&5
|
||||
@@ -5598,12 +5529,12 @@ if test -n "$Qt5Svg_CFLAGS"; then
|
||||
pkg_cv_Qt5Svg_CFLAGS="$Qt5Svg_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.11\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.11") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.14\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.14") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_Qt5Svg_CFLAGS=`$PKG_CONFIG --cflags "Qt5Svg >= 5.11" 2>/dev/null`
|
||||
pkg_cv_Qt5Svg_CFLAGS=`$PKG_CONFIG --cflags "Qt5Svg >= 5.14" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -5615,12 +5546,12 @@ if test -n "$Qt5Svg_LIBS"; then
|
||||
pkg_cv_Qt5Svg_LIBS="$Qt5Svg_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.11\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.11") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.14\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.14") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_Qt5Svg_LIBS=`$PKG_CONFIG --libs "Qt5Svg >= 5.11" 2>/dev/null`
|
||||
pkg_cv_Qt5Svg_LIBS=`$PKG_CONFIG --libs "Qt5Svg >= 5.14" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -5641,14 +5572,14 @@ else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "Qt5Svg >= 5.11" 2>&1`
|
||||
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "Qt5Svg >= 5.14" 2>&1`
|
||||
else
|
||||
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "Qt5Svg >= 5.11" 2>&1`
|
||||
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "Qt5Svg >= 5.14" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$Qt5Svg_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (Qt5Svg >= 5.11) were not met:
|
||||
as_fn_error $? "Package requirements (Qt5Svg >= 5.14) were not met:
|
||||
|
||||
$Qt5Svg_PKG_ERRORS
|
||||
|
||||
@@ -5688,11 +5619,11 @@ case "x$enable_qt_dbus" in #(
|
||||
"xyes") :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
printf "%s\n" "yes" >&6; }
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5DBus >= 5.11" >&5
|
||||
printf %s "checking for Qt5DBus >= 5.11... " >&6; }
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5DBus >= 5.14" >&5
|
||||
printf %s "checking for Qt5DBus >= 5.14... " >&6; }
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5DBus >= 5.11\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5DBus >= 5.11") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5DBus >= 5.14\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5DBus >= 5.14") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
@@ -6100,252 +6031,6 @@ fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||
|
||||
|
||||
|
||||
# Check whether --with-boost-system was given.
|
||||
if test ${with_boost_system+y}
|
||||
then :
|
||||
withval=$with_boost_system;
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ax_boost_user_system_lib=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ax_boost_user_system_lib="$withval"
|
||||
fi
|
||||
|
||||
else $as_nop
|
||||
want_boost="yes"
|
||||
|
||||
fi
|
||||
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
|
||||
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5
|
||||
printf %s "checking whether the Boost::System library is available... " >&6; }
|
||||
if test ${ax_cv_boost_system+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_ext=cpp
|
||||
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
|
||||
CXXFLAGS_SAVE=$CXXFLAGS
|
||||
CXXFLAGS=
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <boost/system/error_code.hpp>
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
boost::system::error_category *a = 0;
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"
|
||||
then :
|
||||
ax_cv_boost_system=yes
|
||||
else $as_nop
|
||||
ax_cv_boost_system=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||
CXXFLAGS=$CXXFLAGS_SAVE
|
||||
ac_ext=cpp
|
||||
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
|
||||
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5
|
||||
printf "%s\n" "$ax_cv_boost_system" >&6; }
|
||||
if test "x$ax_cv_boost_system" = "xyes"; then
|
||||
|
||||
|
||||
|
||||
printf "%s\n" "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h
|
||||
|
||||
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'`
|
||||
|
||||
LDFLAGS_SAVE=$LDFLAGS
|
||||
if test "x$ax_boost_user_system_lib" = "x"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
|
||||
ax_lib=${libextension}
|
||||
as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ax_lib""_exit" | $as_tr_sh`
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5
|
||||
printf %s "checking for exit in -l$ax_lib... " >&6; }
|
||||
if eval test \${$as_ac_Lib+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-l$ax_lib $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
namespace conftest {
|
||||
extern "C" int exit ();
|
||||
}
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return conftest::exit ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_link "$LINENO"
|
||||
then :
|
||||
eval "$as_ac_Lib=yes"
|
||||
else $as_nop
|
||||
eval "$as_ac_Lib=no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
eval ac_res=\$$as_ac_Lib
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
printf "%s\n" "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_Lib"\" = x"yes"
|
||||
then :
|
||||
BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break
|
||||
else $as_nop
|
||||
link_system="no"
|
||||
fi
|
||||
|
||||
done
|
||||
if test "x$link_system" != "xyes"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
|
||||
ax_lib=${libextension}
|
||||
as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ax_lib""_exit" | $as_tr_sh`
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5
|
||||
printf %s "checking for exit in -l$ax_lib... " >&6; }
|
||||
if eval test \${$as_ac_Lib+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-l$ax_lib $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
namespace conftest {
|
||||
extern "C" int exit ();
|
||||
}
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return conftest::exit ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_link "$LINENO"
|
||||
then :
|
||||
eval "$as_ac_Lib=yes"
|
||||
else $as_nop
|
||||
eval "$as_ac_Lib=no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
eval ac_res=\$$as_ac_Lib
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
printf "%s\n" "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_Lib"\" = x"yes"
|
||||
then :
|
||||
BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break
|
||||
else $as_nop
|
||||
link_system="no"
|
||||
fi
|
||||
|
||||
done
|
||||
fi
|
||||
|
||||
else
|
||||
for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
|
||||
as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ax_lib""_exit" | $as_tr_sh`
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5
|
||||
printf %s "checking for exit in -l$ax_lib... " >&6; }
|
||||
if eval test \${$as_ac_Lib+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-l$ax_lib $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
namespace conftest {
|
||||
extern "C" int exit ();
|
||||
}
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return conftest::exit ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_link "$LINENO"
|
||||
then :
|
||||
eval "$as_ac_Lib=yes"
|
||||
else $as_nop
|
||||
eval "$as_ac_Lib=no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
eval ac_res=\$$as_ac_Lib
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
printf "%s\n" "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_Lib"\" = x"yes"
|
||||
then :
|
||||
BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break
|
||||
else $as_nop
|
||||
link_system="no"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
fi
|
||||
if test "x$ax_lib" = "x"; then
|
||||
as_fn_error $? "Could not find a version of the Boost::System library!" "$LINENO" 5
|
||||
fi
|
||||
if test "x$link_system" = "xno"; then
|
||||
as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Boost.System LIB: \"$BOOST_SYSTEM_LIB\"" >&5
|
||||
printf "%s\n" "$as_me: Boost.System LIB: \"$BOOST_SYSTEM_LIB\"" >&6;}
|
||||
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
||||
|
||||
|
||||
pkg_failed=no
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5
|
||||
printf %s "checking for libtorrent... " >&6; }
|
||||
@@ -6354,12 +6039,12 @@ if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.12\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.12") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.13\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.13") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.12" 2>/dev/null`
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.13" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6371,12 +6056,12 @@ if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.12\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.12") 2>&5
|
||||
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.13\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.13") 2>&5
|
||||
ac_status=$?
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.12" 2>/dev/null`
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.13" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
@@ -6397,14 +6082,14 @@ else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.12" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.13" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.12" 2>&1`
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.13" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.12) were not met:
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.13) were not met:
|
||||
|
||||
$libtorrent_PKG_ERRORS
|
||||
|
||||
@@ -6711,7 +6396,7 @@ if ac_fn_cxx_try_compile "$LINENO"
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
printf "%s\n" "no" >&6; }
|
||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++1z"
|
||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++17"
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: C++17 mode is now force enabled. The C++ mode should match the mode that other libraries were built with, otherwise you'll likely get linking errors." >&5
|
||||
printf "%s\n" "$as_me: WARNING: C++17 mode is now force enabled. The C++ mode should match the mode that other libraries were built with, otherwise you'll likely get linking errors." >&2;}
|
||||
else $as_nop
|
||||
@@ -7401,8 +7086,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by qbittorrent $as_me v4.3.8, which was
|
||||
generated by GNU Autoconf 2.70. Invocation command line was
|
||||
This file was extended by qbittorrent $as_me v4.4.0alpha, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
CONFIG_HEADERS = $CONFIG_HEADERS
|
||||
@@ -7461,11 +7146,11 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v4.3.8
|
||||
configured by $0, generated by GNU Autoconf 2.70,
|
||||
qbittorrent config.status v4.4.0alpha
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
This config.status script is free software; the Free Software Foundation
|
||||
gives unlimited permission to copy, distribute and modify it."
|
||||
|
||||
|
||||
12
configure.ac
12
configure.ac
@@ -1,4 +1,4 @@
|
||||
AC_INIT([qbittorrent], [v4.3.8], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_INIT([qbittorrent], [v4.4.0alpha], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
: ${CFLAGS=""}
|
||||
@@ -141,7 +141,7 @@ AS_IF([test "x$QT_QMAKE" = "x"],
|
||||
[AC_MSG_ERROR([Could not find qmake])
|
||||
])
|
||||
AS_IF([test "x$enable_gui" = "xyes"],
|
||||
[PKG_CHECK_MODULES(Qt5Svg, [Qt5Svg >= 5.11])
|
||||
[PKG_CHECK_MODULES(Qt5Svg, [Qt5Svg >= 5.14])
|
||||
])
|
||||
AC_MSG_CHECKING([whether QtDBus should be enabled])
|
||||
AS_CASE(["x$enable_qt_dbus"],
|
||||
@@ -175,12 +175,8 @@ m4_define([DETECT_BOOST_VERSION_PROGRAM],
|
||||
AC_COMPILE_IFELSE([DETECT_BOOST_VERSION_PROGRAM(106000)], [],
|
||||
[QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"])
|
||||
|
||||
AX_BOOST_SYSTEM()
|
||||
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
|
||||
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
||||
|
||||
PKG_CHECK_MODULES(libtorrent,
|
||||
[libtorrent-rasterbar >= 1.2.12],
|
||||
[libtorrent-rasterbar >= 1.2.13],
|
||||
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"])
|
||||
|
||||
@@ -216,7 +212,7 @@ AS_IF([test "x$QBT_CXX17_FOUND" = "xno"],
|
||||
CXXFLAGS="-std=c++17 $TMP_CXXFLAGS"
|
||||
AC_COMPILE_IFELSE([DETECT_CPP17_PROGRAM()],
|
||||
[AC_MSG_RESULT([no])
|
||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++1z"
|
||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++17"
|
||||
AC_MSG_WARN([C++17 mode is now force enabled. The C++ mode should match the mode that other libraries were built with, otherwise you'll likely get linking errors.])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
AC_MSG_ERROR([The compiler supports C++17 but the user or a dependency has explicitly enabled a lower mode.])])],
|
||||
|
||||
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -55,7 +55,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.3.8</string>
|
||||
<string>4.4.0</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
BIN
dist/mac/qBitTorrentDocument.icns
vendored
BIN
dist/mac/qBitTorrentDocument.icns
vendored
Binary file not shown.
BIN
dist/mac/qbittorrent_mac.icns
vendored
BIN
dist/mac/qbittorrent_mac.icns
vendored
Binary file not shown.
11
dist/unix/CMakeLists.txt
vendored
11
dist/unix/CMakeLists.txt
vendored
@@ -30,12 +30,6 @@ install(FILES ${MAN_FILES}
|
||||
)
|
||||
|
||||
if (GUI)
|
||||
install(DIRECTORY menuicons/
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
||||
COMPONENT data
|
||||
FILES_MATCHING PATTERN "*.png"
|
||||
)
|
||||
|
||||
install(FILES org.qbittorrent.qBittorrent.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
||||
COMPONENT data
|
||||
@@ -46,6 +40,11 @@ if (GUI)
|
||||
COMPONENT data
|
||||
)
|
||||
|
||||
install(DIRECTORY menuicons/
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
||||
COMPONENT data
|
||||
)
|
||||
|
||||
install(FILES
|
||||
${PROJECT_SOURCE_DIR}/src/icons/qbittorrent-tray.svg
|
||||
${PROJECT_SOURCE_DIR}/src/icons/qbittorrent-tray-dark.svg
|
||||
|
||||
16
dist/unix/menuicons/scalable/apps/qbittorrent.svg
vendored
Normal file
16
dist/unix/menuicons/scalable/apps/qbittorrent.svg
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<title>
|
||||
qbittorrent-new-light
|
||||
</title>
|
||||
<defs>
|
||||
<linearGradient x1="34.012%" y1="0%" x2="76.373%" y2="76.805%" id="a">
|
||||
<stop stop-color="#72B4F5" offset="0%"/>
|
||||
<stop stop-color="#356EBF" offset="100%"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle stroke="#DAEFFF" stroke-width="32" fill="url(#a)" cx="512" cy="512" r="496"/>
|
||||
<path d="M712.898 332.399q66.657 0 103.38 45.671 37.03 45.364 37.03 128.684t-37.34 129.61q-37.03 45.98-103.07 45.98-33.02 0-60.484-12.035-27.156-12.344-45.672-37.649h-3.703l-10.8 43.512h-36.724V196h51.227v116.65q0 39.191-2.469 70.359h2.47q35.796-50.61 106.155-50.61zm-7.406 42.894q-52.46 0-75.605 30.242-23.145 29.934-23.145 101.219t23.762 102.145q23.761 30.55 76.222 30.55 47.215 0 70.36-34.254 23.144-34.562 23.144-99.058 0-66.04-23.144-98.442-23.145-32.402-71.594-32.402z" fill="#fff"/>
|
||||
<path d="M317.273 639.45q51.227 0 74.68-27.466 23.453-27.464 24.996-92.578v-11.418q0-70.976-24.07-102.144-24.07-31.168-76.223-31.168-45.055 0-69.125 35.18-23.762 34.87-23.762 98.75 0 63.879 23.454 97.515 23.761 33.328 70.05 33.328zm-7.715 42.894q-65.421 0-102.144-45.98-36.723-45.981-36.723-128.376 0-83.011 37.032-129.609 37.03-46.598 103.07-46.598 69.433 0 106.773 52.461h2.778l7.406-46.289h40.426V828h-51.227V683.27q0-30.86 3.395-52.461h-4.012q-35.488 51.535-106.774 51.535z" fill="#c8e8ff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -74,6 +74,6 @@
|
||||
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
<release version="4.3.8" date="2021-08-29"/>
|
||||
<release version="4.4.0" date="2020-10-18"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
||||
@@ -177,8 +177,8 @@ Name[ur]=قیو بٹ ٹورنٹ
|
||||
Comment[uk]=Завантажуйте та поширюйте файли через BitTorrent
|
||||
GenericName[uk]=BitTorrent-клієнт
|
||||
Name[uk]=qBittorrent
|
||||
Comment[vi]=Tải về và chia sẻ tệp qua BitTorrent
|
||||
GenericName[vi]=Máy khách BitTorrent
|
||||
Comment[vi]=Tải xuống và chia sẻ tệp qua BitTorrent
|
||||
GenericName[vi]=Client BitTorrent
|
||||
Name[vi]=qBittorrent
|
||||
Comment[az@latin]=Faylları BitTorrent vasitəsilə göndərin və paylaşın
|
||||
GenericName[az@latin]=BitTorrent client
|
||||
|
||||
10
dist/windows/installer-translations/danish.nsi
vendored
10
dist/windows/installer-translations/danish.nsi
vendored
@@ -7,7 +7,7 @@ LangString inst_dekstop ${LANG_DANISH} "Opret skrivebordsgenvej"
|
||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_DANISH} "Opret genvej i menuen Start"
|
||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||
LangString inst_startup ${LANG_DANISH} "Start qBittorrent når Windows starter"
|
||||
LangString inst_startup ${LANG_DANISH} "Start qBittorrent i Windows opstart"
|
||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||
LangString inst_torrent ${LANG_DANISH} "Åbn .torrent-filer med qBittorrent"
|
||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||
@@ -15,7 +15,7 @@ LangString inst_magnet ${LANG_DANISH} "Åbn magnet-links med qBittorrent"
|
||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_DANISH} "Tilføj Windows Firewall-regel"
|
||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_DANISH} "Deaktivér grænse for for lang Windows-stien kan være (MAX_PATH-begrænsning på 260 tegn, kræver Windows 10 1607 eller senere)"
|
||||
LangString inst_pathlimit ${LANG_DANISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_DANISH} "Tilføjer Windows Firewall-regel"
|
||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||
@@ -27,11 +27,11 @@ LangString inst_unist ${LANG_DANISH} "Afinstallerer tidligere version."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_DANISH} "Start qBittorrent."
|
||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_DANISH} "Installationsprogrammet virker kun i Windows-versioner som er 64-bit."
|
||||
LangString inst_requires_64bit ${LANG_DANISH} "Dette installationsprogram virker kun i 64-bit Windows versioner."
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_DANISH} "qBittorrent-versionen kræver mindst Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_DANISH} "This qBittorrent version requires at least Windows 7."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_DANISH} "Afinstaller qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_DANISH} "Uninstall qBittorrent"
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
|
||||
54
dist/windows/installer-translations/polish.nsi
vendored
54
dist/windows/installer-translations/polish.nsi
vendored
@@ -1,60 +1,60 @@
|
||||
;Installer strings
|
||||
|
||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_POLISH} "qBittorrent (wymagany)"
|
||||
LangString inst_qbt_req ${LANG_POLISH} "qBittorrent (required)"
|
||||
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_POLISH} "Utwórz skrót na pulpicie"
|
||||
LangString inst_dekstop ${LANG_POLISH} "Create Desktop Shortcut"
|
||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_POLISH} "Utwórz skrót w menu Start"
|
||||
LangString inst_startmenu ${LANG_POLISH} "Create Start Menu Shortcut"
|
||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||
LangString inst_startup ${LANG_POLISH} "Uruchamiaj qBittorrent wraz z systemem Windows"
|
||||
LangString inst_startup ${LANG_POLISH} "Start qBittorrent on Windows start up"
|
||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||
LangString inst_torrent ${LANG_POLISH} "Otwieraj pliki .torrent za pomocą qBittorrent"
|
||||
LangString inst_torrent ${LANG_POLISH} "Open .torrent files with qBittorrent"
|
||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||
LangString inst_magnet ${LANG_POLISH} "Otwieraj odnośniki magnet za pomocą qBittorrent"
|
||||
LangString inst_magnet ${LANG_POLISH} "Open magnet links with qBittorrent"
|
||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_POLISH} "Dodaj regułę Zapory systemu Windows"
|
||||
LangString inst_firewall ${LANG_POLISH} "Add Windows Firewall rule"
|
||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_POLISH} "Wyłącz ograniczenie długości ścieżki systemu Windows (ograniczenie MAX_PATH do 260 znaków, wymaga systemu Windows 10 1607 lub nowszego)"
|
||||
LangString inst_pathlimit ${LANG_POLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_POLISH} "Dodawanie reguły Zapory systemu Windows"
|
||||
LangString inst_firewallinfo ${LANG_POLISH} "Adding Windows Firewall rule"
|
||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_POLISH} "qBittorrent jest uruchomiony. Proszę zamknąć aplikację przed instalacją."
|
||||
LangString inst_warning ${LANG_POLISH} "qBittorrent is running. Please close the application before installing."
|
||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_POLISH} "Obecna wersja zostanie odinstalowana. Ustawienia użytkownika i torrenty pozostaną nienaruszone."
|
||||
LangString inst_uninstall_question ${LANG_POLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_POLISH} "Odinstalowywanie poprzedniej wersji."
|
||||
LangString inst_unist ${LANG_POLISH} "Uninstalling previous version."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_POLISH} "Uruchom qBittorrent."
|
||||
LangString launch_qbt ${LANG_POLISH} "Launch qBittorrent."
|
||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_POLISH} "Ten instalator działa tylko w 64-bitowych wersjach systemu Windows."
|
||||
LangString inst_requires_64bit ${LANG_POLISH} "This installer works only in 64-bit Windows versions."
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_POLISH} "Ta wersja qBittorrent wymaga co najmniej systemu Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_POLISH} "This qBittorrent version requires at least Windows 7."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_POLISH} "Odinstaluj qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_POLISH} "Uninstall qBittorrent"
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
|
||||
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
||||
LangString remove_files ${LANG_POLISH} "Usuń pliki"
|
||||
LangString remove_files ${LANG_POLISH} "Remove files"
|
||||
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
||||
LangString remove_shortcuts ${LANG_POLISH} "Usuń skróty"
|
||||
LangString remove_shortcuts ${LANG_POLISH} "Remove shortcuts"
|
||||
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
||||
LangString remove_associations ${LANG_POLISH} "Usuń skojarzenia plików"
|
||||
LangString remove_associations ${LANG_POLISH} "Remove file associations"
|
||||
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
||||
LangString remove_registry ${LANG_POLISH} "Usuń klucze rejestru"
|
||||
LangString remove_registry ${LANG_POLISH} "Remove registry keys"
|
||||
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
||||
LangString remove_conf ${LANG_POLISH} "Usuń pliki konfiguracyjne"
|
||||
LangString remove_conf ${LANG_POLISH} "Remove configuration files"
|
||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_POLISH} "Usuń regułę Zapory systemu Windows"
|
||||
LangString remove_firewall ${LANG_POLISH} "Remove Windows Firewall rule"
|
||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_POLISH} "Usuwanie reguły Zapory systemu Windows"
|
||||
LangString remove_firewallinfo ${LANG_POLISH} "Removing Windows Firewall rule"
|
||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_POLISH} "Usuń torrenty i dane z pamięci podręcznej"
|
||||
LangString remove_cache ${LANG_POLISH} "Remove torrents and cached data"
|
||||
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_POLISH} "qBittorrent jest uruchomiony. Zamknij aplikację przed odinstalowaniem."
|
||||
LangString uninst_warning ${LANG_POLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_POLISH} "Bez usuwania skojarzenia .torrent. Skojarzono z:"
|
||||
LangString uninst_tor_warn ${LANG_POLISH} "Not removing .torrent association. It is associated with:"
|
||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_POLISH} "Bez usuwania skojarzenia magnet. Skojarzono z:"
|
||||
LangString uninst_mag_warn ${LANG_POLISH} "Not removing magnet association. It is associated with:"
|
||||
|
||||
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -28,7 +28,7 @@ XPStyle on
|
||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||
|
||||
; Program specific
|
||||
!define PROG_VERSION "4.3.8"
|
||||
!define PROG_VERSION "4.4.0"
|
||||
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_boost_system.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_SYSTEM
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for System library from the Boost C++ libraries. The macro requires
|
||||
# a preceding call to AX_BOOST_BASE. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_SYSTEM_LIB)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST_SYSTEM
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2008 Michael Tindal
|
||||
# Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 20
|
||||
|
||||
AC_DEFUN([AX_BOOST_SYSTEM],
|
||||
[
|
||||
AC_ARG_WITH([boost-system],
|
||||
AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@],
|
||||
[use the System library from boost - it is possible to specify a certain library for the linker
|
||||
e.g. --with-boost-system=boost_system-gcc-mt ]),
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ax_boost_user_system_lib=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ax_boost_user_system_lib="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
AC_REQUIRE([AC_CANONICAL_BUILD])
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_CACHE_CHECK(whether the Boost::System library is available,
|
||||
ax_cv_boost_system,
|
||||
[AC_LANG_PUSH([C++])
|
||||
CXXFLAGS_SAVE=$CXXFLAGS
|
||||
CXXFLAGS=
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]],
|
||||
[[boost::system::error_category *a = 0;]])],
|
||||
ax_cv_boost_system=yes, ax_cv_boost_system=no)
|
||||
CXXFLAGS=$CXXFLAGS_SAVE
|
||||
AC_LANG_POP([C++])
|
||||
])
|
||||
if test "x$ax_cv_boost_system" = "xyes"; then
|
||||
AC_SUBST(BOOST_CPPFLAGS)
|
||||
|
||||
AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available])
|
||||
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
|
||||
|
||||
LDFLAGS_SAVE=$LDFLAGS
|
||||
if test "x$ax_boost_user_system_lib" = "x"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
|
||||
ax_lib=${libextension}
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
|
||||
[link_system="no"])
|
||||
done
|
||||
if test "x$link_system" != "xyes"; then
|
||||
for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
|
||||
ax_lib=${libextension}
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
|
||||
[link_system="no"])
|
||||
done
|
||||
fi
|
||||
|
||||
else
|
||||
for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
|
||||
AC_CHECK_LIB($ax_lib, exit,
|
||||
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
|
||||
[link_system="no"])
|
||||
done
|
||||
|
||||
fi
|
||||
if test "x$ax_lib" = "x"; then
|
||||
AC_MSG_ERROR(Could not find a version of the Boost::System library!)
|
||||
fi
|
||||
if test "x$link_system" = "xno"; then
|
||||
AC_MSG_ERROR(Could not link against $ax_lib !)
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
])
|
||||
@@ -5,9 +5,9 @@
|
||||
# Sets the QT_QMAKE variable to the path of Qt5 qmake if found.
|
||||
# --------------------------------------
|
||||
AC_DEFUN([FIND_QT5],
|
||||
[PKG_CHECK_EXISTS([Qt5Core >= 5.11],
|
||||
[PKG_CHECK_EXISTS([Qt5Core >= 5.14],
|
||||
[PKG_CHECK_VAR(QT_QMAKE,
|
||||
[Qt5Core >= 5.11],
|
||||
[Qt5Core >= 5.14],
|
||||
[host_bins])
|
||||
])
|
||||
|
||||
@@ -18,7 +18,7 @@ AS_IF([test -f "$QT_QMAKE/qmake"],
|
||||
[QT_QMAKE=""])
|
||||
])
|
||||
|
||||
AC_MSG_CHECKING([for Qt5 qmake >= 5.11])
|
||||
AC_MSG_CHECKING([for Qt5 qmake >= 5.14])
|
||||
AS_IF([test "x$QT_QMAKE" != "x"],
|
||||
[AC_MSG_RESULT([$QT_QMAKE])],
|
||||
[AC_MSG_RESULT([not found])]
|
||||
@@ -29,8 +29,8 @@ AS_IF([test "x$QT_QMAKE" != "x"],
|
||||
# Sets the HAVE_QTDBUS variable to true or false.
|
||||
# --------------------------------------
|
||||
AC_DEFUN([FIND_QTDBUS],
|
||||
[AC_MSG_CHECKING([for Qt5DBus >= 5.11])
|
||||
PKG_CHECK_EXISTS([Qt5DBus >= 5.11],
|
||||
[AC_MSG_CHECKING([for Qt5DBus >= 5.14])
|
||||
PKG_CHECK_EXISTS([Qt5DBus >= 5.14],
|
||||
[AC_MSG_RESULT([found])
|
||||
HAVE_QTDBUS=[true]],
|
||||
[AC_MSG_RESULT([not found])
|
||||
|
||||
@@ -35,7 +35,7 @@ set_property(CACHE LibtorrentRasterbar_DIR PROPERTY TYPE PATH)
|
||||
find_package(Boost ${minBoostVersion} REQUIRED)
|
||||
find_package(OpenSSL ${minOpenSSLVersion} REQUIRED)
|
||||
find_package(ZLIB ${minZlibVersion} REQUIRED)
|
||||
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Core Network Xml LinguistTools)
|
||||
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
|
||||
if (DBUS)
|
||||
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS DBus)
|
||||
set_package_properties(Qt5DBus PROPERTIES
|
||||
@@ -61,9 +61,7 @@ add_subdirectory(base)
|
||||
|
||||
if (GUI)
|
||||
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Widgets Svg)
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS MacExtras)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS WinExtras)
|
||||
endif()
|
||||
add_subdirectory(gui)
|
||||
|
||||
@@ -4,22 +4,14 @@
|
||||
# Based on https://gist.github.com/giraldeau/546ba5512a74dfe9d8ea0862d66db412
|
||||
file(GLOB QBT_TS_FILES "${qBittorrent_SOURCE_DIR}/src/lang/*.ts")
|
||||
set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${qBittorrent_BINARY_DIR}/src/lang")
|
||||
if (Qt5_VERSION VERSION_LESS 5.12)
|
||||
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
||||
else()
|
||||
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES} OPTIONS -silent)
|
||||
endif()
|
||||
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES} OPTIONS -silent)
|
||||
configure_file("${qBittorrent_SOURCE_DIR}/src/lang/lang.qrc" "${qBittorrent_BINARY_DIR}/src/lang/lang.qrc" COPYONLY)
|
||||
|
||||
if (WEBUI)
|
||||
file(GLOB QBT_WEBUI_TS_FILES "${qBittorrent_SOURCE_DIR}/src/webui/www/translations/*.ts")
|
||||
set_source_files_properties(${QBT_WEBUI_TS_FILES}
|
||||
PROPERTIES OUTPUT_LOCATION "${qBittorrent_BINARY_DIR}/src/webui/www/translations")
|
||||
if (Qt5_VERSION VERSION_LESS 5.12)
|
||||
qt5_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES})
|
||||
else()
|
||||
qt5_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES} OPTIONS -silent)
|
||||
endif()
|
||||
qt5_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES} OPTIONS -silent)
|
||||
configure_file("${qBittorrent_SOURCE_DIR}/src/webui/www/translations/webui_translations.qrc"
|
||||
"${qBittorrent_BINARY_DIR}/src/webui/www/translations/webui_translations.qrc" COPYONLY)
|
||||
endif()
|
||||
@@ -147,11 +139,7 @@ endif()
|
||||
if (GUI)
|
||||
target_link_libraries(qbt_app PRIVATE qbt_gui)
|
||||
if ((CMAKE_SYSTEM_NAME STREQUAL "Windows") OR (CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
|
||||
if (Qt5_VERSION VERSION_LESS 5.14)
|
||||
set_property(TARGET qbt_app APPEND PROPERTY QT_PLUGINS Qt5::QSvgIconPlugin Qt5::QSvgPlugin)
|
||||
else()
|
||||
qt_import_plugins(qbt_app INCLUDE Qt5::QSvgIconPlugin Qt5::QSvgPlugin)
|
||||
endif()
|
||||
qt_import_plugins(qbt_app INCLUDE Qt5::QSvgIconPlugin Qt5::QSvgPlugin)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@
|
||||
#include "base/search/searchpluginmanager.h"
|
||||
#include "base/settingsstorage.h"
|
||||
#include "base/torrentfileswatcher.h"
|
||||
#include "base/utils/compare.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "base/version.h"
|
||||
#include "applicationinstancemanager.h"
|
||||
#include "filelogger.h"
|
||||
@@ -302,7 +302,7 @@ void Application::setFileLoggerAgeType(const int value)
|
||||
|
||||
void Application::processMessage(const QString &message)
|
||||
{
|
||||
const QStringList params = message.split(PARAMS_SEPARATOR, QString::SkipEmptyParts);
|
||||
const QStringList params = message.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts);
|
||||
// If Application is not running (i.e., other
|
||||
// components are not ready) store params
|
||||
if (m_running)
|
||||
@@ -350,13 +350,15 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
|
||||
#endif
|
||||
break;
|
||||
case u'G':
|
||||
{
|
||||
QStringList tags = torrent->tags().values();
|
||||
std::sort(tags.begin(), tags.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||
program.replace(i, 2, tags.join(','));
|
||||
}
|
||||
program.replace(i, 2, torrent->tags().join(QLatin1String(",")));
|
||||
break;
|
||||
case u'I':
|
||||
program.replace(i, 2, (torrent->infoHash().v1().isValid() ? torrent->infoHash().v1().toString() : QLatin1String("-")));
|
||||
break;
|
||||
case u'J':
|
||||
program.replace(i, 2, (torrent->infoHash().v2().isValid() ? torrent->infoHash().v2().toString() : QLatin1String("-")));
|
||||
break;
|
||||
case u'K':
|
||||
program.replace(i, 2, torrent->id().toString());
|
||||
break;
|
||||
case u'L':
|
||||
|
||||
@@ -126,7 +126,9 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
|
||||
if (!m_logFile.isOpen()) return;
|
||||
|
||||
QTextStream stream(&m_logFile);
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
stream.setCodec("UTF-8");
|
||||
#endif
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
|
||||
@@ -134,7 +134,7 @@ int main(int argc, char *argv[])
|
||||
// We must save it here because QApplication constructor may change it
|
||||
bool isOneArg = (argc == 2);
|
||||
|
||||
#if !defined(DISABLE_GUI) && (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
#if !defined(DISABLE_GUI)
|
||||
// Attribute Qt::AA_EnableHighDpiScaling must be set before QCoreApplication is created
|
||||
if (qgetenv("QT_ENABLE_HIGHDPI_SCALING").isEmpty() && qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR").isEmpty())
|
||||
Application::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
@@ -235,7 +235,7 @@ int main(int argc, char *argv[])
|
||||
// 3. https://bugreports.qt.io/browse/QTBUG-46015
|
||||
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
|
||||
#if !defined(DISABLE_GUI)
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && !defined(DISABLE_GUI)
|
||||
// this is the default in Qt6
|
||||
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
|
||||
#endif
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
#include <QDir>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "base/utils/misc.h"
|
||||
|
||||
@@ -108,7 +109,7 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
|
||||
#endif
|
||||
prefix = id.section(QLatin1Char('/'), -1);
|
||||
}
|
||||
prefix.remove(QRegExp("[^a-zA-Z]"));
|
||||
prefix.remove(QRegularExpression("[^a-zA-Z]"));
|
||||
prefix.truncate(6);
|
||||
|
||||
QByteArray idc = id.toUtf8();
|
||||
|
||||
@@ -5,13 +5,16 @@ add_library(qbt_base STATIC
|
||||
bittorrent/abstractfilestorage.h
|
||||
bittorrent/addtorrentparams.h
|
||||
bittorrent/bandwidthscheduler.h
|
||||
bittorrent/bencoderesumedatastorage.h
|
||||
bittorrent/cachestatus.h
|
||||
bittorrent/common.h
|
||||
bittorrent/customstorage.h
|
||||
bittorrent/dbresumedatastorage.h
|
||||
bittorrent/downloadpriority.h
|
||||
bittorrent/filesearcher.h
|
||||
bittorrent/filterparserthread.h
|
||||
bittorrent/infohash.h
|
||||
bittorrent/loadtorrentparams.h
|
||||
bittorrent/ltqhash.h
|
||||
bittorrent/ltunderlyingtype.h
|
||||
bittorrent/magneturi.h
|
||||
@@ -20,7 +23,7 @@ add_library(qbt_base STATIC
|
||||
bittorrent/peeraddress.h
|
||||
bittorrent/peerinfo.h
|
||||
bittorrent/portforwarderimpl.h
|
||||
bittorrent/resumedatasavingmanager.h
|
||||
bittorrent/resumedatastorage.h
|
||||
bittorrent/session.h
|
||||
bittorrent/sessionstatus.h
|
||||
bittorrent/speedmonitor.h
|
||||
@@ -55,6 +58,7 @@ add_library(qbt_base STATIC
|
||||
net/proxyconfigurationmanager.h
|
||||
net/reverseresolution.h
|
||||
net/smtp.h
|
||||
orderedset.h
|
||||
preferences.h
|
||||
profile.h
|
||||
profile_p.h
|
||||
@@ -70,12 +74,14 @@ add_library(qbt_base STATIC
|
||||
search/searchhandler.h
|
||||
search/searchpluginmanager.h
|
||||
settingsstorage.h
|
||||
tagset.h
|
||||
torrentfileguard.h
|
||||
torrentfileswatcher.h
|
||||
torrentfilter.h
|
||||
types.h
|
||||
unicodestrings.h
|
||||
utils/bytearray.h
|
||||
utils/compare.h
|
||||
utils/foreignapps.h
|
||||
utils/fs.h
|
||||
utils/gzip.h
|
||||
@@ -92,7 +98,9 @@ add_library(qbt_base STATIC
|
||||
asyncfilestorage.cpp
|
||||
bittorrent/abstractfilestorage.cpp
|
||||
bittorrent/bandwidthscheduler.cpp
|
||||
bittorrent/bencoderesumedatastorage.cpp
|
||||
bittorrent/customstorage.cpp
|
||||
bittorrent/dbresumedatastorage.cpp
|
||||
bittorrent/downloadpriority.cpp
|
||||
bittorrent/filesearcher.cpp
|
||||
bittorrent/filterparserthread.cpp
|
||||
@@ -103,7 +111,6 @@ add_library(qbt_base STATIC
|
||||
bittorrent/peeraddress.cpp
|
||||
bittorrent/peerinfo.cpp
|
||||
bittorrent/portforwarderimpl.cpp
|
||||
bittorrent/resumedatasavingmanager.cpp
|
||||
bittorrent/session.cpp
|
||||
bittorrent/speedmonitor.cpp
|
||||
bittorrent/statistics.cpp
|
||||
@@ -146,10 +153,12 @@ add_library(qbt_base STATIC
|
||||
search/searchhandler.cpp
|
||||
search/searchpluginmanager.cpp
|
||||
settingsstorage.cpp
|
||||
tagset.cpp
|
||||
torrentfileguard.cpp
|
||||
torrentfileswatcher.cpp
|
||||
torrentfilter.cpp
|
||||
utils/bytearray.cpp
|
||||
utils/compare.cpp
|
||||
utils/foreignapps.cpp
|
||||
utils/fs.cpp
|
||||
utils/gzip.cpp
|
||||
@@ -167,7 +176,7 @@ target_link_libraries(qbt_base
|
||||
ZLIB::ZLIB
|
||||
PUBLIC
|
||||
LibtorrentRasterbar::torrent-rasterbar
|
||||
Qt5::Core Qt5::Network Qt5::Xml
|
||||
Qt5::Core Qt5::Network Qt5::Sql Qt5::Xml
|
||||
qbt_common_cfg
|
||||
)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Algorithm
|
||||
{
|
||||
};
|
||||
|
||||
// To be used with associative array types, such as QMap, QHash and it's variants
|
||||
// To be used with associative array types, such as QMap, QHash and its variants
|
||||
template <typename T, typename BinaryPredicate
|
||||
, typename std::enable_if_t<HasMappedType<T>::value, int> = 0>
|
||||
void removeIf(T &dict, BinaryPredicate &&p)
|
||||
|
||||
@@ -4,13 +4,16 @@ HEADERS += \
|
||||
$$PWD/bittorrent/abstractfilestorage.h \
|
||||
$$PWD/bittorrent/addtorrentparams.h \
|
||||
$$PWD/bittorrent/bandwidthscheduler.h \
|
||||
$$PWD/bittorrent/bencoderesumedatastorage.h \
|
||||
$$PWD/bittorrent/cachestatus.h \
|
||||
$$PWD/bittorrent/common.h \
|
||||
$$PWD/bittorrent/customstorage.h \
|
||||
$$PWD/bittorrent/downloadpriority.h \
|
||||
$$PWD/bittorrent/dbresumedatastorage.h \
|
||||
$$PWD/bittorrent/filesearcher.h \
|
||||
$$PWD/bittorrent/filterparserthread.h \
|
||||
$$PWD/bittorrent/infohash.h \
|
||||
$$PWD/bittorrent/loadtorrentparams.h \
|
||||
$$PWD/bittorrent/ltqhash.h \
|
||||
$$PWD/bittorrent/ltunderlyingtype.h \
|
||||
$$PWD/bittorrent/magneturi.h \
|
||||
@@ -19,7 +22,7 @@ HEADERS += \
|
||||
$$PWD/bittorrent/peeraddress.h \
|
||||
$$PWD/bittorrent/peerinfo.h \
|
||||
$$PWD/bittorrent/portforwarderimpl.h \
|
||||
$$PWD/bittorrent/resumedatasavingmanager.h \
|
||||
$$PWD/bittorrent/resumedatastorage.h \
|
||||
$$PWD/bittorrent/session.h \
|
||||
$$PWD/bittorrent/sessionstatus.h \
|
||||
$$PWD/bittorrent/speedmonitor.h \
|
||||
@@ -54,6 +57,7 @@ HEADERS += \
|
||||
$$PWD/net/proxyconfigurationmanager.h \
|
||||
$$PWD/net/reverseresolution.h \
|
||||
$$PWD/net/smtp.h \
|
||||
$$PWD/orderedset.h \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/profile.h \
|
||||
$$PWD/profile_p.h \
|
||||
@@ -70,12 +74,14 @@ HEADERS += \
|
||||
$$PWD/search/searchpluginmanager.h \
|
||||
$$PWD/settingsstorage.h \
|
||||
$$PWD/settingvalue.h \
|
||||
$$PWD/tagset.h \
|
||||
$$PWD/torrentfileguard.h \
|
||||
$$PWD/torrentfileswatcher.h \
|
||||
$$PWD/torrentfilter.h \
|
||||
$$PWD/types.h \
|
||||
$$PWD/unicodestrings.h \
|
||||
$$PWD/utils/bytearray.h \
|
||||
$$PWD/utils/compare.h \
|
||||
$$PWD/utils/foreignapps.h \
|
||||
$$PWD/utils/fs.h \
|
||||
$$PWD/utils/gzip.h \
|
||||
@@ -92,7 +98,9 @@ SOURCES += \
|
||||
$$PWD/asyncfilestorage.cpp \
|
||||
$$PWD/bittorrent/abstractfilestorage.cpp \
|
||||
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
|
||||
$$PWD/bittorrent/customstorage.cpp \
|
||||
$$PWD/bittorrent/dbresumedatastorage.cpp \
|
||||
$$PWD/bittorrent/downloadpriority.cpp \
|
||||
$$PWD/bittorrent/filesearcher.cpp \
|
||||
$$PWD/bittorrent/filterparserthread.cpp \
|
||||
@@ -103,7 +111,6 @@ SOURCES += \
|
||||
$$PWD/bittorrent/peeraddress.cpp \
|
||||
$$PWD/bittorrent/peerinfo.cpp \
|
||||
$$PWD/bittorrent/portforwarderimpl.cpp \
|
||||
$$PWD/bittorrent/resumedatasavingmanager.cpp \
|
||||
$$PWD/bittorrent/session.cpp \
|
||||
$$PWD/bittorrent/speedmonitor.cpp \
|
||||
$$PWD/bittorrent/statistics.cpp \
|
||||
@@ -146,10 +153,12 @@ SOURCES += \
|
||||
$$PWD/search/searchhandler.cpp \
|
||||
$$PWD/search/searchpluginmanager.cpp \
|
||||
$$PWD/settingsstorage.cpp \
|
||||
$$PWD/tagset.cpp \
|
||||
$$PWD/torrentfileguard.cpp \
|
||||
$$PWD/torrentfileswatcher.cpp \
|
||||
$$PWD/torrentfilter.cpp \
|
||||
$$PWD/utils/bytearray.cpp \
|
||||
$$PWD/utils/compare.cpp \
|
||||
$$PWD/utils/foreignapps.cpp \
|
||||
$$PWD/utils/fs.cpp \
|
||||
$$PWD/utils/gzip.cpp \
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
#include <optional>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/tagset.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace BitTorrent
|
||||
{
|
||||
QString name;
|
||||
QString category;
|
||||
QSet<QString> tags;
|
||||
TagSet tags;
|
||||
QString savePath;
|
||||
bool disableTempPath = false; // e.g. for imported torrents
|
||||
bool sequential = false;
|
||||
|
||||
408
src/base/bittorrent/bencoderesumedatastorage.cpp
Normal file
408
src/base/bittorrent/bencoderesumedatastorage.cpp
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 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 "bencoderesumedatastorage.h"
|
||||
|
||||
#include <libtorrent/bdecode.hpp>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/create_torrent.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/read_resume_data.hpp>
|
||||
#include <libtorrent/write_resume_data.hpp>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QRegularExpression>
|
||||
#include <QSaveFile>
|
||||
#include <QThread>
|
||||
|
||||
#include "base/algorithm.h"
|
||||
#include "base/exceptions.h"
|
||||
#include "base/global.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/tagset.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/io.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "infohash.h"
|
||||
#include "loadtorrentparams.h"
|
||||
#include "torrentinfo.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class BencodeResumeDataStorage::Worker final : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(Worker)
|
||||
|
||||
public:
|
||||
explicit Worker(const QDir &resumeDataDir);
|
||||
|
||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
||||
void remove(const TorrentID &id) const;
|
||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||
|
||||
private:
|
||||
const QDir m_resumeDataDir;
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename LTStr>
|
||||
QString fromLTString(const LTStr &str)
|
||||
{
|
||||
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
|
||||
}
|
||||
|
||||
using ListType = lt::entry::list_type;
|
||||
|
||||
ListType setToEntryList(const TagSet &input)
|
||||
{
|
||||
ListType entryList;
|
||||
entryList.reserve(input.size());
|
||||
for (const QString &setValue : input)
|
||||
entryList.emplace_back(setValue.toStdString());
|
||||
return entryList;
|
||||
}
|
||||
|
||||
void writeEntryToFile(const QString &filepath, const lt::entry &data)
|
||||
{
|
||||
QSaveFile file {filepath};
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
throw RuntimeError(file.errorString());
|
||||
|
||||
lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, data);
|
||||
if (file.error() != QFileDevice::NoError || !file.commit())
|
||||
throw RuntimeError(file.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &path, QObject *parent)
|
||||
: ResumeDataStorage {parent}
|
||||
, m_resumeDataDir {path}
|
||||
, m_ioThread {new QThread {this}}
|
||||
, m_asyncWorker {new Worker {m_resumeDataDir}}
|
||||
{
|
||||
if (!m_resumeDataDir.exists() && !m_resumeDataDir.mkpath(m_resumeDataDir.absolutePath()))
|
||||
{
|
||||
throw RuntimeError {tr("Cannot create torrent resume folder: \"%1\"")
|
||||
.arg(Utils::Fs::toNativePath(m_resumeDataDir.absolutePath()))};
|
||||
}
|
||||
|
||||
const QRegularExpression filenamePattern {QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$")};
|
||||
const QStringList filenames = m_resumeDataDir.entryList(QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
|
||||
m_registeredTorrents.reserve(filenames.size());
|
||||
for (const QString &filename : filenames)
|
||||
{
|
||||
const QRegularExpressionMatch rxMatch = filenamePattern.match(filename);
|
||||
if (rxMatch.hasMatch())
|
||||
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
|
||||
}
|
||||
|
||||
loadQueue(m_resumeDataDir.absoluteFilePath(QLatin1String("queue")));
|
||||
|
||||
qDebug("Registered torrents count: %d", m_registeredTorrents.size());
|
||||
|
||||
m_asyncWorker->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
||||
m_ioThread->start();
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::~BencodeResumeDataStorage()
|
||||
{
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
}
|
||||
|
||||
QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
|
||||
{
|
||||
return m_registeredTorrents;
|
||||
}
|
||||
|
||||
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::load(const TorrentID &id) const
|
||||
{
|
||||
const QString idString = id.toString();
|
||||
const QString fastresumePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(idString));
|
||||
const QString torrentFilePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(idString));
|
||||
|
||||
QFile file {fastresumePath};
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath, file.errorString()), Log::WARNING);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const QByteArray data = file.readAll();
|
||||
const TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
|
||||
|
||||
return loadTorrentResumeData(data, metadata);
|
||||
}
|
||||
|
||||
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::loadTorrentResumeData(
|
||||
const QByteArray &data, const TorrentInfo &metadata) const
|
||||
{
|
||||
lt::error_code ec;
|
||||
const lt::bdecode_node root = lt::bdecode(data, ec);
|
||||
if (ec || (root.type() != lt::bdecode_node::dict_t)) return std::nullopt;
|
||||
|
||||
LoadTorrentParams torrentParams;
|
||||
torrentParams.restored = true;
|
||||
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
|
||||
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
|
||||
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
|
||||
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
|
||||
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
||||
// === BEGIN DEPRECATED CODE === //
|
||||
const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
|
||||
if (contentLayoutNode.type() == lt::bdecode_node::string_t)
|
||||
{
|
||||
const QString contentLayoutStr = fromLTString(contentLayoutNode.string_value());
|
||||
torrentParams.contentLayout = Utils::String::toEnum(contentLayoutStr, TorrentContentLayout::Original);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
|
||||
torrentParams.contentLayout = (hasRootFolder ? TorrentContentLayout::Original : TorrentContentLayout::NoSubfolder);
|
||||
}
|
||||
// === END DEPRECATED CODE === //
|
||||
// === BEGIN REPLACEMENT CODE === //
|
||||
// torrentParams.contentLayout = Utils::String::parse(
|
||||
// fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default);
|
||||
// === END REPLACEMENT CODE === //
|
||||
|
||||
const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
|
||||
if (ratioLimitString.empty())
|
||||
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
|
||||
else
|
||||
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
|
||||
|
||||
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
|
||||
if (tagsNode.type() == lt::bdecode_node::list_t)
|
||||
{
|
||||
for (int i = 0; i < tagsNode.list_size(); ++i)
|
||||
{
|
||||
const QString tag = fromLTString(tagsNode.list_string_value_at(i));
|
||||
torrentParams.tags.insert(tag);
|
||||
}
|
||||
}
|
||||
|
||||
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
|
||||
|
||||
p = lt::read_resume_data(root, ec);
|
||||
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
|
||||
if (metadata.isValid())
|
||||
p.ti = metadata.nativeInfo();
|
||||
|
||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||
{
|
||||
// If torrent has "stop_when_ready" flag set then it is actually "stopped"
|
||||
torrentParams.stopped = true;
|
||||
torrentParams.operatingMode = TorrentOperatingMode::AutoManaged;
|
||||
// ...but temporarily "resumed" to perform some service jobs (e.g. checking)
|
||||
p.flags &= ~lt::torrent_flags::paused;
|
||||
p.flags |= lt::torrent_flags::auto_managed;
|
||||
}
|
||||
else
|
||||
{
|
||||
torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
|
||||
torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed)
|
||||
? TorrentOperatingMode::AutoManaged : TorrentOperatingMode::Forced;
|
||||
}
|
||||
|
||||
const bool hasMetadata = (p.ti && p.ti->is_valid());
|
||||
if (!hasMetadata && !root.dict_find("info-hash"))
|
||||
return std::nullopt;
|
||||
|
||||
return torrentParams;
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
|
||||
{
|
||||
m_asyncWorker->store(id, resumeData);
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::remove(const TorrentID &id) const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id]()
|
||||
{
|
||||
m_asyncWorker->remove(id);
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
||||
{
|
||||
m_asyncWorker->storeQueue(queue);
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::loadQueue(const QString &queueFilename)
|
||||
{
|
||||
QFile queueFile {queueFilename};
|
||||
if (!queueFile.exists())
|
||||
return;
|
||||
|
||||
if (queueFile.open(QFile::ReadOnly))
|
||||
{
|
||||
const QRegularExpression hashPattern {QLatin1String("^([A-Fa-f0-9]{40})$")};
|
||||
QByteArray line;
|
||||
int start = 0;
|
||||
while (!(line = queueFile.readLine().trimmed()).isEmpty())
|
||||
{
|
||||
const QRegularExpressionMatch rxMatch = hashPattern.match(line);
|
||||
if (rxMatch.hasMatch())
|
||||
{
|
||||
const auto torrentID = TorrentID::fromString(rxMatch.captured(1));
|
||||
const int pos = m_registeredTorrents.indexOf(torrentID, start);
|
||||
if (pos != -1)
|
||||
{
|
||||
std::swap(m_registeredTorrents[start], m_registeredTorrents[pos]);
|
||||
++start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't load torrents queue from '%1'. Error: %2")
|
||||
.arg(queueFile.fileName(), queueFile.errorString()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::Worker::Worker(const QDir &resumeDataDir)
|
||||
: m_resumeDataDir {resumeDataDir}
|
||||
{
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
||||
{
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
if (resumeData.stopped)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Torrent can be actually "running" but temporarily "paused" to perform some
|
||||
// service jobs behind the scenes so we need to restore it as "running"
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
// metadata is stored in separate .torrent file
|
||||
const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti);
|
||||
if (torrentInfo)
|
||||
{
|
||||
const QString torrentFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(id.toString()));
|
||||
try
|
||||
{
|
||||
const auto torrentCreator = lt::create_torrent(*torrentInfo);
|
||||
const lt::entry metadata = torrentCreator.generate();
|
||||
writeEntryToFile(torrentFilepath, metadata);
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
|
||||
.arg(torrentFilepath, err.message()), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
catch (const std::exception &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
|
||||
.arg(torrentFilepath, QString::fromLocal8Bit(err.what())), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lt::entry data = lt::write_resume_data(p);
|
||||
|
||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
|
||||
data["qBt-ratioLimit"] = static_cast<int>(resumeData.ratioLimit * 1000);
|
||||
data["qBt-seedingTimeLimit"] = resumeData.seedingTimeLimit;
|
||||
data["qBt-category"] = resumeData.category.toStdString();
|
||||
data["qBt-tags"] = setToEntryList(resumeData.tags);
|
||||
data["qBt-name"] = resumeData.name.toStdString();
|
||||
data["qBt-seedStatus"] = resumeData.hasSeedStatus;
|
||||
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
|
||||
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
|
||||
|
||||
const QString resumeFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
||||
try
|
||||
{
|
||||
writeEntryToFile(resumeFilepath, data);
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
|
||||
.arg(resumeFilepath, err.message()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
|
||||
{
|
||||
const QString resumeFilename = QString::fromLatin1("%1.fastresume").arg(id.toString());
|
||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(resumeFilename));
|
||||
|
||||
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(id.toString());
|
||||
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(torrentFilename));
|
||||
}
|
||||
|
||||
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
|
||||
{
|
||||
QByteArray data;
|
||||
data.reserve(((BitTorrent::TorrentID::length() * 2) + 1) * queue.size());
|
||||
for (const BitTorrent::TorrentID &torrentID : queue)
|
||||
data += (torrentID.toString().toLatin1() + '\n');
|
||||
|
||||
const QString filepath = m_resumeDataDir.absoluteFilePath(QLatin1String("queue"));
|
||||
QSaveFile file {filepath};
|
||||
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.commit())
|
||||
{
|
||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
||||
.arg(filepath, file.errorString()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
69
src/base/bittorrent/bencoderesumedatastorage.h
Normal file
69
src/base/bittorrent/bencoderesumedatastorage.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 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 <QDir>
|
||||
#include <QVector>
|
||||
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QByteArray;
|
||||
class QThread;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentInfo;
|
||||
|
||||
class BencodeResumeDataStorage final : public ResumeDataStorage
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(BencodeResumeDataStorage)
|
||||
|
||||
public:
|
||||
explicit BencodeResumeDataStorage(const QString &path, QObject *parent = nullptr);
|
||||
~BencodeResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
std::optional<LoadTorrentParams> load(const TorrentID &id) const override;
|
||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const override;
|
||||
void remove(const TorrentID &id) const override;
|
||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||
|
||||
private:
|
||||
void loadQueue(const QString &queueFilename);
|
||||
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata) const;
|
||||
|
||||
const QDir m_resumeDataDir;
|
||||
QVector<TorrentID> m_registeredTorrents;
|
||||
QThread *m_ioThread = nullptr;
|
||||
|
||||
class Worker;
|
||||
Worker *m_asyncWorker = nullptr;
|
||||
};
|
||||
}
|
||||
586
src/base/bittorrent/dbresumedatastorage.cpp
Normal file
586
src/base/bittorrent/dbresumedatastorage.cpp
Normal file
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 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 "dbresumedatastorage.h"
|
||||
|
||||
#include <libtorrent/bdecode.hpp>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/create_torrent.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/read_resume_data.hpp>
|
||||
#include <libtorrent/write_resume_data.hpp>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <QSet>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
#include <QThread>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/global.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "infohash.h"
|
||||
#include "loadtorrentparams.h"
|
||||
#include "torrentinfo.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char DB_CONNECTION_NAME[] = "ResumeDataStorage";
|
||||
|
||||
const int DB_VERSION = 1;
|
||||
|
||||
const char DB_TABLE_META[] = "meta";
|
||||
const char DB_TABLE_TORRENTS[] = "torrents";
|
||||
|
||||
struct Column
|
||||
{
|
||||
QString name;
|
||||
QString placeholder;
|
||||
};
|
||||
|
||||
Column makeColumn(const char *columnName)
|
||||
{
|
||||
return {QLatin1String(columnName), (QLatin1Char(':') + QLatin1String(columnName))};
|
||||
}
|
||||
|
||||
const Column DB_COLUMN_ID = makeColumn("id");
|
||||
const Column DB_COLUMN_TORRENT_ID = makeColumn("torrent_id");
|
||||
const Column DB_COLUMN_QUEUE_POSITION = makeColumn("queue_position");
|
||||
const Column DB_COLUMN_NAME = makeColumn("name");
|
||||
const Column DB_COLUMN_CATEGORY = makeColumn("category");
|
||||
const Column DB_COLUMN_TAGS = makeColumn("tags");
|
||||
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn("target_save_path");
|
||||
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
||||
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
||||
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
||||
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_OPERATING_MODE = makeColumn("operating_mode");
|
||||
const Column DB_COLUMN_STOPPED = makeColumn("stopped");
|
||||
const Column DB_COLUMN_RESUMEDATA = makeColumn("libtorrent_resume_data");
|
||||
const Column DB_COLUMN_METADATA = makeColumn("metadata");
|
||||
const Column DB_COLUMN_VALUE = makeColumn("value");
|
||||
|
||||
template <typename LTStr>
|
||||
QString fromLTString(const LTStr &str)
|
||||
{
|
||||
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
|
||||
}
|
||||
|
||||
QString quoted(const QString &name)
|
||||
{
|
||||
const QLatin1Char quote {'`'};
|
||||
|
||||
return (quote + name + quote);
|
||||
}
|
||||
|
||||
QString makeCreateTableStatement(const QString &tableName, const QStringList &items)
|
||||
{
|
||||
return QString::fromLatin1("CREATE TABLE %1 (%2)").arg(quoted(tableName), items.join(QLatin1Char(',')));
|
||||
}
|
||||
|
||||
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
||||
{
|
||||
QStringList names;
|
||||
names.reserve(columns.size());
|
||||
QStringList values;
|
||||
values.reserve(columns.size());
|
||||
for (const Column &column : columns)
|
||||
{
|
||||
names.append(quoted(column.name));
|
||||
values.append(column.placeholder);
|
||||
}
|
||||
|
||||
const QString jointNames = names.join(QLatin1Char(','));
|
||||
const QString jointValues = values.join(QLatin1Char(','));
|
||||
|
||||
return QString::fromLatin1("INSERT INTO %1 (%2) VALUES (%3)")
|
||||
.arg(quoted(tableName), jointNames, jointValues);
|
||||
}
|
||||
|
||||
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
||||
{
|
||||
QStringList names;
|
||||
names.reserve(columns.size());
|
||||
QStringList values;
|
||||
values.reserve(columns.size());
|
||||
for (const Column &column : columns)
|
||||
{
|
||||
names.append(quoted(column.name));
|
||||
values.append(column.placeholder);
|
||||
}
|
||||
|
||||
const QString jointNames = names.join(QLatin1Char(','));
|
||||
const QString jointValues = values.join(QLatin1Char(','));
|
||||
|
||||
return QString::fromLatin1(" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)")
|
||||
.arg(quoted(constraint.name), jointNames, jointValues);
|
||||
}
|
||||
|
||||
QString makeColumnDefinition(const Column &column, const char *definition)
|
||||
{
|
||||
return QString::fromLatin1("%1 %2").arg(quoted(column.name), QLatin1String(definition));
|
||||
}
|
||||
}
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class DBResumeDataStorage::Worker final : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(Worker)
|
||||
|
||||
public:
|
||||
Worker(const QString &dbPath, const QString &dbConnectionName);
|
||||
|
||||
void openDatabase() const;
|
||||
void closeDatabase() const;
|
||||
|
||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
||||
void remove(const TorrentID &id) const;
|
||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||
|
||||
private:
|
||||
const QString m_path;
|
||||
const QString m_connectionName;
|
||||
};
|
||||
}
|
||||
|
||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const QString &dbPath, QObject *parent)
|
||||
: ResumeDataStorage {parent}
|
||||
, m_ioThread {new QThread(this)}
|
||||
{
|
||||
const bool needCreateDB = !QFile::exists(dbPath);
|
||||
|
||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), DB_CONNECTION_NAME);
|
||||
db.setDatabaseName(dbPath);
|
||||
if (!db.open())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
|
||||
if (needCreateDB)
|
||||
createDB();
|
||||
|
||||
m_asyncWorker = new Worker(dbPath, QLatin1String("ResumeDataStorageWorker"));
|
||||
m_asyncWorker->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &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()
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, &Worker::closeDatabase);
|
||||
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
||||
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
}
|
||||
|
||||
QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
||||
{
|
||||
const auto selectTorrentIDStatement = QString::fromLatin1("SELECT %1 FROM %2 ORDER BY %3;")
|
||||
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||
|
||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||
QSqlQuery query {db};
|
||||
|
||||
if (!query.exec(selectTorrentIDStatement))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
QVector<TorrentID> registeredTorrents;
|
||||
registeredTorrents.reserve(query.size());
|
||||
while (query.next())
|
||||
registeredTorrents.append(BitTorrent::TorrentID::fromString(query.value(0).toString()));
|
||||
|
||||
return registeredTorrents;
|
||||
}
|
||||
|
||||
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::load(const TorrentID &id) const
|
||||
{
|
||||
const QString selectTorrentStatement =
|
||||
QString(QLatin1String("SELECT * FROM %1 WHERE %2 = %3;"))
|
||||
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
|
||||
|
||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||
QSqlQuery query {db};
|
||||
try
|
||||
{
|
||||
if (!query.prepare(selectTorrentStatement))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
|
||||
if (!query.exec())
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
if (!query.next())
|
||||
throw RuntimeError(tr("Not found."));
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't load resume data of torrent '%1'. Error: %2")
|
||||
.arg(id.toString(), err.message()), Log::CRITICAL);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
LoadTorrentParams resumeData;
|
||||
resumeData.restored = true;
|
||||
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(QLatin1Char(','));
|
||||
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
||||
}
|
||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||
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();
|
||||
|
||||
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
||||
|
||||
lt::error_code ec;
|
||||
const lt::bdecode_node root = lt::bdecode(bencodedResumeData, ec);
|
||||
|
||||
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||
|
||||
p = lt::read_resume_data(root, ec);
|
||||
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
|
||||
|
||||
const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray();
|
||||
auto metadata = TorrentInfo::load(bencodedMetadata);
|
||||
if (metadata.isValid())
|
||||
p.ti = metadata.nativeInfo();
|
||||
|
||||
return resumeData;
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
|
||||
{
|
||||
m_asyncWorker->store(id, resumeData);
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id]()
|
||||
{
|
||||
m_asyncWorker->remove(id);
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
||||
{
|
||||
m_asyncWorker->storeQueue(queue);
|
||||
});
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::createDB() const
|
||||
{
|
||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||
|
||||
if (!db.transaction())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
|
||||
QSqlQuery query {db};
|
||||
|
||||
try
|
||||
{
|
||||
const QStringList tableMetaItems = {
|
||||
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
|
||||
makeColumnDefinition(DB_COLUMN_NAME, "TEXT NOT NULL UNIQUE"),
|
||||
makeColumnDefinition(DB_COLUMN_VALUE, "BLOB")
|
||||
};
|
||||
const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
|
||||
if (!query.exec(createTableMetaQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
const QString insertMetaVersionQuery = makeInsertStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
||||
if (!query.prepare(insertMetaVersionQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1("version"));
|
||||
query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
|
||||
|
||||
if (!query.exec())
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
const QStringList tableTorrentsItems = {
|
||||
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
|
||||
makeColumnDefinition(DB_COLUMN_TORRENT_ID, "BLOB NOT NULL UNIQUE"),
|
||||
makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, "INTEGER NOT NULL DEFAULT -1"),
|
||||
makeColumnDefinition(DB_COLUMN_NAME, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_CATEGORY, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_TAGS, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, "TEXT NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, "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_OPERATING_MODE, "TEXT NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_STOPPED, "INTEGER NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_RESUMEDATA, "BLOB NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_METADATA, "BLOB")
|
||||
};
|
||||
const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
|
||||
if (!query.exec(createTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
if (!db.commit())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
}
|
||||
catch (const RuntimeError &)
|
||||
{
|
||||
db.rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QString &dbConnectionName)
|
||||
: m_path {dbPath}
|
||||
, m_connectionName {dbConnectionName}
|
||||
{
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
|
||||
{
|
||||
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
|
||||
db.setDatabaseName(m_path);
|
||||
if (!db.open())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::Worker::closeDatabase() const
|
||||
{
|
||||
QSqlDatabase::removeDatabase(m_connectionName);
|
||||
}
|
||||
|
||||
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
||||
{
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
if (resumeData.stopped)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Torrent can be actually "running" but temporarily "paused" to perform some
|
||||
// service jobs behind the scenes so we need to restore it as "running"
|
||||
if (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 {
|
||||
DB_COLUMN_TORRENT_ID,
|
||||
DB_COLUMN_NAME,
|
||||
DB_COLUMN_CATEGORY,
|
||||
DB_COLUMN_TAGS,
|
||||
DB_COLUMN_TARGET_SAVE_PATH,
|
||||
DB_COLUMN_CONTENT_LAYOUT,
|
||||
DB_COLUMN_RATIO_LIMIT,
|
||||
DB_COLUMN_SEEDING_TIME_LIMIT,
|
||||
DB_COLUMN_HAS_OUTER_PIECES_PRIORITY,
|
||||
DB_COLUMN_HAS_SEED_STATUS,
|
||||
DB_COLUMN_OPERATING_MODE,
|
||||
DB_COLUMN_STOPPED,
|
||||
DB_COLUMN_RESUMEDATA
|
||||
};
|
||||
|
||||
// metadata is stored in separate column
|
||||
QByteArray bencodedMetadata;
|
||||
bencodedMetadata.reserve(512 * 1024);
|
||||
const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti);
|
||||
if (torrentInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto torrentCreator = lt::create_torrent(*torrentInfo);
|
||||
const lt::entry metadata = torrentCreator.generate();
|
||||
lt::bencode(std::back_inserter(bencodedMetadata), metadata);
|
||||
}
|
||||
catch (const std::exception &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata. Error: %1.")
|
||||
.arg(QString::fromLocal8Bit(err.what())), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
|
||||
columns.append(DB_COLUMN_METADATA);
|
||||
}
|
||||
|
||||
QByteArray bencodedResumeData;
|
||||
bencodedResumeData.reserve(256 * 1024);
|
||||
lt::bencode(std::back_inserter(bencodedResumeData), lt::write_resume_data(p));
|
||||
|
||||
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(QLatin1String(","))));
|
||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
|
||||
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_RESUMEDATA.placeholder, bencodedResumeData);
|
||||
if (torrentInfo)
|
||||
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
|
||||
|
||||
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 = QString::fromLatin1("DELETE FROM %1 WHERE %2 = %3;")
|
||||
.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());
|
||||
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 = QString::fromLatin1("UPDATE %1 SET %2 = %3 WHERE %4 = %5;")
|
||||
.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
|
||||
{
|
||||
if (!db.transaction())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
|
||||
QSqlQuery query {db};
|
||||
|
||||
try
|
||||
{
|
||||
if (!query.prepare(updateQueuePosStatement))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
|
||||
int pos = 0;
|
||||
for (const TorrentID &torrentID : queue)
|
||||
{
|
||||
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, torrentID.toString());
|
||||
query.bindValue(DB_COLUMN_QUEUE_POSITION.placeholder, pos++);
|
||||
if (!query.exec())
|
||||
throw RuntimeError(query.lastError().text());
|
||||
}
|
||||
|
||||
if (!db.commit())
|
||||
throw RuntimeError(db.lastError().text());
|
||||
}
|
||||
catch (const RuntimeError &)
|
||||
{
|
||||
db.rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't store torrents queue positions. Error: %1")
|
||||
.arg(err.message()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
60
src/base/bittorrent/dbresumedatastorage.h
Normal file
60
src/base/bittorrent/dbresumedatastorage.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 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 "resumedatastorage.h"
|
||||
|
||||
class QThread;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class DBResumeDataStorage final : public ResumeDataStorage
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(DBResumeDataStorage)
|
||||
|
||||
public:
|
||||
explicit DBResumeDataStorage(const QString &dbPath, QObject *parent = nullptr);
|
||||
~DBResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
std::optional<LoadTorrentParams> load(const TorrentID &id) const override;
|
||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const override;
|
||||
void remove(const TorrentID &id) const override;
|
||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||
|
||||
private:
|
||||
void createDB() const;
|
||||
|
||||
QThread *m_ioThread = nullptr;
|
||||
|
||||
class Worker;
|
||||
Worker *m_asyncWorker = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -41,6 +41,24 @@ bool BitTorrent::InfoHash::isValid() const
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
SHA1Hash BitTorrent::InfoHash::v1() const
|
||||
{
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
return (m_nativeHash.has_v1() ? SHA1Hash(m_nativeHash.v1) : SHA1Hash());
|
||||
#else
|
||||
return {m_nativeHash};
|
||||
#endif
|
||||
}
|
||||
|
||||
SHA256Hash BitTorrent::InfoHash::v2() const
|
||||
{
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
return (m_nativeHash.has_v2() ? SHA256Hash(m_nativeHash.v2) : SHA256Hash());
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
BitTorrent::TorrentID BitTorrent::InfoHash::toTorrentID() const
|
||||
{
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
|
||||
@@ -69,6 +69,8 @@ namespace BitTorrent
|
||||
InfoHash(const WrappedType &nativeHash);
|
||||
|
||||
bool isValid() const;
|
||||
SHA1Hash v1() const;
|
||||
SHA256Hash v2() const;
|
||||
TorrentID toTorrentID() const;
|
||||
|
||||
operator WrappedType() const;
|
||||
|
||||
60
src/base/bittorrent/loadtorrentparams.h
Normal file
60
src/base/bittorrent/loadtorrentparams.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 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 <libtorrent/add_torrent_params.hpp>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "base/tagset.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
struct LoadTorrentParams
|
||||
{
|
||||
lt::add_torrent_params ltAddTorrentParams {};
|
||||
|
||||
QString name;
|
||||
QString category;
|
||||
TagSet tags;
|
||||
QString savePath;
|
||||
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
||||
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
||||
bool firstLastPiecePriority = false;
|
||||
bool hasSeedStatus = false;
|
||||
bool stopped = false;
|
||||
|
||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
|
||||
bool restored = false; // is existing torrent job?
|
||||
};
|
||||
}
|
||||
@@ -257,123 +257,86 @@ qreal PeerInfo::relevance() const
|
||||
|
||||
void PeerInfo::determineFlags()
|
||||
{
|
||||
const auto updateFlags = [this](const QChar specifier, const QString &explanation)
|
||||
{
|
||||
m_flags += (specifier + QLatin1Char(' '));
|
||||
m_flagsDescription += QString::fromLatin1("%1 = %2\n").arg(specifier, explanation);
|
||||
};
|
||||
|
||||
if (isInteresting())
|
||||
{
|
||||
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
|
||||
if (isRemoteChocked())
|
||||
{
|
||||
m_flags += "d ";
|
||||
m_flagsDescription += ("d = "
|
||||
+ tr("Interested(local) and Choked(peer)") + '\n');
|
||||
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
|
||||
updateFlags(QLatin1Char('d'), tr("Interested (local) and choked (peer)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// D = Currently downloading (interested and not choked)
|
||||
m_flags += "D ";
|
||||
m_flagsDescription += ("D = "
|
||||
+ tr("interested(local) and unchoked(peer)") + '\n');
|
||||
updateFlags(QLatin1Char('D'), tr("Interested (local) and unchoked (peer)"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isRemoteInterested())
|
||||
{
|
||||
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
|
||||
if (isChocked())
|
||||
{
|
||||
m_flags += "u ";
|
||||
m_flagsDescription += ("u = "
|
||||
+ tr("interested(peer) and choked(local)") + '\n');
|
||||
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
|
||||
updateFlags(QLatin1Char('u'), tr("Interested (peer) and choked (local)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// U = Currently uploading (interested and not choked)
|
||||
m_flags += "U ";
|
||||
m_flagsDescription += ("U = "
|
||||
+ tr("interested(peer) and unchoked(local)") + '\n');
|
||||
updateFlags(QLatin1Char('U'), tr("Interested (peer) and unchoked (local)"));
|
||||
}
|
||||
}
|
||||
|
||||
// O = Optimistic unchoke
|
||||
if (optimisticUnchoke())
|
||||
{
|
||||
m_flags += "O ";
|
||||
m_flagsDescription += ("O = " + tr("optimistic unchoke") + '\n');
|
||||
}
|
||||
|
||||
// S = Peer is snubbed
|
||||
if (isSnubbed())
|
||||
{
|
||||
m_flags += "S ";
|
||||
m_flagsDescription += ("S = " + tr("peer snubbed") + '\n');
|
||||
}
|
||||
|
||||
// I = Peer is an incoming connection
|
||||
if (!isLocalConnection())
|
||||
{
|
||||
m_flags += "I ";
|
||||
m_flagsDescription += ("I = " + tr("incoming connection") + '\n');
|
||||
}
|
||||
|
||||
// K = Peer is unchoking your client, but your client is not interested
|
||||
if (!isRemoteChocked() && !isInteresting())
|
||||
{
|
||||
m_flags += "K ";
|
||||
m_flagsDescription += ("K = "
|
||||
+ tr("not interested(local) and unchoked(peer)") + '\n');
|
||||
}
|
||||
updateFlags(QLatin1Char('K'), tr("Not interested (local) and unchoked (peer)"));
|
||||
|
||||
// ? = Your client unchoked the peer but the peer is not interested
|
||||
if (!isChocked() && !isRemoteInterested())
|
||||
{
|
||||
m_flags += "? ";
|
||||
m_flagsDescription += ("? = "
|
||||
+ tr("not interested(peer) and unchoked(local)") + '\n');
|
||||
}
|
||||
updateFlags(QLatin1Char('?'), tr("Not interested (peer) and unchoked (local)"));
|
||||
|
||||
// X = Peer was included in peerlists obtained through Peer Exchange (PEX)
|
||||
if (fromPeX())
|
||||
{
|
||||
m_flags += "X ";
|
||||
m_flagsDescription += ("X = " + tr("peer from PEX") + '\n');
|
||||
}
|
||||
// O = Optimistic unchoke
|
||||
if (optimisticUnchoke())
|
||||
updateFlags(QLatin1Char('O'), tr("Optimistic unchoke"));
|
||||
|
||||
// S = Peer is snubbed
|
||||
if (isSnubbed())
|
||||
updateFlags(QLatin1Char('S'), tr("Peer snubbed"));
|
||||
|
||||
// I = Peer is an incoming connection
|
||||
if (!isLocalConnection())
|
||||
updateFlags(QLatin1Char('I'), tr("Incoming connection"));
|
||||
|
||||
// H = Peer was obtained through DHT
|
||||
if (fromDHT())
|
||||
{
|
||||
m_flags += "H ";
|
||||
m_flagsDescription += ("H = " + tr("peer from DHT") + '\n');
|
||||
}
|
||||
updateFlags(QLatin1Char('H'), tr("Peer from DHT"));
|
||||
|
||||
// E = Peer is using Protocol Encryption (all traffic)
|
||||
if (isRC4Encrypted())
|
||||
{
|
||||
m_flags += "E ";
|
||||
m_flagsDescription += ("E = " + tr("encrypted traffic") + '\n');
|
||||
}
|
||||
|
||||
// e = Peer is using Protocol Encryption (handshake)
|
||||
if (isPlaintextEncrypted())
|
||||
{
|
||||
m_flags += "e ";
|
||||
m_flagsDescription += ("e = " + tr("encrypted handshake") + '\n');
|
||||
}
|
||||
|
||||
// P = Peer is using uTorrent uTP
|
||||
if (useUTPSocket())
|
||||
{
|
||||
m_flags += "P ";
|
||||
m_flagsDescription += ("P = " + QString::fromUtf8(C_UTP) + '\n');
|
||||
}
|
||||
// X = Peer was included in peerlists obtained through Peer Exchange (PEX)
|
||||
if (fromPeX())
|
||||
updateFlags(QLatin1Char('X'), tr("Peer from PEX"));
|
||||
|
||||
// L = Peer is local
|
||||
if (fromLSD())
|
||||
{
|
||||
m_flags += "L ";
|
||||
m_flagsDescription += ("L = " + tr("peer from LSD") + '\n');
|
||||
}
|
||||
updateFlags(QLatin1Char('L'), tr("Peer from LSD"));
|
||||
|
||||
m_flags = m_flags.trimmed();
|
||||
m_flagsDescription = m_flagsDescription.trimmed();
|
||||
// E = Peer is using Protocol Encryption (all traffic)
|
||||
if (isRC4Encrypted())
|
||||
updateFlags(QLatin1Char('E'), tr("Encrypted traffic"));
|
||||
|
||||
// e = Peer is using Protocol Encryption (handshake)
|
||||
if (isPlaintextEncrypted())
|
||||
updateFlags(QLatin1Char('e'), tr("Encrypted handshake"));
|
||||
|
||||
// P = Peer is using uTorrent uTP
|
||||
if (useUTPSocket())
|
||||
updateFlags(QLatin1Char('P'), QString::fromUtf8(C_UTP));
|
||||
|
||||
m_flags.chop(1);
|
||||
m_flagsDescription.chop(1);
|
||||
}
|
||||
|
||||
QString PeerInfo::flags() const
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 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 "resumedatasavingmanager.h"
|
||||
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSaveFile>
|
||||
|
||||
#include "base/logger.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/io.h"
|
||||
|
||||
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
|
||||
: m_resumeDataDir(resumeFolderPath)
|
||||
{
|
||||
}
|
||||
|
||||
void ResumeDataSavingManager::save(const QString &filename, const QByteArray &data) const
|
||||
{
|
||||
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
|
||||
|
||||
QSaveFile file {filepath};
|
||||
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.commit())
|
||||
{
|
||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
||||
.arg(filepath, file.errorString()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
void ResumeDataSavingManager::save(const QString &filename, const std::shared_ptr<lt::entry> &data) const
|
||||
{
|
||||
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
|
||||
|
||||
QSaveFile file {filepath};
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
||||
.arg(filepath, file.errorString()), Log::CRITICAL);
|
||||
return;
|
||||
}
|
||||
|
||||
lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, *data);
|
||||
if ((file.error() != QFileDevice::NoError) || !file.commit())
|
||||
{
|
||||
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
|
||||
.arg(filepath, file.errorString()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
void ResumeDataSavingManager::remove(const QString &filename) const
|
||||
{
|
||||
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
|
||||
|
||||
Utils::Fs::forceRemove(filepath);
|
||||
}
|
||||
55
src/base/bittorrent/resumedatastorage.h
Normal file
55
src/base/bittorrent/resumedatastorage.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 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 <optional>
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QObject>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentID;
|
||||
struct LoadTorrentParams;
|
||||
|
||||
class ResumeDataStorage : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ResumeDataStorage)
|
||||
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
virtual QVector<TorrentID> registeredTorrents() const = 0;
|
||||
virtual std::optional<LoadTorrentParams> load(const TorrentID &id) const = 0;
|
||||
virtual void store(const TorrentID &id, const LoadTorrentParams &resumeData) const = 0;
|
||||
virtual void remove(const TorrentID &id) const = 0;
|
||||
virtual void storeQueue(const QVector<TorrentID> &queue) const = 0;
|
||||
};
|
||||
}
|
||||
@@ -42,28 +42,25 @@
|
||||
#endif
|
||||
|
||||
#include <libtorrent/alert_types.hpp>
|
||||
#include <libtorrent/bdecode.hpp>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/error_code.hpp>
|
||||
#include <libtorrent/extensions/smart_ban.hpp>
|
||||
#include <libtorrent/extensions/ut_metadata.hpp>
|
||||
#include <libtorrent/extensions/ut_pex.hpp>
|
||||
#include <libtorrent/ip_filter.hpp>
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
#include <libtorrent/read_resume_data.hpp>
|
||||
#include <libtorrent/session.hpp>
|
||||
#include <libtorrent/session_stats.hpp>
|
||||
#include <libtorrent/session_status.hpp>
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
#include <libtorrent/write_resume_data.hpp>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkAddressEntry>
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QNetworkConfigurationManager>
|
||||
#endif
|
||||
#include <QNetworkInterface>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
@@ -88,33 +85,27 @@
|
||||
#include "base/utils/random.h"
|
||||
#include "base/version.h"
|
||||
#include "bandwidthscheduler.h"
|
||||
#include "bencoderesumedatastorage.h"
|
||||
#include "common.h"
|
||||
#include "customstorage.h"
|
||||
#include "dbresumedatastorage.h"
|
||||
#include "filesearcher.h"
|
||||
#include "filterparserthread.h"
|
||||
#include "loadtorrentparams.h"
|
||||
#include "ltunderlyingtype.h"
|
||||
#include "magneturi.h"
|
||||
#include "nativesessionextension.h"
|
||||
#include "portforwarderimpl.h"
|
||||
#include "resumedatasavingmanager.h"
|
||||
#include "statistics.h"
|
||||
#include "torrentimpl.h"
|
||||
#include "tracker.h"
|
||||
#include "trackerentry.h"
|
||||
|
||||
static const char PEER_ID[] = "qB";
|
||||
static const char RESUME_FOLDER[] = "BT_backup";
|
||||
static const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2;
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename LTStr>
|
||||
QString fromLTString(const LTStr &str)
|
||||
{
|
||||
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
|
||||
}
|
||||
const char PEER_ID[] = "qB";
|
||||
const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2;
|
||||
|
||||
void torrentQueuePositionUp(const lt::torrent_handle &handle)
|
||||
{
|
||||
@@ -305,17 +296,6 @@ namespace
|
||||
};
|
||||
}
|
||||
|
||||
using ListType = lt::entry::list_type;
|
||||
|
||||
ListType setToEntryList(const QSet<QString> &input)
|
||||
{
|
||||
ListType entryList;
|
||||
entryList.reserve(input.size());
|
||||
for (const QString &setValue : input)
|
||||
entryList.emplace_back(setValue.toStdString());
|
||||
return entryList;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString convertIfaceNameToGuid(const QString &name)
|
||||
{
|
||||
@@ -374,6 +354,7 @@ Session::Session(QObject *parent)
|
||||
, m_sendBufferWatermark(BITTORRENT_SESSION_KEY("SendBufferWatermark"), 500)
|
||||
, m_sendBufferLowWatermark(BITTORRENT_SESSION_KEY("SendBufferLowWatermark"), 10)
|
||||
, m_sendBufferWatermarkFactor(BITTORRENT_SESSION_KEY("SendBufferWatermarkFactor"), 50)
|
||||
, m_connectionSpeed(BITTORRENT_SESSION_KEY("ConnectionSpeed"), 30)
|
||||
, m_socketBacklogSize(BITTORRENT_SESSION_KEY("SocketBacklogSize"), 30)
|
||||
, m_isAnonymousModeEnabled(BITTORRENT_SESSION_KEY("AnonymousModeEnabled"), false)
|
||||
, m_isQueueingEnabled(BITTORRENT_SESSION_KEY("QueueingSystemEnabled"), false)
|
||||
@@ -392,6 +373,7 @@ Session::Session(QObject *parent)
|
||||
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
|
||||
, m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
|
||||
, m_maxConcurrentHTTPAnnounces(BITTORRENT_SESSION_KEY("MaxConcurrentHTTPAnnounces"), 50)
|
||||
, m_isReannounceWhenAddressChangedEnabled(BITTORRENT_SESSION_KEY("ReannounceWhenAddressChanged"), false)
|
||||
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 5)
|
||||
, m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
|
||||
, m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), 20, lowerLimited(0, -1))
|
||||
@@ -405,7 +387,6 @@ Session::Session(QObject *parent)
|
||||
, m_IDNSupportEnabled(BITTORRENT_SESSION_KEY("IDNSupportEnabled"), false)
|
||||
, m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
|
||||
, m_validateHTTPSTrackerCertificate(BITTORRENT_SESSION_KEY("ValidateHTTPSTrackerCertificate"), true)
|
||||
, m_SSRFMitigationEnabled(BITTORRENT_SESSION_KEY("SSRFMitigation"), true)
|
||||
, m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY("BlockPeersOnPrivilegedPorts"), false)
|
||||
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
|
||||
, m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
|
||||
@@ -460,22 +441,22 @@ Session::Session(QObject *parent)
|
||||
return tmp;
|
||||
}
|
||||
)
|
||||
, m_resumeDataStorageType(BITTORRENT_SESSION_KEY("ResumeDataStorageType"), ResumeDataStorageType::Legacy)
|
||||
#if defined(Q_OS_WIN)
|
||||
, m_OSMemoryPriority(BITTORRENT_KEY("OSMemoryPriority"), OSMemoryPriority::BelowNormal)
|
||||
#endif
|
||||
, m_resumeFolderLock {new QFile {this}}
|
||||
, m_seedingLimitTimer {new QTimer {this}}
|
||||
, m_resumeDataTimer {new QTimer {this}}
|
||||
, m_statistics {new Statistics {this}}
|
||||
, m_ioThread {new QThread {this}}
|
||||
, m_recentErroredTorrentsTimer {new QTimer {this}}
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
, m_networkManager {new QNetworkConfigurationManager {this}}
|
||||
#endif
|
||||
{
|
||||
if (port() < 0)
|
||||
m_port = Utils::Random::rand(1024, 65535);
|
||||
|
||||
initResumeFolder();
|
||||
|
||||
m_recentErroredTorrentsTimer->setSingleShot(true);
|
||||
m_recentErroredTorrentsTimer->setInterval(1000);
|
||||
connect(m_recentErroredTorrentsTimer, &QTimer::timeout
|
||||
@@ -498,7 +479,8 @@ Session::Session(QObject *parent)
|
||||
m_storedCategories = map_cast(m_categories);
|
||||
}
|
||||
|
||||
m_tags = List::toSet(m_storedTags.get());
|
||||
const QStringList storedTags = m_storedTags.get();
|
||||
m_tags = {storedTags.cbegin(), storedTags.cend()};
|
||||
|
||||
enqueueRefresh();
|
||||
updateSeedingLimitTimer();
|
||||
@@ -510,15 +492,13 @@ Session::Session(QObject *parent)
|
||||
, &Net::ProxyConfigurationManager::proxyConfigurationChanged
|
||||
, this, &Session::configureDeferred);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Network configuration monitor
|
||||
connect(m_networkManager, &QNetworkConfigurationManager::onlineStateChanged, this, &Session::networkOnlineStateChanged);
|
||||
connect(m_networkManager, &QNetworkConfigurationManager::configurationAdded, this, &Session::networkConfigurationChange);
|
||||
connect(m_networkManager, &QNetworkConfigurationManager::configurationRemoved, this, &Session::networkConfigurationChange);
|
||||
connect(m_networkManager, &QNetworkConfigurationManager::configurationChanged, this, &Session::networkConfigurationChange);
|
||||
|
||||
m_resumeDataSavingManager = new ResumeDataSavingManager {m_resumeFolderPath};
|
||||
m_resumeDataSavingManager->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
|
||||
#endif
|
||||
|
||||
m_fileSearcher = new FileSearcher;
|
||||
m_fileSearcher->moveToThread(m_ioThread);
|
||||
@@ -1000,9 +980,6 @@ Session::~Session()
|
||||
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
|
||||
m_resumeFolderLock->close();
|
||||
m_resumeFolderLock->remove();
|
||||
}
|
||||
|
||||
void Session::initInstance()
|
||||
@@ -1034,9 +1011,9 @@ void Session::adjustLimits()
|
||||
|
||||
void Session::applyBandwidthLimits()
|
||||
{
|
||||
lt::settings_pack settingsPack = m_nativeSession->get_settings();
|
||||
applyBandwidthLimits(settingsPack);
|
||||
m_nativeSession->apply_settings(settingsPack);
|
||||
lt::settings_pack settingsPack = m_nativeSession->get_settings();
|
||||
applyBandwidthLimits(settingsPack);
|
||||
m_nativeSession->apply_settings(settingsPack);
|
||||
}
|
||||
|
||||
void Session::configure()
|
||||
@@ -1094,7 +1071,6 @@ void Session::initializeNativeSession()
|
||||
// Speed up exit
|
||||
pack.set_int(lt::settings_pack::auto_scrape_interval, 1200); // 20 minutes
|
||||
pack.set_int(lt::settings_pack::auto_scrape_min_interval, 900); // 15 minutes
|
||||
pack.set_int(lt::settings_pack::connection_speed, 20); // default is 10
|
||||
// libtorrent 1.1 enables UPnP & NAT-PMP by default
|
||||
// turn them off before `lt::session` ctor to avoid split second effects
|
||||
pack.set_bool(lt::settings_pack::enable_upnp, false);
|
||||
@@ -1211,6 +1187,8 @@ void Session::initMetrics()
|
||||
|
||||
void Session::loadLTSettings(lt::settings_pack &settingsPack)
|
||||
{
|
||||
settingsPack.set_int(lt::settings_pack::connection_speed, connectionSpeed());
|
||||
|
||||
// from libtorrent doc:
|
||||
// It will not take affect until the listen_interfaces settings is updated
|
||||
settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize());
|
||||
@@ -1403,8 +1381,6 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
|
||||
|
||||
settingsPack.set_bool(lt::settings_pack::validate_https_trackers, validateHTTPSTrackerCertificate());
|
||||
|
||||
settingsPack.set_bool(lt::settings_pack::ssrf_mitigation, isSSRFMitigationEnabled());
|
||||
|
||||
settingsPack.set_bool(lt::settings_pack::no_connect_privileged_ports, blockPeersOnPrivilegedPorts());
|
||||
|
||||
settingsPack.set_bool(lt::settings_pack::apply_ip_filter_to_trackers, isTrackerFilteringEnabled());
|
||||
@@ -1866,13 +1842,7 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
|
||||
}
|
||||
|
||||
// Remove it from torrent resume directory
|
||||
const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
|
||||
const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
|
||||
QMetaObject::invokeMethod(m_resumeDataSavingManager, [this, resumedataFile, metadataFile]()
|
||||
{
|
||||
m_resumeDataSavingManager->remove(resumedataFile);
|
||||
m_resumeDataSavingManager->remove(metadataFile);
|
||||
});
|
||||
m_resumeDataStorage->remove(torrent->id());
|
||||
|
||||
delete torrent;
|
||||
return true;
|
||||
@@ -2085,8 +2055,8 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
|
||||
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
|
||||
loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
|
||||
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
|
||||
loadTorrentParams.forced = addTorrentParams.addForced;
|
||||
loadTorrentParams.paused = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
|
||||
loadTorrentParams.operatingMode = (addTorrentParams.addForced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged);
|
||||
loadTorrentParams.stopped = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
|
||||
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
||||
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
||||
|
||||
@@ -2213,11 +2183,11 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||
else
|
||||
p.flags &= ~lt::torrent_flags::seed_mode;
|
||||
|
||||
if (loadTorrentParams.paused || !loadTorrentParams.forced)
|
||||
if (loadTorrentParams.stopped || (loadTorrentParams.operatingMode == TorrentOperatingMode::AutoManaged))
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
else
|
||||
p.flags &= ~lt::torrent_flags::paused;
|
||||
if (loadTorrentParams.paused || loadTorrentParams.forced)
|
||||
if (loadTorrentParams.stopped || (loadTorrentParams.operatingMode == TorrentOperatingMode::Forced))
|
||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||
else
|
||||
p.flags |= lt::torrent_flags::auto_managed;
|
||||
@@ -2332,23 +2302,28 @@ void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder fold
|
||||
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
|
||||
|
||||
const QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
|
||||
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
|
||||
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
|
||||
const QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename);
|
||||
const QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
|
||||
if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath()))
|
||||
{
|
||||
QString newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
|
||||
int counter = 0;
|
||||
while (QFile::exists(newTorrentPath) && !Utils::Fs::sameFiles(torrentPath, newTorrentPath))
|
||||
while (QFile::exists(newTorrentPath))
|
||||
{
|
||||
// Append number to torrent name to make it unique
|
||||
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
|
||||
newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
|
||||
}
|
||||
|
||||
if (!QFile::exists(newTorrentPath))
|
||||
QFile::copy(torrentPath, newTorrentPath);
|
||||
try
|
||||
{
|
||||
torrent->info().saveToFile(newTorrentPath);
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
|
||||
.arg(newTorrentPath, err.message()), Log::WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2401,31 +2376,25 @@ void Session::saveResumeData()
|
||||
|
||||
void Session::saveTorrentsQueue() const
|
||||
{
|
||||
// store hash in textual representation
|
||||
QMap<int, QString> queue; // Use QMap since it should be ordered by key
|
||||
QVector<TorrentID> queue;
|
||||
for (const TorrentImpl *torrent : asConst(m_torrents))
|
||||
{
|
||||
// We require actual (non-cached) queue position here!
|
||||
const int queuePos = static_cast<LTUnderlyingType<lt::queue_position_t>>(torrent->nativeHandle().queue_position());
|
||||
if (queuePos >= 0)
|
||||
queue[queuePos] = torrent->id().toString();
|
||||
{
|
||||
if (queuePos >= queue.size())
|
||||
queue.resize(queuePos + 1);
|
||||
queue[queuePos] = torrent->id();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
data.reserve(((TorrentID::length() * 2) + 1) * queue.size());
|
||||
for (const QString &torrentID : asConst(queue))
|
||||
data += (torrentID.toLatin1() + '\n');
|
||||
|
||||
const QString filename = QLatin1String {"queue"};
|
||||
QMetaObject::invokeMethod(m_resumeDataSavingManager
|
||||
, [this, data, filename]() { m_resumeDataSavingManager->save(filename, data); });
|
||||
m_resumeDataStorage->storeQueue(queue);
|
||||
}
|
||||
|
||||
void Session::removeTorrentsQueue() const
|
||||
{
|
||||
const QString filename = QLatin1String {"queue"};
|
||||
QMetaObject::invokeMethod(m_resumeDataSavingManager
|
||||
, [this, filename]() { m_resumeDataSavingManager->remove(filename); });
|
||||
m_resumeDataStorage->storeQueue({});
|
||||
}
|
||||
|
||||
void Session::setDefaultSavePath(QString path)
|
||||
@@ -2454,6 +2423,7 @@ void Session::setTempPath(QString path)
|
||||
torrent->handleTempPathChanged();
|
||||
}
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
void Session::networkOnlineStateChanged(const bool online)
|
||||
{
|
||||
LogMsg(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
|
||||
@@ -2474,6 +2444,7 @@ void Session::networkConfigurationChange(const QNetworkConfiguration &cfg)
|
||||
configureListeningInterface();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QStringList Session::getListeningIPs() const
|
||||
{
|
||||
@@ -2777,6 +2748,9 @@ void Session::setPort(const int port)
|
||||
{
|
||||
m_port = port;
|
||||
configureListeningInterface();
|
||||
|
||||
if (isReannounceWhenAddressChangedEnabled())
|
||||
reannounceToAllTrackers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2976,6 +2950,16 @@ void Session::setBannedIPs(const QStringList &newList)
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
ResumeDataStorageType Session::resumeDataStorageType() const
|
||||
{
|
||||
return m_resumeDataStorageType;
|
||||
}
|
||||
|
||||
void Session::setResumeDataStorageType(const ResumeDataStorageType type)
|
||||
{
|
||||
m_resumeDataStorageType = type;
|
||||
}
|
||||
|
||||
QStringList Session::bannedIPs() const
|
||||
{
|
||||
return m_bannedIPs;
|
||||
@@ -3057,7 +3041,6 @@ void Session::setMaxConnectionsPerTorrent(int max)
|
||||
// Apply this to all session torrents
|
||||
for (const lt::torrent_handle &handle : m_nativeSession->get_torrents())
|
||||
{
|
||||
if (!handle.is_valid()) continue;
|
||||
try
|
||||
{
|
||||
handle.set_max_connections(max);
|
||||
@@ -3082,7 +3065,6 @@ void Session::setMaxUploadsPerTorrent(int max)
|
||||
// Apply this to all session torrents
|
||||
for (const lt::torrent_handle &handle : m_nativeSession->get_torrents())
|
||||
{
|
||||
if (!handle.is_valid()) continue;
|
||||
try
|
||||
{
|
||||
handle.set_max_uploads(max);
|
||||
@@ -3352,6 +3334,19 @@ void Session::setSendBufferWatermarkFactor(const int value)
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
int Session::connectionSpeed() const
|
||||
{
|
||||
return m_connectionSpeed;
|
||||
}
|
||||
|
||||
void Session::setConnectionSpeed(const int value)
|
||||
{
|
||||
if (value == m_connectionSpeed) return;
|
||||
|
||||
m_connectionSpeed = value;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
int Session::socketBacklogSize() const
|
||||
{
|
||||
return m_socketBacklogSize;
|
||||
@@ -3613,6 +3608,25 @@ void Session::setMaxConcurrentHTTPAnnounces(const int value)
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
bool Session::isReannounceWhenAddressChangedEnabled() const
|
||||
{
|
||||
return m_isReannounceWhenAddressChangedEnabled;
|
||||
}
|
||||
|
||||
void Session::setReannounceWhenAddressChangedEnabled(const bool enabled)
|
||||
{
|
||||
if (enabled == m_isReannounceWhenAddressChangedEnabled)
|
||||
return;
|
||||
|
||||
m_isReannounceWhenAddressChangedEnabled = enabled;
|
||||
}
|
||||
|
||||
void Session::reannounceToAllTrackers() const
|
||||
{
|
||||
for (const lt::torrent_handle &torrent : m_nativeSession->get_torrents())
|
||||
torrent.force_reannounce(0, -1, lt::torrent_handle::ignore_min_interval);
|
||||
}
|
||||
|
||||
int Session::stopTrackerTimeout() const
|
||||
{
|
||||
return m_stopTrackerTimeout;
|
||||
@@ -3739,19 +3753,6 @@ void Session::setValidateHTTPSTrackerCertificate(const bool enabled)
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
bool Session::isSSRFMitigationEnabled() const
|
||||
{
|
||||
return m_SSRFMitigationEnabled;
|
||||
}
|
||||
|
||||
void Session::setSSRFMitigationEnabled(const bool enabled)
|
||||
{
|
||||
if (enabled == m_SSRFMitigationEnabled) return;
|
||||
|
||||
m_SSRFMitigationEnabled = enabled;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
bool Session::blockPeersOnPrivilegedPorts() const
|
||||
{
|
||||
return m_blockPeersOnPrivilegedPorts;
|
||||
@@ -3817,14 +3818,13 @@ void Session::updateSeedingLimitTimer()
|
||||
}
|
||||
}
|
||||
|
||||
void Session::handleTorrentShareLimitChanged(TorrentImpl *const torrent)
|
||||
void Session::handleTorrentShareLimitChanged(TorrentImpl *const)
|
||||
{
|
||||
updateSeedingLimitTimer();
|
||||
}
|
||||
|
||||
void Session::handleTorrentNameChanged(TorrentImpl *const torrent)
|
||||
void Session::handleTorrentNameChanged(TorrentImpl *const)
|
||||
{
|
||||
Q_UNUSED(torrent);
|
||||
}
|
||||
|
||||
void Session::handleTorrentSavePathChanged(TorrentImpl *const torrent)
|
||||
@@ -3891,21 +3891,9 @@ void Session::handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVe
|
||||
|
||||
void Session::handleTorrentMetadataReceived(TorrentImpl *const torrent)
|
||||
{
|
||||
// Save metadata
|
||||
const QDir resumeDataDir {m_resumeFolderPath};
|
||||
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->id().toString())};
|
||||
try
|
||||
{
|
||||
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
|
||||
// Copy the torrent file to the export folder
|
||||
if (!torrentExportDirectory().isEmpty())
|
||||
exportTorrentFile(torrent);
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2")
|
||||
.arg(torrentFileName, err.message()), Log::CRITICAL);
|
||||
}
|
||||
// Copy the torrent file to the export folder
|
||||
if (!torrentExportDirectory().isEmpty())
|
||||
exportTorrentFile(torrent);
|
||||
|
||||
emit torrentMetadataReceived(torrent);
|
||||
}
|
||||
@@ -3966,48 +3954,7 @@ void Session::handleTorrentResumeDataReady(TorrentImpl *const torrent, const Loa
|
||||
{
|
||||
--m_numResumeData;
|
||||
|
||||
// We need to adjust native libtorrent resume data
|
||||
lt::add_torrent_params p = data.ltAddTorrentParams;
|
||||
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
|
||||
if (data.paused)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::paused;
|
||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Torrent can be actually "running" but temporarily "paused" to perform some
|
||||
// service jobs behind the scenes so we need to restore it as "running"
|
||||
if (!data.forced)
|
||||
{
|
||||
p.flags |= lt::torrent_flags::auto_managed;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.flags &= ~lt::torrent_flags::paused;
|
||||
p.flags &= ~lt::torrent_flags::auto_managed;
|
||||
}
|
||||
}
|
||||
|
||||
// Separated thread is used for the blocking IO which results in slow processing of many torrents.
|
||||
// Copying lt::entry objects around isn't cheap.
|
||||
|
||||
auto resumeDataPtr = std::make_shared<lt::entry>(lt::write_resume_data(p));
|
||||
lt::entry &resumeData = *resumeDataPtr;
|
||||
|
||||
resumeData["qBt-savePath"] = Profile::instance()->toPortablePath(data.savePath).toStdString();
|
||||
resumeData["qBt-ratioLimit"] = static_cast<int>(data.ratioLimit * 1000);
|
||||
resumeData["qBt-seedingTimeLimit"] = data.seedingTimeLimit;
|
||||
resumeData["qBt-category"] = data.category.toStdString();
|
||||
resumeData["qBt-tags"] = setToEntryList(data.tags);
|
||||
resumeData["qBt-name"] = data.name.toStdString();
|
||||
resumeData["qBt-seedStatus"] = data.hasSeedStatus;
|
||||
resumeData["qBt-contentLayout"] = Utils::String::fromEnum(data.contentLayout).toStdString();
|
||||
resumeData["qBt-firstLastPiecePriority"] = data.firstLastPiecePriority;
|
||||
|
||||
const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
|
||||
QMetaObject::invokeMethod(m_resumeDataSavingManager
|
||||
, [this, filename, resumeDataPtr]() { m_resumeDataSavingManager->save(filename, resumeDataPtr); });
|
||||
m_resumeDataStorage->store(torrent->id(), data);
|
||||
}
|
||||
|
||||
void Session::handleTorrentTrackerReply(TorrentImpl *const torrent, const QString &trackerUrl)
|
||||
@@ -4140,28 +4087,6 @@ bool Session::hasPerTorrentSeedingTimeLimit() const
|
||||
});
|
||||
}
|
||||
|
||||
void Session::initResumeFolder()
|
||||
{
|
||||
m_resumeFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER);
|
||||
const QDir resumeFolderDir(m_resumeFolderPath);
|
||||
if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath()))
|
||||
{
|
||||
m_resumeFolderLock->setFileName(resumeFolderDir.absoluteFilePath("session.lock"));
|
||||
if (!m_resumeFolderLock->open(QFile::WriteOnly))
|
||||
{
|
||||
throw RuntimeError
|
||||
{tr("Cannot write to torrent resume folder: \"%1\"")
|
||||
.arg(Utils::Fs::toNativePath(m_resumeFolderPath))};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError
|
||||
{tr("Cannot create torrent resume folder: \"%1\"")
|
||||
.arg(Utils::Fs::toNativePath(m_resumeFolderPath))};
|
||||
}
|
||||
}
|
||||
|
||||
void Session::configureDeferred()
|
||||
{
|
||||
if (m_deferredConfigureScheduled)
|
||||
@@ -4240,159 +4165,60 @@ const CacheStatus &Session::cacheStatus() const
|
||||
return m_cacheStatus;
|
||||
}
|
||||
|
||||
bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams)
|
||||
{
|
||||
torrentParams = {};
|
||||
|
||||
lt::error_code ec;
|
||||
const lt::bdecode_node root = lt::bdecode(data, ec);
|
||||
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false;
|
||||
|
||||
torrentParams.restored = true;
|
||||
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
|
||||
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
|
||||
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
||||
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
|
||||
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
|
||||
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
|
||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
||||
// === BEGIN DEPRECATED CODE === //
|
||||
const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
|
||||
if (contentLayoutNode.type() == lt::bdecode_node::string_t)
|
||||
{
|
||||
const QString contentLayoutStr = fromLTString(contentLayoutNode.string_value());
|
||||
torrentParams.contentLayout = Utils::String::toEnum(contentLayoutStr, TorrentContentLayout::Original);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
|
||||
torrentParams.contentLayout = (hasRootFolder ? TorrentContentLayout::Original : TorrentContentLayout::NoSubfolder);
|
||||
}
|
||||
// === END DEPRECATED CODE === //
|
||||
// === BEGIN REPLACEMENT CODE === //
|
||||
// torrentParams.contentLayout = Utils::String::parse(
|
||||
// fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default);
|
||||
// === END REPLACEMENT CODE === //
|
||||
|
||||
const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
|
||||
if (ratioLimitString.empty())
|
||||
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
|
||||
else
|
||||
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
|
||||
|
||||
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
|
||||
if (tagsNode.type() == lt::bdecode_node::list_t)
|
||||
{
|
||||
for (int i = 0; i < tagsNode.list_size(); ++i)
|
||||
{
|
||||
const QString tag = fromLTString(tagsNode.list_string_value_at(i));
|
||||
if (Session::isValidTag(tag))
|
||||
torrentParams.tags << tag;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Do we really need the following block in case of existing (restored) torrent?
|
||||
torrentParams.savePath = normalizePath(torrentParams.savePath);
|
||||
if (!torrentParams.category.isEmpty())
|
||||
{
|
||||
if (!m_categories.contains(torrentParams.category) && !addCategory(torrentParams.category))
|
||||
torrentParams.category = "";
|
||||
}
|
||||
|
||||
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
|
||||
|
||||
p = lt::read_resume_data(root, ec);
|
||||
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
|
||||
if (metadata.isValid())
|
||||
p.ti = metadata.nativeInfo();
|
||||
|
||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||
{
|
||||
// If torrent has "stop_when_ready" flag set then it is actually "stopped"
|
||||
torrentParams.paused = true;
|
||||
torrentParams.forced = false;
|
||||
// ...but temporarily "resumed" to perform some service jobs (e.g. checking)
|
||||
p.flags &= ~lt::torrent_flags::paused;
|
||||
p.flags |= lt::torrent_flags::auto_managed;
|
||||
}
|
||||
else
|
||||
{
|
||||
torrentParams.paused = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
|
||||
torrentParams.forced = !(p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
|
||||
}
|
||||
|
||||
const bool hasMetadata = (p.ti && p.ti->is_valid());
|
||||
if (!hasMetadata && !root.dict_find("info-hash"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Will resume torrents in backup directory
|
||||
void Session::startUpTorrents()
|
||||
{
|
||||
const QDir resumeDataDir {m_resumeFolderPath};
|
||||
QStringList fastresumes = resumeDataDir.entryList(
|
||||
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
qDebug("Initializing torrents resume data storage...");
|
||||
|
||||
const auto readFile = [](const QString &path, QByteArray &buf) -> bool
|
||||
const QString dbPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("torrents.db"));
|
||||
const bool dbStorageExists = QFile::exists(dbPath);
|
||||
|
||||
ResumeDataStorage *startupStorage = nullptr;
|
||||
if (resumeDataStorageType() == ResumeDataStorageType::SQLite)
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
m_resumeDataStorage = new DBResumeDataStorage(dbPath, this);
|
||||
|
||||
if (!dbStorageExists)
|
||||
{
|
||||
LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING);
|
||||
return false;
|
||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("BT_backup"));
|
||||
startupStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||
}
|
||||
|
||||
buf = file.readAll();
|
||||
return true;
|
||||
};
|
||||
|
||||
qDebug("Starting up torrents...");
|
||||
qDebug("Queue size: %d", fastresumes.size());
|
||||
|
||||
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
||||
|
||||
if (isQueueingSystemEnabled())
|
||||
}
|
||||
else
|
||||
{
|
||||
QFile queueFile {resumeDataDir.absoluteFilePath(QLatin1String {"queue"})};
|
||||
QStringList queue;
|
||||
if (queueFile.open(QFile::ReadOnly))
|
||||
{
|
||||
QByteArray line;
|
||||
while (!(line = queueFile.readLine()).isEmpty())
|
||||
queue.append(QString::fromLatin1(line.trimmed()) + QLatin1String {".fastresume"});
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't load torrents queue from '%1'. Error: %2")
|
||||
.arg(queueFile.fileName(), queueFile.errorString()), Log::WARNING);
|
||||
}
|
||||
const QString dataPath = Utils::Fs::expandPathAbs(
|
||||
specialFolderLocation(SpecialFolder::Data) + QLatin1String("BT_backup"));
|
||||
m_resumeDataStorage = new BencodeResumeDataStorage(dataPath, this);
|
||||
|
||||
if (!queue.empty())
|
||||
fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values();
|
||||
if (dbStorageExists)
|
||||
startupStorage = new DBResumeDataStorage(dbPath, this);
|
||||
}
|
||||
|
||||
int resumedTorrentsCount = 0;
|
||||
for (const QString &fastresumeName : asConst(fastresumes))
|
||||
{
|
||||
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
|
||||
if (!rxMatch.hasMatch()) continue;
|
||||
if (!startupStorage)
|
||||
startupStorage = m_resumeDataStorage;
|
||||
|
||||
const QString hash = rxMatch.captured(1);
|
||||
const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
|
||||
QByteArray data;
|
||||
LoadTorrentParams torrentParams;
|
||||
const QString torrentFilePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(hash));
|
||||
TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
|
||||
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, metadata, torrentParams))
|
||||
qDebug("Starting up torrents...");
|
||||
|
||||
const QVector<TorrentID> torrents = startupStorage->registeredTorrents();
|
||||
int resumedTorrentsCount = 0;
|
||||
QVector<TorrentID> queue;
|
||||
for (const TorrentID &torrentID : torrents)
|
||||
{
|
||||
const std::optional<LoadTorrentParams> resumeData = startupStorage->load(torrentID);
|
||||
if (resumeData)
|
||||
{
|
||||
qDebug() << "Starting up torrent" << hash << "...";
|
||||
if (!loadTorrent(torrentParams))
|
||||
if (m_resumeDataStorage != startupStorage)
|
||||
{
|
||||
m_resumeDataStorage->store(torrentID, *resumeData);
|
||||
if (isQueueingSystemEnabled() && !resumeData->hasSeedStatus)
|
||||
queue.append(torrentID);
|
||||
}
|
||||
|
||||
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
|
||||
if (!loadTorrent(*resumeData))
|
||||
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
||||
.arg(hash), Log::CRITICAL);
|
||||
.arg(torrentID.toString()), Log::CRITICAL);
|
||||
|
||||
// process add torrent messages before message queue overflow
|
||||
if ((resumedTorrentsCount % 100) == 0) readAlerts();
|
||||
@@ -4402,9 +4228,19 @@ void Session::startUpTorrents()
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
||||
.arg(hash), Log::CRITICAL);
|
||||
.arg(torrentID.toString()), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_resumeDataStorage != startupStorage)
|
||||
{
|
||||
delete startupStorage;
|
||||
if (resumeDataStorageType() == ResumeDataStorageType::Legacy)
|
||||
Utils::Fs::forceRemove(dbPath);
|
||||
|
||||
if (isQueueingSystemEnabled())
|
||||
m_resumeDataStorage->storeQueue(queue);
|
||||
}
|
||||
}
|
||||
|
||||
quint64 Session::getAlltimeDL() const
|
||||
@@ -4486,6 +4322,9 @@ void Session::handleAlert(const lt::alert *a)
|
||||
{
|
||||
switch (a->type())
|
||||
{
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20003)
|
||||
case lt::file_prio_alert::alert_type:
|
||||
#endif
|
||||
case lt::file_renamed_alert::alert_type:
|
||||
case lt::file_completed_alert::alert_type:
|
||||
case lt::torrent_finished_alert::alert_type:
|
||||
@@ -4585,9 +4424,15 @@ void Session::dispatchTorrentAlert(const lt::alert *a)
|
||||
|
||||
void Session::createTorrent(const lt::torrent_handle &nativeHandle)
|
||||
{
|
||||
Q_ASSERT(m_loadingTorrents.contains(nativeHandle.info_hash()));
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
const auto torrentID = TorrentID::fromInfoHash(nativeHandle.info_hashes());
|
||||
#else
|
||||
const auto torrentID = TorrentID::fromInfoHash(nativeHandle.info_hash());
|
||||
#endif
|
||||
|
||||
const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash());
|
||||
Q_ASSERT(m_loadingTorrents.contains(torrentID));
|
||||
|
||||
const LoadTorrentParams params = m_loadingTorrents.take(torrentID);
|
||||
|
||||
auto *const torrent = new TorrentImpl {this, m_nativeSession, nativeHandle, params};
|
||||
m_torrents.insert(torrent->id(), torrent);
|
||||
@@ -4600,24 +4445,14 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_resumeDataStorage->store(torrent->id(), params);
|
||||
|
||||
// The following is useless for newly added magnet
|
||||
if (hasMetadata)
|
||||
{
|
||||
// Backup torrent file
|
||||
const QDir resumeDataDir {m_resumeFolderPath};
|
||||
const QString torrentFileName {QString::fromLatin1("%1.torrent").arg(torrent->id().toString())};
|
||||
try
|
||||
{
|
||||
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
|
||||
// Copy the torrent file to the export folder
|
||||
if (!torrentExportDirectory().isEmpty())
|
||||
exportTorrentFile(torrent);
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2")
|
||||
.arg(torrentFileName, err.message()), Log::CRITICAL);
|
||||
}
|
||||
// Copy the torrent file to the export folder
|
||||
if (!torrentExportDirectory().isEmpty())
|
||||
exportTorrentFile(torrent);
|
||||
}
|
||||
|
||||
if (isAddTrackersEnabled() && !torrent->isPrivate())
|
||||
@@ -4625,10 +4460,6 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
|
||||
|
||||
LogMsg(tr("'%1' added to download list.", "'torrent name' was added to download list.")
|
||||
.arg(torrent->name()));
|
||||
|
||||
// In case of crash before the scheduled generation
|
||||
// of the fastresumes.
|
||||
torrent->saveResumeData();
|
||||
}
|
||||
|
||||
if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0))
|
||||
@@ -4650,10 +4481,18 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
|
||||
{
|
||||
if (p->error)
|
||||
{
|
||||
qDebug("/!\\ Error: Failed to add torrent!");
|
||||
QString msg = QString::fromStdString(p->message());
|
||||
LogMsg(tr("Couldn't load torrent. Reason: %1").arg(msg), Log::WARNING);
|
||||
const QString msg = QString::fromStdString(p->message());
|
||||
LogMsg(tr("Couldn't load torrent. Reason: %1.").arg(msg), Log::WARNING);
|
||||
emit loadTorrentFailed(msg);
|
||||
|
||||
const lt::add_torrent_params ¶ms = p->params;
|
||||
const bool hasMetadata = (params.ti && params.ti->is_valid());
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
const auto id = TorrentID::fromInfoHash(hasMetadata ? params.ti->info_hashes() : params.info_hashes);
|
||||
#else
|
||||
const auto id = TorrentID::fromInfoHash(hasMetadata ? params.ti->info_hash() : params.info_hash);
|
||||
#endif
|
||||
m_loadingTorrents.remove(id);
|
||||
}
|
||||
else if (m_loadingTorrents.contains(p->handle.info_hash()))
|
||||
{
|
||||
@@ -4850,8 +4689,7 @@ void Session::handleListenSucceededAlert(const lt::listen_succeeded_alert *p)
|
||||
.arg(toString(p->address), proto, QString::number(p->port)), Log::INFO);
|
||||
|
||||
// Force reannounce on all torrents because some trackers blacklist some ports
|
||||
for (const lt::torrent_handle &torrent : m_nativeSession->get_torrents())
|
||||
torrent.force_reannounce();
|
||||
reannounceToAllTrackers();
|
||||
}
|
||||
|
||||
void Session::handleListenFailedAlert(const lt::listen_failed_alert *p)
|
||||
@@ -4865,8 +4703,16 @@ void Session::handleListenFailedAlert(const lt::listen_failed_alert *p)
|
||||
|
||||
void Session::handleExternalIPAlert(const lt::external_ip_alert *p)
|
||||
{
|
||||
const QString externalIP {toString(p->external_address)};
|
||||
LogMsg(tr("Detected external IP: %1", "e.g. Detected external IP: 1.1.1.1")
|
||||
.arg(toString(p->external_address)), Log::INFO);
|
||||
.arg(externalIP), Log::INFO);
|
||||
|
||||
if (m_lastExternalIP != externalIP)
|
||||
{
|
||||
if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIP.isEmpty())
|
||||
reannounceToAllTrackers();
|
||||
m_lastExternalIP = externalIP;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::handleSessionStatsAlert(const lt::session_stats_alert *p)
|
||||
@@ -4923,11 +4769,11 @@ void Session::handleSessionStatsAlert(const lt::session_stats_alert *p)
|
||||
m_status.diskWriteQueue = stats[m_metricIndices.peer.numPeersDownDisk];
|
||||
m_status.peersCount = stats[m_metricIndices.peer.numPeersConnected];
|
||||
|
||||
const int64_t numBlocksRead = stats[m_metricIndices.disk.numBlocksRead];
|
||||
m_cacheStatus.totalUsedBuffers = stats[m_metricIndices.disk.diskBlocksInUse];
|
||||
m_cacheStatus.jobQueueLength = stats[m_metricIndices.disk.queuedDiskJobs];
|
||||
|
||||
#if (LIBTORRENT_VERSION_NUM < 20000)
|
||||
const int64_t numBlocksRead = stats[m_metricIndices.disk.numBlocksRead];
|
||||
const int64_t numBlocksCacheHits = stats[m_metricIndices.disk.numBlocksCacheHits];
|
||||
m_cacheStatus.readRatio = static_cast<qreal>(numBlocksCacheHits) / std::max<int64_t>((numBlocksCacheHits + numBlocksRead), 1);
|
||||
#endif
|
||||
|
||||
@@ -50,10 +50,12 @@
|
||||
#include "cachestatus.h"
|
||||
#include "sessionstatus.h"
|
||||
#include "torrentinfo.h"
|
||||
#include "trackerentry.h"
|
||||
|
||||
class QFile;
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
class QNetworkConfiguration;
|
||||
class QNetworkConfigurationManager;
|
||||
#endif
|
||||
class QString;
|
||||
class QThread;
|
||||
class QTimer;
|
||||
@@ -62,7 +64,6 @@ class QUrl;
|
||||
class BandwidthScheduler;
|
||||
class FileSearcher;
|
||||
class FilterParserThread;
|
||||
class ResumeDataSavingManager;
|
||||
class Statistics;
|
||||
|
||||
// These values should remain unchanged when adding new items
|
||||
@@ -96,11 +97,11 @@ namespace BitTorrent
|
||||
{
|
||||
class InfoHash;
|
||||
class MagnetUri;
|
||||
class ResumeDataStorage;
|
||||
class Torrent;
|
||||
class TorrentImpl;
|
||||
class Tracker;
|
||||
struct LoadTorrentParams;
|
||||
struct TrackerEntry;
|
||||
|
||||
enum class MoveStorageMode;
|
||||
|
||||
@@ -141,6 +142,13 @@ namespace BitTorrent
|
||||
};
|
||||
Q_ENUM_NS(SeedChokingAlgorithm)
|
||||
|
||||
enum class ResumeDataStorageType
|
||||
{
|
||||
Legacy,
|
||||
SQLite
|
||||
};
|
||||
Q_ENUM_NS(ResumeDataStorageType)
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
enum class OSMemoryPriority : int
|
||||
{
|
||||
@@ -364,6 +372,8 @@ namespace BitTorrent
|
||||
void setSendBufferLowWatermark(int value);
|
||||
int sendBufferWatermarkFactor() const;
|
||||
void setSendBufferWatermarkFactor(int value);
|
||||
int connectionSpeed() const;
|
||||
void setConnectionSpeed(int value);
|
||||
int socketBacklogSize() const;
|
||||
void setSocketBacklogSize(int value);
|
||||
bool isAnonymousModeEnabled() const;
|
||||
@@ -394,6 +404,9 @@ namespace BitTorrent
|
||||
void setAnnounceIP(const QString &ip);
|
||||
int maxConcurrentHTTPAnnounces() const;
|
||||
void setMaxConcurrentHTTPAnnounces(int value);
|
||||
bool isReannounceWhenAddressChangedEnabled() const;
|
||||
void setReannounceWhenAddressChangedEnabled(bool enabled);
|
||||
void reannounceToAllTrackers() const;
|
||||
int stopTrackerTimeout() const;
|
||||
void setStopTrackerTimeout(int value);
|
||||
int maxConnections() const;
|
||||
@@ -422,14 +435,14 @@ namespace BitTorrent
|
||||
void setMultiConnectionsPerIpEnabled(bool enabled);
|
||||
bool validateHTTPSTrackerCertificate() const;
|
||||
void setValidateHTTPSTrackerCertificate(bool enabled);
|
||||
bool isSSRFMitigationEnabled() const;
|
||||
void setSSRFMitigationEnabled(bool enabled);
|
||||
bool blockPeersOnPrivilegedPorts() const;
|
||||
void setBlockPeersOnPrivilegedPorts(bool enabled);
|
||||
bool isTrackerFilteringEnabled() const;
|
||||
void setTrackerFilteringEnabled(bool enabled);
|
||||
QStringList bannedIPs() const;
|
||||
void setBannedIPs(const QStringList &newList);
|
||||
ResumeDataStorageType resumeDataStorageType() const;
|
||||
void setResumeDataStorageType(ResumeDataStorageType type);
|
||||
#if defined(Q_OS_WIN)
|
||||
OSMemoryPriority getOSMemoryPriority() const;
|
||||
void setOSMemoryPriority(OSMemoryPriority priority);
|
||||
@@ -544,9 +557,11 @@ namespace BitTorrent
|
||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
||||
void fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Session reconfiguration triggers
|
||||
void networkOnlineStateChanged(bool online);
|
||||
void networkConfigurationChange(const QNetworkConfiguration &);
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct MoveStorageJob
|
||||
@@ -569,8 +584,6 @@ namespace BitTorrent
|
||||
bool hasPerTorrentRatioLimit() const;
|
||||
bool hasPerTorrentSeedingTimeLimit() const;
|
||||
|
||||
void initResumeFolder();
|
||||
|
||||
// Session configuration
|
||||
Q_INVOKABLE void configure();
|
||||
void configureComponents();
|
||||
@@ -595,7 +608,6 @@ namespace BitTorrent
|
||||
void applyOSMemoryPriority() const;
|
||||
#endif
|
||||
|
||||
bool loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams);
|
||||
bool loadTorrent(LoadTorrentParams params);
|
||||
LoadTorrentParams initLoadTorrentParams(const AddTorrentParams &addTorrentParams);
|
||||
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
|
||||
@@ -665,6 +677,7 @@ namespace BitTorrent
|
||||
CachedSettingValue<int> m_sendBufferWatermark;
|
||||
CachedSettingValue<int> m_sendBufferLowWatermark;
|
||||
CachedSettingValue<int> m_sendBufferWatermarkFactor;
|
||||
CachedSettingValue<int> m_connectionSpeed;
|
||||
CachedSettingValue<int> m_socketBacklogSize;
|
||||
CachedSettingValue<bool> m_isAnonymousModeEnabled;
|
||||
CachedSettingValue<bool> m_isQueueingEnabled;
|
||||
@@ -683,6 +696,7 @@ namespace BitTorrent
|
||||
CachedSettingValue<bool> m_includeOverheadInLimits;
|
||||
CachedSettingValue<QString> m_announceIP;
|
||||
CachedSettingValue<int> m_maxConcurrentHTTPAnnounces;
|
||||
CachedSettingValue<bool> m_isReannounceWhenAddressChangedEnabled;
|
||||
CachedSettingValue<int> m_stopTrackerTimeout;
|
||||
CachedSettingValue<int> m_maxConnections;
|
||||
CachedSettingValue<int> m_maxUploads;
|
||||
@@ -694,7 +708,6 @@ namespace BitTorrent
|
||||
CachedSettingValue<bool> m_IDNSupportEnabled;
|
||||
CachedSettingValue<bool> m_multiConnectionsPerIpEnabled;
|
||||
CachedSettingValue<bool> m_validateHTTPSTrackerCertificate;
|
||||
CachedSettingValue<bool> m_SSRFMitigationEnabled;
|
||||
CachedSettingValue<bool> m_blockPeersOnPrivilegedPorts;
|
||||
CachedSettingValue<bool> m_isAddTrackersEnabled;
|
||||
CachedSettingValue<QString> m_additionalTrackers;
|
||||
@@ -739,6 +752,7 @@ namespace BitTorrent
|
||||
CachedSettingValue<int> m_peerTurnoverCutoff;
|
||||
CachedSettingValue<int> m_peerTurnoverInterval;
|
||||
CachedSettingValue<QStringList> m_bannedIPs;
|
||||
CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType;
|
||||
#if defined(Q_OS_WIN)
|
||||
CachedSettingValue<OSMemoryPriority> m_OSMemoryPriority;
|
||||
#endif
|
||||
@@ -751,8 +765,6 @@ namespace BitTorrent
|
||||
int m_numResumeData = 0;
|
||||
int m_extraLimit = 0;
|
||||
QVector<TrackerEntry> m_additionalTrackerList;
|
||||
QString m_resumeFolderPath;
|
||||
QFile *m_resumeFolderLock = nullptr;
|
||||
|
||||
bool m_refreshEnqueued = false;
|
||||
QTimer *m_seedingLimitTimer = nullptr;
|
||||
@@ -763,9 +775,9 @@ namespace BitTorrent
|
||||
QPointer<BandwidthScheduler> m_bwScheduler;
|
||||
// Tracker
|
||||
QPointer<Tracker> m_tracker;
|
||||
// fastresume data writing thread
|
||||
|
||||
QThread *m_ioThread = nullptr;
|
||||
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
|
||||
ResumeDataStorage *m_resumeDataStorage = nullptr;
|
||||
FileSearcher *m_fileSearcher = nullptr;
|
||||
|
||||
QSet<TorrentID> m_downloadedMetadata;
|
||||
@@ -787,11 +799,14 @@ namespace BitTorrent
|
||||
|
||||
SessionStatus m_status;
|
||||
CacheStatus m_cacheStatus;
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QNetworkConfigurationManager *m_networkManager = nullptr;
|
||||
#endif
|
||||
|
||||
QList<MoveStorageJob> m_moveStorageQueue;
|
||||
|
||||
QString m_lastExternalIP;
|
||||
|
||||
static Session *m_instance;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <QString>
|
||||
#include <QtContainerFwd>
|
||||
|
||||
#include "base/tagset.h"
|
||||
#include "abstractfilestorage.h"
|
||||
|
||||
class QBitArray;
|
||||
@@ -178,7 +179,7 @@ namespace BitTorrent
|
||||
virtual bool belongsToCategory(const QString &category) const = 0;
|
||||
virtual bool setCategory(const QString &category) = 0;
|
||||
|
||||
virtual QSet<QString> tags() const = 0;
|
||||
virtual TagSet tags() const = 0;
|
||||
virtual bool hasTag(const QString &tag) const = 0;
|
||||
virtual bool addTag(const QString &tag) = 0;
|
||||
virtual bool removeTag(const QString &tag) = 0;
|
||||
|
||||
@@ -42,9 +42,9 @@
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/global.h"
|
||||
#include "base/utils/compare.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/io.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "base/version.h"
|
||||
#include "ltunderlyingtype.h"
|
||||
|
||||
@@ -98,6 +98,12 @@ void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPiec
|
||||
emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::checkInterruptionRequested() const
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
throw RuntimeError(tr("Operation aborted"));
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::run()
|
||||
{
|
||||
emit updateProgress(0);
|
||||
@@ -105,6 +111,7 @@ void TorrentCreatorThread::run()
|
||||
try
|
||||
{
|
||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
|
||||
const Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> naturalLessThan {};
|
||||
|
||||
// Adding files to the torrent
|
||||
lt::file_storage fs;
|
||||
@@ -117,13 +124,13 @@ void TorrentCreatorThread::run()
|
||||
// need to sort the file names by natural sort order
|
||||
QStringList dirs = {m_params.inputPath};
|
||||
|
||||
QDirIterator dirIter(m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
|
||||
QDirIterator dirIter {m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
dirIter.next();
|
||||
dirs += dirIter.filePath();
|
||||
}
|
||||
std::sort(dirs.begin(), dirs.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
||||
|
||||
QStringList fileNames;
|
||||
QHash<QString, qint64> fileSizeMap;
|
||||
@@ -142,7 +149,7 @@ void TorrentCreatorThread::run()
|
||||
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
|
||||
}
|
||||
|
||||
std::sort(tmpNames.begin(), tmpNames.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
||||
fileNames += tmpNames;
|
||||
}
|
||||
|
||||
@@ -150,7 +157,7 @@ void TorrentCreatorThread::run()
|
||||
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
|
||||
}
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
checkInterruptionRequested();
|
||||
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
|
||||
@@ -180,9 +187,7 @@ void TorrentCreatorThread::run()
|
||||
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
|
||||
, [this, &newTorrent](const lt::piece_index_t n)
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
throw RuntimeError {tr("Create new torrent aborted.")};
|
||||
|
||||
checkInterruptionRequested();
|
||||
sendProgressSignal(static_cast<LTUnderlyingType<lt::piece_index_t>>(n), newTorrent.num_pieces());
|
||||
});
|
||||
|
||||
@@ -193,7 +198,7 @@ void TorrentCreatorThread::run()
|
||||
// Is private ?
|
||||
newTorrent.set_priv(m_params.isPrivate);
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
checkInterruptionRequested();
|
||||
|
||||
lt::entry entry = newTorrent.generate();
|
||||
|
||||
@@ -201,32 +206,30 @@ void TorrentCreatorThread::run()
|
||||
if (!m_params.source.isEmpty())
|
||||
entry["info"]["source"] = m_params.source.toStdString();
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
checkInterruptionRequested();
|
||||
|
||||
// create the torrent
|
||||
QFile outfile {m_params.savePath};
|
||||
if (!outfile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
|
||||
.arg(outfile.errorString())};
|
||||
}
|
||||
throw RuntimeError(outfile.errorString());
|
||||
|
||||
if (isInterruptionRequested()) return;
|
||||
checkInterruptionRequested();
|
||||
|
||||
lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry);
|
||||
if (outfile.error() != QFileDevice::NoError)
|
||||
{
|
||||
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
|
||||
.arg(outfile.errorString())};
|
||||
}
|
||||
throw RuntimeError(outfile.errorString());
|
||||
outfile.close();
|
||||
|
||||
emit updateProgress(100);
|
||||
emit creationSuccess(m_params.savePath, parentPath);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
emit creationFailure(e.what());
|
||||
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(err.message()));
|
||||
}
|
||||
catch (const std::exception &err)
|
||||
{
|
||||
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(QString::fromLocal8Bit(err.what())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,10 +65,11 @@ namespace BitTorrent
|
||||
class TorrentCreatorThread final : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(TorrentCreatorThread)
|
||||
|
||||
public:
|
||||
TorrentCreatorThread(QObject *parent = nullptr);
|
||||
~TorrentCreatorThread();
|
||||
explicit TorrentCreatorThread(QObject *parent = nullptr);
|
||||
~TorrentCreatorThread() override;
|
||||
|
||||
void create(const TorrentCreatorParams ¶ms);
|
||||
|
||||
@@ -79,16 +80,15 @@ namespace BitTorrent
|
||||
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
|
||||
signals:
|
||||
void creationFailure(const QString &msg);
|
||||
void creationSuccess(const QString &path, const QString &branchPath);
|
||||
void updateProgress(int progress);
|
||||
|
||||
private:
|
||||
void run() override;
|
||||
void sendProgressSignal(int currentPieceIdx, int totalPieces);
|
||||
void checkInterruptionRequested() const;
|
||||
|
||||
TorrentCreatorParams m_params;
|
||||
};
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "base/utils/string.h"
|
||||
#include "common.h"
|
||||
#include "downloadpriority.h"
|
||||
#include "loadtorrentparams.h"
|
||||
#include "ltqhash.h"
|
||||
#include "ltunderlyingtype.h"
|
||||
#include "peeraddress.h"
|
||||
@@ -275,12 +276,12 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||
, m_tags(params.tags)
|
||||
, m_ratioLimit(params.ratioLimit)
|
||||
, m_seedingTimeLimit(params.seedingTimeLimit)
|
||||
, m_operatingMode(params.forced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged)
|
||||
, m_operatingMode(params.operatingMode)
|
||||
, m_contentLayout(params.contentLayout)
|
||||
, m_hasSeedStatus(params.hasSeedStatus)
|
||||
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
|
||||
, m_useAutoTMM(params.savePath.isEmpty())
|
||||
, m_isStopped(params.paused)
|
||||
, m_isStopped(params.stopped)
|
||||
, m_ltAddTorrentParams(params.ltAddTorrentParams)
|
||||
{
|
||||
if (m_useAutoTMM)
|
||||
@@ -702,7 +703,7 @@ bool TorrentImpl::belongsToCategory(const QString &category) const
|
||||
return false;
|
||||
}
|
||||
|
||||
QSet<QString> TorrentImpl::tags() const
|
||||
TagSet TorrentImpl::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
@@ -716,18 +717,18 @@ bool TorrentImpl::addTag(const QString &tag)
|
||||
{
|
||||
if (!Session::isValidTag(tag))
|
||||
return false;
|
||||
if (hasTag(tag))
|
||||
return false;
|
||||
|
||||
if (!hasTag(tag))
|
||||
if (!m_session->hasTag(tag))
|
||||
{
|
||||
if (!m_session->hasTag(tag))
|
||||
if (!m_session->addTag(tag))
|
||||
return false;
|
||||
m_tags.insert(tag);
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
m_session->handleTorrentTagAdded(this, tag);
|
||||
return true;
|
||||
if (!m_session->addTag(tag))
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
m_tags.insert(tag);
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
m_session->handleTorrentTagAdded(this, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TorrentImpl::removeTag(const QString &tag)
|
||||
@@ -1259,7 +1260,7 @@ QVector<int> TorrentImpl::pieceAvailability() const
|
||||
std::vector<int> avail;
|
||||
m_nativeHandle.piece_availability(avail);
|
||||
|
||||
return Vector::fromStdVector(avail);
|
||||
return {avail.cbegin(), avail.cend()};
|
||||
}
|
||||
|
||||
qreal TorrentImpl::distributedCopies() const
|
||||
@@ -1517,6 +1518,7 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
|
||||
|
||||
m_maintenanceJob = MaintenanceJob::None;
|
||||
updateStatus();
|
||||
prepareResumeData(p);
|
||||
|
||||
m_session->handleTorrentMetadataReceived(this);
|
||||
}
|
||||
@@ -1769,31 +1771,10 @@ void TorrentImpl::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p)
|
||||
|
||||
void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
||||
{
|
||||
if (m_hasMissingFiles)
|
||||
{
|
||||
const auto havePieces = m_ltAddTorrentParams.have_pieces;
|
||||
const auto unfinishedPieces = m_ltAddTorrentParams.unfinished_pieces;
|
||||
const auto verifiedPieces = m_ltAddTorrentParams.verified_pieces;
|
||||
|
||||
// Update recent resume data but preserve existing progress
|
||||
m_ltAddTorrentParams = p->params;
|
||||
m_ltAddTorrentParams.have_pieces = havePieces;
|
||||
m_ltAddTorrentParams.unfinished_pieces = unfinishedPieces;
|
||||
m_ltAddTorrentParams.verified_pieces = verifiedPieces;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update recent resume data
|
||||
m_ltAddTorrentParams = p->params;
|
||||
}
|
||||
|
||||
m_ltAddTorrentParams.added_time = addedTime().toSecsSinceEpoch();
|
||||
|
||||
// We shouldn't save upload_mode flag to allow torrent operate normally on next run
|
||||
m_ltAddTorrentParams.flags &= ~lt::torrent_flags::upload_mode;
|
||||
|
||||
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
|
||||
{
|
||||
m_ltAddTorrentParams = p->params;
|
||||
|
||||
m_ltAddTorrentParams.have_pieces.clear();
|
||||
m_ltAddTorrentParams.verified_pieces.clear();
|
||||
|
||||
@@ -1802,6 +1783,36 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
||||
|
||||
m_session->findIncompleteFiles(metadata, m_savePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
prepareResumeData(p->params);
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentImpl::prepareResumeData(const lt::add_torrent_params ¶ms)
|
||||
{
|
||||
if (m_hasMissingFiles)
|
||||
{
|
||||
const auto havePieces = m_ltAddTorrentParams.have_pieces;
|
||||
const auto unfinishedPieces = m_ltAddTorrentParams.unfinished_pieces;
|
||||
const auto verifiedPieces = m_ltAddTorrentParams.verified_pieces;
|
||||
|
||||
// Update recent resume data but preserve existing progress
|
||||
m_ltAddTorrentParams = params;
|
||||
m_ltAddTorrentParams.have_pieces = havePieces;
|
||||
m_ltAddTorrentParams.unfinished_pieces = unfinishedPieces;
|
||||
m_ltAddTorrentParams.verified_pieces = verifiedPieces;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update recent resume data
|
||||
m_ltAddTorrentParams = params;
|
||||
}
|
||||
|
||||
m_ltAddTorrentParams.added_time = addedTime().toSecsSinceEpoch();
|
||||
|
||||
// We shouldn't save upload_mode flag to allow torrent operate normally on next run
|
||||
m_ltAddTorrentParams.flags &= ~lt::torrent_flags::upload_mode;
|
||||
|
||||
LoadTorrentParams resumeData;
|
||||
resumeData.name = m_name;
|
||||
@@ -1813,8 +1824,8 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
||||
resumeData.seedingTimeLimit = m_seedingTimeLimit;
|
||||
resumeData.firstLastPiecePriority = m_hasFirstLastPiecePriority;
|
||||
resumeData.hasSeedStatus = m_hasSeedStatus;
|
||||
resumeData.paused = m_isStopped;
|
||||
resumeData.forced = (m_operatingMode == TorrentOperatingMode::Forced);
|
||||
resumeData.stopped = m_isStopped;
|
||||
resumeData.operatingMode = m_operatingMode;
|
||||
resumeData.ltAddTorrentParams = m_ltAddTorrentParams;
|
||||
|
||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||
@@ -1854,9 +1865,9 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
|
||||
if (m_oldPath[p->index].isEmpty())
|
||||
m_oldPath.remove(p->index);
|
||||
|
||||
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', QString::SkipEmptyParts);
|
||||
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', Qt::SkipEmptyParts);
|
||||
oldPathParts.removeLast(); // drop file name part
|
||||
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', QString::SkipEmptyParts);
|
||||
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', Qt::SkipEmptyParts);
|
||||
newPathParts.removeLast(); // drop file name part
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -1924,6 +1935,14 @@ void TorrentImpl::handleFileErrorAlert(const lt::file_error_alert *p)
|
||||
m_lastFileError = {p->error, p->op};
|
||||
}
|
||||
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20003)
|
||||
void TorrentImpl::handleFilePrioAlert(const lt::file_prio_alert *)
|
||||
{
|
||||
if (m_nativeHandle.need_save_resume_data())
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TorrentImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
|
||||
{
|
||||
Q_UNUSED(p);
|
||||
@@ -1961,6 +1980,11 @@ void TorrentImpl::handleAlert(const lt::alert *a)
|
||||
{
|
||||
switch (a->type())
|
||||
{
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20003)
|
||||
case lt::file_prio_alert::alert_type:
|
||||
handleFilePrioAlert(static_cast<const lt::file_prio_alert*>(a));
|
||||
break;
|
||||
#endif
|
||||
case lt::file_renamed_alert::alert_type:
|
||||
handleFileRenamedAlert(static_cast<const lt::file_renamed_alert*>(a));
|
||||
break;
|
||||
|
||||
@@ -42,39 +42,29 @@
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/tagset.h"
|
||||
#include "infohash.h"
|
||||
#include "speedmonitor.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentinfo.h"
|
||||
|
||||
#if (LIBTORRENT_VERSION_NUM == 20003)
|
||||
// file_prio_alert is missing to be forward declared in "libtorrent/fwd.hpp"
|
||||
namespace libtorrent
|
||||
{
|
||||
TORRENT_VERSION_NAMESPACE_3
|
||||
struct file_prio_alert;
|
||||
TORRENT_VERSION_NAMESPACE_3_END
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class Session;
|
||||
struct AddTorrentParams;
|
||||
|
||||
struct LoadTorrentParams
|
||||
{
|
||||
lt::add_torrent_params ltAddTorrentParams {};
|
||||
|
||||
QString name;
|
||||
QString category;
|
||||
QSet<QString> tags;
|
||||
QString savePath;
|
||||
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
||||
bool firstLastPiecePriority = false;
|
||||
bool hasSeedStatus = false;
|
||||
bool forced = false;
|
||||
bool paused = false;
|
||||
|
||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
|
||||
bool restored = false; // is existing torrent job?
|
||||
};
|
||||
struct LoadTorrentParams;
|
||||
|
||||
enum class MoveStorageMode
|
||||
{
|
||||
@@ -131,7 +121,7 @@ namespace BitTorrent
|
||||
bool belongsToCategory(const QString &category) const override;
|
||||
bool setCategory(const QString &category) override;
|
||||
|
||||
QSet<QString> tags() const override;
|
||||
TagSet tags() const override;
|
||||
bool hasTag(const QString &tag) const override;
|
||||
bool addTag(const QString &tag) override;
|
||||
bool removeTag(const QString &tag) override;
|
||||
@@ -263,7 +253,7 @@ namespace BitTorrent
|
||||
QString actualStorageLocation() const;
|
||||
|
||||
private:
|
||||
typedef std::function<void ()> EventTrigger;
|
||||
using EventTrigger = std::function<void ()>;
|
||||
|
||||
void updateStatus();
|
||||
void updateStatus(const lt::torrent_status &nativeStatus);
|
||||
@@ -272,6 +262,9 @@ namespace BitTorrent
|
||||
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
|
||||
void handleFileCompletedAlert(const lt::file_completed_alert *p);
|
||||
void handleFileErrorAlert(const lt::file_error_alert *p);
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20003)
|
||||
void handleFilePrioAlert(const lt::file_prio_alert *p);
|
||||
#endif
|
||||
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
|
||||
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
|
||||
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
|
||||
@@ -297,6 +290,7 @@ namespace BitTorrent
|
||||
void manageIncompleteFiles();
|
||||
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
||||
|
||||
void prepareResumeData(const lt::add_torrent_params ¶ms);
|
||||
void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames);
|
||||
void reload();
|
||||
|
||||
@@ -322,14 +316,14 @@ namespace BitTorrent
|
||||
// we will rely on this workaround to remove empty leftover folders
|
||||
QHash<lt::file_index_t, QVector<QString>> m_oldPath;
|
||||
|
||||
FileErrorInfo m_lastFileError;
|
||||
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;
|
||||
FileErrorInfo m_lastFileError;
|
||||
|
||||
// Persistent data
|
||||
QString m_name;
|
||||
QString m_savePath;
|
||||
QString m_category;
|
||||
QSet<QString> m_tags;
|
||||
TagSet m_tags;
|
||||
qreal m_ratioLimit;
|
||||
int m_seedingTimeLimit;
|
||||
TorrentOperatingMode m_operatingMode;
|
||||
|
||||
@@ -167,18 +167,25 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
|
||||
void TorrentInfo::saveToFile(const QString &path) const
|
||||
{
|
||||
if (!isValid())
|
||||
throw RuntimeError {tr("Invalid metadata.")};
|
||||
throw RuntimeError {tr("Invalid metadata")};
|
||||
|
||||
const lt::create_torrent torrentCreator = lt::create_torrent(*(nativeInfo()));
|
||||
const lt::entry torrentEntry = torrentCreator.generate();
|
||||
try
|
||||
{
|
||||
const auto torrentCreator = lt::create_torrent(*nativeInfo());
|
||||
const lt::entry torrentEntry = torrentCreator.generate();
|
||||
|
||||
QFile torrentFile {path};
|
||||
if (!torrentFile.open(QIODevice::WriteOnly))
|
||||
throw RuntimeError {torrentFile.errorString()};
|
||||
QFile torrentFile {path};
|
||||
if (!torrentFile.open(QIODevice::WriteOnly))
|
||||
throw RuntimeError(torrentFile.errorString());
|
||||
|
||||
lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
|
||||
if (torrentFile.error() != QFileDevice::NoError)
|
||||
throw RuntimeError {torrentFile.errorString()};
|
||||
lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
|
||||
if (torrentFile.error() != QFileDevice::NoError)
|
||||
throw RuntimeError(torrentFile.errorString());
|
||||
}
|
||||
catch (const lt::system_error &err)
|
||||
{
|
||||
throw RuntimeError(QString::fromLocal8Bit(err.what()));
|
||||
}
|
||||
}
|
||||
|
||||
bool TorrentInfo::isValid() const
|
||||
|
||||
@@ -53,10 +53,10 @@ namespace BitTorrent
|
||||
int numSeeds = -1;
|
||||
int numLeeches = -1;
|
||||
int numDownloaded = -1;
|
||||
QString message;
|
||||
QString message {};
|
||||
};
|
||||
|
||||
QString url;
|
||||
QString url {};
|
||||
int tier = 0;
|
||||
|
||||
QVector<EndpointStats> endpoints {};
|
||||
@@ -67,7 +67,7 @@ namespace BitTorrent
|
||||
int numSeeds = -1;
|
||||
int numLeeches = -1;
|
||||
int numDownloaded = -1;
|
||||
QString message;
|
||||
QString message {};
|
||||
};
|
||||
|
||||
bool operator==(const TrackerEntry &left, const TrackerEntry &right);
|
||||
|
||||
@@ -35,44 +35,16 @@
|
||||
#define QBT_APP_64BIT
|
||||
#endif
|
||||
|
||||
const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
|
||||
const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
|
||||
inline const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
|
||||
inline const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
|
||||
|
||||
template <typename T>
|
||||
constexpr typename std::add_const<T>::type &asConst(T &t) noexcept { return t; }
|
||||
constexpr typename std::add_const_t<T> &asConst(T &t) noexcept { return t; }
|
||||
|
||||
// Forward rvalue as const
|
||||
template <typename T>
|
||||
constexpr typename std::add_const<T>::type asConst(T &&t) noexcept { return std::move(t); }
|
||||
constexpr typename std::add_const_t<T> asConst(T &&t) noexcept { return std::move(t); }
|
||||
|
||||
// Prevent const rvalue arguments
|
||||
template <typename T>
|
||||
void asConst(const T &&) = delete;
|
||||
|
||||
namespace List
|
||||
{
|
||||
// Replacement for the deprecated`QSet<T> QSet::fromList(const QList<T> &list)`
|
||||
template <typename T>
|
||||
QSet<T> toSet(const QList<T> &list)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
return {list.cbegin(), list.cend()};
|
||||
#else
|
||||
return QSet<T>::fromList(list);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace Vector
|
||||
{
|
||||
// Replacement for the deprecated `QVector<T> QVector::fromStdVector(const std::vector<T> &vector)`
|
||||
template <typename T>
|
||||
QVector<T> fromStdVector(const std::vector<T> &vector)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
return {vector.cbegin(), vector.cend()};
|
||||
#else
|
||||
return QVector<T>::fromStdVector(vector);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ bool Connection::acceptsGzipEncoding(QString codings)
|
||||
return false;
|
||||
};
|
||||
|
||||
const QVector<QStringRef> list = codings.remove(' ').remove('\t').splitRef(',', QString::SkipEmptyParts);
|
||||
const QVector<QStringRef> list = codings.remove(' ').remove('\t').splitRef(',', Qt::SkipEmptyParts);
|
||||
if (list.isEmpty())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ RequestParser::ParseResult RequestParser::doParse(const QByteArray &data)
|
||||
bool RequestParser::parseStartLines(const QString &data)
|
||||
{
|
||||
// we don't handle malformed request which uses `LF` for newline
|
||||
const QVector<QStringRef> lines = data.splitRef(CRLF, QString::SkipEmptyParts);
|
||||
const QVector<QStringRef> lines = data.splitRef(CRLF, Qt::SkipEmptyParts);
|
||||
|
||||
// [rfc7230] 3.2.2. Field Order
|
||||
QStringList requestLines;
|
||||
@@ -276,7 +276,7 @@ bool RequestParser::parsePostMessage(const QByteArray &data)
|
||||
|
||||
// split data by "dash-boundary"
|
||||
const QByteArray dashDelimiter = QByteArray("--") + delimiter + CRLF;
|
||||
QVector<QByteArray> multipart = splitToViews(data, dashDelimiter, QString::SkipEmptyParts);
|
||||
QVector<QByteArray> multipart = splitToViews(data, dashDelimiter, Qt::SkipEmptyParts);
|
||||
if (multipart.isEmpty())
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << "multipart empty";
|
||||
@@ -299,7 +299,7 @@ bool RequestParser::parsePostMessage(const QByteArray &data)
|
||||
|
||||
bool RequestParser::parseFormData(const QByteArray &data)
|
||||
{
|
||||
const QVector<QByteArray> list = splitToViews(data, EOH, QString::KeepEmptyParts);
|
||||
const QVector<QByteArray> list = splitToViews(data, EOH, Qt::KeepEmptyParts);
|
||||
|
||||
if (list.size() != 2)
|
||||
{
|
||||
@@ -311,13 +311,13 @@ bool RequestParser::parseFormData(const QByteArray &data)
|
||||
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
|
||||
|
||||
HeaderMap headersMap;
|
||||
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts);
|
||||
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, Qt::SkipEmptyParts);
|
||||
for (const auto &line : headerLines)
|
||||
{
|
||||
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive))
|
||||
{
|
||||
// extract out filename & name
|
||||
const QVector<QStringRef> directives = line.split(';', QString::SkipEmptyParts);
|
||||
const QVector<QStringRef> directives = line.split(';', Qt::SkipEmptyParts);
|
||||
|
||||
for (const auto &directive : directives)
|
||||
{
|
||||
|
||||
@@ -35,43 +35,44 @@
|
||||
|
||||
namespace Http
|
||||
{
|
||||
const char METHOD_GET[] = "GET";
|
||||
const char METHOD_POST[] = "POST";
|
||||
inline const char METHOD_GET[] = "GET";
|
||||
inline const char METHOD_POST[] = "POST";
|
||||
|
||||
const char HEADER_CACHE_CONTROL[] = "cache-control";
|
||||
const char HEADER_CONNECTION[] = "connection";
|
||||
const char HEADER_CONTENT_DISPOSITION[] = "content-disposition";
|
||||
const char HEADER_CONTENT_ENCODING[] = "content-encoding";
|
||||
const char HEADER_CONTENT_LENGTH[] = "content-length";
|
||||
const char HEADER_CONTENT_SECURITY_POLICY[] = "content-security-policy";
|
||||
const char HEADER_CONTENT_TYPE[] = "content-type";
|
||||
const char HEADER_DATE[] = "date";
|
||||
const char HEADER_HOST[] = "host";
|
||||
const char HEADER_ORIGIN[] = "origin";
|
||||
const char HEADER_REFERER[] = "referer";
|
||||
const char HEADER_REFERRER_POLICY[] = "referrer-policy";
|
||||
const char HEADER_SET_COOKIE[] = "set-cookie";
|
||||
const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "x-content-type-options";
|
||||
const char HEADER_X_FORWARDED_HOST[] = "x-forwarded-host";
|
||||
const char HEADER_X_FRAME_OPTIONS[] = "x-frame-options";
|
||||
const char HEADER_X_XSS_PROTECTION[] = "x-xss-protection";
|
||||
inline const char HEADER_CACHE_CONTROL[] = "cache-control";
|
||||
inline const char HEADER_CONNECTION[] = "connection";
|
||||
inline const char HEADER_CONTENT_DISPOSITION[] = "content-disposition";
|
||||
inline const char HEADER_CONTENT_ENCODING[] = "content-encoding";
|
||||
inline const char HEADER_CONTENT_LENGTH[] = "content-length";
|
||||
inline const char HEADER_CONTENT_SECURITY_POLICY[] = "content-security-policy";
|
||||
inline const char HEADER_CONTENT_TYPE[] = "content-type";
|
||||
inline const char HEADER_DATE[] = "date";
|
||||
inline const char HEADER_HOST[] = "host";
|
||||
inline const char HEADER_ORIGIN[] = "origin";
|
||||
inline const char HEADER_REFERER[] = "referer";
|
||||
inline const char HEADER_REFERRER_POLICY[] = "referrer-policy";
|
||||
inline const char HEADER_SET_COOKIE[] = "set-cookie";
|
||||
inline const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "x-content-type-options";
|
||||
inline const char HEADER_X_FORWARDED_FOR[] = "x-forwarded-for";
|
||||
inline const char HEADER_X_FORWARDED_HOST[] = "x-forwarded-host";
|
||||
inline const char HEADER_X_FRAME_OPTIONS[] = "x-frame-options";
|
||||
inline const char HEADER_X_XSS_PROTECTION[] = "x-xss-protection";
|
||||
|
||||
const char HEADER_REQUEST_METHOD_GET[] = "GET";
|
||||
const char HEADER_REQUEST_METHOD_HEAD[] = "HEAD";
|
||||
const char HEADER_REQUEST_METHOD_POST[] = "POST";
|
||||
inline const char HEADER_REQUEST_METHOD_GET[] = "GET";
|
||||
inline const char HEADER_REQUEST_METHOD_HEAD[] = "HEAD";
|
||||
inline const char HEADER_REQUEST_METHOD_POST[] = "POST";
|
||||
|
||||
const char CONTENT_TYPE_HTML[] = "text/html";
|
||||
const char CONTENT_TYPE_CSS[] = "text/css";
|
||||
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
|
||||
const char CONTENT_TYPE_JS[] = "application/javascript";
|
||||
const char CONTENT_TYPE_JSON[] = "application/json";
|
||||
const char CONTENT_TYPE_GIF[] = "image/gif";
|
||||
const char CONTENT_TYPE_PNG[] = "image/png";
|
||||
const char CONTENT_TYPE_FORM_ENCODED[] = "application/x-www-form-urlencoded";
|
||||
const char CONTENT_TYPE_FORM_DATA[] = "multipart/form-data";
|
||||
inline const char CONTENT_TYPE_HTML[] = "text/html";
|
||||
inline const char CONTENT_TYPE_CSS[] = "text/css";
|
||||
inline const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
|
||||
inline const char CONTENT_TYPE_JS[] = "application/javascript";
|
||||
inline const char CONTENT_TYPE_JSON[] = "application/json";
|
||||
inline const char CONTENT_TYPE_GIF[] = "image/gif";
|
||||
inline const char CONTENT_TYPE_PNG[] = "image/png";
|
||||
inline const char CONTENT_TYPE_FORM_ENCODED[] = "application/x-www-form-urlencoded";
|
||||
inline const char CONTENT_TYPE_FORM_DATA[] = "multipart/form-data";
|
||||
|
||||
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
|
||||
const char CRLF[] = {0x0D, 0x0A, '\0'};
|
||||
inline const char CRLF[] = {0x0D, 0x0A, '\0'};
|
||||
|
||||
struct Environment
|
||||
{
|
||||
@@ -120,7 +121,7 @@ namespace Http
|
||||
HeaderMap headers;
|
||||
QByteArray content;
|
||||
|
||||
Response(uint code = 200, const QString &text = "OK")
|
||||
Response(uint code = 200, const QString &text = QLatin1String("OK"))
|
||||
: status {code, text}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "reverseresolution.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QHostInfo>
|
||||
#include <QString>
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QCache>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
|
||||
class QHostAddress;
|
||||
class QHostInfo;
|
||||
class QString;
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ void Smtp::authenticate()
|
||||
// AUTH extension is supported, check which
|
||||
// authentication modes are supported by
|
||||
// the server
|
||||
const QStringList auth = m_extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts);
|
||||
const QStringList auth = m_extensions["AUTH"].toUpper().split(' ', Qt::SkipEmptyParts);
|
||||
if (auth.contains("CRAM-MD5"))
|
||||
{
|
||||
qDebug() << "Using CRAM-MD5 authentication...";
|
||||
|
||||
108
src/base/orderedset.h
Normal file
108
src/base/orderedset.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* 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 <functional>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
|
||||
#include "algorithm.h"
|
||||
|
||||
template <typename T, typename Compare = std::less<T>>
|
||||
class OrderedSet : public std::set<T, Compare>
|
||||
{
|
||||
using ThisType = OrderedSet<T, Compare>;
|
||||
|
||||
public:
|
||||
using BaseType = std::set<T, Compare>;
|
||||
|
||||
using key_type = typename BaseType::key_type;
|
||||
using value_type = typename BaseType::value_type;
|
||||
|
||||
using BaseType::BaseType;
|
||||
using BaseType::operator=;
|
||||
|
||||
// The following are custom functions that are in line with Qt API interface, such as `QSet`
|
||||
|
||||
#if __cplusplus < 202002L
|
||||
bool contains(const key_type &value) const
|
||||
{
|
||||
return (BaseType::find(value) != BaseType::cend());
|
||||
}
|
||||
#endif
|
||||
|
||||
int count() const
|
||||
{
|
||||
return static_cast<int>(BaseType::size());
|
||||
}
|
||||
|
||||
ThisType &intersect(const ThisType &other)
|
||||
{
|
||||
Algorithm::removeIf(*this, [&other](const value_type &value) -> bool
|
||||
{
|
||||
return !other.contains(value);
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return BaseType::empty();
|
||||
}
|
||||
|
||||
template <typename std::enable_if_t<std::is_same_v<value_type, QString>, int> = 0>
|
||||
QString join(const QString &separator) const
|
||||
{
|
||||
auto iter = BaseType::cbegin();
|
||||
if (iter == BaseType::cend())
|
||||
return {};
|
||||
|
||||
QString ret = *iter;
|
||||
++iter;
|
||||
|
||||
while (iter != BaseType::cend())
|
||||
{
|
||||
ret.push_back(separator + *iter);
|
||||
++iter;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool remove(const key_type &value)
|
||||
{
|
||||
return (BaseType::erase(value) > 0);
|
||||
}
|
||||
|
||||
ThisType &unite(const ThisType &other)
|
||||
{
|
||||
BaseType::insert(other.cbegin(), other.cend());
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@@ -761,6 +761,26 @@ void Preferences::setWebUICustomHTTPHeaders(const QString &headers)
|
||||
setValue("Preferences/WebUI/CustomHTTPHeaders", headers);
|
||||
}
|
||||
|
||||
bool Preferences::isWebUIReverseProxySupportEnabled() const
|
||||
{
|
||||
return value("Preferences/WebUI/ReverseProxySupportEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setWebUIReverseProxySupportEnabled(const bool enabled)
|
||||
{
|
||||
setValue("Preferences/WebUI/ReverseProxySupportEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Preferences::getWebUITrustedReverseProxiesList() const
|
||||
{
|
||||
return value("Preferences/WebUI/TrustedReverseProxiesList").toString();
|
||||
}
|
||||
|
||||
void Preferences::setWebUITrustedReverseProxiesList(const QString &addr)
|
||||
{
|
||||
setValue("Preferences/WebUI/TrustedReverseProxiesList", addr);
|
||||
}
|
||||
|
||||
bool Preferences::isDynDNSEnabled() const
|
||||
{
|
||||
return value("Preferences/DynDNS/Enabled", false).toBool();
|
||||
|
||||
@@ -225,6 +225,12 @@ public:
|
||||
QString getWebUICustomHTTPHeaders() const;
|
||||
void setWebUICustomHTTPHeaders(const QString &headers);
|
||||
|
||||
// Reverse proxy
|
||||
bool isWebUIReverseProxySupportEnabled() const;
|
||||
void setWebUIReverseProxySupportEnabled(bool enabled);
|
||||
QString getWebUITrustedReverseProxiesList() const;
|
||||
void setWebUITrustedReverseProxiesList(const QString &addr);
|
||||
|
||||
// Dynamic DNS
|
||||
bool isDynDNSEnabled() const;
|
||||
void setDynDNSEnabled(bool enabled);
|
||||
|
||||
@@ -213,10 +213,8 @@ QRegularExpression AutoDownloadRule::cachedRegex(const QString &expression, cons
|
||||
QRegularExpression ®ex = m_dataPtr->cachedRegexes[expression];
|
||||
if (regex.pattern().isEmpty())
|
||||
{
|
||||
regex = QRegularExpression
|
||||
{
|
||||
(isRegex ? expression : Utils::String::wildcardToRegex(expression))
|
||||
, QRegularExpression::CaseInsensitiveOption};
|
||||
const QString pattern = (isRegex ? expression : Utils::String::wildcardToRegexPattern(expression));
|
||||
regex = QRegularExpression {pattern, QRegularExpression::CaseInsensitiveOption};
|
||||
}
|
||||
|
||||
return regex;
|
||||
@@ -240,7 +238,7 @@ bool AutoDownloadRule::matchesExpression(const QString &articleTitle, const QStr
|
||||
|
||||
// Only match if every wildcard token (separated by spaces) is present in the article name.
|
||||
// Order of wildcard tokens is unimportant (if order is important, they should have used *).
|
||||
const QStringList wildcards {expression.split(whitespace, QString::SplitBehavior::SkipEmptyParts)};
|
||||
const QStringList wildcards {expression.split(whitespace, Qt::SkipEmptyParts)};
|
||||
for (const QString &wildcard : wildcards)
|
||||
{
|
||||
const QRegularExpression reg {cachedRegex(wildcard, false)};
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <QGlobalStatic>
|
||||
#include <QHash>
|
||||
#include <QMetaObject>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
#include <QXmlStreamEntityResolver>
|
||||
@@ -391,12 +391,13 @@ namespace
|
||||
int nmin = 8;
|
||||
int nsec = 9;
|
||||
// Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm"
|
||||
QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$");
|
||||
QRegularExpression rx {"^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$"};
|
||||
QRegularExpressionMatch rxMatch;
|
||||
QStringList parts;
|
||||
if (!str.indexOf(rx))
|
||||
if (str.indexOf(rx, 0, &rxMatch) == 0)
|
||||
{
|
||||
// Check that if date has '-' separators, both separators are '-'.
|
||||
parts = rx.capturedTexts();
|
||||
parts = rxMatch.capturedTexts();
|
||||
const bool h1 = (parts[3] == QLatin1String("-"));
|
||||
const bool h2 = (parts[5] == QLatin1String("-"));
|
||||
if (h1 != h2)
|
||||
@@ -405,9 +406,10 @@ namespace
|
||||
else
|
||||
{
|
||||
// Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY"
|
||||
rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$");
|
||||
if (str.indexOf(rx))
|
||||
rx = QRegularExpression {"^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"};
|
||||
if (str.indexOf(rx, 0, &rxMatch) != 0)
|
||||
return QDateTime::currentDateTime();
|
||||
|
||||
nyear = 7;
|
||||
nmonth = 2;
|
||||
nday = 3;
|
||||
@@ -415,7 +417,7 @@ namespace
|
||||
nhour = 4;
|
||||
nmin = 5;
|
||||
nsec = 6;
|
||||
parts = rx.capturedTexts();
|
||||
parts = rxMatch.capturedTexts();
|
||||
}
|
||||
|
||||
bool ok[4];
|
||||
@@ -463,11 +465,11 @@ namespace
|
||||
bool negOffset = false;
|
||||
if (parts.count() > 10)
|
||||
{
|
||||
rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$");
|
||||
if (!parts[10].indexOf(rx))
|
||||
rx = QRegularExpression {"^([+-])(\\d\\d)(\\d\\d)$"};
|
||||
if (parts[10].indexOf(rx, 0, &rxMatch) == 0)
|
||||
{
|
||||
// It's a UTC offset ±hhmm
|
||||
parts = rx.capturedTexts();
|
||||
parts = rxMatch.capturedTexts();
|
||||
offset = parts[2].toInt(&ok[0]) * 3600;
|
||||
const int offsetMin = parts[3].toInt(&ok[1]);
|
||||
if (!ok[0] || !ok[1] || offsetMin > 59)
|
||||
@@ -561,12 +563,12 @@ void Parser::parse_impl(const QByteArray &feedData)
|
||||
|
||||
while (xml.readNextStartElement())
|
||||
{
|
||||
if (xml.name() == "rss")
|
||||
if (xml.name() == QLatin1String("rss"))
|
||||
{
|
||||
// Find channels
|
||||
while (xml.readNextStartElement())
|
||||
{
|
||||
if (xml.name() == "channel")
|
||||
if (xml.name() == QLatin1String("channel"))
|
||||
{
|
||||
parseRSSChannel(xml);
|
||||
foundChannel = true;
|
||||
@@ -578,7 +580,7 @@ void Parser::parse_impl(const QByteArray &feedData)
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (xml.name() == "feed")
|
||||
if (xml.name() == QLatin1String("feed"))
|
||||
{ // Atom feed
|
||||
parseAtomChannel(xml);
|
||||
foundChannel = true;
|
||||
@@ -821,21 +823,24 @@ void Parser::addArticle(QVariantHash article)
|
||||
{
|
||||
QVariant &torrentURL = article[Article::KeyTorrentURL];
|
||||
if (torrentURL.toString().isEmpty())
|
||||
torrentURL = article[Article::KeyLink];
|
||||
torrentURL = article.value(Article::KeyLink);
|
||||
|
||||
// If item does not have an ID, fall back to some other identifier.
|
||||
QVariant &localId = article[Article::KeyId];
|
||||
if (localId.toString().isEmpty())
|
||||
localId = article.value(Article::KeyTorrentURL);
|
||||
if (localId.toString().isEmpty())
|
||||
localId = article.value(Article::KeyTitle);
|
||||
|
||||
if (localId.toString().isEmpty())
|
||||
{
|
||||
// The article could not be uniquely identified
|
||||
// since it has no appropriate data.
|
||||
// Just ignore it.
|
||||
return;
|
||||
localId = article.value(Article::KeyTorrentURL);
|
||||
if (localId.toString().isEmpty())
|
||||
{
|
||||
localId = article.value(Article::KeyTitle);
|
||||
if (localId.toString().isEmpty())
|
||||
{
|
||||
// The article could not be uniquely identified
|
||||
// since it has no appropriate data.
|
||||
// Just ignore it.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_articleIDs.contains(localId.toString()))
|
||||
|
||||
@@ -516,14 +516,14 @@ void SearchPluginManager::parseVersionInfo(const QByteArray &info)
|
||||
QHash<QString, PluginVersion> updateInfo;
|
||||
int numCorrectData = 0;
|
||||
|
||||
const QVector<QByteArray> lines = Utils::ByteArray::splitToViews(info, "\n", QString::SkipEmptyParts);
|
||||
const QVector<QByteArray> lines = Utils::ByteArray::splitToViews(info, "\n", Qt::SkipEmptyParts);
|
||||
for (QByteArray line : lines)
|
||||
{
|
||||
line = line.trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
if (line.startsWith('#')) continue;
|
||||
|
||||
const QVector<QByteArray> list = Utils::ByteArray::splitToViews(line, ":", QString::SkipEmptyParts);
|
||||
const QVector<QByteArray> list = Utils::ByteArray::splitToViews(line, ":", Qt::SkipEmptyParts);
|
||||
if (list.size() != 2) continue;
|
||||
|
||||
const QString pluginName = list.first().trimmed();
|
||||
|
||||
@@ -184,9 +184,8 @@ SettingsStorage *SettingsStorage::instance()
|
||||
|
||||
bool SettingsStorage::save()
|
||||
{
|
||||
if (!m_dirty) return true; // Obtaining the lock is expensive, let's check early
|
||||
const QWriteLocker locker(&m_lock); // to guard for `m_dirty`
|
||||
if (!m_dirty) return true; // something might have changed while we were getting the lock
|
||||
const QWriteLocker locker(&m_lock); // guard for `m_dirty` too
|
||||
if (!m_dirty) return true;
|
||||
|
||||
const TransactionalSettings settings(QLatin1String("qBittorrent"));
|
||||
if (!settings.write(m_data))
|
||||
@@ -224,7 +223,11 @@ void SettingsStorage::removeValue(const QString &key)
|
||||
{
|
||||
const QString realKey = mapKey(key);
|
||||
const QWriteLocker locker(&m_lock);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
if (m_data.remove(realKey))
|
||||
#else
|
||||
if (m_data.remove(realKey) > 0)
|
||||
#endif
|
||||
{
|
||||
m_dirty = true;
|
||||
m_timer.start();
|
||||
|
||||
37
src/base/tagset.cpp
Normal file
37
src/base/tagset.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* 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 "tagset.h"
|
||||
|
||||
bool TagLessThan::operator()(const QString &left, const QString &right) const
|
||||
{
|
||||
const int result = m_compare(left, right);
|
||||
if (result != 0)
|
||||
return (result < 0);
|
||||
return (m_subCompare(left, right) < 0);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2021 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -28,28 +28,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
|
||||
#include <libtorrent/fwd.hpp>
|
||||
#include "base/orderedset.h"
|
||||
#include "base/utils/compare.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QObject>
|
||||
|
||||
class QByteArray;
|
||||
|
||||
class ResumeDataSavingManager : public QObject
|
||||
class TagLessThan
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ResumeDataSavingManager)
|
||||
|
||||
public:
|
||||
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
|
||||
|
||||
public slots:
|
||||
void save(const QString &filename, const QByteArray &data) const;
|
||||
void save(const QString &filename, const std::shared_ptr<lt::entry> &data) const;
|
||||
void remove(const QString &filename) const;
|
||||
bool operator()(const QString &left, const QString &right) const;
|
||||
|
||||
private:
|
||||
const QDir m_resumeDataDir;
|
||||
Utils::Compare::NaturalCompare<Qt::CaseInsensitive> m_compare;
|
||||
Utils::Compare::NaturalCompare<Qt::CaseSensitive> m_subCompare;
|
||||
};
|
||||
|
||||
using TagSet = OrderedSet<QString, TagLessThan>;
|
||||
|
||||
Q_DECLARE_METATYPE(TagSet)
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "base/logger.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/settingsstorage.h"
|
||||
#include "base/tagset.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/string.h"
|
||||
|
||||
@@ -84,16 +85,16 @@ const QString PARAM_RATIOLIMIT {QStringLiteral("ratio_limit")};
|
||||
|
||||
namespace
|
||||
{
|
||||
QSet<QString> parseTagSet(const QJsonArray &jsonArr)
|
||||
TagSet parseTagSet(const QJsonArray &jsonArr)
|
||||
{
|
||||
QSet<QString> tags;
|
||||
TagSet tags;
|
||||
for (const QJsonValue &jsonVal : jsonArr)
|
||||
tags.insert(jsonVal.toString());
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
QJsonArray serializeTagSet(const QSet<QString> &tags)
|
||||
QJsonArray serializeTagSet(const TagSet &tags)
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const QString &tag : tags)
|
||||
@@ -206,7 +207,6 @@ signals:
|
||||
|
||||
private:
|
||||
void onTimeout();
|
||||
void scheduleWatchedFolderProcessing(const QString &path);
|
||||
void processWatchedFolder(const QString &path);
|
||||
void processFolder(const QString &path, const QString &watchedFolderPath, const TorrentFilesWatcher::WatchedFolderOptions &options);
|
||||
void processFailedTorrents();
|
||||
@@ -268,10 +268,11 @@ QString TorrentFilesWatcher::makeCleanPath(const QString &path)
|
||||
if (path.isEmpty())
|
||||
throw InvalidArgument(tr("Watched folder path cannot be empty."));
|
||||
|
||||
if (QDir::isRelativePath(path))
|
||||
const QDir dir {path};
|
||||
if (dir.isRelative())
|
||||
throw InvalidArgument(tr("Watched folder path cannot be relative."));
|
||||
|
||||
return QDir::cleanPath(path);
|
||||
return dir.canonicalPath();
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::load()
|
||||
@@ -294,7 +295,7 @@ void TorrentFilesWatcher::load()
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(confFile.readAll(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("Couldn't parse Watched Folders configuration from %1. Error: %2")
|
||||
LogMsg(tr("Couldn't parse Watched Folders configuration from %1. Error: %2")
|
||||
.arg(confFile.fileName(), jsonError.errorString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
@@ -436,7 +437,7 @@ TorrentFilesWatcher::Worker::Worker()
|
||||
, m_watchTimer {new QTimer(this)}
|
||||
, m_retryTorrentTimer {new QTimer(this)}
|
||||
{
|
||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &Worker::scheduleWatchedFolderProcessing);
|
||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &Worker::processWatchedFolder);
|
||||
connect(m_watchTimer, &QTimer::timeout, this, &Worker::onTimeout);
|
||||
|
||||
connect(m_retryTorrentTimer, &QTimer::timeout, this, &Worker::processFailedTorrents);
|
||||
@@ -470,14 +471,6 @@ void TorrentFilesWatcher::Worker::removeWatchedFolder(const QString &path)
|
||||
m_retryTorrentTimer->stop();
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const QString &path)
|
||||
{
|
||||
QTimer::singleShot(2000, this, [this, path]()
|
||||
{
|
||||
processWatchedFolder(path);
|
||||
});
|
||||
}
|
||||
|
||||
void TorrentFilesWatcher::Worker::processWatchedFolder(const QString &path)
|
||||
{
|
||||
const TorrentFilesWatcher::WatchedFolderOptions options = m_watchedFolders.value(path);
|
||||
@@ -608,10 +601,13 @@ void TorrentFilesWatcher::Worker::addWatchedFolder(const QString &path, const To
|
||||
{
|
||||
#if !defined Q_OS_HAIKU
|
||||
// Check if the path points to a network file system or not
|
||||
if (Utils::Fs::isNetworkFileSystem(path) || options.recursive)
|
||||
#else
|
||||
if (options.recursive)
|
||||
if (Utils::Fs::isNetworkFileSystem(path))
|
||||
{
|
||||
m_watchedByTimeoutFolders.insert(path);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (options.recursive)
|
||||
{
|
||||
m_watchedByTimeoutFolders.insert(path);
|
||||
if (!m_watchTimer->isActive())
|
||||
@@ -620,7 +616,7 @@ void TorrentFilesWatcher::Worker::addWatchedFolder(const QString &path, const To
|
||||
else
|
||||
{
|
||||
m_watcher->addPath(path);
|
||||
scheduleWatchedFolderProcessing(path);
|
||||
QTimer::singleShot(2000, this, [this, path]() { processWatchedFolder(path); });
|
||||
}
|
||||
|
||||
m_watchedFolders[path] = options;
|
||||
@@ -652,7 +648,7 @@ void TorrentFilesWatcher::Worker::updateWatchedFolder(const QString &path, const
|
||||
m_watchTimer->stop();
|
||||
|
||||
m_watcher->addPath(path);
|
||||
scheduleWatchedFolderProcessing(path);
|
||||
QTimer::singleShot(2000, this, [this, path]() { processWatchedFolder(path); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
#include <QByteArray>
|
||||
#include <QVector>
|
||||
|
||||
QVector<QByteArray> Utils::ByteArray::splitToViews(const QByteArray &in, const QByteArray &sep, const QString::SplitBehavior behavior)
|
||||
QVector<QByteArray> Utils::ByteArray::splitToViews(const QByteArray &in, const QByteArray &sep, const Qt::SplitBehavior behavior)
|
||||
{
|
||||
if (sep.isEmpty())
|
||||
return {in};
|
||||
|
||||
QVector<QByteArray> ret;
|
||||
ret.reserve((behavior == QString::KeepEmptyParts)
|
||||
ret.reserve((behavior == Qt::KeepEmptyParts)
|
||||
? (1 + (in.size() / sep.size()))
|
||||
: (1 + (in.size() / (sep.size() + 1))));
|
||||
int head = 0;
|
||||
@@ -49,7 +49,7 @@ QVector<QByteArray> Utils::ByteArray::splitToViews(const QByteArray &in, const Q
|
||||
|
||||
// omit empty parts
|
||||
const QByteArray part = QByteArray::fromRawData((in.constData() + head), (end - head));
|
||||
if (!part.isEmpty() || (behavior == QString::KeepEmptyParts))
|
||||
if (!part.isEmpty() || (behavior == Qt::KeepEmptyParts))
|
||||
ret += part;
|
||||
|
||||
head = end + sep.size();
|
||||
|
||||
@@ -36,7 +36,7 @@ class QByteArray;
|
||||
namespace Utils::ByteArray
|
||||
{
|
||||
// Mimic QString::splitRef(sep, behavior)
|
||||
QVector<QByteArray> splitToViews(const QByteArray &in, const QByteArray &sep, const QString::SplitBehavior behavior = QString::KeepEmptyParts);
|
||||
QVector<QByteArray> splitToViews(const QByteArray &in, const QByteArray &sep, const Qt::SplitBehavior behavior = Qt::KeepEmptyParts);
|
||||
|
||||
// Mimic QByteArray::mid(pos, len) but instead of returning a full-copy,
|
||||
// we only return a partial view
|
||||
|
||||
96
src/base/utils/compare.cpp
Normal file
96
src/base/utils/compare.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* 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 "compare.h"
|
||||
|
||||
#include <QChar>
|
||||
#include <QString>
|
||||
|
||||
#ifndef QBT_USE_QCOLLATOR
|
||||
int Utils::Compare::naturalCompare(const QString &left, const QString &right, const Qt::CaseSensitivity caseSensitivity)
|
||||
{
|
||||
// Return value <0: `left` is smaller than `right`
|
||||
// Return value >0: `left` is greater than `right`
|
||||
// Return value =0: both strings are equal
|
||||
|
||||
int posL = 0;
|
||||
int posR = 0;
|
||||
while (true)
|
||||
{
|
||||
if ((posL == left.size()) || (posR == right.size()))
|
||||
return (left.size() - right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
|
||||
|
||||
const QChar leftChar = (caseSensitivity == Qt::CaseSensitive) ? left[posL] : left[posL].toLower();
|
||||
const QChar rightChar = (caseSensitivity == Qt::CaseSensitive) ? right[posR] : right[posR].toLower();
|
||||
// Compare only non-digits.
|
||||
// Numbers should be compared as a whole
|
||||
// otherwise the string->int conversion can yield a wrong value
|
||||
if ((leftChar == rightChar) && !leftChar.isDigit())
|
||||
{
|
||||
// compare next character
|
||||
++posL;
|
||||
++posR;
|
||||
}
|
||||
else if (leftChar.isDigit() && rightChar.isDigit())
|
||||
{
|
||||
// Both are digits, compare the numbers
|
||||
|
||||
const auto numberView = [](const QString &str, int &pos) -> QStringRef
|
||||
{
|
||||
const int start = pos;
|
||||
while ((pos < str.size()) && str[pos].isDigit())
|
||||
++pos;
|
||||
return str.midRef(start, (pos - start));
|
||||
};
|
||||
|
||||
const QStringRef numViewL = numberView(left, posL);
|
||||
const QStringRef numViewR = numberView(right, posR);
|
||||
|
||||
if (numViewL.length() != numViewR.length())
|
||||
return (numViewL.length() - numViewR.length());
|
||||
|
||||
// both string/view has the same length
|
||||
for (int i = 0; i < numViewL.length(); ++i)
|
||||
{
|
||||
const QChar numL = numViewL[i];
|
||||
const QChar numR = numViewR[i];
|
||||
|
||||
if (numL != numR)
|
||||
return (numL.unicode() - numR.unicode());
|
||||
}
|
||||
|
||||
// String + digits do match and we haven't hit the end of both strings
|
||||
// then continue to consume the remainings
|
||||
}
|
||||
else
|
||||
{
|
||||
return (leftChar.unicode() - rightChar.unicode());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
88
src/base/utils/compare.h
Normal file
88
src/base/utils/compare.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* 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 <Qt>
|
||||
#include <QtGlobal>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#define QBT_USE_QCOLLATOR
|
||||
#include <QCollator>
|
||||
#endif
|
||||
|
||||
class QString;
|
||||
|
||||
namespace Utils::Compare
|
||||
{
|
||||
#ifdef QBT_USE_QCOLLATOR
|
||||
template <Qt::CaseSensitivity caseSensitivity>
|
||||
class NaturalCompare
|
||||
{
|
||||
public:
|
||||
NaturalCompare()
|
||||
{
|
||||
m_collator.setNumericMode(true);
|
||||
m_collator.setCaseSensitivity(caseSensitivity);
|
||||
}
|
||||
|
||||
int operator()(const QString &left, const QString &right) const
|
||||
{
|
||||
return m_collator.compare(left, right);
|
||||
}
|
||||
|
||||
private:
|
||||
QCollator m_collator;
|
||||
};
|
||||
#else
|
||||
int naturalCompare(const QString &left, const QString &right, Qt::CaseSensitivity caseSensitivity);
|
||||
|
||||
template <Qt::CaseSensitivity caseSensitivity>
|
||||
class NaturalCompare
|
||||
{
|
||||
public:
|
||||
int operator()(const QString &left, const QString &right) const
|
||||
{
|
||||
return naturalCompare(left, right, caseSensitivity);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <Qt::CaseSensitivity caseSensitivity>
|
||||
class NaturalLessThan
|
||||
{
|
||||
public:
|
||||
bool operator()(const QString &left, const QString &right) const
|
||||
{
|
||||
return (m_comparator(left, right) < 0);
|
||||
}
|
||||
|
||||
private:
|
||||
NaturalCompare<caseSensitivity> m_comparator;
|
||||
};
|
||||
}
|
||||
@@ -63,7 +63,7 @@ namespace
|
||||
// Software 'Anaconda' installs its own python interpreter
|
||||
// and `python --version` returns a string like this:
|
||||
// "Python 3.4.3 :: Anaconda 2.3.0 (64-bit)"
|
||||
const QVector<QByteArray> outputSplit = Utils::ByteArray::splitToViews(procOutput, " ", QString::SkipEmptyParts);
|
||||
const QVector<QByteArray> outputSplit = Utils::ByteArray::splitToViews(procOutput, " ", Qt::SkipEmptyParts);
|
||||
if (outputSplit.size() <= 1)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -498,7 +498,7 @@ QString Utils::Misc::opensslVersionString()
|
||||
#else
|
||||
static const auto version {QString::fromLatin1(SSLeay_version(SSLEAY_VERSION))};
|
||||
#endif
|
||||
return version.splitRef(' ', QString::SkipEmptyParts)[1].toString();
|
||||
return version.splitRef(' ', Qt::SkipEmptyParts)[1].toString();
|
||||
}
|
||||
|
||||
QString Utils::Misc::zlibVersionString()
|
||||
|
||||
@@ -99,7 +99,7 @@ bool Utils::Password::PBKDF2::verify(const QByteArray &secret, const QString &pa
|
||||
|
||||
bool Utils::Password::PBKDF2::verify(const QByteArray &secret, const QByteArray &password)
|
||||
{
|
||||
const QVector<QByteArray> list = ByteArray::splitToViews(secret, ":", QString::SkipEmptyParts);
|
||||
const QVector<QByteArray> list = ByteArray::splitToViews(secret, ":", Qt::SkipEmptyParts);
|
||||
if (list.size() != 2)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -31,142 +31,14 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QCollator>
|
||||
#include <QLocale>
|
||||
#include <QRegExp>
|
||||
#include <QtGlobal>
|
||||
#include <QVector>
|
||||
|
||||
#if defined(Q_OS_MACOS) || defined(__MINGW32__)
|
||||
#define QBT_USES_QTHREADSTORAGE
|
||||
#include <QThreadStorage>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
class NaturalCompare
|
||||
{
|
||||
public:
|
||||
explicit NaturalCompare(const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
|
||||
: m_caseSensitivity(caseSensitivity)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
|
||||
// sorts older versions of μTorrent differently than the newer ones because the
|
||||
// 'μ' character is encoded differently and the native API can't cope with that.
|
||||
// So default to using our custom natural sorting algorithm instead.
|
||||
// See #5238 and #5240
|
||||
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on an OS older than Win7
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QRegularExpression>
|
||||
#else
|
||||
m_collator.setNumericMode(true);
|
||||
m_collator.setCaseSensitivity(caseSensitivity);
|
||||
#include <QRegExp>
|
||||
#endif
|
||||
}
|
||||
|
||||
int operator()(const QString &left, const QString &right) const
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return compare(left, right);
|
||||
#else
|
||||
return m_collator.compare(left, right);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
int compare(const QString &left, const QString &right) const
|
||||
{
|
||||
// Return value <0: `left` is smaller than `right`
|
||||
// Return value >0: `left` is greater than `right`
|
||||
// Return value =0: both strings are equal
|
||||
|
||||
int posL = 0;
|
||||
int posR = 0;
|
||||
while (true)
|
||||
{
|
||||
if ((posL == left.size()) || (posR == right.size()))
|
||||
return (left.size() - right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
|
||||
|
||||
const QChar leftChar = (m_caseSensitivity == Qt::CaseSensitive) ? left[posL] : left[posL].toLower();
|
||||
const QChar rightChar = (m_caseSensitivity == Qt::CaseSensitive) ? right[posR] : right[posR].toLower();
|
||||
// Compare only non-digits.
|
||||
// Numbers should be compared as a whole
|
||||
// otherwise the string->int conversion can yield a wrong value
|
||||
if ((leftChar == rightChar) && !leftChar.isDigit())
|
||||
{
|
||||
// compare next character
|
||||
++posL;
|
||||
++posR;
|
||||
}
|
||||
else if (leftChar.isDigit() && rightChar.isDigit())
|
||||
{
|
||||
// Both are digits, compare the numbers
|
||||
|
||||
const auto numberView = [](const QString &str, int &pos) -> QStringRef
|
||||
{
|
||||
const int start = pos;
|
||||
while ((pos < str.size()) && str[pos].isDigit())
|
||||
++pos;
|
||||
return str.midRef(start, (pos - start));
|
||||
};
|
||||
|
||||
const QStringRef numViewL = numberView(left, posL);
|
||||
const QStringRef numViewR = numberView(right, posR);
|
||||
|
||||
if (numViewL.length() != numViewR.length())
|
||||
return (numViewL.length() - numViewR.length());
|
||||
|
||||
// both string/view has the same length
|
||||
for (int i = 0; i < numViewL.length(); ++i)
|
||||
{
|
||||
const QChar numL = numViewL[i];
|
||||
const QChar numR = numViewR[i];
|
||||
|
||||
if (numL != numR)
|
||||
return (numL.unicode() - numR.unicode());
|
||||
}
|
||||
|
||||
// String + digits do match and we haven't hit the end of both strings
|
||||
// then continue to consume the remainings
|
||||
}
|
||||
else
|
||||
{
|
||||
return (leftChar.unicode() - rightChar.unicode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QCollator m_collator;
|
||||
const Qt::CaseSensitivity m_caseSensitivity;
|
||||
};
|
||||
}
|
||||
|
||||
int Utils::String::naturalCompare(const QString &left, const QString &right, const Qt::CaseSensitivity caseSensitivity)
|
||||
{
|
||||
// provide a single `NaturalCompare` instance for easy use
|
||||
// https://doc.qt.io/qt-5/threads-reentrancy.html
|
||||
if (caseSensitivity == Qt::CaseSensitive)
|
||||
{
|
||||
#ifdef QBT_USES_QTHREADSTORAGE
|
||||
static QThreadStorage<NaturalCompare> nCmp;
|
||||
if (!nCmp.hasLocalData())
|
||||
nCmp.setLocalData(NaturalCompare(Qt::CaseSensitive));
|
||||
return (nCmp.localData())(left, right);
|
||||
#else
|
||||
thread_local NaturalCompare nCmp(Qt::CaseSensitive);
|
||||
return nCmp(left, right);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_QTHREADSTORAGE
|
||||
static QThreadStorage<NaturalCompare> nCmp;
|
||||
if (!nCmp.hasLocalData())
|
||||
nCmp.setLocalData(NaturalCompare(Qt::CaseInsensitive));
|
||||
return (nCmp.localData())(left, right);
|
||||
#else
|
||||
thread_local NaturalCompare nCmp(Qt::CaseInsensitive);
|
||||
return nCmp(left, right);
|
||||
#endif
|
||||
}
|
||||
|
||||
// to send numbers instead of strings with suffixes
|
||||
QString Utils::String::fromDouble(const double n, const int precision)
|
||||
@@ -181,14 +53,21 @@ QString Utils::String::fromDouble(const double n, const int precision)
|
||||
return QLocale::system().toString(std::floor(n * prec) / prec, 'f', precision);
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
QString Utils::String::wildcardToRegexPattern(const QString &pattern)
|
||||
{
|
||||
return QRegularExpression::wildcardToRegularExpression(pattern, QRegularExpression::UnanchoredWildcardConversion);
|
||||
}
|
||||
#else
|
||||
// This is marked as internal in QRegExp.cpp, but is exported. The alternative would be to
|
||||
// copy the code from QRegExp::wc2rx().
|
||||
QString qt_regexp_toCanonical(const QString &pattern, QRegExp::PatternSyntax patternSyntax);
|
||||
|
||||
QString Utils::String::wildcardToRegex(const QString &pattern)
|
||||
QString Utils::String::wildcardToRegexPattern(const QString &pattern)
|
||||
{
|
||||
return qt_regexp_toCanonical(pattern, QRegExp::Wildcard);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::optional<bool> Utils::String::parseBool(const QString &string)
|
||||
{
|
||||
|
||||
@@ -41,16 +41,7 @@ class QStringRef;
|
||||
|
||||
namespace Utils::String
|
||||
{
|
||||
QString fromDouble(double n, int precision);
|
||||
|
||||
int naturalCompare(const QString &left, const QString &right, const Qt::CaseSensitivity caseSensitivity);
|
||||
template <Qt::CaseSensitivity caseSensitivity>
|
||||
bool naturalLessThan(const QString &left, const QString &right)
|
||||
{
|
||||
return (naturalCompare(left, right, caseSensitivity) < 0);
|
||||
}
|
||||
|
||||
QString wildcardToRegex(const QString &pattern);
|
||||
QString wildcardToRegexPattern(const QString &pattern);
|
||||
|
||||
template <typename T>
|
||||
T unquote(const T &str, const QString "es = QChar('"'))
|
||||
@@ -72,6 +63,8 @@ namespace Utils::String
|
||||
|
||||
QString join(const QVector<QStringRef> &strings, const QString &separator);
|
||||
|
||||
QString fromDouble(double n, int precision);
|
||||
|
||||
template <typename T, typename std::enable_if_t<std::is_enum_v<T>, int> = 0>
|
||||
QString fromEnum(const T &value)
|
||||
{
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
#pragma once
|
||||
|
||||
#define QBT_VERSION_MAJOR 4
|
||||
#define QBT_VERSION_MINOR 3
|
||||
#define QBT_VERSION_BUGFIX 8
|
||||
#define QBT_VERSION_MINOR 4
|
||||
#define QBT_VERSION_BUGFIX 0
|
||||
#define QBT_VERSION_BUILD 0
|
||||
#define QBT_VERSION_STATUS "" // Should be empty for stable releases!
|
||||
#define QBT_VERSION_STATUS "beta1" // Should be empty for stable releases!
|
||||
|
||||
#define QBT__STRINGIFY(x) #x
|
||||
#define QBT_STRINGIFY(x) QBT__STRINGIFY(x)
|
||||
|
||||
@@ -231,7 +231,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
macutilities.mm
|
||||
)
|
||||
target_link_libraries(qbt_gui PRIVATE
|
||||
Qt5::MacExtras
|
||||
objc
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
#include "base/net/downloadmanager.h"
|
||||
#include "base/settingsstorage.h"
|
||||
#include "base/torrentfileguard.h"
|
||||
#include "base/utils/compare.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
#include "properties/proplistdelegate.h"
|
||||
#include "raisedmessagebox.h"
|
||||
@@ -129,7 +129,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
|
||||
|
||||
// Load categories
|
||||
QStringList categories = session->categories().keys();
|
||||
std::sort(categories.begin(), categories.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
||||
auto defaultCategory = settings()->loadValue<QString>(KEY_DEFAULTCATEGORY);
|
||||
|
||||
if (!m_torrentParams.category.isEmpty())
|
||||
@@ -296,7 +296,8 @@ bool AddNewTorrentDialog::loadTorrentImpl()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ui->labelHashData->setText(torrentID.toString());
|
||||
m_ui->labelInfohash1Data->setText(m_torrentInfo.infoHash().v1().isValid() ? m_torrentInfo.infoHash().v1().toString() : tr("N/A"));
|
||||
m_ui->labelInfohash2Data->setText(m_torrentInfo.infoHash().v2().isValid() ? m_torrentInfo.infoHash().v2().toString() : tr("N/A"));
|
||||
setupTreeview();
|
||||
TMMChanged(m_ui->comboTTM->currentIndex());
|
||||
return true;
|
||||
@@ -348,7 +349,8 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
|
||||
|
||||
BitTorrent::Session::instance()->downloadMetadata(magnetUri);
|
||||
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
|
||||
m_ui->labelHashData->setText(torrentID.toString());
|
||||
m_ui->labelInfohash1Data->setText(magnetUri.infoHash().v1().isValid() ? magnetUri.infoHash().v1().toString() : tr("N/A"));
|
||||
m_ui->labelInfohash2Data->setText(magnetUri.infoHash().v2().isValid() ? magnetUri.infoHash().v2().toString() : tr("N/A"));
|
||||
|
||||
m_magnetURI = magnetUri;
|
||||
return true;
|
||||
@@ -475,7 +477,8 @@ void AddNewTorrentDialog::saveTorrentFile()
|
||||
}
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
QMessageBox::critical(this, tr("I/O Error"), err.message());
|
||||
QMessageBox::critical(this, tr("I/O Error")
|
||||
, tr("Couldn't export torrent metadata file '%1'. Reason: %2.").arg(path, err.message()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,37 +514,7 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
||||
, static_cast<int>(prio));
|
||||
}
|
||||
};
|
||||
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (selectedRows.size() == 1)
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename...")
|
||||
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
|
||||
menu->addSeparator();
|
||||
}
|
||||
|
||||
QMenu *subMenu = menu->addMenu(tr("Priority"));
|
||||
|
||||
subMenu->addAction(tr("Do not download"), subMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Ignored);
|
||||
});
|
||||
subMenu->addAction(tr("Normal"), subMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Normal);
|
||||
});
|
||||
subMenu->addAction(tr("High"), subMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::High);
|
||||
});
|
||||
subMenu->addAction(tr("Maximum"), subMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Maximum);
|
||||
});
|
||||
subMenu->addSeparator();
|
||||
subMenu->addAction(tr("By shown file order"), subMenu, [this]()
|
||||
const auto applyPrioritiesByOrder = [this]()
|
||||
{
|
||||
// Equally distribute the selected items into groups and for each group assign
|
||||
// a download priority that will apply to each item. The number of groups depends on how
|
||||
@@ -549,10 +522,10 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
||||
|
||||
const QModelIndexList selectedRows = m_ui->contentTreeView->selectionModel()->selectedRows(0);
|
||||
|
||||
const int priorityGroups = 3;
|
||||
const int priorityGroupSize = std::max((selectedRows.length() / priorityGroups), 1);
|
||||
const qsizetype priorityGroups = 3;
|
||||
const auto priorityGroupSize = std::max<qsizetype>((selectedRows.length() / priorityGroups), 1);
|
||||
|
||||
for (int i = 0; i < selectedRows.length(); ++i)
|
||||
for (qsizetype i = 0; i < selectedRows.length(); ++i)
|
||||
{
|
||||
auto priority = BitTorrent::DownloadPriority::Ignored;
|
||||
switch (i / priorityGroupSize)
|
||||
@@ -573,7 +546,57 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
||||
m_contentModel->setData(index.sibling(index.row(), PRIORITY)
|
||||
, static_cast<int>(priority));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (selectedRows.size() == 1)
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename...")
|
||||
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
|
||||
menu->addSeparator();
|
||||
|
||||
QMenu *priorityMenu = menu->addMenu(tr("Priority"));
|
||||
priorityMenu->addAction(tr("Do not download"), priorityMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Ignored);
|
||||
});
|
||||
priorityMenu->addAction(tr("Normal"), priorityMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Normal);
|
||||
});
|
||||
priorityMenu->addAction(tr("High"), priorityMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::High);
|
||||
});
|
||||
priorityMenu->addAction(tr("Maximum"), priorityMenu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Maximum);
|
||||
});
|
||||
priorityMenu->addSeparator();
|
||||
priorityMenu->addAction(tr("By shown file order"), priorityMenu, applyPrioritiesByOrder);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->addAction(tr("Do not download"), menu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Ignored);
|
||||
});
|
||||
menu->addAction(tr("Normal priority"), menu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Normal);
|
||||
});
|
||||
menu->addAction(tr("High priority"), menu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::High);
|
||||
});
|
||||
menu->addAction(tr("Maximum priority"), menu, [applyPriorities]()
|
||||
{
|
||||
applyPriorities(BitTorrent::DownloadPriority::Maximum);
|
||||
});
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Priority by shown file order"), menu, applyPrioritiesByOrder);
|
||||
}
|
||||
|
||||
menu->popup(QCursor::pos());
|
||||
}
|
||||
|
||||
@@ -249,10 +249,10 @@
|
||||
<string>Torrent information</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelDate">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelInfohash1">
|
||||
<property name="text">
|
||||
<string>Date:</string>
|
||||
<string>Info hash v1:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -262,24 +262,7 @@
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="labelDateData"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelSize">
|
||||
<property name="text">
|
||||
<string>Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="labelHashData">
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgba(0, 0, 0, 0);</string>
|
||||
@@ -295,7 +278,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>333</width>
|
||||
<width>317</width>
|
||||
<height>69</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -335,14 +318,7 @@
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelHash">
|
||||
<property name="text">
|
||||
<string>Hash:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="labelComment">
|
||||
<property name="text">
|
||||
<string>Comment:</string>
|
||||
@@ -352,6 +328,44 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelSize">
|
||||
<property name="text">
|
||||
<string>Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="labelInfohash1Data">
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelDate">
|
||||
<property name="text">
|
||||
<string>Date:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="labelInfohash2">
|
||||
<property name="text">
|
||||
<string>Info hash v2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="labelInfohash2Data">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace
|
||||
{
|
||||
// qBittorrent section
|
||||
QBITTORRENT_HEADER,
|
||||
RESUME_DATA_STORAGE,
|
||||
#if defined(Q_OS_WIN)
|
||||
OS_MEMORY_PRIORITY,
|
||||
#endif
|
||||
@@ -78,7 +79,11 @@ namespace
|
||||
RESOLVE_COUNTRIES,
|
||||
PROGRAM_NOTIFICATIONS,
|
||||
TORRENT_ADDED_NOTIFICATIONS,
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
|
||||
NOTIFICATION_TIMEOUT,
|
||||
#endif
|
||||
CONFIRM_REMOVE_ALL_TAGS,
|
||||
REANNOUNCE_WHEN_ADDRESS_CHANGED,
|
||||
DOWNLOAD_TRACKER_FAVICON,
|
||||
SAVE_PATH_HISTORY_LENGTH,
|
||||
ENABLE_SPEED_WIDGET,
|
||||
@@ -111,6 +116,7 @@ namespace
|
||||
SEND_BUF_LOW_WATERMARK,
|
||||
SEND_BUF_WATERMARK_FACTOR,
|
||||
// networking & ports
|
||||
CONNECTION_SPEED,
|
||||
SOCKET_BACKLOG_SIZE,
|
||||
OUTGOING_PORT_MIN,
|
||||
OUTGOING_PORT_MAX,
|
||||
@@ -120,7 +126,6 @@ namespace
|
||||
IDN_SUPPORT,
|
||||
MULTI_CONNECTIONS_PER_IP,
|
||||
VALIDATE_HTTPS_TRACKER_CERTIFICATE,
|
||||
SSRF_MITIGATION,
|
||||
BLOCK_PEERS_ON_PRIVILEGED_PORTS,
|
||||
// seeding
|
||||
CHOKING_ALGORITHM,
|
||||
@@ -164,6 +169,10 @@ void AdvancedSettings::saveAdvancedSettings()
|
||||
Preferences *const pref = Preferences::instance();
|
||||
BitTorrent::Session *const session = BitTorrent::Session::instance();
|
||||
|
||||
session->setResumeDataStorageType((m_comboBoxResumeDataStorage.currentIndex() == 0)
|
||||
? BitTorrent::ResumeDataStorageType::Legacy
|
||||
: BitTorrent::ResumeDataStorageType::SQLite);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
BitTorrent::OSMemoryPriority prio = BitTorrent::OSMemoryPriority::Normal;
|
||||
switch (m_comboBoxOSMemoryPriority.currentIndex())
|
||||
@@ -216,6 +225,8 @@ void AdvancedSettings::saveAdvancedSettings()
|
||||
session->setSendBufferWatermark(m_spinBoxSendBufferWatermark.value());
|
||||
session->setSendBufferLowWatermark(m_spinBoxSendBufferLowWatermark.value());
|
||||
session->setSendBufferWatermarkFactor(m_spinBoxSendBufferWatermarkFactor.value());
|
||||
// Outgoing connections per second
|
||||
session->setConnectionSpeed(m_spinBoxConnectionSpeed.value());
|
||||
// Socket listen backlog size
|
||||
session->setSocketBacklogSize(m_spinBoxSocketBacklogSize.value());
|
||||
// Save resume data interval
|
||||
@@ -235,8 +246,6 @@ void AdvancedSettings::saveAdvancedSettings()
|
||||
session->setMultiConnectionsPerIpEnabled(m_checkBoxMultiConnectionsPerIp.isChecked());
|
||||
// Validate HTTPS tracker certificate
|
||||
session->setValidateHTTPSTrackerCertificate(m_checkBoxValidateHTTPSTrackerCertificate.isChecked());
|
||||
// SSRF mitigation
|
||||
session->setSSRFMitigationEnabled(m_checkBoxSSRFMitigation.isChecked());
|
||||
// Disallow connection to peers on privileged ports
|
||||
session->setBlockPeersOnPrivilegedPorts(m_checkBoxBlockPeersOnPrivilegedPorts.isChecked());
|
||||
// Recheck torrents on completion
|
||||
@@ -276,6 +285,11 @@ void AdvancedSettings::saveAdvancedSettings()
|
||||
MainWindow *const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
|
||||
mainWindow->setNotificationsEnabled(m_checkBoxProgramNotifications.isChecked());
|
||||
mainWindow->setTorrentAddedNotificationsEnabled(m_checkBoxTorrentAddedNotifications.isChecked());
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
|
||||
mainWindow->setNotificationTimeout(m_spinBoxNotificationTimeout.value());
|
||||
#endif
|
||||
// Reannounce to all trackers when ip/port changed
|
||||
session->setReannounceWhenAddressChangedEnabled(m_checkBoxReannounceWhenAddressChanged.isChecked());
|
||||
// Misc GUI properties
|
||||
mainWindow->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked());
|
||||
AddNewTorrentDialog::setSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value());
|
||||
@@ -389,6 +403,10 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
addRow(LIBTORRENT_HEADER, QString::fromLatin1("<b>%1</b>").arg(tr("libtorrent Section")), labelLibtorrentLink);
|
||||
static_cast<QLabel *>(cellWidget(LIBTORRENT_HEADER, PROPERTY))->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
|
||||
|
||||
m_comboBoxResumeDataStorage.addItems({tr("Fastresume files"), tr("SQLite database (experimental)")});
|
||||
m_comboBoxResumeDataStorage.setCurrentIndex((session->resumeDataStorageType() == BitTorrent::ResumeDataStorageType::Legacy) ? 0 : 1);
|
||||
addRow(RESUME_DATA_STORAGE, tr("Resume data storage type (requires restart)"), &m_comboBoxResumeDataStorage);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
m_comboBoxOSMemoryPriority.addItems({tr("Normal"), tr("Below normal"), tr("Medium"), tr("Low"), tr("Very low")});
|
||||
int OSMemoryPriorityIndex = 0;
|
||||
@@ -513,6 +531,12 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
m_spinBoxSendBufferWatermarkFactor.setValue(session->sendBufferWatermarkFactor());
|
||||
addRow(SEND_BUF_WATERMARK_FACTOR, (tr("Send buffer watermark factor") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#send_buffer_watermark_factor", "(?)"))
|
||||
, &m_spinBoxSendBufferWatermarkFactor);
|
||||
// Outgoing connections per second
|
||||
m_spinBoxConnectionSpeed.setMinimum(0);
|
||||
m_spinBoxConnectionSpeed.setMaximum(std::numeric_limits<int>::max());
|
||||
m_spinBoxConnectionSpeed.setValue(session->connectionSpeed());
|
||||
addRow(CONNECTION_SPEED, (tr("Outgoing connections per second") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#connection_speed", "(?)"))
|
||||
, &m_spinBoxConnectionSpeed);
|
||||
// Socket listen backlog size
|
||||
m_spinBoxSocketBacklogSize.setMinimum(1);
|
||||
m_spinBoxSocketBacklogSize.setMaximum(std::numeric_limits<int>::max());
|
||||
@@ -575,11 +599,6 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
addRow(VALIDATE_HTTPS_TRACKER_CERTIFICATE, (tr("Validate HTTPS tracker certificates")
|
||||
+ ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#validate_https_trackers", "(?)"))
|
||||
, &m_checkBoxValidateHTTPSTrackerCertificate);
|
||||
// SSRF mitigation
|
||||
m_checkBoxSSRFMitigation.setChecked(session->isSSRFMitigationEnabled());
|
||||
addRow(SSRF_MITIGATION, (tr("Server-side request forgery (SSRF) mitigation")
|
||||
+ ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#ssrf_mitigation", "(?)"))
|
||||
, &m_checkBoxSSRFMitigation);
|
||||
// Disallow connection to peers on privileged ports
|
||||
m_checkBoxBlockPeersOnPrivilegedPorts.setChecked(session->blockPeersOnPrivilegedPorts());
|
||||
addRow(BLOCK_PEERS_ON_PRIVILEGED_PORTS, (tr("Disallow connection to peers on privileged ports") + ' ' + makeLink("https://libtorrent.org/single-page-ref.html#no_connect_privileged_ports", "(?)")), &m_checkBoxBlockPeersOnPrivilegedPorts);
|
||||
@@ -648,6 +667,18 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
// Torrent added notifications
|
||||
m_checkBoxTorrentAddedNotifications.setChecked(mainWindow->isTorrentAddedNotificationsEnabled());
|
||||
addRow(TORRENT_ADDED_NOTIFICATIONS, tr("Display notifications for added torrents"), &m_checkBoxTorrentAddedNotifications);
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
|
||||
// Notification timeout
|
||||
m_spinBoxNotificationTimeout.setMinimum(-1);
|
||||
m_spinBoxNotificationTimeout.setMaximum(std::numeric_limits<int>::max());
|
||||
m_spinBoxNotificationTimeout.setValue(mainWindow->getNotificationTimeout());
|
||||
m_spinBoxNotificationTimeout.setSpecialValueText(tr("System default"));
|
||||
m_spinBoxNotificationTimeout.setSuffix(tr(" ms", " milliseconds"));
|
||||
addRow(NOTIFICATION_TIMEOUT, tr("Notification timeout [0: infinite]"), &m_spinBoxNotificationTimeout);
|
||||
#endif
|
||||
// Reannounce to all trackers when ip/port changed
|
||||
m_checkBoxReannounceWhenAddressChanged.setChecked(session->isReannounceWhenAddressChangedEnabled());
|
||||
addRow(REANNOUNCE_WHEN_ADDRESS_CHANGED, tr("Reannounce to all trackers when IP or port changed"), &m_checkBoxReannounceWhenAddressChanged);
|
||||
// Download tracker's favicon
|
||||
m_checkBoxTrackerFavicon.setChecked(mainWindow->isDownloadTrackerFavicon());
|
||||
addRow(DOWNLOAD_TRACKER_FAVICON, tr("Download tracker's favicon"), &m_checkBoxTrackerFavicon);
|
||||
|
||||
@@ -63,14 +63,15 @@ private:
|
||||
QSpinBox m_spinBoxAsyncIOThreads, m_spinBoxFilePoolSize, m_spinBoxCheckingMemUsage,
|
||||
m_spinBoxSaveResumeDataInterval, m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxPeerToS,
|
||||
m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark,
|
||||
m_spinBoxSendBufferWatermarkFactor, m_spinBoxSocketBacklogSize, m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout,
|
||||
m_spinBoxSendBufferWatermarkFactor, m_spinBoxConnectionSpeed, m_spinBoxSocketBacklogSize, m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout,
|
||||
m_spinBoxSavePathHistoryLength, m_spinBoxPeerTurnover, m_spinBoxPeerTurnoverCutoff, m_spinBoxPeerTurnoverInterval;
|
||||
QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts,
|
||||
m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,
|
||||
m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxReannounceWhenAddressChanged, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,
|
||||
m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers,
|
||||
m_checkBoxMultiConnectionsPerIp, m_checkBoxValidateHTTPSTrackerCertificate, m_checkBoxSSRFMitigation, m_checkBoxBlockPeersOnPrivilegedPorts, m_checkBoxPieceExtentAffinity,
|
||||
m_checkBoxMultiConnectionsPerIp, m_checkBoxValidateHTTPSTrackerCertificate, m_checkBoxBlockPeersOnPrivilegedPorts, m_checkBoxPieceExtentAffinity,
|
||||
m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport;
|
||||
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm, m_comboBoxSeedChokingAlgorithm;
|
||||
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm,
|
||||
m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage;
|
||||
QLineEdit m_lineEditAnnounceIP;
|
||||
|
||||
#if (LIBTORRENT_VERSION_NUM < 20000)
|
||||
@@ -88,4 +89,8 @@ private:
|
||||
#ifndef Q_OS_MACOS
|
||||
QCheckBox m_checkBoxIconsInMenusEnabled;
|
||||
#endif
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
|
||||
QSpinBox m_spinBoxNotificationTimeout;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "categoryfilterproxymodel.h"
|
||||
|
||||
#include "base/utils/string.h"
|
||||
#include "categoryfiltermodel.h"
|
||||
|
||||
CategoryFilterProxyModel::CategoryFilterProxyModel(QObject *parent)
|
||||
@@ -51,9 +50,5 @@ bool CategoryFilterProxyModel::lessThan(const QModelIndex &left, const QModelInd
|
||||
// "All" and "Uncategorized" must be left in place
|
||||
if (CategoryFilterModel::isSpecialItem(left) || CategoryFilterModel::isSpecialItem(right))
|
||||
return (left < right);
|
||||
|
||||
int result = Utils::String::naturalCompare(left.data().toString(), right.data().toString()
|
||||
, Qt::CaseInsensitive);
|
||||
|
||||
return (result < 0);
|
||||
return m_naturalLessThan(left.data().toString(), right.data().toString());
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "base/utils/compare.h"
|
||||
|
||||
class QString;
|
||||
|
||||
class CategoryFilterProxyModel final : public QSortFilterProxyModel
|
||||
@@ -47,4 +49,6 @@ protected:
|
||||
private:
|
||||
// we added another overload of index(), hence this using directive:
|
||||
using QSortFilterProxyModel::index;
|
||||
|
||||
Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> m_naturalLessThan;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user