Compare commits
591 commits
release-5.
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
b7a43ea118 | ||
|
4f94eac235 | ||
|
a3e6d1a0ad | ||
|
efedbcb407 | ||
|
55de9b07d2 | ||
|
9ad4a94940 | ||
|
c47b981a56 | ||
|
5028f68d48 | ||
|
ef4957a9f4 | ||
|
99d25eec71 | ||
|
70a6153b78 | ||
|
690a139538 | ||
|
e447baa04a | ||
|
fdfdbae30c | ||
|
dd4a2eb583 | ||
|
41d7d672ce | ||
|
d379fa3035 | ||
|
71af105a89 | ||
|
f6ee6b92a4 | ||
|
fe1679d778 | ||
|
67ef356064 | ||
|
254f39f89d | ||
|
d702a02c1f | ||
|
86e11d344f | ||
|
6972962ee0 | ||
|
599a2d0c93 | ||
|
e27cbab7ee | ||
|
794310dca9 | ||
|
3cd40cc5a2 | ||
|
380d9af34c | ||
|
5605e08347 | ||
|
753c6629a3 | ||
|
9b66693cb8 | ||
|
406a389d7c | ||
|
d7a5430893 | ||
|
7ac160a481 | ||
|
4c190b0d4f | ||
|
69bf31f4e9 | ||
|
e45ca3fde7 | ||
|
5750de6270 | ||
|
ff07591a87 | ||
|
c215c1e8b1 | ||
|
06756936f3 | ||
|
7ed026ef78 | ||
|
78fae0ae76 | ||
|
8aa1a96d71 | ||
|
4132173b30 | ||
|
8e2125ee72 | ||
|
05bcc4e175 | ||
|
5dfb51a8d2 | ||
|
526abdf7ce | ||
|
617b1da842 | ||
|
c59ac3b970 | ||
|
0c48b70e5b | ||
|
1cb6173ad1 | ||
|
2cbfb91b88 | ||
|
0729c9a2f8 | ||
|
7982f66fa6 | ||
|
50d60b9589 | ||
|
96f0eebc4e | ||
|
4b07597d54 | ||
|
a9213627a9 | ||
|
28c1ba869b | ||
|
054003970e | ||
|
44bb1ac7eb | ||
|
e309b17732 | ||
|
e10fb40a48 | ||
|
7648a2838d | ||
|
8cd1a80852 | ||
|
c79a9624af | ||
|
4e9c514c2f | ||
|
84ed24e257 | ||
|
48f7f6fb8c | ||
|
f47754838b | ||
|
a3d1ff0eb2 | ||
|
afcfea5b8f | ||
|
84cd8e1535 | ||
|
eb3718fc91 | ||
|
7e0247fefe | ||
|
f9f031cdb4 | ||
|
d56b353c52 | ||
|
83799f4f07 | ||
|
c7caae1150 | ||
|
1662a9deb2 | ||
|
6c310aa311 | ||
|
368748ac52 | ||
|
9c81e58de6 | ||
|
778a158597 | ||
|
09071d2b69 | ||
|
b79ac0d716 | ||
|
de767871f1 | ||
|
2477e13b3f | ||
|
eb82c9078d | ||
|
663da093bd | ||
|
13f9c20a69 | ||
|
86e4b662ce | ||
|
dcaf4b6d80 | ||
|
d29f47c36e | ||
|
ab04064adc | ||
|
9afbd47b52 | ||
|
582dc99cae | ||
|
3691eb948e | ||
|
76bb4e5f98 | ||
|
0262faa915 | ||
|
f1b7c4572b | ||
|
c494314a29 | ||
|
559f47ab0c | ||
|
380b25e22f | ||
|
7745ac19d8 | ||
|
6cd6267c26 | ||
|
e7dee969e1 | ||
|
bb68a39b53 | ||
|
4c91cd9372 | ||
|
0791828b84 | ||
|
c2f2a38582 | ||
|
ad4bdc0653 | ||
|
f3095935ea | ||
|
732b2bcbdb | ||
|
33aaa867b5 | ||
|
fdd17159eb | ||
|
e9fee414df | ||
|
1077cbba2b | ||
|
70dbe9468a | ||
|
45babc336d | ||
|
70822e8942 | ||
|
b5394e7939 | ||
|
411ca0f668 | ||
|
7b3aa51bb1 | ||
|
0b3bce8993 | ||
|
0160aa28b6 | ||
|
0187f19f60 | ||
|
e87dfe35f3 | ||
|
e51be45ce6 | ||
|
b4a16f6464 | ||
|
250fef4ee7 | ||
|
8fc5d0914d | ||
|
fc5daf6e1d | ||
|
c878a09d27 | ||
|
2aee875642 | ||
|
2785636d3f | ||
|
15069b2643 | ||
|
f0361f1bed | ||
|
110e6d32b4 | ||
|
3d73026ff2 | ||
|
abafbc0685 | ||
|
5465605377 | ||
|
9331580e86 | ||
|
795889c417 | ||
|
ff03eeab5b | ||
|
f0b9a17566 | ||
|
72e8b3272b | ||
|
6c36830e5e | ||
|
cdddaae939 | ||
|
f540381caf | ||
|
055d82bda4 | ||
|
0796f96ee4 | ||
|
841cffafa7 | ||
|
ade39432be | ||
|
830d2c207b | ||
|
97865545c3 | ||
|
3abdc3134b | ||
|
5a716a40fb | ||
|
943e403241 | ||
|
103ea813dc | ||
|
52b1f3588a | ||
|
4bd50672e8 | ||
|
8c8a0ac54c | ||
|
7b4a3fccc6 | ||
|
d21653e8cf | ||
|
627d89813c | ||
|
b28c229f85 | ||
|
8d0870c953 | ||
|
5a4b3b25d3 | ||
|
d174bc75e4 | ||
|
882da47609 | ||
|
b74b334e34 | ||
|
53f919aea8 | ||
|
62a7fd86d6 | ||
|
96295adc08 | ||
|
8f53fb8178 | ||
|
37eb80919c | ||
|
1b044d9476 | ||
|
83599f1f7b | ||
|
6e1b5ec18b | ||
|
249c80aaaf | ||
|
0ac47496d4 | ||
|
4ec80de268 | ||
|
f432c1e615 | ||
|
41d9ee91a1 | ||
|
ba3d89b674 | ||
|
1ca33d45ba | ||
|
a9b54d94a0 | ||
|
693390ff27 | ||
|
5ddc5a8b87 | ||
|
ad9100ac07 | ||
|
1043bea896 | ||
|
955688c125 | ||
|
8da43a4054 | ||
|
ddf6dd5fa2 | ||
|
8c02bbb4bc | ||
|
7e95375cec | ||
|
29201fa016 | ||
|
1a3d0f6fab | ||
|
f58d6ae984 | ||
|
7f0134108a | ||
|
d79dc86d00 | ||
|
38070c6eee | ||
|
c9eb1fbac8 | ||
|
7238bad5a6 | ||
|
bd564a99a3 | ||
|
b052ad0923 | ||
|
c65a68251e | ||
|
93925042dd | ||
|
e55b59d9ca | ||
|
f8469d02f7 | ||
|
dc10b88cec | ||
|
4406a3f173 | ||
|
9c2e698514 | ||
|
463700b76d | ||
|
86387fbe49 | ||
|
a018cfa56c | ||
|
b76054beba | ||
|
f8536162f2 | ||
|
af65ddd012 | ||
|
fe9dc131bc | ||
|
bb4a668ddd | ||
|
3978137534 | ||
|
3ef4d0d798 | ||
|
e2341f5217 | ||
|
abd3cd54bc | ||
|
dc8ac38494 | ||
|
e3eacf2bf7 | ||
|
5098519d46 | ||
|
82c36aea89 | ||
|
05787d94ec | ||
|
f8c48349a1 | ||
|
1ee84033ec | ||
|
f2eecf8a4e | ||
|
76e1040232 | ||
|
4686d6709e | ||
|
2cc7ec90a8 | ||
|
99adb16090 | ||
|
c622d50814 | ||
|
11991e62f5 | ||
|
82d90e599c | ||
|
45b7947cd0 | ||
|
2e21cf76de | ||
|
76151110e5 | ||
|
5875d8bff3 | ||
|
68ecb13d14 | ||
|
f9f4b60b83 | ||
|
4fc36b9e99 | ||
|
4f3d77963f | ||
|
d911928c59 | ||
|
22e156e0af | ||
|
6fe02895a8 | ||
|
395dbaa5c6 | ||
|
efe06f133d | ||
|
9c0475ebfa | ||
|
e740a42366 | ||
|
cc31a90931 | ||
|
90e457a671 | ||
|
7487cd7e6d | ||
|
bbc3c2832f | ||
|
879c6bf9ff | ||
|
f2097dc4b5 | ||
|
166feb5bdf | ||
|
a841fe9320 | ||
|
9709672b34 | ||
|
e2db0bc866 | ||
|
fee45e4ba6 | ||
|
257d928ab3 | ||
|
34c8849f22 | ||
|
1c82eb3dff | ||
|
d96ab6ba84 | ||
|
7886ca65f9 | ||
|
85c4ddf616 | ||
|
0a36171999 | ||
|
eb2eea8d34 | ||
|
14684c8c83 | ||
|
c887a6f7d8 | ||
|
4c6dd8e68d | ||
|
27451469fa | ||
|
a311c259cc | ||
|
0ad65ceef6 | ||
|
cbf7c09bf4 | ||
|
3fcc298539 | ||
|
7080f85b59 | ||
|
9f0fa4c215 | ||
|
6f31a13f22 | ||
|
200f7fc628 | ||
|
a180162405 | ||
|
2d1c4fc809 | ||
|
e8d8de8f19 | ||
|
90aecfea02 | ||
|
a85736fd27 | ||
|
dafbcf8709 | ||
|
d11622e3c0 | ||
|
d90a9d15ac | ||
|
f8aaea3476 | ||
|
b84a51c76d | ||
|
83b0dd3026 | ||
|
ef5506321a | ||
|
24d349ffba | ||
|
2109e13746 | ||
|
5eec0c0213 | ||
|
f34787e6ba | ||
|
15ea836bb9 | ||
|
72e033db79 | ||
|
e1bd1038c0 | ||
|
3ebdb50457 | ||
|
b0fe6e6c59 | ||
|
8d847eeb18 | ||
|
f022ce8f84 | ||
|
78a5e4ff3e | ||
|
61ff683f11 | ||
|
7300b9f759 | ||
|
6ce2869108 | ||
|
0eba285ff1 | ||
|
88161a6467 | ||
|
7f901a812d | ||
|
6578fd06fd | ||
|
6ddde3f4b6 | ||
|
530631322d | ||
|
928de36093 | ||
|
1e851b3637 | ||
|
c9c85eeb95 | ||
|
ea35aa45d6 | ||
|
e51fcc6ea0 | ||
|
f4eec75488 | ||
|
f73f31619d | ||
|
ede08f3845 | ||
|
c9a55fce95 | ||
|
1cd3c586c1 | ||
|
0f12d077c8 | ||
|
92daca1fef | ||
|
889df72ab3 | ||
|
631e873ff2 | ||
|
69f19d4a0b | ||
|
3ec645674a | ||
|
71f83cf9ba | ||
|
06fe3e5fb0 | ||
|
fe153f8919 | ||
|
568de90923 | ||
|
f89c4c32ed | ||
|
fb9b3c0f34 | ||
|
33e3fb2f46 | ||
|
4bec9b90c4 | ||
|
a6c7aef6c1 | ||
|
4527536858 | ||
|
3da9444688 | ||
|
75d1ac8889 | ||
|
051d7137ea | ||
|
b462a2bf0c | ||
|
c02f80cec5 | ||
|
3bb1e34233 | ||
|
dc30b9c2ec | ||
|
b083029841 | ||
|
6f642776b6 | ||
|
1a7ebfc8f0 | ||
|
0771970627 | ||
|
0f18e80154 | ||
|
08b51fc869 | ||
|
13e3192444 | ||
|
3aefc16c57 | ||
|
7b0b3a1522 | ||
|
8991d994c2 | ||
|
72cbc83569 | ||
|
7af6ac18aa | ||
|
41236d8e58 | ||
|
03dfd983d0 | ||
|
84d895231c | ||
|
91b2687032 | ||
|
be3eefd8de | ||
|
e0e61ffd02 | ||
|
c3c91be578 | ||
|
e0431e3ffb | ||
|
67b6cf5a6f | ||
|
e8dc6b3f73 | ||
|
dfe9daf25d | ||
|
ca933c60a1 | ||
|
c080fc3aa0 | ||
|
5dd41f506e | ||
|
a3ac692c25 | ||
|
e91412ec8b | ||
|
337730ddef | ||
|
3ab9fe55e5 | ||
|
ab8d0d1dae | ||
|
25dbea1388 | ||
|
a47e1cdb48 | ||
|
4805afc1a2 | ||
|
5a0914e333 | ||
|
7031c52d16 | ||
|
8e941a06f1 | ||
|
966387859a | ||
|
fb40275507 | ||
|
3d9e9715b4 | ||
|
21b0367629 | ||
|
ac646f47a2 | ||
|
c4eeb4a14a | ||
|
6418033cc8 | ||
|
cbcb46bcfb | ||
|
0704049026 | ||
|
81509dfb65 | ||
|
b1fd61af3a | ||
|
2d185dc1c7 | ||
|
2d857b6200 | ||
|
871438f557 | ||
|
87644441ad | ||
|
d73201c098 | ||
|
3ea2be41e7 | ||
|
6bbedbea8a | ||
|
56a0692b68 | ||
|
dfa4eebbce | ||
|
6ed662c68b | ||
|
f81d8a85e9 | ||
|
9e5433bcf8 | ||
|
3fb5d7764c | ||
|
e75bcbed6d | ||
|
7f38216d22 | ||
|
e309148147 | ||
|
6981217369 | ||
|
4ff0687b94 | ||
|
dc02a0fc56 | ||
|
c48d2c1dde | ||
|
b5b34c9ff4 | ||
|
7b45566efc | ||
|
c30a07702d | ||
|
d8e24314ec | ||
|
449ca96e28 | ||
|
cebaedf485 | ||
|
fd311fd5ff | ||
|
50acb670b0 | ||
|
3888b465d8 | ||
|
6bbb7b71cd | ||
|
10eb921d70 | ||
|
5e3161a3f9 | ||
|
4cc3fedf37 | ||
|
2952480f37 | ||
|
6bfabad92f | ||
|
0e03e4f8a7 | ||
|
10149de205 | ||
|
960edd95cc | ||
|
8b2d8f3afd | ||
|
81def39d8c | ||
|
a23f45cc70 | ||
|
8a6207d3fc | ||
|
c3224459db | ||
|
183c7c75b1 | ||
|
1c43286616 | ||
|
4555a46e5d | ||
|
23f7275bd5 | ||
|
d2b2afad23 | ||
|
d19f7b12d9 | ||
|
6df1f68ead | ||
|
e06b7f8f4d | ||
|
3058158b69 | ||
|
0ea35c54a3 | ||
|
dbef6da544 | ||
|
a0c32110f1 | ||
|
435385816a | ||
|
1b53fdf9ee | ||
|
f00c5c9fa3 | ||
|
130c0d8487 | ||
|
d9bc7935eb | ||
|
944499814b | ||
|
0e63b83aed | ||
|
f681e954c7 | ||
|
a7f7c5fb73 | ||
|
9d0fa213be | ||
|
fc82abe7f6 | ||
|
72feee6fdd | ||
|
58eab8d453 | ||
|
7ab4758279 | ||
|
e6cd9b90d2 | ||
|
5b7c9d5725 | ||
|
39dd415d43 | ||
|
9a9c375b9d | ||
|
fda797cb76 | ||
|
a91bac8aa0 | ||
|
0904f4a89b | ||
|
9c370bf391 | ||
|
f09d43d073 | ||
|
f818d0dbe0 | ||
|
98623b2cf7 | ||
|
29379232aa | ||
|
0c580c3174 | ||
|
1179fc3de3 | ||
|
e069fbc37f | ||
|
efdc4af448 | ||
|
0535993e41 | ||
|
f5aa0bb126 | ||
|
0da383e7b6 | ||
|
c5b7c82344 | ||
|
b1d2b9d02b | ||
|
d9667b5221 | ||
|
155fe96bdd | ||
|
9a8572bd21 | ||
|
04eb40376e | ||
|
ea06eb9fe6 | ||
|
3e18b1d30c | ||
|
9df3ee0de8 | ||
|
0c7045042d | ||
|
5afeecbf18 | ||
|
4570c0ef9e | ||
|
d0af02cc17 | ||
|
62c5f41f39 | ||
|
bee56f2567 | ||
|
cbabe56fcf | ||
|
2d9e3b3330 | ||
|
989b1d176d | ||
|
142780b863 | ||
|
7b2886e477 | ||
|
66c1acbce2 | ||
|
49507ad670 | ||
|
d74f49111b | ||
|
642a9c29eb | ||
|
9d494e84bf | ||
|
aed103d06e | ||
|
b67495464d | ||
|
bf7e1516d5 | ||
|
7131d1bd6b | ||
|
062904c2bd | ||
|
6b52a04ff1 | ||
|
69a829dfb0 | ||
|
3c5baac150 | ||
|
8e9680bf69 | ||
|
b75c42f850 | ||
|
3b38d0de7f | ||
|
8b7fdf0f22 | ||
|
83d730ffda | ||
|
3acd5409a6 | ||
|
a61df019b3 | ||
|
7df98e1c9a | ||
|
c3b7dfa918 | ||
|
0fd24358ce | ||
|
7e8e6269d0 | ||
|
25dd6c72f7 | ||
|
adde3c3f65 | ||
|
7119de9b8d | ||
|
3999b9a4f9 | ||
|
7f4cb43a33 | ||
|
9feefc8144 | ||
|
9c26e5d4d6 | ||
|
815ab180c1 | ||
|
eba5cbb803 | ||
|
87a202c71e | ||
|
a4f63a5c30 | ||
|
ccdf178ee7 | ||
|
b52fa98a02 | ||
|
d87533bf4c | ||
|
5ef2a1df07 | ||
|
d2fceaa228 | ||
|
4e27e88f6a | ||
|
c5fa05299b | ||
|
0cbe4882c3 | ||
|
610d5ef5ff | ||
|
9d87a813b2 | ||
|
5740238933 | ||
|
ea918da931 | ||
|
9317c25ecb | ||
|
7a2bfae5e4 | ||
|
9894f654cf | ||
|
d71086e400 | ||
|
2000be12ba | ||
|
914728d9a1 | ||
|
c36100fa85 | ||
|
1c49e0973c | ||
|
65d143d4c4 | ||
|
d89f289f82 | ||
|
648dd9988d | ||
|
dd34c85884 | ||
|
1903ddada1 | ||
|
bf4e0df386 | ||
|
b9a1bbbb8a | ||
|
41d8f473b7 | ||
|
4155d4660f | ||
|
455a04b68e | ||
|
4c57318e89 | ||
|
d52995015e | ||
|
b1b6685663 | ||
|
534615373e | ||
|
1ba69be869 | ||
|
c54750469e | ||
|
3ebd15d408 | ||
|
64dfb7e122 | ||
|
b07afa3ea9 | ||
|
24a1537cdd | ||
|
55bff4f07a | ||
|
cb90b6769c | ||
|
6d073771ca |
2
.github/FUNDING.yml
vendored
|
@ -1 +1 @@
|
||||||
custom: "https://www.qbittorrent.org/donate.php"
|
custom: "https://www.qbittorrent.org/donate"
|
||||||
|
|
18
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -7,19 +7,17 @@ body:
|
||||||
#### ADVISORY
|
#### ADVISORY
|
||||||
"We do not support any versions older than the current release series"
|
"We do not support any versions older than the current release series"
|
||||||
|
|
||||||
"We do not support any 3rd party/forked versions e.g. `portableapps`/`Enhanced Edition`etc."
|
"We do not support any 3rd party/forked versions e.g. `portableapps`/`Enhanced Edition` etc."
|
||||||
|
|
||||||
"Please post all details in **English**."
|
"Please post all details in **English**."
|
||||||
|
|
||||||
#### Prerequisites before submitting an issue!
|
#### Prerequisites before submitting an issue!
|
||||||
- Read the issue reporting section in the **[contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md)**, to know how to submit a good bug report with the required information.
|
- Read the issue reporting section in the **[contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md)**, to know how to submit a good bug report with the required information.
|
||||||
- Verify that the issue is not fixed and is reproducible in the **[latest official qBittorrent version](https://www.qbittorrent.org/download.php).**
|
- Verify that the issue is not fixed and is reproducible in the **[latest official qBittorrent version](https://www.qbittorrent.org/download.php).**
|
||||||
- (Optional, but recommended) Verify that the issue is not fixed and is reproducible in the latest CI (currently only on **[Windows](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_windows.yaml?query=branch%3Amaster+event%3Apush)**) builds.
|
- (Optional, but recommended) Verify that the issue is not fixed and is reproducible in the latest CI (**[macOS](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_macos.yaml?query=branch%3Amaster+event%3Apush)** / **[Ubuntu](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_ubuntu.yaml?query=branch%3Amaster+event%3Apush)** / **[Windows](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_windows.yaml?query=branch%3Amaster+event%3Apush)**) builds.
|
||||||
- Check the **[frequent/common issues list](https://github.com/qbittorrent/qBittorrent/projects/2)** and perform a **[search of the issue tracker (including closed ones)](https://github.com/qbittorrent/qBittorrent/issues)** to avoid posting a duplicate.
|
- Perform a **[search of the issue tracker (including closed ones)](https://github.com/qbittorrent/qBittorrent/issues?q=is%3Aissue+is%3Aopen+-label%3A%22Feature+request%22)** to avoid posting a duplicate.
|
||||||
- Make sure this 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/)**.
|
- Make sure this 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/)**.
|
||||||
- Verify that the **[wiki](https://github.com/qbittorrent/qBittorrent/wiki)** did not contain a suitable solution either.
|
- Verify that the **[wiki](https://github.com/qbittorrent/qBittorrent/wiki)** did not contain a suitable solution either.
|
||||||
- If relevant to issue/when asked, the qBittorrent preferences file, qBittorrent.log & watched_folders.json (if using "Watched Folders" feature) must be provided.
|
|
||||||
See **[Where does qBittorrent save its settings?](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings)**
|
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
@ -28,10 +26,10 @@ body:
|
||||||
Qt and libtorrent-rasterbar versions are required when: 1. You are using linux. 2. You are not using an official build downloaded from our website.
|
Qt and libtorrent-rasterbar versions are required when: 1. You are using linux. 2. You are not using an official build downloaded from our website.
|
||||||
|
|
||||||
Example of preferred formatting:
|
Example of preferred formatting:
|
||||||
qBittorrent: 4.3.7 x64
|
qBittorrent: 4.6.6 x64
|
||||||
Operating system: Windows 10 Pro 21H1/2009 x64
|
Operating system: Windows 10 Pro x64 (22H2) 10.0.19045
|
||||||
Qt: 5.15.2
|
Qt: 6.4.3
|
||||||
libtorrent-rasterbar: 1.2.14
|
libtorrent-rasterbar: 1.2.19
|
||||||
placeholder: |
|
placeholder: |
|
||||||
qBittorrent:
|
qBittorrent:
|
||||||
Operating system:
|
Operating system:
|
||||||
|
@ -73,4 +71,4 @@ body:
|
||||||
See **[Where does qBittorrent save its settings?](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings)**
|
See **[Where does qBittorrent save its settings?](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings)**
|
||||||
#### Note: It's the user's responsibility to redact any sensitive information
|
#### Note: It's the user's responsibility to redact any sensitive information
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: true
|
||||||
|
|
31
.github/workflows/ci_file_health.yaml
vendored
|
@ -12,11 +12,15 @@ jobs:
|
||||||
ci:
|
ci:
|
||||||
name: Check
|
name: Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install tools
|
- name: Setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "*"
|
python-version: "*"
|
||||||
|
@ -32,7 +36,7 @@ jobs:
|
||||||
curl \
|
curl \
|
||||||
-L \
|
-L \
|
||||||
-o "${{ runner.temp }}/pandoc.tar.gz" \
|
-o "${{ runner.temp }}/pandoc.tar.gz" \
|
||||||
"https://github.com/jgm/pandoc/releases/download/3.1.7/pandoc-3.1.7-linux-amd64.tar.gz"
|
"https://github.com/jgm/pandoc/releases/download/3.7.0.2/pandoc-3.7.0.2-linux-amd64.tar.gz"
|
||||||
tar -xf "${{ runner.temp }}/pandoc.tar.gz" -C "${{ github.workspace }}/.."
|
tar -xf "${{ runner.temp }}/pandoc.tar.gz" -C "${{ github.workspace }}/.."
|
||||||
mv "${{ github.workspace }}/.."/pandoc-* "${{ env.pandoc_path }}"
|
mv "${{ github.workspace }}/.."/pandoc-* "${{ env.pandoc_path }}"
|
||||||
# run pandoc
|
# run pandoc
|
||||||
|
@ -42,3 +46,26 @@ jobs:
|
||||||
done
|
done
|
||||||
# check diff, ignore "Automatically generated by ..." part
|
# check diff, ignore "Automatically generated by ..." part
|
||||||
git diff -I '\.\\".*' --exit-code
|
git diff -I '\.\\".*' --exit-code
|
||||||
|
|
||||||
|
- name: Check GitHub Actions workflow
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
pip install zizmor
|
||||||
|
IGNORE_RULEID='(.ruleId != "zizmor/template-injection")
|
||||||
|
and (.ruleId != "zizmor/unpinned-uses")'
|
||||||
|
IGNORE_ID='(.id != "zizmor/template-injection")
|
||||||
|
and (.id != "zizmor/unpinned-uses")'
|
||||||
|
zizmor \
|
||||||
|
--format sarif \
|
||||||
|
--persona auditor \
|
||||||
|
./ \
|
||||||
|
| jq "(.runs[].results |= map(select($IGNORE_RULEID)))
|
||||||
|
| (.runs[].tool.driver.rules |= map(select($IGNORE_ID)))" \
|
||||||
|
> "${{ runner.temp }}/zizmor_results.sarif"
|
||||||
|
|
||||||
|
- name: Upload zizmor results
|
||||||
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
|
with:
|
||||||
|
category: zizmor
|
||||||
|
sarif_file: "${{ runner.temp }}/zizmor_results.sarif"
|
||||||
|
|
28
.github/workflows/ci_macos.yaml
vendored
|
@ -2,8 +2,7 @@ name: CI - macOS
|
||||||
|
|
||||||
on: [pull_request, push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
actions: write
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
@ -13,13 +12,15 @@ jobs:
|
||||||
ci:
|
ci:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.10", "1.2.19"]
|
libt_version: ["2.0.11", "1.2.20"]
|
||||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||||
qt_version: ["6.7.0"]
|
qt_version: ["6.9.1"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
|
@ -28,6 +29,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
uses: Wandalen/wretry.action@v3
|
uses: Wandalen/wretry.action@v3
|
||||||
|
@ -49,15 +52,15 @@ jobs:
|
||||||
store_cache: ${{ github.ref == 'refs/heads/master' }}
|
store_cache: ${{ github.ref == 'refs/heads/master' }}
|
||||||
update_packager_index: false
|
update_packager_index: false
|
||||||
ccache_options: |
|
ccache_options: |
|
||||||
max_size=2G
|
max_size=1G
|
||||||
|
|
||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "85"
|
BOOST_MINOR_VERSION: "86"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
set +e
|
set +e
|
||||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||||
|
@ -67,6 +70,9 @@ jobs:
|
||||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
||||||
fi
|
fi
|
||||||
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
||||||
|
cd "${{ env.boost_path }}"
|
||||||
|
./bootstrap.sh
|
||||||
|
./b2 stage --stagedir=./ --with-headers
|
||||||
|
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
@ -90,23 +96,23 @@ jobs:
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DBUILD_SHARED_LIBS=OFF \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_CXX_STANDARD=17 \
|
-DCMAKE_CXX_STANDARD=20 \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
cmake --build build
|
cmake --build build
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
|
|
||||||
- name: Build qBittorrent
|
- name: Build qBittorrent
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
CXXFLAGS="$CXXFLAGS -DQT_FORCE_ASSERTS -Werror -Wno-error=deprecated-declarations" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
cmake \
|
cmake \
|
||||||
-B build \
|
-B build \
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" \
|
||||||
-DTESTING=ON \
|
-DTESTING=ON \
|
||||||
-DVERBOSE_CONFIGURE=ON \
|
-DVERBOSE_CONFIGURE=ON \
|
||||||
-D${{ matrix.qbt_gui }}
|
-D${{ matrix.qbt_gui }}
|
||||||
|
|
20
.github/workflows/ci_python.yaml
vendored
|
@ -16,6 +16,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup python (auxiliary scripts)
|
- name: Setup python (auxiliary scripts)
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
|
@ -23,7 +25,7 @@ jobs:
|
||||||
python-version: '3' # use default version
|
python-version: '3' # use default version
|
||||||
|
|
||||||
- name: Install tools (auxiliary scripts)
|
- name: Install tools (auxiliary scripts)
|
||||||
run: pip install bandit pycodestyle pyflakes
|
run: pip install bandit isort pycodestyle pyflakes
|
||||||
|
|
||||||
- name: Gather files (auxiliary scripts)
|
- name: Gather files (auxiliary scripts)
|
||||||
run: |
|
run: |
|
||||||
|
@ -34,7 +36,7 @@ jobs:
|
||||||
- name: Lint code (auxiliary scripts)
|
- name: Lint code (auxiliary scripts)
|
||||||
run: |
|
run: |
|
||||||
pyflakes $PY_FILES
|
pyflakes $PY_FILES
|
||||||
bandit --skip B314,B405 $PY_FILES
|
bandit --skip B101,B314,B405 $PY_FILES
|
||||||
|
|
||||||
- name: Format code (auxiliary scripts)
|
- name: Format code (auxiliary scripts)
|
||||||
run: |
|
run: |
|
||||||
|
@ -42,6 +44,10 @@ jobs:
|
||||||
--max-line-length=1000 \
|
--max-line-length=1000 \
|
||||||
--statistics \
|
--statistics \
|
||||||
$PY_FILES
|
$PY_FILES
|
||||||
|
isort \
|
||||||
|
--check \
|
||||||
|
--diff \
|
||||||
|
$PY_FILES
|
||||||
|
|
||||||
- name: Build code (auxiliary scripts)
|
- name: Build code (auxiliary scripts)
|
||||||
run: |
|
run: |
|
||||||
|
@ -50,10 +56,10 @@ jobs:
|
||||||
- name: Setup python (search engine)
|
- name: Setup python (search engine)
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.9'
|
||||||
|
|
||||||
- name: Install tools (search engine)
|
- name: Install tools (search engine)
|
||||||
run: pip install bandit mypy pycodestyle pyflakes pyright
|
run: pip install bandit isort mypy pycodestyle pyflakes pyright
|
||||||
|
|
||||||
- name: Gather files (search engine)
|
- name: Gather files (search engine)
|
||||||
run: |
|
run: |
|
||||||
|
@ -61,7 +67,7 @@ jobs:
|
||||||
echo $PY_FILES
|
echo $PY_FILES
|
||||||
echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV"
|
echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Check typings (search engine)
|
- name: Check typings (search engine)
|
||||||
run: |
|
run: |
|
||||||
MYPYPATH="src/searchengine/nova3" \
|
MYPYPATH="src/searchengine/nova3" \
|
||||||
mypy \
|
mypy \
|
||||||
|
@ -83,6 +89,10 @@ jobs:
|
||||||
--max-line-length=1000 \
|
--max-line-length=1000 \
|
||||||
--statistics \
|
--statistics \
|
||||||
$PY_FILES
|
$PY_FILES
|
||||||
|
isort \
|
||||||
|
--check \
|
||||||
|
--diff \
|
||||||
|
$PY_FILES
|
||||||
|
|
||||||
- name: Build code (search engine)
|
- name: Build code (search engine)
|
||||||
run: |
|
run: |
|
||||||
|
|
45
.github/workflows/ci_ubuntu.yaml
vendored
|
@ -2,9 +2,7 @@ name: CI - Ubuntu
|
||||||
|
|
||||||
on: [pull_request, push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
actions: write
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
@ -14,22 +12,27 @@ jobs:
|
||||||
ci:
|
ci:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.10", "1.2.19"]
|
libt_version: ["2.0.11", "1.2.20"]
|
||||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||||
qt_version: ["6.5.2"]
|
qt_version: ["6.6.3"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
harden_flags: "-D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS"
|
harden_flags: "-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS"
|
||||||
libtorrent_path: "${{ github.workspace }}/../libtorrent"
|
libtorrent_path: "${{ github.workspace }}/../libtorrent"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
@ -44,15 +47,15 @@ jobs:
|
||||||
store_cache: ${{ github.ref == 'refs/heads/master' }}
|
store_cache: ${{ github.ref == 'refs/heads/master' }}
|
||||||
update_packager_index: false
|
update_packager_index: false
|
||||||
ccache_options: |
|
ccache_options: |
|
||||||
max_size=2G
|
max_size=1G
|
||||||
|
|
||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "76"
|
BOOST_MINOR_VERSION: "77"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
set +e
|
set +e
|
||||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||||
|
@ -62,6 +65,9 @@ jobs:
|
||||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
||||||
fi
|
fi
|
||||||
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
||||||
|
cd "${{ env.boost_path }}"
|
||||||
|
./bootstrap.sh
|
||||||
|
./b2 stage --stagedir=./ --with-headers
|
||||||
|
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
@ -85,8 +91,9 @@ jobs:
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DBUILD_SHARED_LIBS=OFF \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
|
-DCMAKE_CXX_STANDARD=20 \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
cmake --build build
|
cmake --build build
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
|
@ -101,14 +108,14 @@ jobs:
|
||||||
|
|
||||||
- name: Build qBittorrent
|
- name: Build qBittorrent
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS ${{ env.harden_flags }} -Werror" \
|
CXXFLAGS="$CXXFLAGS ${{ env.harden_flags }} -DQT_FORCE_ASSERTS -Werror" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
cmake \
|
cmake \
|
||||||
-B build \
|
-B build \
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" \
|
||||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||||
-DTESTING=ON \
|
-DTESTING=ON \
|
||||||
-DVERBOSE_CONFIGURE=ON \
|
-DVERBOSE_CONFIGURE=ON \
|
||||||
|
@ -134,16 +141,15 @@ jobs:
|
||||||
|
|
||||||
- name: Install AppImage
|
- name: Install AppImage
|
||||||
run: |
|
run: |
|
||||||
sudo apt install libfuse2
|
|
||||||
curl \
|
curl \
|
||||||
-L \
|
-L \
|
||||||
-Z \
|
-Z \
|
||||||
-O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-static-x86_64.AppImage \
|
-O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \
|
||||||
-O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-static-x86_64.AppImage \
|
-O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage \
|
||||||
-O https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage
|
-O https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage
|
||||||
chmod +x \
|
chmod +x \
|
||||||
linuxdeploy-static-x86_64.AppImage \
|
linuxdeploy-x86_64.AppImage \
|
||||||
linuxdeploy-plugin-qt-static-x86_64.AppImage \
|
linuxdeploy-plugin-qt-x86_64.AppImage \
|
||||||
linuxdeploy-plugin-appimage-x86_64.AppImage
|
linuxdeploy-plugin-appimage-x86_64.AppImage
|
||||||
|
|
||||||
- name: Prepare files for AppImage
|
- name: Prepare files for AppImage
|
||||||
|
@ -156,12 +162,13 @@ jobs:
|
||||||
|
|
||||||
- name: Package AppImage
|
- name: Package AppImage
|
||||||
run: |
|
run: |
|
||||||
./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --plugin qt
|
rm -f "${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/sqldrivers/libqsqlmimer.so"
|
||||||
|
./linuxdeploy-x86_64.AppImage --appdir qbittorrent --plugin qt
|
||||||
rm qbittorrent/apprun-hooks/*
|
rm qbittorrent/apprun-hooks/*
|
||||||
cp .github/workflows/helper/appimage/export_vars.sh qbittorrent/apprun-hooks/export_vars.sh
|
cp .github/workflows/helper/appimage/export_vars.sh qbittorrent/apprun-hooks/export_vars.sh
|
||||||
NO_APPSTREAM=1 \
|
NO_APPSTREAM=1 \
|
||||||
OUTPUT=upload/qbittorrent-CI_Ubuntu_x86_64.AppImage \
|
OUTPUT=upload/qbittorrent-CI_Ubuntu_x86_64.AppImage \
|
||||||
./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --output appimage
|
./linuxdeploy-x86_64.AppImage --appdir qbittorrent --output appimage
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|
17
.github/workflows/ci_webui.yaml
vendored
|
@ -2,8 +2,7 @@ name: CI - WebUI
|
||||||
|
|
||||||
on: [pull_request, push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
security-events: write
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
@ -13,6 +12,8 @@ jobs:
|
||||||
ci:
|
ci:
|
||||||
name: Check
|
name: Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
@ -21,6 +22,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup nodejs
|
- name: Setup nodejs
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
@ -28,7 +31,15 @@ jobs:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
|
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: npm install
|
run: |
|
||||||
|
npm install
|
||||||
|
npm ls
|
||||||
|
echo "::group::npm ls --all"
|
||||||
|
npm ls --all
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm test
|
||||||
|
|
||||||
- name: Lint code
|
- name: Lint code
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
|
|
33
.github/workflows/ci_windows.yaml
vendored
|
@ -2,8 +2,7 @@ name: CI - Windows
|
||||||
|
|
||||||
on: [pull_request, push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
actions: write
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
@ -13,11 +12,13 @@ jobs:
|
||||||
ci:
|
ci:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.10", "1.2.19"]
|
libt_version: ["2.0.11", "1.2.20"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
|
@ -27,6 +28,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup devcmd
|
- name: Setup devcmd
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
@ -64,6 +67,7 @@ jobs:
|
||||||
"set(VCPKG_BUILD_TYPE release)")
|
"set(VCPKG_BUILD_TYPE release)")
|
||||||
# clear buildtrees after each package installation to reduce disk space requirements
|
# clear buildtrees after each package installation to reduce disk space requirements
|
||||||
$packages = `
|
$packages = `
|
||||||
|
"boost-build:x64-windows-static-md-release",
|
||||||
"openssl:x64-windows-static-md-release",
|
"openssl:x64-windows-static-md-release",
|
||||||
"zlib:x64-windows-static-md-release"
|
"zlib:x64-windows-static-md-release"
|
||||||
${{ env.vcpkg_path }}/vcpkg.exe upgrade `
|
${{ env.vcpkg_path }}/vcpkg.exe upgrade `
|
||||||
|
@ -78,10 +82,10 @@ jobs:
|
||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "85"
|
BOOST_MINOR_VERSION: "86"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
$boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
$boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
$boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
$boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."
|
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."
|
||||||
|
@ -91,11 +95,19 @@ jobs:
|
||||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."
|
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."
|
||||||
}
|
}
|
||||||
move "${{ github.workspace }}/../boost_*" "${{ env.boost_path }}"
|
move "${{ github.workspace }}/../boost_*" "${{ env.boost_path }}"
|
||||||
|
cd "${{ env.boost_path }}"
|
||||||
|
#.\bootstrap.bat
|
||||||
|
${{ env.vcpkg_path }}/installed/x64-windows-static-md-release/tools/boost-build/b2.exe `
|
||||||
|
stage `
|
||||||
|
toolset=msvc `
|
||||||
|
--stagedir=.\ `
|
||||||
|
--with-headers
|
||||||
|
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
version: "6.7.3"
|
version: "6.9.1"
|
||||||
|
arch: win64_msvc2022_64
|
||||||
archives: qtbase qtsvg qttools
|
archives: qtbase qtsvg qttools
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
|
@ -109,15 +121,16 @@ jobs:
|
||||||
${{ env.libtorrent_path }}
|
${{ env.libtorrent_path }}
|
||||||
cd ${{ env.libtorrent_path }}
|
cd ${{ env.libtorrent_path }}
|
||||||
$env:CXXFLAGS+=" /guard:cf"
|
$env:CXXFLAGS+=" /guard:cf"
|
||||||
$env:LDFLAGS+=" /guard:cf"
|
$env:LDFLAGS+=" /GUARD:CF"
|
||||||
cmake `
|
cmake `
|
||||||
-B build `
|
-B build `
|
||||||
-G "Ninja" `
|
-G "Ninja" `
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||||
|
-DCMAKE_CXX_STANDARD=20 `
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
|
||||||
-DCMAKE_INSTALL_PREFIX="${{ env.libtorrent_path }}/install" `
|
-DCMAKE_INSTALL_PREFIX="${{ env.libtorrent_path }}/install" `
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${{ env.vcpkg_path }}/scripts/buildsystems/vcpkg.cmake" `
|
-DCMAKE_TOOLCHAIN_FILE="${{ env.vcpkg_path }}/scripts/buildsystems/vcpkg.cmake" `
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" `
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" `
|
||||||
-DBUILD_SHARED_LIBS=OFF `
|
-DBUILD_SHARED_LIBS=OFF `
|
||||||
-Ddeprecated-functions=OFF `
|
-Ddeprecated-functions=OFF `
|
||||||
-Dstatic_runtime=OFF `
|
-Dstatic_runtime=OFF `
|
||||||
|
@ -127,14 +140,14 @@ jobs:
|
||||||
|
|
||||||
- name: Build qBittorrent
|
- name: Build qBittorrent
|
||||||
run: |
|
run: |
|
||||||
$env:CXXFLAGS+=" /WX"
|
$env:CXXFLAGS+="/DQT_FORCE_ASSERTS /WX"
|
||||||
cmake `
|
cmake `
|
||||||
-B build `
|
-B build `
|
||||||
-G "Ninja" `
|
-G "Ninja" `
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${{ env.vcpkg_path }}/scripts/buildsystems/vcpkg.cmake" `
|
-DCMAKE_TOOLCHAIN_FILE="${{ env.vcpkg_path }}/scripts/buildsystems/vcpkg.cmake" `
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" `
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" `
|
||||||
-DLibtorrentRasterbar_DIR="${{ env.libtorrent_path }}/install/lib/cmake/LibtorrentRasterbar" `
|
-DLibtorrentRasterbar_DIR="${{ env.libtorrent_path }}/install/lib/cmake/LibtorrentRasterbar" `
|
||||||
-DMSVC_RUNTIME_DYNAMIC=ON `
|
-DMSVC_RUNTIME_DYNAMIC=ON `
|
||||||
-DTESTING=ON `
|
-DTESTING=ON `
|
||||||
|
|
18
.github/workflows/coverity-scan.yaml
vendored
|
@ -14,9 +14,9 @@ jobs:
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.10"]
|
libt_version: ["2.0.11"]
|
||||||
qbt_gui: ["GUI=ON"]
|
qbt_gui: ["GUI=ON"]
|
||||||
qt_version: ["6.5.2"]
|
qt_version: ["6.9.1"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
|
@ -26,6 +26,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
@ -37,10 +39,10 @@ jobs:
|
||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "85"
|
BOOST_MINOR_VERSION: "88"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
|
||||||
set +e
|
set +e
|
||||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||||
|
@ -50,6 +52,9 @@ jobs:
|
||||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
||||||
fi
|
fi
|
||||||
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
||||||
|
cd "${{ env.boost_path }}"
|
||||||
|
./bootstrap.sh
|
||||||
|
./b2 stage --stagedir=./ --with-headers
|
||||||
|
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
@ -71,7 +76,8 @@ jobs:
|
||||||
-B build \
|
-B build \
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DCMAKE_CXX_STANDARD=20 \
|
||||||
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
cmake --build build
|
cmake --build build
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
|
@ -95,7 +101,7 @@ jobs:
|
||||||
-B build \
|
-B build \
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}/lib/cmake" \
|
||||||
-DVERBOSE_CONFIGURE=ON \
|
-DVERBOSE_CONFIGURE=ON \
|
||||||
-D${{ matrix.qbt_gui }}
|
-D${{ matrix.qbt_gui }}
|
||||||
PATH="${{ env.coverity_path }}/bin:$PATH" \
|
PATH="${{ env.coverity_path }}/bin:$PATH" \
|
||||||
|
|
|
@ -16,3 +16,5 @@ ths = "ths"
|
||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
BA = "BA"
|
BA = "BA"
|
||||||
helo = "helo"
|
helo = "helo"
|
||||||
|
Pn = "Pn"
|
||||||
|
UIU = "UIU"
|
||||||
|
|
95
.github/workflows/helper/pre-commit/check_grid_items_order.py
vendored
Executable file
|
@ -0,0 +1,95 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# A pre-commit hook for checking items order in grid layouts
|
||||||
|
# Copyright (C) 2024 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.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ElementTree
|
||||||
|
from collections.abc import Callable, Sequence
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def traversePostOrder(root: ElementTree.Element, visitFunc: Callable[[ElementTree.Element], None]) -> None:
|
||||||
|
stack = [(root, False)]
|
||||||
|
|
||||||
|
while len(stack) > 0:
|
||||||
|
(element, visit) = stack.pop()
|
||||||
|
if visit:
|
||||||
|
visitFunc(element)
|
||||||
|
else:
|
||||||
|
stack.append((element, True))
|
||||||
|
stack.extend((child, False) for child in reversed(element))
|
||||||
|
|
||||||
|
|
||||||
|
def modifyElement(element: ElementTree.Element) -> None:
|
||||||
|
def getSortKey(e: ElementTree.Element) -> tuple[int, int]:
|
||||||
|
if e.tag == 'item':
|
||||||
|
return (int(e.attrib['row']), int(e.attrib['column']))
|
||||||
|
return (-1, -1) # don't care
|
||||||
|
|
||||||
|
if element.tag == 'layout' and element.attrib['class'] == 'QGridLayout' and len(element) > 0:
|
||||||
|
element[:] = sorted(element, key=getSortKey)
|
||||||
|
|
||||||
|
# workaround_2a: ElementTree will unescape `"` and we need to escape it back
|
||||||
|
if element.tag == 'string' and element.text is not None:
|
||||||
|
element.text = element.text.replace('"', '"')
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('filenames', nargs='*', help='Filenames to check')
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
for filename in args.filenames:
|
||||||
|
with open(filename, 'r+') as f:
|
||||||
|
orig = f.read()
|
||||||
|
root = ElementTree.fromstring(orig)
|
||||||
|
traversePostOrder(root, modifyElement)
|
||||||
|
ElementTree.indent(root, ' ')
|
||||||
|
|
||||||
|
# workaround_1: cannot use `xml_declaration=True` since it uses single quotes instead of Qt preferred double quotes
|
||||||
|
ret = f'<?xml version="1.0" encoding="UTF-8"?>\n{ElementTree.tostring(root, 'unicode')}\n'
|
||||||
|
|
||||||
|
# workaround_2b: ElementTree will turn `"` into `&quot;`, so revert it back
|
||||||
|
ret = ret.replace('&quot;', '"')
|
||||||
|
|
||||||
|
# workaround_3: Qt prefers no whitespaces in self-closing tags
|
||||||
|
ret = re.sub('<(.+) +/>', r'<\1/>', ret)
|
||||||
|
|
||||||
|
if ret != orig:
|
||||||
|
print(f'Tip: run this script to apply the fix: `python {__file__} {filename}`', file=sys.stderr)
|
||||||
|
|
||||||
|
f.seek(0)
|
||||||
|
f.write(ret)
|
||||||
|
f.truncate()
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
|
@ -26,9 +26,11 @@
|
||||||
# but you are not obligated to do so. If you do not wish to do so, delete this
|
# but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
# exception statement from your version.
|
# exception statement from your version.
|
||||||
|
|
||||||
from typing import Optional, Sequence
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Optional[Sequence[str]] = None) -> int:
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
|
@ -67,4 +69,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
exit(main())
|
sys.exit(main())
|
||||||
|
|
5
.github/workflows/stale_bot.yaml
vendored
|
@ -4,12 +4,13 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Mark and close stale PRs
|
- name: Mark and close stale PRs
|
||||||
uses: actions/stale@v9
|
uses: actions/stale@v9
|
||||||
|
|
4
.gitignore
vendored
|
@ -41,7 +41,3 @@ src/icons/skin/build-icons/icons/*.png
|
||||||
|
|
||||||
# CMake build directory
|
# CMake build directory
|
||||||
build/
|
build/
|
||||||
|
|
||||||
# Web UI tools
|
|
||||||
node_modules
|
|
||||||
package-lock.json
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
|
- id: check-grid-order
|
||||||
|
name: Check items order in grid layouts
|
||||||
|
entry: .github/workflows/helper/pre-commit/check_grid_items_order.py
|
||||||
|
language: script
|
||||||
|
files: \.ui$
|
||||||
|
|
||||||
- id: check-translation-tag
|
- id: check-translation-tag
|
||||||
name: Check newline characters in <translation> tag
|
name: Check newline characters in <translation> tag
|
||||||
entry: .github/workflows/helper/pre-commit/check_translation_tag.py
|
entry: .github/workflows/helper/pre-commit/check_translation_tag.py
|
||||||
|
@ -13,7 +19,7 @@ repos:
|
||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
||||||
rev: v4.5.0
|
rev: v5.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-json
|
- id: check-json
|
||||||
name: Check JSON files
|
name: Check JSON files
|
||||||
|
@ -63,19 +69,17 @@ repos:
|
||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/codespell-project/codespell.git
|
- repo: https://github.com/codespell-project/codespell.git
|
||||||
rev: v2.2.6
|
rev: v2.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
name: Check spelling (codespell)
|
name: Check spelling (codespell)
|
||||||
args: ["--ignore-words-list", "additionals,curren,fo,ist,ket,searchin,superseeding,te,ths"]
|
args: ["--ignore-words-list", "additionals,categor,curren,fo,indexIn,ist,ket,notin,searchin,sectionin,superseeding,te,ths"]
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
.*\.desktop |
|
.*\.desktop |
|
||||||
.*\.qrc |
|
.*\.qrc |
|
||||||
build-aux/.* |
|
|
||||||
Changelog |
|
Changelog |
|
||||||
dist/windows/installer-translations/.* |
|
dist/windows/installer-translations/.* |
|
||||||
m4/.* |
|
|
||||||
src/base/3rdparty/.* |
|
src/base/3rdparty/.* |
|
||||||
src/searchengine/nova3/socks.py |
|
src/searchengine/nova3/socks.py |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.*
|
||||||
|
@ -84,7 +88,7 @@ repos:
|
||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/crate-ci/typos.git
|
- repo: https://github.com/crate-ci/typos.git
|
||||||
rev: v1.16.18
|
rev: v1.32.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: typos
|
- id: typos
|
||||||
name: Check spelling (typos)
|
name: Check spelling (typos)
|
||||||
|
@ -95,11 +99,8 @@ repos:
|
||||||
.*\.desktop |
|
.*\.desktop |
|
||||||
.*\.qrc |
|
.*\.qrc |
|
||||||
\.pre-commit-config\.yaml |
|
\.pre-commit-config\.yaml |
|
||||||
build-aux/.* |
|
|
||||||
Changelog |
|
Changelog |
|
||||||
configure.* |
|
|
||||||
dist/windows/installer-translations/.* |
|
dist/windows/installer-translations/.* |
|
||||||
m4/.* |
|
|
||||||
src/base/3rdparty/.* |
|
src/base/3rdparty/.* |
|
||||||
src/searchengine/nova3/socks.py |
|
src/searchengine/nova3/socks.py |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
|
|
||||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_v50x]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_master]
|
||||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||||
source_file = src/lang/qbittorrent_en.ts
|
source_file = src/lang/qbittorrent_en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
@ -9,7 +9,7 @@ type = QT
|
||||||
minimum_perc = 23
|
minimum_perc = 23
|
||||||
lang_map = pt: pt_PT, zh: zh_CN
|
lang_map = pt: pt_PT, zh: zh_CN
|
||||||
|
|
||||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_v50x]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui]
|
||||||
file_filter = src/webui/www/translations/webui_<lang>.ts
|
file_filter = src/webui/www/translations/webui_<lang>.ts
|
||||||
source_file = src/webui/www/translations/webui_en.ts
|
source_file = src/webui/www/translations/webui_en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
|
|
@ -8,7 +8,7 @@ project(qBittorrent
|
||||||
|
|
||||||
# version requirements - older versions may work, but you are on your own
|
# version requirements - older versions may work, but you are on your own
|
||||||
set(minBoostVersion 1.76)
|
set(minBoostVersion 1.76)
|
||||||
set(minQt6Version 6.5.0)
|
set(minQt6Version 6.6.0)
|
||||||
set(minOpenSSLVersion 3.0.2)
|
set(minOpenSSLVersion 3.0.2)
|
||||||
set(minLibtorrent1Version 1.2.19)
|
set(minLibtorrent1Version 1.2.19)
|
||||||
set(minLibtorrentVersion 2.0.10)
|
set(minLibtorrentVersion 2.0.10)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Version 2, June 1991
|
Version 2, June 1991
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
<https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
@ -304,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
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.,
|
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
@ -329,8 +328,8 @@ necessary. Here is a sample; alter the names:
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
<signature of Moe Ghoul>, 1 April 1989
|
||||||
Ty Coon, President of Vice
|
Moe Ghoul, President of Vice
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
This General Public License does not permit incorporating your program into
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
|
50
Changelog
|
@ -1,16 +1,4 @@
|
||||||
Sun Nov 17th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.2
|
Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.0
|
||||||
- BUGFIX: Remove trackers from previous category when moved to new one (glassez)
|
|
||||||
- BUGFIX: Fix `.torrent` file could not be deleted when torrent is canceled (glassez)
|
|
||||||
- BUGFIX: Reset tracker entries when pausing the session (glassez)
|
|
||||||
- BUGFIX: Check real palette darkness to detect "dark theme" (glassez)
|
|
||||||
- BUGFIX: Correctly handle "torrent finished" events (glassez)
|
|
||||||
- BUGFIX: Preserve initial torrent progress while checking resume data (glassez)
|
|
||||||
- BUGFIX: Avoid reapplying Mark-of-the-Web when it already exists (Chocobo1)
|
|
||||||
- BUGFIX: Don't apply Mark-of-the-Web on existing files (Chocobo1)
|
|
||||||
- WEBUI: Add color scheme switcher (sledgehammer999)
|
|
||||||
- SEARCH: Correctly delete the moved search tab (glassez)
|
|
||||||
- WINDOWS: Correctly save and restore Qt style setting (glassez)
|
|
||||||
- WINDOWS: NSIS: update Luxembourgish, Simplified Chinese and Traditional Chinese translations (Ikko Eltociear Ashimine, 3gf8jv4dv)
|
|
||||||
|
|
||||||
Mon Oct 28th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.1
|
Mon Oct 28th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.1
|
||||||
- FEATURE: Add "Simple pread/pwrite" disk IO type (Hanabishi)
|
- FEATURE: Add "Simple pread/pwrite" disk IO type (Hanabishi)
|
||||||
|
@ -118,6 +106,42 @@ Sun Sep 29th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.0
|
||||||
- OTHER: Minimum supported versions: Qt: 6.5, Boost: 1.76, OpenSSL: 3.0.2
|
- OTHER: Minimum supported versions: Qt: 6.5, Boost: 1.76, OpenSSL: 3.0.2
|
||||||
- OTHER: Switch to C++20
|
- OTHER: Switch to C++20
|
||||||
|
|
||||||
|
Mon Sep 16th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.7
|
||||||
|
- BUGFIX: The updater will launch the link to the build variant you're currently using (sledgehammer999)
|
||||||
|
- BUGFIX: Focus on Download button if torrent link retrieved from the clipboard (glassez)
|
||||||
|
- WEBUI: RSS: The list of feeds wouldn't load for Apply Rule (glassez)
|
||||||
|
|
||||||
|
Sun Aug 18th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.6
|
||||||
|
- BUGFIX: Fix handling of tags containing '&' character (glassez)
|
||||||
|
- BUGFIX: Show scroll bar in Torrent Tags dialog (glassez)
|
||||||
|
- BUGFIX: Apply bulk changes to correct content widget items (glassez)
|
||||||
|
- BUGFIX: Hide zero status filters when torrents are removed (glassez)
|
||||||
|
- BUGFIX: Fix `Incomplete Save Path` cannot be changed for torrents without metadata (glassez)
|
||||||
|
- WEBUI: Correctly apply changed "save path" of RSS rules (glassez)
|
||||||
|
- WEBUI: Clear tracker list on full update (skomerko)
|
||||||
|
- OTHER: Update User-Agent string for internal downloader and search engines (cayenne17)
|
||||||
|
|
||||||
|
Sun May 26th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.5
|
||||||
|
- BUGFIX: Prevent app from being closed when disabling system tray icon (glassez)
|
||||||
|
- BUGFIX: Fix <kbd>Enter</kbd> key behavior in Add new torrent dialog (glassez)
|
||||||
|
- BUGFIX: Prevent invalid status filter index from being used (glassez)
|
||||||
|
- BUGFIX: Add extra offset for dialog frame (glassez)
|
||||||
|
- BUGFIX: Don't overwrite stored layout of main window with incorrect one (glassez)
|
||||||
|
- BUGFIX: Don't forget to resume "missing files" torrent when rechecking (glassez)
|
||||||
|
- WEBUI: Restore ability to use server-side translation by custom WebUI (glassez)
|
||||||
|
- WEBUI: Fix wrong peer number (Chocobo1)
|
||||||
|
- LINUX: Improve AppStream metadata (Chocobo1)
|
||||||
|
|
||||||
|
Sun Mar 24th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.4
|
||||||
|
- BUGFIX: Correctly adjust "Add New torrent" dialog position in all the cases (glassez)
|
||||||
|
- BUGFIX: Change "metadata received" stop condition behavior (glassez)
|
||||||
|
- BUGFIX: Add a small delay before processing the key input of search boxes (Chocobo1)
|
||||||
|
- BUGFIX: Ensure the profile path is pointing to a directory (Chocobo1)
|
||||||
|
- RSS: Use better icons for RSS articles (glassez)
|
||||||
|
- WINDOWS: NSIS: Update French, Hungarian translations (MarcDrieu, foxi69)
|
||||||
|
- LINUX: Fix sorting when ICU isn't used (Chocobo1)
|
||||||
|
- LINUX: Fix invisible tray icon on Plasma 6 (tehcneko)
|
||||||
|
|
||||||
Mon Jan 15th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.3
|
Mon Jan 15th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.3
|
||||||
- BUGFIX: Correctly update number of filtered items (glassez)
|
- BUGFIX: Correctly update number of filtered items (glassez)
|
||||||
- BUGFIX: Don't forget to store Stop condition value (glassez)
|
- BUGFIX: Don't forget to store Stop condition value (glassez)
|
||||||
|
|
4
INSTALL
|
@ -11,14 +11,14 @@ qBittorrent - A BitTorrent client in C++ / Qt
|
||||||
|
|
||||||
- OpenSSL >= 3.0.2
|
- OpenSSL >= 3.0.2
|
||||||
|
|
||||||
- Qt 6.5.0 - 6.x
|
- Qt 6.6.0 - 6.x
|
||||||
|
|
||||||
- zlib >= 1.2.11
|
- zlib >= 1.2.11
|
||||||
|
|
||||||
- CMake >= 3.16
|
- CMake >= 3.16
|
||||||
* Compile-time only
|
* Compile-time only
|
||||||
|
|
||||||
- Python >= 3.7.0
|
- Python >= 3.9.0
|
||||||
* Optional, run-time only
|
* Optional, run-time only
|
||||||
* Used by the bundled search engine
|
* Used by the bundled search engine
|
||||||
|
|
||||||
|
|
18
SECURITY.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
qBittorrent takes the security of our software seriously, including all source code repositories managed through our GitHub organisation.
|
||||||
|
If you believe you have found a security vulnerability in qBittorrent, please report it to us as described below.
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
Please do not report security vulnerabilities through public GitHub issues. Instead, please use GitHubs private vulnerability reporting functionality associated to this repository. Additionally, you may email us with all security-related inquiries and notifications at `security@qbittorrent.org`.
|
||||||
|
|
||||||
|
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||||
|
1. Type of issue
|
||||||
|
2. Step-by-step instructions to reproduce the issue
|
||||||
|
3. Proof-of-concept or exploit code (if possible)
|
||||||
|
4. Potential impact of the issue, including how an attacker might exploit the issue
|
||||||
|
|
||||||
|
This information will help us triage your report more quickly. Any and all CVEs will be requested and issued through GitHubs private vulnerability reporting functionality, which will be published alongside the disclosure.
|
||||||
|
|
||||||
|
This security policy only applies to the most recent stable branch of qBittorrent. Flaws in old versions that are not present in the current stable branch will not be fixed.
|
39
WebAPI_Changelog.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# WebAPI Changelog
|
||||||
|
|
||||||
|
## 2.11.9
|
||||||
|
|
||||||
|
* [#21015](https://github.com/qbittorrent/qBittorrent/pull/21015)
|
||||||
|
* Add `torrents/fetchMetadata` endpoint for retrieving torrent metadata associated with a URL
|
||||||
|
* Add `torrents/parseMetadata` endpoint for retrieving torrent metadata associated with a .torrent file
|
||||||
|
* Add `torrents/saveMetadata` endpoint for saving retrieved torrent metadata to a .torrent file
|
||||||
|
* `torrents/add` allows adding a torrent with metadata previously retrieved via `torrents/fetchMetadata` or `torrents/parseMetadata`
|
||||||
|
* `torrents/add` allows specifying a torrent's file priorities
|
||||||
|
* [#22698](https://github.com/qbittorrent/qBittorrent/pull/22698)
|
||||||
|
* `torrents/addTrackers` and `torrents/removeTrackers` now accept `hash=all` and adds/removes the tracker to/from *all* torrents
|
||||||
|
* For compatibility, `torrents/removeTrackers` still accepts `hash=*` internally we transform it into `all`
|
||||||
|
* Allow passing a pipe (`|`) separated list of hashes in `hash` for `torrents/addTrackers` and `torrents/removeTrackers`
|
||||||
|
|
||||||
|
## 2.11.8
|
||||||
|
|
||||||
|
* [#21349](https://github.com/qbittorrent/qBittorrent/pull/21349)
|
||||||
|
* Handle sending `204 No Content` status code when response contains no data
|
||||||
|
* Some endpoints still return `200 OK` to ensure smooth transition
|
||||||
|
* [#22750](https://github.com/qbittorrent/qBittorrent/pull/22750)
|
||||||
|
* `torrents/info` allows an optional parameter `includeFiles` that defaults to `false`
|
||||||
|
* Each torrent will contain a new key `files` which will list all files similar to the `torrents/files` endpoint
|
||||||
|
* [#22813](https://github.com/qbittorrent/qBittorrent/pull/22813)
|
||||||
|
* `app/getDirectoryContent` allows an optional parameter `withMetadata` to send file metadata
|
||||||
|
* Fields are `name`, `type`, `size`, `creation_date`, `last_access_date`, `last_modification_date`
|
||||||
|
* See PR for TypeScript types
|
||||||
|
|
||||||
|
## 2.11.7
|
||||||
|
|
||||||
|
* [#22166](https://github.com/qbittorrent/qBittorrent/pull/22166)
|
||||||
|
* `sync/maindata` returns 3 new torrent fields: `has_tracker_warning`, `has_tracker_error`, `has_other_announce_error`
|
||||||
|
|
||||||
|
## 2.11.6
|
||||||
|
|
||||||
|
* [#22460](https://github.com/qbittorrent/qBittorrent/pull/22460)
|
||||||
|
* `app/setPreferences` allows only one of `max_ratio_enabled`, `max_ratio` to be present
|
||||||
|
* `app/setPreferences` allows only one of `max_seeding_time_enabled`, `max_seeding_time` to be present
|
||||||
|
* `app/setPreferences` allows only one of `max_inactive_seeding_time_enabled`, `max_inactive_seeding_time` to be present
|
|
@ -47,6 +47,9 @@ find_package(Boost ${minBoostVersion} REQUIRED)
|
||||||
find_package(OpenSSL ${minOpenSSLVersion} REQUIRED)
|
find_package(OpenSSL ${minOpenSSLVersion} REQUIRED)
|
||||||
find_package(ZLIB ${minZlibVersion} REQUIRED)
|
find_package(ZLIB ${minZlibVersion} REQUIRED)
|
||||||
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
|
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
|
||||||
|
if (Qt6_FOUND AND (Qt6_VERSION VERSION_GREATER_EQUAL 6.10))
|
||||||
|
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS CorePrivate)
|
||||||
|
endif()
|
||||||
if (DBUS)
|
if (DBUS)
|
||||||
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus)
|
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus)
|
||||||
set_package_properties(Qt6DBus PROPERTIES
|
set_package_properties(Qt6DBus PROPERTIES
|
||||||
|
|
|
@ -20,7 +20,7 @@ target_compile_features(qbt_common_cfg INTERFACE
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(qbt_common_cfg INTERFACE
|
target_compile_definitions(qbt_common_cfg INTERFACE
|
||||||
QT_DISABLE_DEPRECATED_UP_TO=0x060500
|
QT_DISABLE_DEPRECATED_UP_TO=0x060600
|
||||||
QT_NO_CAST_FROM_ASCII
|
QT_NO_CAST_FROM_ASCII
|
||||||
QT_NO_CAST_TO_ASCII
|
QT_NO_CAST_TO_ASCII
|
||||||
QT_NO_CAST_FROM_BYTEARRAY
|
QT_NO_CAST_FROM_BYTEARRAY
|
||||||
|
@ -89,7 +89,7 @@ if (MSVC)
|
||||||
/Zc:__cplusplus
|
/Zc:__cplusplus
|
||||||
)
|
)
|
||||||
target_link_options(qbt_common_cfg INTERFACE
|
target_link_options(qbt_common_cfg INTERFACE
|
||||||
/guard:cf
|
/GUARD:CF
|
||||||
$<$<NOT:$<CONFIG:Debug>>:/OPT:REF /OPT:ICF>
|
$<$<NOT:$<CONFIG:Debug>>:/OPT:REF /OPT:ICF>
|
||||||
# suppress linking warning due to /INCREMENTAL and /OPT:ICF being both ON
|
# suppress linking warning due to /INCREMENTAL and /OPT:ICF being both ON
|
||||||
$<$<CONFIG:RelWithDebInfo>:/INCREMENTAL:NO>
|
$<$<CONFIG:RelWithDebInfo>:/INCREMENTAL:NO>
|
||||||
|
|
4
dist/mac/Info.plist
vendored
|
@ -55,7 +55,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>5.0.2</string>
|
<string>5.2.0</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
<key>NSAppleScriptEnabled</key>
|
<key>NSAppleScriptEnabled</key>
|
||||||
<string>YES</string>
|
<string>YES</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2006-2024 The qBittorrent project</string>
|
<string>Copyright © 2006-2025 The qBittorrent project</string>
|
||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
|
BIN
dist/mac/qBitTorrentDocument.icns
vendored
BIN
dist/mac/qbittorrent_mac.icns
vendored
9
dist/unix/CMakeLists.txt
vendored
|
@ -34,12 +34,12 @@ endforeach()
|
||||||
|
|
||||||
if (GUI)
|
if (GUI)
|
||||||
install(FILES org.qbittorrent.qBittorrent.desktop
|
install(FILES org.qbittorrent.qBittorrent.desktop
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications
|
||||||
COMPONENT data
|
COMPONENT data
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES org.qbittorrent.qBittorrent.metainfo.xml
|
install(FILES org.qbittorrent.qBittorrent.metainfo.xml
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo/
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo
|
||||||
COMPONENT data
|
COMPONENT data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,4 +55,9 @@ if (GUI)
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/status
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/status
|
||||||
COMPONENT data
|
COMPONENT data
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
install(FILES org.qbittorrent.qBittorrent-nox.metainfo.xml
|
||||||
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo
|
||||||
|
COMPONENT data
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
BIN
dist/unix/menuicons/16x16/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 747 B |
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 747 B |
BIN
dist/unix/menuicons/192x192/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
dist/unix/menuicons/24x24/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
dist/unix/menuicons/32x32/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
dist/unix/menuicons/36x36/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
dist/unix/menuicons/64x64/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.6 KiB |
BIN
dist/unix/menuicons/72x72/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
dist/unix/menuicons/96x96/apps/qbittorrent.png
vendored
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
57
dist/unix/org.qbittorrent.qBittorrent-nox.metainfo.xml
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Copyright 2014 sledgehammer999 <sledgehammer999@qbittorrent.org> -->
|
||||||
|
<component type="console-application">
|
||||||
|
<id>org.qbittorrent.qBittorrent-nox</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0-or-later and OpenSSL</project_license>
|
||||||
|
<name>qBittorrent-nox</name>
|
||||||
|
<summary>An open-source Bittorrent client (nox version)</summary>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
The qBittorrent project aims to provide an open-source software alternative to µTorrent.
|
||||||
|
Additionally, qBittorrent runs and provides the same features on all major platforms (FreeBSD, Linux, macOS, OS/2, Windows).
|
||||||
|
qBittorrent is based on the Qt toolkit and libtorrent-rasterbar library.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Polished µTorrent-like User Interface</li>
|
||||||
|
<li>Well-integrated and extensible Search Engine</li>
|
||||||
|
<li>RSS feed support with advanced download filters (incl. regex)</li>
|
||||||
|
<li>Many Bittorrent extensions supported</li>
|
||||||
|
<li>Remote control through Web user interface, written with AJAX</li>
|
||||||
|
<li>Sequential downloading (Download in order)</li>
|
||||||
|
<li>Advanced control over torrents, trackers and peers</li>
|
||||||
|
<li>Bandwidth scheduler</li>
|
||||||
|
<li>Torrent creation tool</li>
|
||||||
|
<li>IP Filtering (eMule & PeerGuardian format compatible)</li>
|
||||||
|
<li>IPv6 compliant</li>
|
||||||
|
<li>UPnP / NAT-PMP port forwarding support</li>
|
||||||
|
<li>Available on all platforms: Windows, Linux, macOS, FreeBSD, OS/2</li>
|
||||||
|
<li>Available in ~70 languages</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
<provides>
|
||||||
|
<binary>qbittorrent-nox</binary>
|
||||||
|
</provides>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<caption>Running headless (nox) version</caption>
|
||||||
|
<image>https://raw.githubusercontent.com/qbittorrent/qBittorrent-website/43fcf4550f567c38fb879b984922b659e90982cc/src/img/screenshots/linux/5.webp</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
|
||||||
|
<developer id="org.qbittorrent">
|
||||||
|
<name>The qBittorrent Project</name>
|
||||||
|
</developer>
|
||||||
|
<url type="homepage">https://www.qbittorrent.org/</url>
|
||||||
|
<url type="bugtracker">https://bugs.qbittorrent.org/</url>
|
||||||
|
<url type="faq">https://wiki.qbittorrent.org/Frequently-Asked-Questions</url>
|
||||||
|
<url type="help">https://forum.qbittorrent.org/</url>
|
||||||
|
<url type="donation">https://www.qbittorrent.org/donate</url>
|
||||||
|
<url type="translate">https://wiki.qbittorrent.org/How-to-translate-qBittorrent</url>
|
||||||
|
<url type="vcs-browser">https://github.com/qbittorrent/qBittorrent</url>
|
||||||
|
<url type="contribute">https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md</url>
|
||||||
|
<content_rating type="oars-1.1"/>
|
||||||
|
<releases>
|
||||||
|
<release version="5.2.0~alpha1" date="2025-02-11"/>
|
||||||
|
</releases>
|
||||||
|
</component>
|
|
@ -105,7 +105,7 @@ GenericName[ka]=BitTorrent კლიენტი
|
||||||
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
||||||
Name[ka]=qBittorrent
|
Name[ka]=qBittorrent
|
||||||
GenericName[ko]=BitTorrent 클라이언트
|
GenericName[ko]=BitTorrent 클라이언트
|
||||||
Comment[ko]=BitTorrent를 통한 파일 다운로드 및 공유
|
Comment[ko]=BitTorrent를 통해 파일 다운로드 및 공유
|
||||||
Name[ko]=qBittorrent
|
Name[ko]=qBittorrent
|
||||||
GenericName[lt]=BitTorrent klientas
|
GenericName[lt]=BitTorrent klientas
|
||||||
Comment[lt]=Atsisiųskite bei dalinkitės failais BitTorrent tinkle
|
Comment[lt]=Atsisiųskite bei dalinkitės failais BitTorrent tinkle
|
||||||
|
@ -161,7 +161,7 @@ Name[ta]=qBittorrent
|
||||||
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
|
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
|
||||||
Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్స్ దిగుమతి చేసుకోండి , పంచుకోండి
|
Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్స్ దిగుమతి చేసుకోండి , పంచుకోండి
|
||||||
Name[te]=qBittorrent
|
Name[te]=qBittorrent
|
||||||
GenericName[th]=โปรแกรมบิตทอร์เรนต์
|
GenericName[th]=ไคลเอนต์บิททอร์เรนต์
|
||||||
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่านบิตทอร์เรนต์
|
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่านบิตทอร์เรนต์
|
||||||
Name[th]=qBittorrent
|
Name[th]=qBittorrent
|
||||||
GenericName[tr]=BitTorrent istemcisi
|
GenericName[tr]=BitTorrent istemcisi
|
||||||
|
|
|
@ -62,6 +62,6 @@
|
||||||
<url type="contribute">https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md</url>
|
<url type="contribute">https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md</url>
|
||||||
<content_rating type="oars-1.1"/>
|
<content_rating type="oars-1.1"/>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="5.0.2" date="2024-11-17"/>
|
<release version="5.2.0~alpha1" date="2025-02-11"/>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|
7
dist/windows/config.nsh
vendored
|
@ -14,7 +14,7 @@
|
||||||
; 4.5.1.3 -> good
|
; 4.5.1.3 -> good
|
||||||
; 4.5.1.3.2 -> bad
|
; 4.5.1.3.2 -> bad
|
||||||
; 4.5.0beta -> bad
|
; 4.5.0beta -> bad
|
||||||
!define /ifndef QBT_VERSION "5.0.2"
|
!define /ifndef QBT_VERSION "5.2.0"
|
||||||
|
|
||||||
; Option that controls the installer's window name
|
; Option that controls the installer's window name
|
||||||
; If set, its value will be used like this:
|
; If set, its value will be used like this:
|
||||||
|
@ -86,7 +86,7 @@ OutFile "qbittorrent_${QBT_INSTALLER_FILENAME}_setup.exe"
|
||||||
;Installer Version Information
|
;Installer Version Information
|
||||||
VIAddVersionKey "ProductName" "qBittorrent"
|
VIAddVersionKey "ProductName" "qBittorrent"
|
||||||
VIAddVersionKey "CompanyName" "The qBittorrent project"
|
VIAddVersionKey "CompanyName" "The qBittorrent project"
|
||||||
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2024 The qBittorrent project"
|
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2025 The qBittorrent project"
|
||||||
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
|
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
|
||||||
VIAddVersionKey "FileVersion" "${QBT_VERSION}"
|
VIAddVersionKey "FileVersion" "${QBT_VERSION}"
|
||||||
|
|
||||||
|
@ -111,7 +111,8 @@ RequestExecutionLevel user
|
||||||
!define MUI_HEADERIMAGE
|
!define MUI_HEADERIMAGE
|
||||||
!define MUI_COMPONENTSPAGE_NODESC
|
!define MUI_COMPONENTSPAGE_NODESC
|
||||||
;!define MUI_ICON "qbittorrent.ico"
|
;!define MUI_ICON "qbittorrent.ico"
|
||||||
!define MUI_LICENSEPAGE_CHECKBOX
|
!define MUI_LICENSEPAGE_BUTTON $(^NextBtn)
|
||||||
|
!define MUI_LICENSEPAGE_TEXT_BOTTOM "$_CLICK"
|
||||||
!define MUI_LANGDLL_ALLLANGUAGES
|
!define MUI_LANGDLL_ALLLANGUAGES
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
|
|
@ -29,7 +29,7 @@ LangString launch_qbt ${LANG_ITALIAN} "Esegui qBittorrent."
|
||||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||||
LangString inst_requires_64bit ${LANG_ITALIAN} "Questo installer funziona solo con versioni di Windows a 64bit."
|
LangString inst_requires_64bit ${LANG_ITALIAN} "Questo installer funziona solo con versioni di Windows a 64bit."
|
||||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
|
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
|
||||||
LangString inst_requires_win10 ${LANG_ITALIAN} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
|
LangString inst_requires_win10 ${LANG_ITALIAN} "Questo installer richiede almeno Windows 10 (1809) / Windows Server 2019."
|
||||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||||
LangString inst_uninstall_link_description ${LANG_ITALIAN} "Disinstalla qBittorrent"
|
LangString inst_uninstall_link_description ${LANG_ITALIAN} "Disinstalla qBittorrent"
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ LangString inst_desktop ${LANG_PORTUGUESE} "Criar atalho no ambiente de trabalho
|
||||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||||
LangString inst_startmenu ${LANG_PORTUGUESE} "Criar atalho no menu Iniciar"
|
LangString inst_startmenu ${LANG_PORTUGUESE} "Criar atalho no menu Iniciar"
|
||||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||||
LangString inst_startup ${LANG_PORTUGUESE} "Iniciar o qBittorrent na inicialização do Windows"
|
LangString inst_startup ${LANG_PORTUGUESE} "Iniciar o qBittorrent no arranque do Windows"
|
||||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||||
LangString inst_torrent ${LANG_PORTUGUESE} "Abrir ficheiros .torrent com o qBittorrent"
|
LangString inst_torrent ${LANG_PORTUGUESE} "Abrir ficheiros .torrent com o qBittorrent"
|
||||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||||
|
@ -29,7 +29,7 @@ LangString launch_qbt ${LANG_PORTUGUESE} "Iniciar qBittorrent."
|
||||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||||
LangString inst_requires_64bit ${LANG_PORTUGUESE} "Este instalador funciona apenas em versões Windows de 64 bits."
|
LangString inst_requires_64bit ${LANG_PORTUGUESE} "Este instalador funciona apenas em versões Windows de 64 bits."
|
||||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
|
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
|
||||||
LangString inst_requires_win10 ${LANG_PORTUGUESE} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
|
LangString inst_requires_win10 ${LANG_PORTUGUESE} "Este instalador requer, pelo menos, o Windows 10 (1809) / Windows Server 2019."
|
||||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||||
LangString inst_uninstall_link_description ${LANG_PORTUGUESE} "Desinstalar qBittorrent"
|
LangString inst_uninstall_link_description ${LANG_PORTUGUESE} "Desinstalar qBittorrent"
|
||||||
|
|
||||||
|
|
12
dist/windows/installer-translations/swedish.nsh
vendored
|
@ -7,21 +7,21 @@ LangString inst_desktop ${LANG_SWEDISH} "Skapa skrivbordsgenväg"
|
||||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||||
LangString inst_startmenu ${LANG_SWEDISH} "Skapa startmenygenväg"
|
LangString inst_startmenu ${LANG_SWEDISH} "Skapa startmenygenväg"
|
||||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||||
LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows start"
|
LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows-uppstart"
|
||||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||||
LangString inst_torrent ${LANG_SWEDISH} "Öppna .torrent-filer med qBittorrent"
|
LangString inst_torrent ${LANG_SWEDISH} "Öppna .torrent-filer med qBittorrent"
|
||||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||||
LangString inst_magnet ${LANG_SWEDISH} "Öppna magnetlänkar med qBittorrent"
|
LangString inst_magnet ${LANG_SWEDISH} "Öppna magnetlänkar med qBittorrent"
|
||||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||||
LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggregel"
|
LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggsregel"
|
||||||
;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_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||||
LangString inst_pathlimit ${LANG_SWEDISH} "Inaktivera gränsen för Windows-sökvägslängd (260 tecken MAX_PATH-begränsning, kräver Windows 10 1607 eller senare)"
|
LangString inst_pathlimit ${LANG_SWEDISH} "Inaktivera gränsen för Windows-sökvägslängd (260 tecken MAX_PATH-begränsning, kräver Windows 10 1607 eller senare)"
|
||||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||||
LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggregel"
|
LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggsregel"
|
||||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||||
LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du installerar."
|
LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Stäng programmet innan du installerar."
|
||||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||||
LangString inst_uninstall_question ${LANG_SWEDISH} "Nuvarande version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta."
|
LangString inst_uninstall_question ${LANG_SWEDISH} "Aktuell version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta."
|
||||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||||
LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version."
|
LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version."
|
||||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||||
|
@ -53,7 +53,7 @@ LangString remove_firewallinfo ${LANG_SWEDISH} "Tar bort Windows-brandväggsrege
|
||||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||||
LangString remove_cache ${LANG_SWEDISH} "Ta bort torrenter och cachade data"
|
LangString remove_cache ${LANG_SWEDISH} "Ta bort torrenter och cachade data"
|
||||||
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||||
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du avinstallerar."
|
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Stäng programmet innan du avinstallerar."
|
||||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||||
LangString uninst_tor_warn ${LANG_SWEDISH} "Tar inte bort .torrent-association. Den är associerad med:"
|
LangString uninst_tor_warn ${LANG_SWEDISH} "Tar inte bort .torrent-association. Den är associerad med:"
|
||||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||||
|
|
|
@ -1,41 +1,44 @@
|
||||||
.\" Automatically generated by Pandoc 3.1.7
|
.\" Automatically generated by Pandoc 3.7.0.2
|
||||||
.\"
|
.\"
|
||||||
.TH "QBITTORRENT-NOX" "1" "January 16th 2010" "Command line Bittorrent client written in C++ / Qt" ""
|
.TH "QBITTORRENT\-NOX" "1" "January 16th 2010" "Command line Bittorrent client written in C++ / Qt"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
qBittorrent-nox - a command line Bittorrent client written in C++ / Qt
|
qBittorrent\-nox \- a command line Bittorrent client written in C++ / Qt
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\f[B]qbittorrent-nox\f[R]
|
\f[B]qbittorrent\-nox\f[R]
|
||||||
\f[CR][--d|--daemon] [--webui-port=x] [TORRENT_FILE | URL]...\f[R]
|
\f[CR][\-\-d|\-\-daemon] [\-\-webui\-port=x] [TORRENT_FILE | URL]...\f[R]
|
||||||
.PP
|
.PP
|
||||||
\f[B]qbittorrent-nox\f[R] \f[CR]--help\f[R]
|
\f[B]qbittorrent\-nox\f[R] \f[CR]\-\-help\f[R]
|
||||||
.PP
|
.PP
|
||||||
\f[B]qbittorrent-nox\f[R] \f[CR]--version\f[R]
|
\f[B]qbittorrent\-nox\f[R] \f[CR]\-\-version\f[R]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
\f[B]qBittorrent-nox\f[R] is an advanced command-line Bittorrent client
|
\f[B]qBittorrent\-nox\f[R] is an advanced command\-line Bittorrent
|
||||||
written in C++ / Qt using the \f[B]libtorrent-rasterbar\f[R] library by
|
client written in C++ / Qt using the \f[B]libtorrent\-rasterbar\f[R]
|
||||||
Arvid Norberg.
|
library by Arvid Norberg.
|
||||||
qBittorrent-nox aims to be a good alternative to other command line
|
qBittorrent\-nox aims to be a good alternative to other command line
|
||||||
bittorrent clients and provides features similar to popular graphical
|
bittorrent clients and provides features similar to popular graphical
|
||||||
clients.
|
clients.
|
||||||
.PP
|
.PP
|
||||||
qBittorrent-nox is fast, stable, light and it supports unicode.
|
qBittorrent\-nox is fast, stable, light and it supports unicode.
|
||||||
It also comes with UPnP port forwarding / NAT-PMP, encryption (Vuze
|
It also comes with UPnP port forwarding / NAT\-PMP, encryption (Vuze
|
||||||
compatible), FAST extension (mainline) and PeX support (utorrent
|
compatible), FAST extension (mainline) and PeX support (utorrent
|
||||||
compatible).
|
compatible).
|
||||||
.PP
|
.PP
|
||||||
qBittorrent-nox is meant to be controlled via its feature-rich Web UI
|
qBittorrent\-nox is meant to be controlled via its feature\-rich Web UI
|
||||||
which is accessible as a default on http://localhost:8080.
|
which is accessible as a default on http://localhost:8080.
|
||||||
The Web UI access is secured and the default account user name is
|
The Web UI access is secured and the default account user name is
|
||||||
\[lq]admin\[rq] with \[lq]adminadmin\[rq] as a password.
|
\(lqadmin\(rq with \(lqadminadmin\(rq as a password.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
\f[B]\f[CB]--help\f[B]\f[R] Prints the command line options.
|
\f[B]\f[CB]\-\-help\f[B]\f[R] Prints the command line options.
|
||||||
.PP
|
.PP
|
||||||
\f[B]\f[CB]--version\f[B]\f[R] Prints qbittorrent program version
|
\f[B]\f[CB]\-\-version\f[B]\f[R] Prints qbittorrent program version
|
||||||
number.
|
number.
|
||||||
.PP
|
.PP
|
||||||
\f[B]\f[CB]--webui-port=x\f[B]\f[R] Changes Web UI port to x (default:
|
\f[B]\f[CB]\-\-webui\-port=x\f[B]\f[R] Changes Web UI port to x
|
||||||
8080).
|
(default: 8080).
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
If you find a bug, please report it at https://bugs.qbittorrent.org
|
If you find a bug, please report it at https://bugs.qbittorrent.org
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
Christophe Dumez <chris@qbittorrent.org>.
|
Christophe Dumez \c
|
||||||
|
.MT chris@qbittorrent.org
|
||||||
|
.ME \c
|
||||||
|
\&.
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
.\" Automatically generated by Pandoc 3.1.7
|
.\" Automatically generated by Pandoc 3.7.0.2
|
||||||
.\"
|
.\"
|
||||||
.TH "QBITTORRENT" "1" "January 16th 2010" "Bittorrent client written in C++ / Qt" ""
|
.TH "QBITTORRENT" "1" "January 16th 2010" "Bittorrent client written in C++ / Qt"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
qBittorrent - a Bittorrent client written in C++ / Qt
|
qBittorrent \- a Bittorrent client written in C++ / Qt
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\f[B]qbittorrent\f[R]
|
\f[B]qbittorrent\f[R]
|
||||||
\f[CR][--no-splash] [--webui-port=x] [TORRENT_FILE | URL]...\f[R]
|
\f[CR][\-\-no\-splash] [\-\-webui\-port=x] [TORRENT_FILE | URL]...\f[R]
|
||||||
.PP
|
.PP
|
||||||
\f[B]qbittorrent\f[R] \f[CR]--help\f[R]
|
\f[B]qbittorrent\f[R] \f[CR]\-\-help\f[R]
|
||||||
.PP
|
.PP
|
||||||
\f[B]qbittorrent\f[R] \f[CR]--version\f[R]
|
\f[B]qbittorrent\f[R] \f[CR]\-\-version\f[R]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
\f[B]qBittorrent\f[R] is an advanced Bittorrent client written in C++ /
|
\f[B]qBittorrent\f[R] is an advanced Bittorrent client written in C++ /
|
||||||
Qt, using the \f[B]libtorrent-rasterbar\f[R] library by Arvid Norberg.
|
Qt, using the \f[B]libtorrent\-rasterbar\f[R] library by Arvid Norberg.
|
||||||
qBittorrent is similar to uTorrent.
|
qBittorrent is similar to uTorrent.
|
||||||
qBittorrent is fast, stable, light, it supports unicode and it provides
|
qBittorrent is fast, stable, light, it supports unicode and it provides
|
||||||
a good integrated search engine.
|
a good integrated search engine.
|
||||||
It also comes with UPnP port forwarding / NAT-PMP, encryption (Vuze
|
It also comes with UPnP port forwarding / NAT\-PMP, encryption (Vuze
|
||||||
compatible), FAST extension (mainline) and PeX support (utorrent
|
compatible), FAST extension (mainline) and PeX support (utorrent
|
||||||
compatible).
|
compatible).
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
\f[B]\f[CB]--help\f[B]\f[R] Prints the command line options.
|
\f[B]\f[CB]\-\-help\f[B]\f[R] Prints the command line options.
|
||||||
.PP
|
.PP
|
||||||
\f[B]\f[CB]--version\f[B]\f[R] Prints qbittorrent program version
|
\f[B]\f[CB]\-\-version\f[B]\f[R] Prints qbittorrent program version
|
||||||
number.
|
number.
|
||||||
.PP
|
.PP
|
||||||
\f[B]\f[CB]--no-splash\f[B]\f[R] Disables splash screen on startup.
|
\f[B]\f[CB]\-\-no\-splash\f[B]\f[R] Disables splash screen on startup.
|
||||||
.PP
|
.PP
|
||||||
\f[B]\f[CB]--webui-port=x\f[B]\f[R] Changes Web UI port to x (default:
|
\f[B]\f[CB]\-\-webui\-port=x\f[B]\f[R] Changes Web UI port to x
|
||||||
8080).
|
(default: 8080).
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
If you find a bug, please report it at https://bugs.qbittorrent.org
|
If you find a bug, please report it at https://bugs.qbittorrent.org
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
Christophe Dumez <chris@qbittorrent.org>.
|
Christophe Dumez \c
|
||||||
|
.MT chris@qbittorrent.org
|
||||||
|
.ME \c
|
||||||
|
\&.
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
.\" Automatically generated by Pandoc 3.1.7
|
.\" Automatically generated by Pandoc 3.7.0.2
|
||||||
.\"
|
.\"
|
||||||
.TH "QBITTORRENT-NOX" "1" "16 января 2010" "Клиент сети БитТоррент для командной строки" ""
|
.TH "QBITTORRENT\-NOX" "1" "16 января 2010" "Клиент сети БитТоррент для командной строки"
|
||||||
.SH НАЗВАНИЕ
|
.SH НАЗВАНИЕ
|
||||||
qBittorrent-nox \[em] клиент сети БитТоррент для командной строки.
|
qBittorrent\-nox \(em клиент сети БитТоррент для командной строки.
|
||||||
.SH АВТОРЫ
|
.SH АВТОРЫ
|
||||||
Christophe Dumez <chris@qbittorrent.org>.
|
Christophe Dumez \c
|
||||||
|
.MT chris@qbittorrent.org
|
||||||
|
.ME \c
|
||||||
|
\&.
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
.\" Automatically generated by Pandoc 3.1.7
|
.\" Automatically generated by Pandoc 3.7.0.2
|
||||||
.\"
|
.\"
|
||||||
.TH "QBITTORRENT" "1" "16 января 2010" "Клиент сети БитТоррент" ""
|
.TH "QBITTORRENT" "1" "16 января 2010" "Клиент сети БитТоррент"
|
||||||
.SH НАЗВАНИЕ
|
.SH НАЗВАНИЕ
|
||||||
qBittorrent \[em] клиент сети БитТоррент.
|
qBittorrent \(em клиент сети БитТоррент.
|
||||||
.SH АВТОРЫ
|
.SH АВТОРЫ
|
||||||
Christophe Dumez <chris@qbittorrent.org>.
|
Christophe Dumez \c
|
||||||
|
.MT chris@qbittorrent.org
|
||||||
|
.ME \c
|
||||||
|
\&.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -124,6 +124,28 @@ namespace
|
||||||
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
|
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const QString PARAM_ADDSTOPPED = u"@addStopped"_s;
|
||||||
|
const QString PARAM_CATEGORY = u"@category"_s;
|
||||||
|
const QString PARAM_FIRSTLASTPIECEPRIORITY = u"@firstLastPiecePriority"_s;
|
||||||
|
const QString PARAM_SAVEPATH = u"@savePath"_s;
|
||||||
|
const QString PARAM_SEQUENTIAL = u"@sequential"_s;
|
||||||
|
const QString PARAM_SKIPCHECKING = u"@skipChecking"_s;
|
||||||
|
const QString PARAM_SKIPDIALOG = u"@skipDialog"_s;
|
||||||
|
|
||||||
|
QString bindParamValue(const QStringView paramName, const QStringView paramValue)
|
||||||
|
{
|
||||||
|
return paramName + u'=' + paramValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<QStringView, QStringView> parseParam(const QStringView param)
|
||||||
|
{
|
||||||
|
const qsizetype sepIndex = param.indexOf(u'=');
|
||||||
|
if (sepIndex >= 0)
|
||||||
|
return {param.first(sepIndex), param.sliced(sepIndex + 1)};
|
||||||
|
|
||||||
|
return {param, {}};
|
||||||
|
}
|
||||||
|
|
||||||
QString serializeParams(const QBtCommandLineParameters ¶ms)
|
QString serializeParams(const QBtCommandLineParameters ¶ms)
|
||||||
{
|
{
|
||||||
QStringList result;
|
QStringList result;
|
||||||
|
@ -138,85 +160,86 @@ namespace
|
||||||
const BitTorrent::AddTorrentParams &addTorrentParams = params.addTorrentParams;
|
const BitTorrent::AddTorrentParams &addTorrentParams = params.addTorrentParams;
|
||||||
|
|
||||||
if (!addTorrentParams.savePath.isEmpty())
|
if (!addTorrentParams.savePath.isEmpty())
|
||||||
result.append(u"@savePath=" + addTorrentParams.savePath.data());
|
result.append(bindParamValue(PARAM_SAVEPATH, addTorrentParams.savePath.data()));
|
||||||
|
|
||||||
if (addTorrentParams.addStopped.has_value())
|
if (addTorrentParams.addStopped.has_value())
|
||||||
result.append(*addTorrentParams.addStopped ? u"@addStopped=1"_s : u"@addStopped=0"_s);
|
result.append(bindParamValue(PARAM_ADDSTOPPED, (*addTorrentParams.addStopped ? u"1" : u"0")));
|
||||||
|
|
||||||
if (addTorrentParams.skipChecking)
|
if (addTorrentParams.skipChecking)
|
||||||
result.append(u"@skipChecking"_s);
|
result.append(PARAM_SKIPCHECKING);
|
||||||
|
|
||||||
if (!addTorrentParams.category.isEmpty())
|
if (!addTorrentParams.category.isEmpty())
|
||||||
result.append(u"@category=" + addTorrentParams.category);
|
result.append(bindParamValue(PARAM_CATEGORY, addTorrentParams.category));
|
||||||
|
|
||||||
if (addTorrentParams.sequential)
|
if (addTorrentParams.sequential)
|
||||||
result.append(u"@sequential"_s);
|
result.append(PARAM_SEQUENTIAL);
|
||||||
|
|
||||||
if (addTorrentParams.firstLastPiecePriority)
|
if (addTorrentParams.firstLastPiecePriority)
|
||||||
result.append(u"@firstLastPiecePriority"_s);
|
result.append(PARAM_FIRSTLASTPIECEPRIORITY);
|
||||||
|
|
||||||
if (params.skipDialog.has_value())
|
if (params.skipDialog.has_value())
|
||||||
result.append(*params.skipDialog ? u"@skipDialog=1"_s : u"@skipDialog=0"_s);
|
result.append(bindParamValue(PARAM_SKIPDIALOG, (*params.skipDialog ? u"1" : u"0")));
|
||||||
|
|
||||||
result += params.torrentSources;
|
result += params.torrentSources;
|
||||||
|
|
||||||
return result.join(PARAMS_SEPARATOR);
|
return result.join(PARAMS_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
QBtCommandLineParameters parseParams(const QString &str)
|
QBtCommandLineParameters parseParams(const QStringView str)
|
||||||
{
|
{
|
||||||
QBtCommandLineParameters parsedParams;
|
QBtCommandLineParameters parsedParams;
|
||||||
BitTorrent::AddTorrentParams &addTorrentParams = parsedParams.addTorrentParams;
|
BitTorrent::AddTorrentParams &addTorrentParams = parsedParams.addTorrentParams;
|
||||||
|
|
||||||
for (QString param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts)))
|
for (QStringView param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts)))
|
||||||
{
|
{
|
||||||
param = param.trimmed();
|
param = param.trimmed();
|
||||||
|
const auto [paramName, paramValue] = parseParam(param);
|
||||||
|
|
||||||
// Process strings indicating options specified by the user.
|
// Process strings indicating options specified by the user.
|
||||||
|
|
||||||
if (param.startsWith(u"@savePath="))
|
if (paramName == PARAM_SAVEPATH)
|
||||||
{
|
{
|
||||||
addTorrentParams.savePath = Path(param.mid(10));
|
addTorrentParams.savePath = Path(paramValue.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.startsWith(u"@addStopped="))
|
if (paramName == PARAM_ADDSTOPPED)
|
||||||
{
|
{
|
||||||
addTorrentParams.addStopped = (QStringView(param).mid(11).toInt() != 0);
|
addTorrentParams.addStopped = (paramValue.toInt() != 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param == u"@skipChecking")
|
if (paramName == PARAM_SKIPCHECKING)
|
||||||
{
|
{
|
||||||
addTorrentParams.skipChecking = true;
|
addTorrentParams.skipChecking = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.startsWith(u"@category="))
|
if (paramName == PARAM_CATEGORY)
|
||||||
{
|
{
|
||||||
addTorrentParams.category = param.mid(10);
|
addTorrentParams.category = paramValue.toString();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param == u"@sequential")
|
if (paramName == PARAM_SEQUENTIAL)
|
||||||
{
|
{
|
||||||
addTorrentParams.sequential = true;
|
addTorrentParams.sequential = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param == u"@firstLastPiecePriority")
|
if (paramName == PARAM_FIRSTLASTPIECEPRIORITY)
|
||||||
{
|
{
|
||||||
addTorrentParams.firstLastPiecePriority = true;
|
addTorrentParams.firstLastPiecePriority = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.startsWith(u"@skipDialog="))
|
if (paramName == PARAM_SKIPDIALOG)
|
||||||
{
|
{
|
||||||
parsedParams.skipDialog = (QStringView(param).mid(12).toInt() != 0);
|
parsedParams.skipDialog = (paramValue.toInt() != 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedParams.torrentSources.append(param);
|
parsedParams.torrentSources.append(param.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedParams;
|
return parsedParams;
|
||||||
|
@ -387,7 +410,7 @@ void Application::setMemoryWorkingSetLimit(const int size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_storeMemoryWorkingSetLimit = size;
|
m_storeMemoryWorkingSetLimit = size;
|
||||||
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS)
|
||||||
applyMemoryWorkingSetLimit();
|
applyMemoryWorkingSetLimit();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -552,6 +575,9 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||||
case u'L':
|
case u'L':
|
||||||
str.replace(i, 2, torrent->category());
|
str.replace(i, 2, torrent->category());
|
||||||
break;
|
break;
|
||||||
|
case u'M':
|
||||||
|
str.replace(i, 2, torrent->comment());
|
||||||
|
break;
|
||||||
case u'N':
|
case u'N':
|
||||||
str.replace(i, 2, torrent->name());
|
str.replace(i, 2, torrent->name());
|
||||||
break;
|
break;
|
||||||
|
@ -579,7 +605,7 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||||
const QString logMsg = tr("Running external program. Torrent: \"%1\". Command: `%2`");
|
const QString logMsg = tr("Running external program. Torrent: \"%1\". Command: `%2`");
|
||||||
const QString logMsgError = tr("Failed to run external program. Torrent: \"%1\". Command: `%2`");
|
const QString logMsgError = tr("Failed to run external program. Torrent: \"%1\". Command: `%2`");
|
||||||
|
|
||||||
// The processing sequenece is different for Windows and other OS, this is intentional
|
// The processing sequence is different for Windows and other OS, this is intentional
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
const QString program = replaceVariables(programTemplate);
|
const QString program = replaceVariables(programTemplate);
|
||||||
const std::wstring programWStr = program.toStdWString();
|
const std::wstring programWStr = program.toStdWString();
|
||||||
|
@ -636,7 +662,13 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||||
{
|
{
|
||||||
// strip redundant quotes
|
// strip redundant quotes
|
||||||
if (arg.startsWith(u'"') && arg.endsWith(u'"'))
|
if (arg.startsWith(u'"') && arg.endsWith(u'"'))
|
||||||
arg = arg.mid(1, (arg.size() - 2));
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||||
|
arg.slice(1, (arg.size() - 2));
|
||||||
|
#else
|
||||||
|
arg.removeLast().removeFirst();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
arg = replaceVariables(arg);
|
arg = replaceVariables(arg);
|
||||||
}
|
}
|
||||||
|
@ -645,6 +677,7 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||||
QProcess proc;
|
QProcess proc;
|
||||||
proc.setProgram(command);
|
proc.setProgram(command);
|
||||||
proc.setArguments(args);
|
proc.setArguments(args);
|
||||||
|
proc.setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors);
|
||||||
|
|
||||||
if (proc.startDetached())
|
if (proc.startDetached())
|
||||||
{
|
{
|
||||||
|
@ -814,7 +847,7 @@ int Application::exec()
|
||||||
printf("%s\n", qUtf8Printable(loadingStr));
|
printf("%s\n", qUtf8Printable(loadingStr));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS)
|
||||||
applyMemoryWorkingSetLimit();
|
applyMemoryWorkingSetLimit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -897,10 +930,10 @@ int Application::exec()
|
||||||
m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name()));
|
m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name()));
|
||||||
});
|
});
|
||||||
connect(m_addTorrentManager, &AddTorrentManager::addTorrentFailed, this
|
connect(m_addTorrentManager, &AddTorrentManager::addTorrentFailed, this
|
||||||
, [this](const QString &source, const QString &reason)
|
, [this](const QString &source, const BitTorrent::AddTorrentError &reason)
|
||||||
{
|
{
|
||||||
m_desktopIntegration->showNotification(tr("Add torrent failed")
|
m_desktopIntegration->showNotification(tr("Add torrent failed")
|
||||||
, tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason));
|
, tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason.message));
|
||||||
});
|
});
|
||||||
|
|
||||||
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
||||||
|
@ -1172,7 +1205,7 @@ void Application::shutdownCleanup([[maybe_unused]] QSessionManager &manager)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS)
|
||||||
void Application::applyMemoryWorkingSetLimit() const
|
void Application::applyMemoryWorkingSetLimit() const
|
||||||
{
|
{
|
||||||
const size_t MiB = 1024 * 1024;
|
const size_t MiB = 1024 * 1024;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
|
#include <QStringView>
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -60,7 +61,7 @@ namespace
|
||||||
class Option
|
class Option
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
explicit constexpr Option(const char *name, char shortcut = 0)
|
explicit constexpr Option(const QStringView name, const QChar shortcut = QChar::Null)
|
||||||
: m_name {name}
|
: m_name {name}
|
||||||
, m_shortcut {shortcut}
|
, m_shortcut {shortcut}
|
||||||
{
|
{
|
||||||
|
@ -68,23 +69,23 @@ namespace
|
||||||
|
|
||||||
QString fullParameter() const
|
QString fullParameter() const
|
||||||
{
|
{
|
||||||
return u"--" + QString::fromLatin1(m_name);
|
return u"--" + m_name.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString shortcutParameter() const
|
QString shortcutParameter() const
|
||||||
{
|
{
|
||||||
return u"-" + QChar::fromLatin1(m_shortcut);
|
return u"-" + m_shortcut;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasShortcut() const
|
bool hasShortcut() const
|
||||||
{
|
{
|
||||||
return m_shortcut != 0;
|
return !m_shortcut.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString envVarName() const
|
QString envVarName() const
|
||||||
{
|
{
|
||||||
return u"QBT_"
|
return u"QBT_"
|
||||||
+ QString::fromLatin1(m_name).toUpper().replace(u'-', u'_');
|
+ m_name.toString().toUpper().replace(u'-', u'_');
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -99,15 +100,15 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char *m_name = nullptr;
|
const QStringView m_name;
|
||||||
const char m_shortcut;
|
const QChar m_shortcut;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Boolean option.
|
// Boolean option.
|
||||||
class BoolOption : protected Option
|
class BoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit constexpr BoolOption(const char *name, char shortcut = 0)
|
explicit constexpr BoolOption(const QStringView name, const QChar shortcut = QChar::Null)
|
||||||
: Option {name, shortcut}
|
: Option {name, shortcut}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -139,8 +140,8 @@ namespace
|
||||||
struct StringOption : protected Option
|
struct StringOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit constexpr StringOption(const char *name)
|
explicit constexpr StringOption(const QStringView name)
|
||||||
: Option {name, 0}
|
: Option {name, QChar::Null}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +182,7 @@ namespace
|
||||||
class IntOption : protected StringOption
|
class IntOption : protected StringOption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit constexpr IntOption(const char *name)
|
explicit constexpr IntOption(const QStringView name)
|
||||||
: StringOption {name}
|
: StringOption {name}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -229,8 +230,8 @@ namespace
|
||||||
class TriStateBoolOption : protected Option
|
class TriStateBoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr TriStateBoolOption(const char *name, bool defaultValue)
|
constexpr TriStateBoolOption(const QStringView name, const bool defaultValue)
|
||||||
: Option {name, 0}
|
: Option {name, QChar::Null}
|
||||||
, m_defaultValue(defaultValue)
|
, m_defaultValue(defaultValue)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -299,31 +300,32 @@ namespace
|
||||||
return arg.section(u'=', 0, 0) == option.fullParameter();
|
return arg.section(u'=', 0, 0) == option.fullParameter();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool m_defaultValue;
|
private:
|
||||||
|
bool m_defaultValue = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr const BoolOption SHOW_HELP_OPTION {"help", 'h'};
|
constexpr const BoolOption SHOW_HELP_OPTION {u"help", u'h'};
|
||||||
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
||||||
constexpr const BoolOption SHOW_VERSION_OPTION {"version", 'v'};
|
constexpr const BoolOption SHOW_VERSION_OPTION {u"version", u'v'};
|
||||||
#endif
|
#endif
|
||||||
constexpr const BoolOption CONFIRM_LEGAL_NOTICE {"confirm-legal-notice"};
|
constexpr const BoolOption CONFIRM_LEGAL_NOTICE {u"confirm-legal-notice"};
|
||||||
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
||||||
constexpr const BoolOption DAEMON_OPTION {"daemon", 'd'};
|
constexpr const BoolOption DAEMON_OPTION {u"daemon", u'd'};
|
||||||
#else
|
#else
|
||||||
constexpr const BoolOption NO_SPLASH_OPTION {"no-splash"};
|
constexpr const BoolOption NO_SPLASH_OPTION {u"no-splash"};
|
||||||
#endif
|
#endif
|
||||||
constexpr const IntOption WEBUI_PORT_OPTION {"webui-port"};
|
constexpr const IntOption WEBUI_PORT_OPTION {u"webui-port"};
|
||||||
constexpr const IntOption TORRENTING_PORT_OPTION {"torrenting-port"};
|
constexpr const IntOption TORRENTING_PORT_OPTION {u"torrenting-port"};
|
||||||
constexpr const StringOption PROFILE_OPTION {"profile"};
|
constexpr const StringOption PROFILE_OPTION {u"profile"};
|
||||||
constexpr const StringOption CONFIGURATION_OPTION {"configuration"};
|
constexpr const StringOption CONFIGURATION_OPTION {u"configuration"};
|
||||||
constexpr const BoolOption RELATIVE_FASTRESUME {"relative-fastresume"};
|
constexpr const BoolOption RELATIVE_FASTRESUME {u"relative-fastresume"};
|
||||||
constexpr const StringOption SAVE_PATH_OPTION {"save-path"};
|
constexpr const StringOption SAVE_PATH_OPTION {u"save-path"};
|
||||||
constexpr const TriStateBoolOption STOPPED_OPTION {"add-stopped", true};
|
constexpr const TriStateBoolOption STOPPED_OPTION {u"add-stopped", true};
|
||||||
constexpr const BoolOption SKIP_HASH_CHECK_OPTION {"skip-hash-check"};
|
constexpr const BoolOption SKIP_HASH_CHECK_OPTION {u"skip-hash-check"};
|
||||||
constexpr const StringOption CATEGORY_OPTION {"category"};
|
constexpr const StringOption CATEGORY_OPTION {u"category"};
|
||||||
constexpr const BoolOption SEQUENTIAL_OPTION {"sequential"};
|
constexpr const BoolOption SEQUENTIAL_OPTION {u"sequential"};
|
||||||
constexpr const BoolOption FIRST_AND_LAST_OPTION {"first-and-last"};
|
constexpr const BoolOption FIRST_AND_LAST_OPTION {u"first-and-last"};
|
||||||
constexpr const TriStateBoolOption SKIP_DIALOG_OPTION {"skip-dialog", true};
|
constexpr const TriStateBoolOption SKIP_DIALOG_OPTION {u"skip-dialog", true};
|
||||||
}
|
}
|
||||||
|
|
||||||
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
|
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
|
||||||
|
@ -463,13 +465,13 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN, int wrapAtColumn = WRAP_AT_COLUMN)
|
QString wrapText(const QString &text, const int initialIndentation = USAGE_TEXT_COLUMN, const int wrapAtColumn = WRAP_AT_COLUMN)
|
||||||
{
|
{
|
||||||
QStringList words = text.split(u' ');
|
const QStringList words = text.split(u' ');
|
||||||
QStringList lines = {words.first()};
|
QStringList lines = {words.first()};
|
||||||
int currentLineMaxLength = wrapAtColumn - initialIndentation;
|
int currentLineMaxLength = wrapAtColumn - initialIndentation;
|
||||||
|
|
||||||
for (const QString &word : asConst(words.mid(1)))
|
for (const QString &word : asConst(words.sliced(1)))
|
||||||
{
|
{
|
||||||
if (lines.last().length() + word.length() + 1 < currentLineMaxLength)
|
if (lines.last().length() + word.length() + 1 < currentLineMaxLength)
|
||||||
{
|
{
|
||||||
|
@ -489,6 +491,12 @@ QString makeUsage(const QString &prgName)
|
||||||
{
|
{
|
||||||
const QString indentation {USAGE_INDENTATION, u' '};
|
const QString indentation {USAGE_INDENTATION, u' '};
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
const QString noSplashCommand = u"set QBT_NO_SPLASH=1 && " + prgName;
|
||||||
|
#else
|
||||||
|
const QString noSplashCommand = u"QBT_NO_SPLASH=1 " + prgName;
|
||||||
|
#endif
|
||||||
|
|
||||||
const QString text = QCoreApplication::translate("CMD Options", "Usage:") + u'\n'
|
const QString text = QCoreApplication::translate("CMD Options", "Usage:") + u'\n'
|
||||||
+ indentation + prgName + u' ' + QCoreApplication::translate("CMD Options", "[options] [(<filename> | <url>)...]") + u'\n'
|
+ indentation + prgName + u' ' + QCoreApplication::translate("CMD Options", "[options] [(<filename> | <url>)...]") + u'\n'
|
||||||
|
|
||||||
|
@ -540,7 +548,7 @@ QString makeUsage(const QString &prgName)
|
||||||
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
|
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
|
||||||
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
|
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
|
||||||
"'TRUE'. For example, to disable the splash screen: "), 0) + u'\n'
|
"'TRUE'. For example, to disable the splash screen: "), 0) + u'\n'
|
||||||
+ u"QBT_NO_SPLASH=1 " + prgName + u'\n'
|
+ noSplashCommand + u'\n'
|
||||||
+ wrapText(QCoreApplication::translate("CMD Options", "Command line parameters take precedence over environment variables"), 0) + u'\n';
|
+ wrapText(QCoreApplication::translate("CMD Options", "Command line parameters take precedence over environment variables"), 0) + u'\n';
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QList>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
@ -175,12 +175,15 @@ void FileLogger::flushLog()
|
||||||
|
|
||||||
void FileLogger::openLogFile()
|
void FileLogger::openLogFile()
|
||||||
{
|
{
|
||||||
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
|
||||||
|| !m_logFile.setPermissions(QFile::ReadOwner | QFile::WriteOwner))
|
|
||||||
{
|
{
|
||||||
m_logFile.close();
|
LogMsg(tr("An error occurred while trying to open the log file. Logging to file is disabled. File: \"%1\". Error: \"%2\".")
|
||||||
LogMsg(tr("An error occurred while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
|
.arg(m_logFile.fileName(), m_logFile.errorString()), Log::CRITICAL);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// best effort, don't report error
|
||||||
|
m_logFile.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLogger::closeLogFile()
|
void FileLogger::closeLogFile()
|
||||||
|
|
|
@ -74,6 +74,7 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
|
@ -90,7 +91,7 @@ namespace QtLP_Private
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const char ACK[] = "ack";
|
const QByteArray ACK = QByteArrayLiteral("ack");
|
||||||
|
|
||||||
QtLocalPeer::QtLocalPeer(const QString &path, QObject *parent)
|
QtLocalPeer::QtLocalPeer(const QString &path, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -169,7 +170,7 @@ bool QtLocalPeer::sendMessage(const QString &message, const int timeout)
|
||||||
{
|
{
|
||||||
res &= socket.waitForReadyRead(timeout); // wait for ack
|
res &= socket.waitForReadyRead(timeout); // wait for ack
|
||||||
if (res)
|
if (res)
|
||||||
res &= (socket.read(qstrlen(ACK)) == ACK);
|
res &= (socket.read(ACK.size()) == ACK);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -220,7 +221,7 @@ void QtLocalPeer::receiveConnection()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString message(QString::fromUtf8(uMsg));
|
QString message(QString::fromUtf8(uMsg));
|
||||||
socket->write(ACK, qstrlen(ACK));
|
socket->write(ACK);
|
||||||
socket->waitForBytesWritten(1000);
|
socket->waitForBytesWritten(1000);
|
||||||
socket->waitForDisconnected(1000); // make sure client reads ack
|
socket->waitForDisconnected(1000); // make sure client reads ack
|
||||||
delete socket;
|
delete socket;
|
||||||
|
|
|
@ -71,8 +71,8 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace QtLP_Private
|
namespace QtLP_Private
|
||||||
|
@ -105,7 +105,7 @@ namespace QtLP_Private
|
||||||
|
|
||||||
Qt::HANDLE m_writeMutex = nullptr;
|
Qt::HANDLE m_writeMutex = nullptr;
|
||||||
Qt::HANDLE m_readMutex = nullptr;
|
Qt::HANDLE m_readMutex = nullptr;
|
||||||
QVector<Qt::HANDLE> m_readMutexes;
|
QList<Qt::HANDLE> m_readMutexes;
|
||||||
QString m_mutexName;
|
QString m_mutexName;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
1096
src/base/3rdparty/expected.hpp
vendored
|
@ -6,7 +6,9 @@ add_library(qbt_base STATIC
|
||||||
applicationcomponent.h
|
applicationcomponent.h
|
||||||
asyncfilestorage.h
|
asyncfilestorage.h
|
||||||
bittorrent/abstractfilestorage.h
|
bittorrent/abstractfilestorage.h
|
||||||
|
bittorrent/addtorrenterror.h
|
||||||
bittorrent/addtorrentparams.h
|
bittorrent/addtorrentparams.h
|
||||||
|
bittorrent/announcetimepoint.h
|
||||||
bittorrent/bandwidthscheduler.h
|
bittorrent/bandwidthscheduler.h
|
||||||
bittorrent/bencoderesumedatastorage.h
|
bittorrent/bencoderesumedatastorage.h
|
||||||
bittorrent/cachestatus.h
|
bittorrent/cachestatus.h
|
||||||
|
@ -53,6 +55,7 @@ add_library(qbt_base STATIC
|
||||||
concepts/stringable.h
|
concepts/stringable.h
|
||||||
digest32.h
|
digest32.h
|
||||||
exceptions.h
|
exceptions.h
|
||||||
|
freediskspacechecker.h
|
||||||
global.h
|
global.h
|
||||||
http/connection.h
|
http/connection.h
|
||||||
http/httperror.h
|
http/httperror.h
|
||||||
|
@ -158,6 +161,7 @@ add_library(qbt_base STATIC
|
||||||
bittorrent/trackerentry.cpp
|
bittorrent/trackerentry.cpp
|
||||||
bittorrent/trackerentrystatus.cpp
|
bittorrent/trackerentrystatus.cpp
|
||||||
exceptions.cpp
|
exceptions.cpp
|
||||||
|
freediskspacechecker.cpp
|
||||||
http/connection.cpp
|
http/connection.cpp
|
||||||
http/httperror.cpp
|
http/httperror.cpp
|
||||||
http/requestparser.cpp
|
http/requestparser.cpp
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "addtorrentmanager.h"
|
#include "addtorrentmanager.h"
|
||||||
|
|
||||||
|
#include "base/bittorrent/addtorrenterror.h"
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/torrentdescriptor.h"
|
#include "base/bittorrent/torrentdescriptor.h"
|
||||||
|
@ -140,7 +141,7 @@ void AddTorrentManager::onSessionTorrentAdded(BitTorrent::Torrent *torrent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason)
|
void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason)
|
||||||
{
|
{
|
||||||
if (const QString source = m_sourcesByInfoHash.take(infoHash); !source.isEmpty())
|
if (const QString source = m_sourcesByInfoHash.take(infoHash); !source.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -154,7 +155,7 @@ void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &in
|
||||||
void AddTorrentManager::handleAddTorrentFailed(const QString &source, const QString &reason)
|
void AddTorrentManager::handleAddTorrentFailed(const QString &source, const QString &reason)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Failed to add torrent. Source: \"%1\". Reason: \"%2\"").arg(source, reason), Log::WARNING);
|
LogMsg(tr("Failed to add torrent. Source: \"%1\". Reason: \"%2\"").arg(source, reason), Log::WARNING);
|
||||||
emit addTorrentFailed(source, reason);
|
emit addTorrentFailed(source, {BitTorrent::AddTorrentError::Other, reason});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTorrentManager::handleDuplicateTorrent(const QString &source
|
void AddTorrentManager::handleDuplicateTorrent(const QString &source
|
||||||
|
@ -185,9 +186,9 @@ void AddTorrentManager::handleDuplicateTorrent(const QString &source
|
||||||
message = tr("Trackers are merged from new source");
|
message = tr("Trackers are merged from new source");
|
||||||
}
|
}
|
||||||
|
|
||||||
LogMsg(tr("Detected an attempt to add a duplicate torrent. Source: %1. Existing torrent: %2. Result: %3")
|
LogMsg(tr("Detected an attempt to add a duplicate torrent. Source: %1. Existing torrent: \"%2\". Torrent infohash: %3. Result: %4")
|
||||||
.arg(source, existingTorrent->name(), message));
|
.arg(source, existingTorrent->name(), existingTorrent->infoHash().toString(), message));
|
||||||
emit addTorrentFailed(source, message);
|
emit addTorrentFailed(source, {BitTorrent::AddTorrentError::DuplicateTorrent, message});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard)
|
void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard)
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace BitTorrent
|
||||||
class Session;
|
class Session;
|
||||||
class Torrent;
|
class Torrent;
|
||||||
class TorrentDescriptor;
|
class TorrentDescriptor;
|
||||||
|
struct AddTorrentError;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
|
@ -66,7 +67,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void torrentAdded(const QString &source, BitTorrent::Torrent *torrent);
|
void torrentAdded(const QString &source, BitTorrent::Torrent *torrent);
|
||||||
void addTorrentFailed(const QString &source, const QString &reason);
|
void addTorrentFailed(const QString &source, const BitTorrent::AddTorrentError &reason);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
|
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
|
||||||
|
@ -79,7 +80,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
void onDownloadFinished(const Net::DownloadResult &result);
|
void onDownloadFinished(const Net::DownloadResult &result);
|
||||||
void onSessionTorrentAdded(BitTorrent::Torrent *torrent);
|
void onSessionTorrentAdded(BitTorrent::Torrent *torrent);
|
||||||
void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason);
|
void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason);
|
||||||
bool processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
|
bool processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
|
||||||
, const BitTorrent::AddTorrentParams &addTorrentParams);
|
, const BitTorrent::AddTorrentParams &addTorrentParams);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QVector>
|
#include <QList>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
@ -71,7 +71,7 @@ void BitTorrent::AbstractFileStorage::renameFolder(const Path &oldFolderPath, co
|
||||||
if (newFolderPath.isAbsolute())
|
if (newFolderPath.isAbsolute())
|
||||||
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath.toString()));
|
throw RuntimeError(tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath.toString()));
|
||||||
|
|
||||||
QVector<int> renamingFileIndexes;
|
QList<int> renamingFileIndexes;
|
||||||
renamingFileIndexes.reserve(filesCount());
|
renamingFileIndexes.reserve(filesCount());
|
||||||
|
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
|
49
src/base/bittorrent/addtorrenterror.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2025 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 <QMetaType>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
struct AddTorrentError
|
||||||
|
{
|
||||||
|
enum Kind
|
||||||
|
{
|
||||||
|
DuplicateTorrent,
|
||||||
|
Other
|
||||||
|
};
|
||||||
|
|
||||||
|
Kind kind = Other;
|
||||||
|
QString message;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(BitTorrent::AddTorrentError)
|
|
@ -30,9 +30,9 @@
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
|
@ -62,7 +62,7 @@ namespace BitTorrent
|
||||||
std::optional<bool> addStopped;
|
std::optional<bool> addStopped;
|
||||||
std::optional<Torrent::StopCondition> stopCondition;
|
std::optional<Torrent::StopCondition> stopCondition;
|
||||||
PathList filePaths; // used if TorrentInfo is set
|
PathList filePaths; // used if TorrentInfo is set
|
||||||
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
|
QList<DownloadPriority> filePriorities; // used if TorrentInfo is set
|
||||||
bool skipChecking = false;
|
bool skipChecking = false;
|
||||||
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
|
||||||
std::optional<bool> useAutoTMM;
|
std::optional<bool> useAutoTMM;
|
||||||
|
|
36
src/base/bittorrent/announcetimepoint.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2024 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 <chrono>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
using AnnounceTimePoint = std::chrono::high_resolution_clock::time_point;
|
||||||
|
}
|
|
@ -102,7 +102,7 @@ bool BandwidthScheduler::isTimeForAlternative() const
|
||||||
alternative = !alternative;
|
alternative = !alternative;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_UNREACHABLE();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
|
||||||
void remove(const TorrentID &id) const;
|
void remove(const TorrentID &id) const;
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const;
|
void storeQueue(const QList<TorrentID> &queue) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Path m_resumeDataDir;
|
const Path m_resumeDataDir;
|
||||||
|
@ -131,10 +131,11 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path,
|
||||||
|
|
||||||
m_asyncWorker->moveToThread(m_ioThread.get());
|
m_asyncWorker->moveToThread(m_ioThread.get());
|
||||||
connect(m_ioThread.get(), &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
connect(m_ioThread.get(), &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
||||||
|
m_ioThread->setObjectName("BencodeResumeDataStorage m_ioThread");
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
|
QList<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
|
||||||
{
|
{
|
||||||
return m_registeredTorrents;
|
return m_registeredTorrents;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +147,7 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::load(cons
|
||||||
const Path torrentFilePath = path() / Path(idString + u".torrent");
|
const Path torrentFilePath = path() / Path(idString + u".torrent");
|
||||||
const qint64 torrentSizeLimit = Preferences::instance()->getTorrentFileSizeLimit();
|
const qint64 torrentSizeLimit = Preferences::instance()->getTorrentFileSizeLimit();
|
||||||
|
|
||||||
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, torrentSizeLimit);
|
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, -1);
|
||||||
if (!resumeDataReadResult)
|
if (!resumeDataReadResult)
|
||||||
return nonstd::make_unexpected(resumeDataReadResult.error().message);
|
return nonstd::make_unexpected(resumeDataReadResult.error().message);
|
||||||
|
|
||||||
|
@ -188,8 +189,13 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QHash<TorrentID, qsizetype> registeredTorrentsIndexes;
|
||||||
|
registeredTorrentsIndexes.reserve(m_registeredTorrents.length());
|
||||||
|
for (qsizetype i = 0; i < m_registeredTorrents.length(); ++i)
|
||||||
|
registeredTorrentsIndexes.insert(m_registeredTorrents.at(i), i);
|
||||||
|
|
||||||
const QRegularExpression hashPattern {u"^([A-Fa-f0-9]{40})$"_s};
|
const QRegularExpression hashPattern {u"^([A-Fa-f0-9]{40})$"_s};
|
||||||
int start = 0;
|
qsizetype queuePos = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
const auto line = QString::fromLatin1(queueFile.readLine(lineMaxLength).trimmed());
|
const auto line = QString::fromLatin1(queueFile.readLine(lineMaxLength).trimmed());
|
||||||
|
@ -200,11 +206,15 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
|
||||||
if (rxMatch.hasMatch())
|
if (rxMatch.hasMatch())
|
||||||
{
|
{
|
||||||
const auto torrentID = BitTorrent::TorrentID::fromString(rxMatch.captured(1));
|
const auto torrentID = BitTorrent::TorrentID::fromString(rxMatch.captured(1));
|
||||||
const int pos = m_registeredTorrents.indexOf(torrentID, start);
|
const qsizetype pos = registeredTorrentsIndexes.value(torrentID, -1);
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
{
|
{
|
||||||
std::swap(m_registeredTorrents[start], m_registeredTorrents[pos]);
|
if (pos != queuePos)
|
||||||
++start;
|
{
|
||||||
|
m_registeredTorrents.swapItemsAt(pos, queuePos);
|
||||||
|
registeredTorrentsIndexes.insert(m_registeredTorrents.at(pos), pos);
|
||||||
|
}
|
||||||
|
++queuePos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,10 +299,11 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
||||||
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
|
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
|
||||||
|
|
||||||
p = lt::read_resume_data(resumeDataRoot, ec);
|
p = lt::read_resume_data(resumeDataRoot, ec);
|
||||||
|
if (ec)
|
||||||
|
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
|
||||||
if (!metadata.isEmpty())
|
if (!metadata.isEmpty())
|
||||||
{
|
{
|
||||||
const auto *pref = Preferences::instance();
|
|
||||||
const lt::bdecode_node torentInfoRoot = lt::bdecode(metadata, ec
|
const lt::bdecode_node torentInfoRoot = lt::bdecode(metadata, ec
|
||||||
, nullptr, pref->getBdecodeDepthLimit(), pref->getBdecodeTokenLimit());
|
, nullptr, pref->getBdecodeDepthLimit(), pref->getBdecodeTokenLimit());
|
||||||
if (ec)
|
if (ec)
|
||||||
|
@ -320,6 +331,8 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
||||||
|
|
||||||
p.save_path = Profile::instance()->fromPortablePath(
|
p.save_path = Profile::instance()->fromPortablePath(
|
||||||
Path(fromLTString(p.save_path))).toString().toStdString();
|
Path(fromLTString(p.save_path))).toString().toStdString();
|
||||||
|
if (p.save_path.empty())
|
||||||
|
return nonstd::make_unexpected(tr("Corrupted resume data: %1").arg(tr("save_path is invalid")));
|
||||||
|
|
||||||
torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
|
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)
|
torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed)
|
||||||
|
@ -338,9 +351,9 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
||||||
return torrentParams;
|
return torrentParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
void BitTorrent::BencodeResumeDataStorage::store(const TorrentID &id, LoadTorrentParams resumeData) const
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
|
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData = std::move(resumeData)]
|
||||||
{
|
{
|
||||||
m_asyncWorker->store(id, resumeData);
|
m_asyncWorker->store(id, resumeData);
|
||||||
});
|
});
|
||||||
|
@ -354,7 +367,7 @@ void BitTorrent::BencodeResumeDataStorage::remove(const TorrentID &id) const
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
void BitTorrent::BencodeResumeDataStorage::storeQueue(const QList<TorrentID> &queue) const
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
||||||
{
|
{
|
||||||
|
@ -460,7 +473,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) c
|
||||||
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
Utils::Fs::removeFile(m_resumeDataDir / torrentFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
|
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QList<TorrentID> &queue) const
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
data.reserve(((BitTorrent::TorrentID::length() * 2) + 1) * queue.size());
|
data.reserve(((BitTorrent::TorrentID::length() * 2) + 1) * queue.size());
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QVector>
|
#include <QList>
|
||||||
|
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
#include "base/utils/thread.h"
|
#include "base/utils/thread.h"
|
||||||
|
@ -37,7 +37,6 @@
|
||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QThread;
|
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
|
@ -49,18 +48,18 @@ namespace BitTorrent
|
||||||
public:
|
public:
|
||||||
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
||||||
|
|
||||||
QVector<TorrentID> registeredTorrents() const override;
|
QList<TorrentID> registeredTorrents() const override;
|
||||||
LoadResumeDataResult load(const TorrentID &id) const override;
|
LoadResumeDataResult load(const TorrentID &id) const override;
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const override;
|
void store(const TorrentID &id, LoadTorrentParams resumeData) const override;
|
||||||
void remove(const TorrentID &id) const override;
|
void remove(const TorrentID &id) const override;
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
void storeQueue(const QList<TorrentID> &queue) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doLoadAll() const override;
|
void doLoadAll() const override;
|
||||||
void loadQueue(const Path &queueFilename);
|
void loadQueue(const Path &queueFilename);
|
||||||
LoadResumeDataResult loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
LoadResumeDataResult loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
||||||
|
|
||||||
QVector<TorrentID> m_registeredTorrents;
|
QList<TorrentID> m_registeredTorrents;
|
||||||
Utils::Thread::UniquePtr m_ioThread;
|
Utils::Thread::UniquePtr m_ioThread;
|
||||||
|
|
||||||
class Worker;
|
class Worker;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2021-2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2021-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -41,6 +41,7 @@
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QList>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
|
@ -48,7 +49,6 @@
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
#include <QSqlRecord>
|
#include <QSqlRecord>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QVector>
|
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
|
@ -67,7 +67,7 @@ namespace
|
||||||
{
|
{
|
||||||
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s;
|
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s;
|
||||||
|
|
||||||
const int DB_VERSION = 7;
|
const int DB_VERSION = 8;
|
||||||
|
|
||||||
const QString DB_TABLE_META = u"meta"_s;
|
const QString DB_TABLE_META = u"meta"_s;
|
||||||
const QString DB_TABLE_TORRENTS = u"torrents"_s;
|
const QString DB_TABLE_TORRENTS = u"torrents"_s;
|
||||||
|
@ -86,7 +86,7 @@ namespace
|
||||||
class StoreJob final : public Job
|
class StoreJob final : public Job
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StoreJob(const TorrentID &torrentID, const LoadTorrentParams &resumeData);
|
StoreJob(const TorrentID &torrentID, LoadTorrentParams resumeData);
|
||||||
void perform(QSqlDatabase db) override;
|
void perform(QSqlDatabase db) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -107,11 +107,11 @@ namespace
|
||||||
class StoreQueueJob final : public Job
|
class StoreQueueJob final : public Job
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit StoreQueueJob(const QVector<TorrentID> &queue);
|
explicit StoreQueueJob(const QList<TorrentID> &queue);
|
||||||
void perform(QSqlDatabase db) override;
|
void perform(QSqlDatabase db) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QVector<TorrentID> m_queue;
|
const QList<TorrentID> m_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Column
|
struct Column
|
||||||
|
@ -120,36 +120,35 @@ namespace
|
||||||
QString placeholder;
|
QString placeholder;
|
||||||
};
|
};
|
||||||
|
|
||||||
Column makeColumn(const char *columnName)
|
Column makeColumn(const QString &columnName)
|
||||||
{
|
{
|
||||||
const QString name = QString::fromLatin1(columnName);
|
return {.name = columnName, .placeholder = (u':' + columnName)};
|
||||||
return {.name = name, .placeholder = (u':' + name)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Column DB_COLUMN_ID = makeColumn("id");
|
const Column DB_COLUMN_ID = makeColumn(u"id"_s);
|
||||||
const Column DB_COLUMN_TORRENT_ID = makeColumn("torrent_id");
|
const Column DB_COLUMN_TORRENT_ID = makeColumn(u"torrent_id"_s);
|
||||||
const Column DB_COLUMN_QUEUE_POSITION = makeColumn("queue_position");
|
const Column DB_COLUMN_QUEUE_POSITION = makeColumn(u"queue_position"_s);
|
||||||
const Column DB_COLUMN_NAME = makeColumn("name");
|
const Column DB_COLUMN_NAME = makeColumn(u"name"_s);
|
||||||
const Column DB_COLUMN_CATEGORY = makeColumn("category");
|
const Column DB_COLUMN_CATEGORY = makeColumn(u"category"_s);
|
||||||
const Column DB_COLUMN_TAGS = makeColumn("tags");
|
const Column DB_COLUMN_TAGS = makeColumn(u"tags"_s);
|
||||||
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn("target_save_path");
|
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn(u"target_save_path"_s);
|
||||||
const Column DB_COLUMN_DOWNLOAD_PATH = makeColumn("download_path");
|
const Column DB_COLUMN_DOWNLOAD_PATH = makeColumn(u"download_path"_s);
|
||||||
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn(u"content_layout"_s);
|
||||||
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
const Column DB_COLUMN_RATIO_LIMIT = makeColumn(u"ratio_limit"_s);
|
||||||
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn(u"seeding_time_limit"_s);
|
||||||
const Column DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT = makeColumn("inactive_seeding_time_limit");
|
const Column DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT = makeColumn(u"inactive_seeding_time_limit"_s);
|
||||||
const Column DB_COLUMN_SHARE_LIMIT_ACTION = makeColumn("share_limit_action");
|
const Column DB_COLUMN_SHARE_LIMIT_ACTION = makeColumn(u"share_limit_action"_s);
|
||||||
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn("has_outer_pieces_priority");
|
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn(u"has_outer_pieces_priority"_s);
|
||||||
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn("has_seed_status");
|
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn(u"has_seed_status"_s);
|
||||||
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
|
const Column DB_COLUMN_OPERATING_MODE = makeColumn(u"operating_mode"_s);
|
||||||
const Column DB_COLUMN_STOPPED = makeColumn("stopped");
|
const Column DB_COLUMN_STOPPED = makeColumn(u"stopped"_s);
|
||||||
const Column DB_COLUMN_STOP_CONDITION = makeColumn("stop_condition");
|
const Column DB_COLUMN_STOP_CONDITION = makeColumn(u"stop_condition"_s);
|
||||||
const Column DB_COLUMN_SSL_CERTIFICATE = makeColumn("ssl_certificate");
|
const Column DB_COLUMN_SSL_CERTIFICATE = makeColumn(u"ssl_certificate"_s);
|
||||||
const Column DB_COLUMN_SSL_PRIVATE_KEY = makeColumn("ssl_private_key");
|
const Column DB_COLUMN_SSL_PRIVATE_KEY = makeColumn(u"ssl_private_key"_s);
|
||||||
const Column DB_COLUMN_SSL_DH_PARAMS = makeColumn("ssl_dh_params");
|
const Column DB_COLUMN_SSL_DH_PARAMS = makeColumn(u"ssl_dh_params"_s);
|
||||||
const Column DB_COLUMN_RESUMEDATA = makeColumn("libtorrent_resume_data");
|
const Column DB_COLUMN_RESUMEDATA = makeColumn(u"libtorrent_resume_data"_s);
|
||||||
const Column DB_COLUMN_METADATA = makeColumn("metadata");
|
const Column DB_COLUMN_METADATA = makeColumn(u"metadata"_s);
|
||||||
const Column DB_COLUMN_VALUE = makeColumn("value");
|
const Column DB_COLUMN_VALUE = makeColumn(u"value"_s);
|
||||||
|
|
||||||
template <typename LTStr>
|
template <typename LTStr>
|
||||||
QString fromLTString(const LTStr &str)
|
QString fromLTString(const LTStr &str)
|
||||||
|
@ -168,7 +167,7 @@ namespace
|
||||||
return u"CREATE TABLE %1 (%2)"_s.arg(quoted(tableName), items.join(u','));
|
return u"CREATE TABLE %1 (%2)"_s.arg(quoted(tableName), items.join(u','));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<QString, QString> joinColumns(const QVector<Column> &columns)
|
std::pair<QString, QString> joinColumns(const QList<Column> &columns)
|
||||||
{
|
{
|
||||||
int namesSize = columns.size();
|
int namesSize = columns.size();
|
||||||
int valuesSize = columns.size();
|
int valuesSize = columns.size();
|
||||||
|
@ -193,104 +192,30 @@ namespace
|
||||||
return std::make_pair(names, values);
|
return std::make_pair(names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
QString makeInsertStatement(const QString &tableName, const QList<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u"INSERT INTO %1 (%2) VALUES (%3)"_s
|
return u"INSERT INTO %1 (%2) VALUES (%3)"_s
|
||||||
.arg(quoted(tableName), names, values);
|
.arg(quoted(tableName), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeUpdateStatement(const QString &tableName, const QVector<Column> &columns)
|
QString makeUpdateStatement(const QString &tableName, const QList<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u"UPDATE %1 SET (%2) = (%3)"_s
|
return u"UPDATE %1 SET (%2) = (%3)"_s
|
||||||
.arg(quoted(tableName), names, values);
|
.arg(quoted(tableName), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
QString makeOnConflictUpdateStatement(const Column &constraint, const QList<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)"_s
|
return u" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)"_s
|
||||||
.arg(quoted(constraint.name), names, values);
|
.arg(quoted(constraint.name), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeColumnDefinition(const Column &column, const char *definition)
|
QString makeColumnDefinition(const Column &column, const QString &definition)
|
||||||
{
|
{
|
||||||
return u"%1 %2"_s.arg(quoted(column.name), QString::fromLatin1(definition));
|
return u"%1 %2"_s.arg(quoted(column.name), definition);
|
||||||
}
|
|
||||||
|
|
||||||
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
|
|
||||||
{
|
|
||||||
LoadTorrentParams resumeData;
|
|
||||||
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
|
||||||
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
|
||||||
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
|
||||||
if (!tagsData.isEmpty())
|
|
||||||
{
|
|
||||||
const QStringList tagList = tagsData.split(u',');
|
|
||||||
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
|
||||||
}
|
|
||||||
resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
|
||||||
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
|
||||||
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
|
||||||
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
|
|
||||||
resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt();
|
|
||||||
resumeData.shareLimitAction = Utils::String::toEnum<ShareLimitAction>(
|
|
||||||
query.value(DB_COLUMN_SHARE_LIMIT_ACTION.name).toString(), ShareLimitAction::Default);
|
|
||||||
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
|
|
||||||
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
|
|
||||||
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
|
|
||||||
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
|
|
||||||
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
|
||||||
resumeData.stopCondition = Utils::String::toEnum(
|
|
||||||
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
|
|
||||||
resumeData.sslParameters =
|
|
||||||
{
|
|
||||||
.certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()),
|
|
||||||
.privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()),
|
|
||||||
.dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray()
|
|
||||||
};
|
|
||||||
|
|
||||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
|
||||||
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
|
||||||
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
|
||||||
if (!resumeData.useAutoTMM)
|
|
||||||
{
|
|
||||||
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
|
||||||
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
|
||||||
const auto *pref = Preferences::instance();
|
|
||||||
const int bdecodeDepthLimit = pref->getBdecodeDepthLimit();
|
|
||||||
const int bdecodeTokenLimit = pref->getBdecodeTokenLimit();
|
|
||||||
|
|
||||||
lt::error_code ec;
|
|
||||||
const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec
|
|
||||||
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
|
|
||||||
|
|
||||||
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
|
||||||
|
|
||||||
p = lt::read_resume_data(resumeDataRoot, ec);
|
|
||||||
|
|
||||||
if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray()
|
|
||||||
; !bencodedMetadata.isEmpty())
|
|
||||||
{
|
|
||||||
const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec
|
|
||||||
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
|
|
||||||
p.ti = std::make_shared<lt::torrent_info>(torentInfoRoot, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
|
|
||||||
.toString().toStdString();
|
|
||||||
|
|
||||||
if (p.flags & lt::torrent_flags::stop_when_ready)
|
|
||||||
{
|
|
||||||
p.flags &= ~lt::torrent_flags::stop_when_ready;
|
|
||||||
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
|
|
||||||
}
|
|
||||||
|
|
||||||
return resumeData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,9 +231,9 @@ namespace BitTorrent
|
||||||
void run() override;
|
void run() override;
|
||||||
void requestInterruption();
|
void requestInterruption();
|
||||||
|
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData);
|
void store(const TorrentID &id, LoadTorrentParams resumeData);
|
||||||
void remove(const TorrentID &id);
|
void remove(const TorrentID &id);
|
||||||
void storeQueue(const QVector<TorrentID> &queue);
|
void storeQueue(const QList<TorrentID> &queue);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addJob(std::unique_ptr<Job> job);
|
void addJob(std::unique_ptr<Job> job);
|
||||||
|
@ -325,7 +250,6 @@ namespace BitTorrent
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
||||||
: ResumeDataStorage(dbPath, parent)
|
: ResumeDataStorage(dbPath, parent)
|
||||||
, m_ioThread {new QThread}
|
|
||||||
{
|
{
|
||||||
const bool needCreateDB = !dbPath.exists();
|
const bool needCreateDB = !dbPath.exists();
|
||||||
|
|
||||||
|
@ -356,7 +280,7 @@ BitTorrent::DBResumeDataStorage::~DBResumeDataStorage()
|
||||||
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
QList<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
||||||
{
|
{
|
||||||
const auto selectTorrentIDStatement = u"SELECT %1 FROM %2 ORDER BY %3;"_s
|
const auto selectTorrentIDStatement = u"SELECT %1 FROM %2 ORDER BY %3;"_s
|
||||||
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
|
||||||
|
@ -367,7 +291,7 @@ QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorren
|
||||||
if (!query.exec(selectTorrentIDStatement))
|
if (!query.exec(selectTorrentIDStatement))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
QVector<TorrentID> registeredTorrents;
|
QList<TorrentID> registeredTorrents;
|
||||||
registeredTorrents.reserve(query.size());
|
registeredTorrents.reserve(query.size());
|
||||||
while (query.next())
|
while (query.next())
|
||||||
registeredTorrents.append(BitTorrent::TorrentID::fromString(query.value(0).toString()));
|
registeredTorrents.append(BitTorrent::TorrentID::fromString(query.value(0).toString()));
|
||||||
|
@ -403,9 +327,9 @@ BitTorrent::LoadResumeDataResult BitTorrent::DBResumeDataStorage::load(const Tor
|
||||||
return parseQueryResultRow(query);
|
return parseQueryResultRow(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
|
void BitTorrent::DBResumeDataStorage::store(const TorrentID &id, LoadTorrentParams resumeData) const
|
||||||
{
|
{
|
||||||
m_asyncWorker->store(id, resumeData);
|
m_asyncWorker->store(id, std::move(resumeData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) const
|
void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) const
|
||||||
|
@ -413,7 +337,7 @@ void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) co
|
||||||
m_asyncWorker->remove(id);
|
m_asyncWorker->remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
void BitTorrent::DBResumeDataStorage::storeQueue(const QList<TorrentID> &queue) const
|
||||||
{
|
{
|
||||||
m_asyncWorker->storeQueue(queue);
|
m_asyncWorker->storeQueue(queue);
|
||||||
}
|
}
|
||||||
|
@ -438,7 +362,7 @@ void BitTorrent::DBResumeDataStorage::doLoadAll() const
|
||||||
if (!query.exec(selectTorrentIDStatement))
|
if (!query.exec(selectTorrentIDStatement))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
QVector<TorrentID> registeredTorrents;
|
QList<TorrentID> registeredTorrents;
|
||||||
registeredTorrents.reserve(query.size());
|
registeredTorrents.reserve(query.size());
|
||||||
while (query.next())
|
while (query.next())
|
||||||
registeredTorrents.append(TorrentID::fromString(query.value(0).toString()));
|
registeredTorrents.append(TorrentID::fromString(query.value(0).toString()));
|
||||||
|
@ -512,9 +436,9 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const QStringList tableMetaItems = {
|
const QStringList tableMetaItems = {
|
||||||
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
|
makeColumnDefinition(DB_COLUMN_ID, u"INTEGER PRIMARY KEY"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_NAME, "TEXT NOT NULL UNIQUE"),
|
makeColumnDefinition(DB_COLUMN_NAME, u"TEXT NOT NULL UNIQUE"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_VALUE, "BLOB")
|
makeColumnDefinition(DB_COLUMN_VALUE, u"BLOB"_s)
|
||||||
};
|
};
|
||||||
const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
|
const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
|
||||||
if (!query.exec(createTableMetaQuery))
|
if (!query.exec(createTableMetaQuery))
|
||||||
|
@ -531,29 +455,29 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
const QStringList tableTorrentsItems = {
|
const QStringList tableTorrentsItems = {
|
||||||
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
|
makeColumnDefinition(DB_COLUMN_ID, u"INTEGER PRIMARY KEY"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_TORRENT_ID, "BLOB NOT NULL UNIQUE"),
|
makeColumnDefinition(DB_COLUMN_TORRENT_ID, u"BLOB NOT NULL UNIQUE"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, "INTEGER NOT NULL DEFAULT -1"),
|
makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, u"INTEGER NOT NULL DEFAULT -1"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_NAME, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_NAME, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_CATEGORY, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_CATEGORY, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_TAGS, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_TAGS, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, "TEXT NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, u"TEXT NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, u"INTEGER NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, u"INTEGER NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, u"INTEGER NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_SHARE_LIMIT_ACTION, "TEXT NOT NULL DEFAULT `Default`"),
|
makeColumnDefinition(DB_COLUMN_SHARE_LIMIT_ACTION, u"TEXT NOT NULL DEFAULT `Default`"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, u"INTEGER NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, u"INTEGER NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, "TEXT NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, u"TEXT NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_STOPPED, "INTEGER NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_STOPPED, u"INTEGER NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"),
|
makeColumnDefinition(DB_COLUMN_STOP_CONDITION, u"TEXT NOT NULL DEFAULT `None`"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_SSL_CERTIFICATE, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_SSL_CERTIFICATE, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_SSL_PRIVATE_KEY, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_SSL_PRIVATE_KEY, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_SSL_DH_PARAMS, "TEXT"),
|
makeColumnDefinition(DB_COLUMN_SSL_DH_PARAMS, u"TEXT"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_RESUMEDATA, "BLOB NOT NULL"),
|
makeColumnDefinition(DB_COLUMN_RESUMEDATA, u"BLOB NOT NULL"_s),
|
||||||
makeColumnDefinition(DB_COLUMN_METADATA, "BLOB")
|
makeColumnDefinition(DB_COLUMN_METADATA, u"BLOB"_s)
|
||||||
};
|
};
|
||||||
const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
|
const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
|
||||||
if (!query.exec(createTableTorrentsQuery))
|
if (!query.exec(createTableTorrentsQuery))
|
||||||
|
@ -591,7 +515,7 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto addColumn = [&query](const QString &table, const Column &column, const char *definition)
|
const auto addColumn = [&query](const QString &table, const Column &column, const QString &definition)
|
||||||
{
|
{
|
||||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s.arg(quoted(column.name), quoted(table));
|
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s.arg(quoted(column.name), quoted(table));
|
||||||
if (query.exec(testQuery))
|
if (query.exec(testQuery))
|
||||||
|
@ -603,10 +527,10 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fromVersion <= 1)
|
if (fromVersion <= 1)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_DOWNLOAD_PATH, "TEXT");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_DOWNLOAD_PATH, u"TEXT"_s);
|
||||||
|
|
||||||
if (fromVersion <= 2)
|
if (fromVersion <= 2)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_STOP_CONDITION, u"TEXT NOT NULL DEFAULT `None`"_s);
|
||||||
|
|
||||||
if (fromVersion <= 3)
|
if (fromVersion <= 3)
|
||||||
{
|
{
|
||||||
|
@ -618,17 +542,41 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromVersion <= 4)
|
if (fromVersion <= 4)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL DEFAULT -2");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, u"INTEGER NOT NULL DEFAULT -2"_s);
|
||||||
|
|
||||||
if (fromVersion <= 5)
|
if (fromVersion <= 5)
|
||||||
{
|
{
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_CERTIFICATE, "TEXT");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_CERTIFICATE, u"TEXT"_s);
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_PRIVATE_KEY, "TEXT");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_PRIVATE_KEY, u"TEXT"_s);
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_DH_PARAMS, "TEXT");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_DH_PARAMS, u"TEXT"_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromVersion <= 6)
|
if (fromVersion <= 6)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, "TEXTNOT NULL DEFAULT `Default`");
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, u"TEXT NOT NULL DEFAULT `Default`"_s);
|
||||||
|
|
||||||
|
if (fromVersion == 7)
|
||||||
|
{
|
||||||
|
const QString TEMP_COLUMN_NAME = DB_COLUMN_SHARE_LIMIT_ACTION.name + u"_temp";
|
||||||
|
|
||||||
|
auto queryStr = u"ALTER TABLE %1 ADD %2 %3"_s
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), TEMP_COLUMN_NAME, u"TEXT NOT NULL DEFAULT `Default`");
|
||||||
|
if (!query.exec(queryStr))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
queryStr = u"UPDATE %1 SET %2 = %3"_s
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), quoted(TEMP_COLUMN_NAME), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name));
|
||||||
|
if (!query.exec(queryStr))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
queryStr = u"ALTER TABLE %1 DROP %2"_s.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name));
|
||||||
|
if (!query.exec(queryStr))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
queryStr = u"ALTER TABLE %1 RENAME %2 TO %3"_s
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), quoted(TEMP_COLUMN_NAME), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name));
|
||||||
|
if (!query.exec(queryStr))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
||||||
if (!query.prepare(updateMetaVersionQuery))
|
if (!query.prepare(updateMetaVersionQuery))
|
||||||
|
@ -666,6 +614,90 @@ void BitTorrent::DBResumeDataStorage::enableWALMode() const
|
||||||
throw RuntimeError(tr("WAL mode is probably unsupported due to filesystem limitations."));
|
throw RuntimeError(tr("WAL mode is probably unsupported due to filesystem limitations."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadResumeDataResult DBResumeDataStorage::parseQueryResultRow(const QSqlQuery &query) const
|
||||||
|
{
|
||||||
|
LoadTorrentParams resumeData;
|
||||||
|
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
||||||
|
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
||||||
|
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
||||||
|
if (!tagsData.isEmpty())
|
||||||
|
{
|
||||||
|
const QStringList tagList = tagsData.split(u',');
|
||||||
|
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
||||||
|
}
|
||||||
|
resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
||||||
|
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
||||||
|
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
||||||
|
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
|
||||||
|
resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt();
|
||||||
|
resumeData.shareLimitAction = Utils::String::toEnum<ShareLimitAction>(
|
||||||
|
query.value(DB_COLUMN_SHARE_LIMIT_ACTION.name).toString(), ShareLimitAction::Default);
|
||||||
|
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
|
||||||
|
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
|
||||||
|
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
|
||||||
|
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
|
||||||
|
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||||
|
resumeData.stopCondition = Utils::String::toEnum(
|
||||||
|
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
|
||||||
|
resumeData.sslParameters =
|
||||||
|
{
|
||||||
|
.certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()),
|
||||||
|
.privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()),
|
||||||
|
.dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray()
|
||||||
|
};
|
||||||
|
|
||||||
|
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||||
|
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||||
|
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||||
|
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
||||||
|
const auto *pref = Preferences::instance();
|
||||||
|
const int bdecodeDepthLimit = pref->getBdecodeDepthLimit();
|
||||||
|
const int bdecodeTokenLimit = pref->getBdecodeTokenLimit();
|
||||||
|
|
||||||
|
lt::error_code ec;
|
||||||
|
const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
|
||||||
|
if (ec)
|
||||||
|
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
|
||||||
|
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||||
|
|
||||||
|
p = lt::read_resume_data(resumeDataRoot, ec);
|
||||||
|
if (ec)
|
||||||
|
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
|
||||||
|
if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray()
|
||||||
|
; !bencodedMetadata.isEmpty())
|
||||||
|
{
|
||||||
|
const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec
|
||||||
|
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
|
||||||
|
if (ec)
|
||||||
|
return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
|
||||||
|
p.ti = std::make_shared<lt::torrent_info>(torentInfoRoot, ec);
|
||||||
|
if (ec)
|
||||||
|
return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message())));
|
||||||
|
}
|
||||||
|
|
||||||
|
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
|
||||||
|
.toString().toStdString();
|
||||||
|
if (p.save_path.empty())
|
||||||
|
return nonstd::make_unexpected(tr("Corrupted resume data: %1").arg(tr("save_path is invalid")));
|
||||||
|
|
||||||
|
if (p.flags & lt::torrent_flags::stop_when_ready)
|
||||||
|
{
|
||||||
|
p.flags &= ~lt::torrent_flags::stop_when_ready;
|
||||||
|
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resumeData;
|
||||||
|
}
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, QReadWriteLock &dbLock, QObject *parent)
|
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, QReadWriteLock &dbLock, QObject *parent)
|
||||||
: QThread(parent)
|
: QThread(parent)
|
||||||
, m_path {dbPath}
|
, m_path {dbPath}
|
||||||
|
@ -737,9 +769,9 @@ void DBResumeDataStorage::Worker::requestInterruption()
|
||||||
m_waitCondition.wakeAll();
|
m_waitCondition.wakeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData)
|
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, LoadTorrentParams resumeData)
|
||||||
{
|
{
|
||||||
addJob(std::make_unique<StoreJob>(id, resumeData));
|
addJob(std::make_unique<StoreJob>(id, std::move(resumeData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::remove(const TorrentID &id)
|
void BitTorrent::DBResumeDataStorage::Worker::remove(const TorrentID &id)
|
||||||
|
@ -747,7 +779,7 @@ void BitTorrent::DBResumeDataStorage::Worker::remove(const TorrentID &id)
|
||||||
addJob(std::make_unique<RemoveJob>(id));
|
addJob(std::make_unique<RemoveJob>(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue)
|
void BitTorrent::DBResumeDataStorage::Worker::storeQueue(const QList<TorrentID> &queue)
|
||||||
{
|
{
|
||||||
addJob(std::make_unique<StoreQueueJob>(queue));
|
addJob(std::make_unique<StoreQueueJob>(queue));
|
||||||
}
|
}
|
||||||
|
@ -765,9 +797,9 @@ namespace
|
||||||
{
|
{
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
StoreJob::StoreJob(const TorrentID &torrentID, const LoadTorrentParams &resumeData)
|
StoreJob::StoreJob(const TorrentID &torrentID, LoadTorrentParams resumeData)
|
||||||
: m_torrentID {torrentID}
|
: m_torrentID {torrentID}
|
||||||
, m_resumeData {resumeData}
|
, m_resumeData {std::move(resumeData)}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,7 +829,7 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Column> columns {
|
QList<Column> columns {
|
||||||
DB_COLUMN_TORRENT_ID,
|
DB_COLUMN_TORRENT_ID,
|
||||||
DB_COLUMN_NAME,
|
DB_COLUMN_NAME,
|
||||||
DB_COLUMN_CATEGORY,
|
DB_COLUMN_CATEGORY,
|
||||||
|
@ -929,7 +961,7 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StoreQueueJob::StoreQueueJob(const QVector<TorrentID> &queue)
|
StoreQueueJob::StoreQueueJob(const QList<TorrentID> &queue)
|
||||||
: m_queue {queue}
|
: m_queue {queue}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2021-2022 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2021-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -31,10 +31,9 @@
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
#include "base/utils/thread.h"
|
|
||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
|
||||||
class QThread;
|
class QSqlQuery;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
|
@ -47,12 +46,12 @@ namespace BitTorrent
|
||||||
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
||||||
~DBResumeDataStorage() override;
|
~DBResumeDataStorage() override;
|
||||||
|
|
||||||
QVector<TorrentID> registeredTorrents() const override;
|
QList<TorrentID> registeredTorrents() const override;
|
||||||
LoadResumeDataResult load(const TorrentID &id) const override;
|
LoadResumeDataResult load(const TorrentID &id) const override;
|
||||||
|
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const override;
|
void store(const TorrentID &id, LoadTorrentParams resumeData) const override;
|
||||||
void remove(const TorrentID &id) const override;
|
void remove(const TorrentID &id) const override;
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
void storeQueue(const QList<TorrentID> &queue) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doLoadAll() const override;
|
void doLoadAll() const override;
|
||||||
|
@ -60,8 +59,7 @@ namespace BitTorrent
|
||||||
void createDB() const;
|
void createDB() const;
|
||||||
void updateDB(int fromVersion) const;
|
void updateDB(int fromVersion) const;
|
||||||
void enableWALMode() const;
|
void enableWALMode() const;
|
||||||
|
LoadResumeDataResult parseQueryResultRow(const QSqlQuery &query) const;
|
||||||
Utils::Thread::UniquePtr m_ioThread;
|
|
||||||
|
|
||||||
class Worker;
|
class Worker;
|
||||||
Worker *m_asyncWorker = nullptr;
|
Worker *m_asyncWorker = nullptr;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2020-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -27,13 +27,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "filesearcher.h"
|
#include "filesearcher.h"
|
||||||
#include "base/bittorrent/common.h"
|
|
||||||
#include "base/bittorrent/infohash.h"
|
|
||||||
|
|
||||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
#include <QPromise>
|
||||||
, const Path &savePath, const Path &downloadPath, const bool forceAppendExt)
|
|
||||||
|
#include "base/bittorrent/common.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
{
|
{
|
||||||
const auto findInDir = [](const Path &dirPath, PathList &fileNames, const bool forceAppendExt) -> bool
|
bool findInDir(const Path &dirPath, PathList &fileNames, const bool forceAppendExt)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (Path &fileName : fileNames)
|
for (Path &fileName : fileNames)
|
||||||
|
@ -58,8 +59,12 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSearcher::search(const PathList &originalFileNames, const Path &savePath
|
||||||
|
, const Path &downloadPath, const bool forceAppendExt, QPromise<FileSearchResult> &promise)
|
||||||
|
{
|
||||||
Path usedPath = savePath;
|
Path usedPath = savePath;
|
||||||
PathList adjustedFileNames = originalFileNames;
|
PathList adjustedFileNames = originalFileNames;
|
||||||
const bool found = findInDir(usedPath, adjustedFileNames, (forceAppendExt && downloadPath.isEmpty()));
|
const bool found = findInDir(usedPath, adjustedFileNames, (forceAppendExt && downloadPath.isEmpty()));
|
||||||
|
@ -69,5 +74,5 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi
|
||||||
findInDir(usedPath, adjustedFileNames, forceAppendExt);
|
findInDir(usedPath, adjustedFileNames, forceAppendExt);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit searchFinished(id, usedPath, adjustedFileNames);
|
promise.addResult(FileSearchResult {.savePath = usedPath, .fileNames = adjustedFileNames});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2020-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -32,10 +32,13 @@
|
||||||
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
|
||||||
namespace BitTorrent
|
template <typename T> class QPromise;
|
||||||
|
|
||||||
|
struct FileSearchResult
|
||||||
{
|
{
|
||||||
class TorrentID;
|
Path savePath;
|
||||||
}
|
PathList fileNames;
|
||||||
|
};
|
||||||
|
|
||||||
class FileSearcher final : public QObject
|
class FileSearcher final : public QObject
|
||||||
{
|
{
|
||||||
|
@ -43,12 +46,8 @@ class FileSearcher final : public QObject
|
||||||
Q_DISABLE_COPY_MOVE(FileSearcher)
|
Q_DISABLE_COPY_MOVE(FileSearcher)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileSearcher() = default;
|
using QObject::QObject;
|
||||||
|
|
||||||
public slots:
|
void search(const PathList &originalFileNames, const Path &savePath
|
||||||
void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
, const Path &downloadPath, bool forceAppendExt, QPromise<FileSearchResult> &promise);
|
||||||
, const Path &savePath, const Path &downloadPath, bool forceAppendExt);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void searchFinished(const BitTorrent::TorrentID &id, const Path &savePath, const PathList &fileNames);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -133,7 +133,7 @@ int FilterParserThread::parseDATFilterFile()
|
||||||
return ruleCount;
|
return ruleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QVector
|
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QList
|
||||||
qint64 bytesRead = 0;
|
qint64 bytesRead = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
@ -297,7 +297,7 @@ int FilterParserThread::parseP2PFilterFile()
|
||||||
return ruleCount;
|
return ruleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QVector
|
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QList
|
||||||
qint64 bytesRead = 0;
|
qint64 bytesRead = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/global.h"
|
||||||
|
|
||||||
const int TorrentIDTypeId = qRegisterMetaType<BitTorrent::TorrentID>();
|
const int TorrentIDTypeId = qRegisterMetaType<BitTorrent::TorrentID>();
|
||||||
|
|
||||||
|
@ -86,6 +89,28 @@ BitTorrent::TorrentID BitTorrent::InfoHash::toTorrentID() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString BitTorrent::InfoHash::toString() const
|
||||||
|
{
|
||||||
|
// Returns a string that is suitable for logging purpose
|
||||||
|
|
||||||
|
QString ret;
|
||||||
|
ret.reserve(40 + 64 + 2); // v1 hash length + v2 hash length + comma
|
||||||
|
|
||||||
|
const SHA1Hash v1Hash = v1();
|
||||||
|
const bool v1IsValid = v1Hash.isValid();
|
||||||
|
if (v1IsValid)
|
||||||
|
ret += v1Hash.toString();
|
||||||
|
|
||||||
|
if (const SHA256Hash v2Hash = v2(); v2Hash.isValid())
|
||||||
|
{
|
||||||
|
if (v1IsValid)
|
||||||
|
ret += u", ";
|
||||||
|
ret += v2Hash.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
BitTorrent::InfoHash::operator WrappedType() const
|
BitTorrent::InfoHash::operator WrappedType() const
|
||||||
{
|
{
|
||||||
return m_nativeHash;
|
return m_nativeHash;
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
#include "base/digest32.h"
|
#include "base/digest32.h"
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
|
||||||
using SHA1Hash = Digest32<160>;
|
using SHA1Hash = Digest32<160>;
|
||||||
using SHA256Hash = Digest32<256>;
|
using SHA256Hash = Digest32<256>;
|
||||||
|
|
||||||
|
@ -79,6 +81,8 @@ namespace BitTorrent
|
||||||
SHA256Hash v2() const;
|
SHA256Hash v2() const;
|
||||||
TorrentID toTorrentID() const;
|
TorrentID toTorrentID() const;
|
||||||
|
|
||||||
|
QString toString() const;
|
||||||
|
|
||||||
operator WrappedType() const;
|
operator WrappedType() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -39,7 +39,11 @@ PeerAddress PeerAddress::parse(const QStringView address)
|
||||||
if (address.startsWith(u'[') && address.contains(u"]:"))
|
if (address.startsWith(u'[') && address.contains(u"]:"))
|
||||||
{ // IPv6
|
{ // IPv6
|
||||||
ipPort = address.split(u"]:");
|
ipPort = address.split(u"]:");
|
||||||
ipPort[0] = ipPort[0].mid(1); // chop '['
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||||
|
ipPort[0].slice(1); // chop '['
|
||||||
|
#else
|
||||||
|
ipPort[0] = ipPort[0].sliced(1); // chop '['
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else if (address.contains(u':'))
|
else if (address.contains(u':'))
|
||||||
{ // IPv4
|
{ // IPv4
|
||||||
|
|
|
@ -30,12 +30,12 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
const int TORRENTIDLIST_TYPEID = qRegisterMetaType<QVector<BitTorrent::TorrentID>>();
|
const int TORRENTIDLIST_TYPEID = qRegisterMetaType<QList<BitTorrent::TorrentID>>();
|
||||||
|
|
||||||
BitTorrent::ResumeDataStorage::ResumeDataStorage(const Path &path, QObject *parent)
|
BitTorrent::ResumeDataStorage::ResumeDataStorage(const Path &path, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -56,6 +56,7 @@ void BitTorrent::ResumeDataStorage::loadAll() const
|
||||||
{
|
{
|
||||||
doLoadAll();
|
doLoadAll();
|
||||||
});
|
});
|
||||||
|
loadingThread->setObjectName("ResumeDataStorage::loadAll loadingThread");
|
||||||
connect(loadingThread, &QThread::finished, loadingThread, &QObject::deleteLater);
|
connect(loadingThread, &QThread::finished, loadingThread, &QObject::deleteLater);
|
||||||
loadingThread->start();
|
loadingThread->start();
|
||||||
}
|
}
|
||||||
|
@ -70,8 +71,8 @@ QList<BitTorrent::LoadedResumeData> BitTorrent::ResumeDataStorage::fetchLoadedRe
|
||||||
return loadedResumeData;
|
return loadedResumeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::ResumeDataStorage::onResumeDataLoaded(const TorrentID &torrentID, const LoadResumeDataResult &loadResumeDataResult) const
|
void BitTorrent::ResumeDataStorage::onResumeDataLoaded(const TorrentID &torrentID, LoadResumeDataResult loadResumeDataResult) const
|
||||||
{
|
{
|
||||||
const QMutexLocker locker {&m_loadedResumeDataMutex};
|
const QMutexLocker locker {&m_loadedResumeDataMutex};
|
||||||
m_loadedResumeData.append({torrentID, loadResumeDataResult});
|
m_loadedResumeData.append({.torrentID = torrentID, .result = std::move(loadResumeDataResult)});
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,21 +58,21 @@ namespace BitTorrent
|
||||||
|
|
||||||
Path path() const;
|
Path path() const;
|
||||||
|
|
||||||
virtual QVector<TorrentID> registeredTorrents() const = 0;
|
virtual QList<TorrentID> registeredTorrents() const = 0;
|
||||||
virtual LoadResumeDataResult load(const TorrentID &id) const = 0;
|
virtual LoadResumeDataResult load(const TorrentID &id) const = 0;
|
||||||
virtual void store(const TorrentID &id, const LoadTorrentParams &resumeData) const = 0;
|
virtual void store(const TorrentID &id, LoadTorrentParams resumeData) const = 0;
|
||||||
virtual void remove(const TorrentID &id) const = 0;
|
virtual void remove(const TorrentID &id) const = 0;
|
||||||
virtual void storeQueue(const QVector<TorrentID> &queue) const = 0;
|
virtual void storeQueue(const QList<TorrentID> &queue) const = 0;
|
||||||
|
|
||||||
void loadAll() const;
|
void loadAll() const;
|
||||||
QList<LoadedResumeData> fetchLoadedResumeData() const;
|
QList<LoadedResumeData> fetchLoadedResumeData() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loadStarted(const QVector<BitTorrent::TorrentID> &torrents);
|
void loadStarted(const QList<BitTorrent::TorrentID> &torrents);
|
||||||
void loadFinished();
|
void loadFinished();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onResumeDataLoaded(const TorrentID &torrentID, const LoadResumeDataResult &loadResumeDataResult) const;
|
void onResumeDataLoaded(const TorrentID &torrentID, LoadResumeDataResult loadResumeDataResult) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void doLoadAll() const = 0;
|
virtual void doLoadAll() const = 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
|
#include "addtorrenterror.h"
|
||||||
#include "addtorrentparams.h"
|
#include "addtorrentparams.h"
|
||||||
#include "categoryoptions.h"
|
#include "categoryoptions.h"
|
||||||
#include "sharelimitaction.h"
|
#include "sharelimitaction.h"
|
||||||
|
@ -237,6 +238,12 @@ namespace BitTorrent
|
||||||
virtual Path finishedTorrentExportDirectory() const = 0;
|
virtual Path finishedTorrentExportDirectory() const = 0;
|
||||||
virtual void setFinishedTorrentExportDirectory(const Path &path) = 0;
|
virtual void setFinishedTorrentExportDirectory(const Path &path) = 0;
|
||||||
|
|
||||||
|
virtual bool isAddTrackersFromURLEnabled() const = 0;
|
||||||
|
virtual void setAddTrackersFromURLEnabled(bool enabled) = 0;
|
||||||
|
virtual QString additionalTrackersURL() const = 0;
|
||||||
|
virtual void setAdditionalTrackersURL(const QString &url) = 0;
|
||||||
|
virtual QString additionalTrackersFromURL() const = 0;
|
||||||
|
|
||||||
virtual int globalDownloadSpeedLimit() const = 0;
|
virtual int globalDownloadSpeedLimit() const = 0;
|
||||||
virtual void setGlobalDownloadSpeedLimit(int limit) = 0;
|
virtual void setGlobalDownloadSpeedLimit(int limit) = 0;
|
||||||
virtual int globalUploadSpeedLimit() const = 0;
|
virtual int globalUploadSpeedLimit() const = 0;
|
||||||
|
@ -258,6 +265,8 @@ namespace BitTorrent
|
||||||
virtual void setPerformanceWarningEnabled(bool enable) = 0;
|
virtual void setPerformanceWarningEnabled(bool enable) = 0;
|
||||||
virtual int saveResumeDataInterval() const = 0;
|
virtual int saveResumeDataInterval() const = 0;
|
||||||
virtual void setSaveResumeDataInterval(int value) = 0;
|
virtual void setSaveResumeDataInterval(int value) = 0;
|
||||||
|
virtual std::chrono::minutes saveStatisticsInterval() const = 0;
|
||||||
|
virtual void setSaveStatisticsInterval(std::chrono::minutes value) = 0;
|
||||||
virtual int shutdownTimeout() const = 0;
|
virtual int shutdownTimeout() const = 0;
|
||||||
virtual void setShutdownTimeout(int value) = 0;
|
virtual void setShutdownTimeout(int value) = 0;
|
||||||
virtual int port() const = 0;
|
virtual int port() const = 0;
|
||||||
|
@ -384,6 +393,8 @@ namespace BitTorrent
|
||||||
virtual void setIncludeOverheadInLimits(bool include) = 0;
|
virtual void setIncludeOverheadInLimits(bool include) = 0;
|
||||||
virtual QString announceIP() const = 0;
|
virtual QString announceIP() const = 0;
|
||||||
virtual void setAnnounceIP(const QString &ip) = 0;
|
virtual void setAnnounceIP(const QString &ip) = 0;
|
||||||
|
virtual int announcePort() const = 0;
|
||||||
|
virtual void setAnnouncePort(int port) = 0;
|
||||||
virtual int maxConcurrentHTTPAnnounces() const = 0;
|
virtual int maxConcurrentHTTPAnnounces() const = 0;
|
||||||
virtual void setMaxConcurrentHTTPAnnounces(int value) = 0;
|
virtual void setMaxConcurrentHTTPAnnounces(int value) = 0;
|
||||||
virtual bool isReannounceWhenAddressChangedEnabled() const = 0;
|
virtual bool isReannounceWhenAddressChangedEnabled() const = 0;
|
||||||
|
@ -411,6 +422,8 @@ namespace BitTorrent
|
||||||
virtual void setUTPRateLimited(bool limited) = 0;
|
virtual void setUTPRateLimited(bool limited) = 0;
|
||||||
virtual MixedModeAlgorithm utpMixedMode() const = 0;
|
virtual MixedModeAlgorithm utpMixedMode() const = 0;
|
||||||
virtual void setUtpMixedMode(MixedModeAlgorithm mode) = 0;
|
virtual void setUtpMixedMode(MixedModeAlgorithm mode) = 0;
|
||||||
|
virtual int hostnameCacheTTL() const = 0;
|
||||||
|
virtual void setHostnameCacheTTL(int value) = 0;
|
||||||
virtual bool isIDNSupportEnabled() const = 0;
|
virtual bool isIDNSupportEnabled() const = 0;
|
||||||
virtual void setIDNSupportEnabled(bool enabled) = 0;
|
virtual void setIDNSupportEnabled(bool enabled) = 0;
|
||||||
virtual bool multiConnectionsPerIpEnabled() const = 0;
|
virtual bool multiConnectionsPerIpEnabled() const = 0;
|
||||||
|
@ -447,7 +460,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
virtual Torrent *getTorrent(const TorrentID &id) const = 0;
|
virtual Torrent *getTorrent(const TorrentID &id) const = 0;
|
||||||
virtual Torrent *findTorrent(const InfoHash &infoHash) const = 0;
|
virtual Torrent *findTorrent(const InfoHash &infoHash) const = 0;
|
||||||
virtual QVector<Torrent *> torrents() const = 0;
|
virtual QList<Torrent *> torrents() const = 0;
|
||||||
virtual qsizetype torrentsCount() const = 0;
|
virtual qsizetype torrentsCount() const = 0;
|
||||||
virtual const SessionStatus &status() const = 0;
|
virtual const SessionStatus &status() const = 0;
|
||||||
virtual const CacheStatus &cacheStatus() const = 0;
|
virtual const CacheStatus &cacheStatus() const = 0;
|
||||||
|
@ -461,21 +474,25 @@ namespace BitTorrent
|
||||||
virtual bool downloadMetadata(const TorrentDescriptor &torrentDescr) = 0;
|
virtual bool downloadMetadata(const TorrentDescriptor &torrentDescr) = 0;
|
||||||
virtual bool cancelDownloadMetadata(const TorrentID &id) = 0;
|
virtual bool cancelDownloadMetadata(const TorrentID &id) = 0;
|
||||||
|
|
||||||
virtual void increaseTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
virtual void increaseTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
||||||
virtual void decreaseTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
virtual void decreaseTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
||||||
virtual void topTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
virtual void topTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
||||||
virtual void bottomTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
virtual void bottomTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
||||||
|
|
||||||
|
virtual QString lastExternalIPv4Address() const = 0;
|
||||||
|
virtual QString lastExternalIPv6Address() const = 0;
|
||||||
|
|
||||||
|
virtual qint64 freeDiskSpace() const = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void startupProgressUpdated(int progress);
|
void startupProgressUpdated(int progress);
|
||||||
void addTorrentFailed(const InfoHash &infoHash, const QString &reason);
|
void addTorrentFailed(const InfoHash &infoHash, const AddTorrentError &reason);
|
||||||
void allTorrentsFinished();
|
void allTorrentsFinished();
|
||||||
void categoryAdded(const QString &categoryName);
|
void categoryAdded(const QString &categoryName);
|
||||||
void categoryRemoved(const QString &categoryName);
|
void categoryRemoved(const QString &categoryName);
|
||||||
void categoryOptionsChanged(const QString &categoryName);
|
void categoryOptionsChanged(const QString &categoryName);
|
||||||
void fullDiskError(Torrent *torrent, const QString &msg);
|
void fullDiskError(Torrent *torrent, const QString &msg);
|
||||||
void IPFilterParsed(bool error, int ruleCount);
|
void IPFilterParsed(bool error, int ruleCount);
|
||||||
void loadTorrentFailed(const QString &error);
|
|
||||||
void metadataDownloaded(const TorrentInfo &info);
|
void metadataDownloaded(const TorrentInfo &info);
|
||||||
void restored();
|
void restored();
|
||||||
void paused();
|
void paused();
|
||||||
|
@ -495,16 +512,17 @@ namespace BitTorrent
|
||||||
void torrentStarted(Torrent *torrent);
|
void torrentStarted(Torrent *torrent);
|
||||||
void torrentSavePathChanged(Torrent *torrent);
|
void torrentSavePathChanged(Torrent *torrent);
|
||||||
void torrentSavingModeChanged(Torrent *torrent);
|
void torrentSavingModeChanged(Torrent *torrent);
|
||||||
void torrentsLoaded(const QVector<Torrent *> &torrents);
|
void torrentsLoaded(const QList<Torrent *> &torrents);
|
||||||
void torrentsUpdated(const QVector<Torrent *> &torrents);
|
void torrentsUpdated(const QList<Torrent *> &torrents);
|
||||||
void torrentTagAdded(Torrent *torrent, const Tag &tag);
|
void torrentTagAdded(Torrent *torrent, const Tag &tag);
|
||||||
void torrentTagRemoved(Torrent *torrent, const Tag &tag);
|
void torrentTagRemoved(Torrent *torrent, const Tag &tag);
|
||||||
void trackerError(Torrent *torrent, const QString &tracker);
|
void trackerError(Torrent *torrent, const QString &tracker);
|
||||||
void trackersAdded(Torrent *torrent, const QVector<TrackerEntry> &trackers);
|
void trackersAdded(Torrent *torrent, const QList<TrackerEntry> &trackers);
|
||||||
void trackersChanged(Torrent *torrent);
|
void trackersChanged(Torrent *torrent);
|
||||||
void trackersRemoved(Torrent *torrent, const QStringList &trackers);
|
void trackersRemoved(Torrent *torrent, const QStringList &trackers);
|
||||||
void trackerSuccess(Torrent *torrent, const QString &tracker);
|
void trackerSuccess(Torrent *torrent, const QString &tracker);
|
||||||
void trackerWarning(Torrent *torrent, const QString &tracker);
|
void trackerWarning(Torrent *torrent, const QString &tracker);
|
||||||
void trackerEntryStatusesUpdated(Torrent *torrent, const QHash<QString, TrackerEntryStatus> &updatedTrackers);
|
void trackerEntryStatusesUpdated(Torrent *torrent, const QHash<QString, TrackerEntryStatus> &updatedTrackers);
|
||||||
|
void freeDiskSpaceChecked(qint64 result);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -29,6 +29,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -37,13 +39,14 @@
|
||||||
#include <libtorrent/torrent_handle.hpp>
|
#include <libtorrent/torrent_handle.hpp>
|
||||||
|
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
#include <QDateTime>
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QMutex>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QVector>
|
#include <QThreadPool>
|
||||||
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
|
@ -54,19 +57,21 @@
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "sessionstatus.h"
|
#include "sessionstatus.h"
|
||||||
#include "torrentinfo.h"
|
#include "torrentinfo.h"
|
||||||
#include "trackerentrystatus.h"
|
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
class QThread;
|
|
||||||
class QThreadPool;
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
|
template <typename T> class QFuture;
|
||||||
|
|
||||||
class BandwidthScheduler;
|
class BandwidthScheduler;
|
||||||
class FileSearcher;
|
class FileSearcher;
|
||||||
class FilterParserThread;
|
class FilterParserThread;
|
||||||
|
class FreeDiskSpaceChecker;
|
||||||
class NativeSessionExtension;
|
class NativeSessionExtension;
|
||||||
|
|
||||||
|
struct FileSearchResult;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
enum class MoveStorageMode;
|
enum class MoveStorageMode;
|
||||||
|
@ -234,6 +239,8 @@ namespace BitTorrent
|
||||||
void setPerformanceWarningEnabled(bool enable) override;
|
void setPerformanceWarningEnabled(bool enable) override;
|
||||||
int saveResumeDataInterval() const override;
|
int saveResumeDataInterval() const override;
|
||||||
void setSaveResumeDataInterval(int value) override;
|
void setSaveResumeDataInterval(int value) override;
|
||||||
|
std::chrono::minutes saveStatisticsInterval() const override;
|
||||||
|
void setSaveStatisticsInterval(std::chrono::minutes value) override;
|
||||||
int shutdownTimeout() const override;
|
int shutdownTimeout() const override;
|
||||||
void setShutdownTimeout(int value) override;
|
void setShutdownTimeout(int value) override;
|
||||||
int port() const override;
|
int port() const override;
|
||||||
|
@ -360,6 +367,8 @@ namespace BitTorrent
|
||||||
void setIncludeOverheadInLimits(bool include) override;
|
void setIncludeOverheadInLimits(bool include) override;
|
||||||
QString announceIP() const override;
|
QString announceIP() const override;
|
||||||
void setAnnounceIP(const QString &ip) override;
|
void setAnnounceIP(const QString &ip) override;
|
||||||
|
int announcePort() const override;
|
||||||
|
void setAnnouncePort(int port) override;
|
||||||
int maxConcurrentHTTPAnnounces() const override;
|
int maxConcurrentHTTPAnnounces() const override;
|
||||||
void setMaxConcurrentHTTPAnnounces(int value) override;
|
void setMaxConcurrentHTTPAnnounces(int value) override;
|
||||||
bool isReannounceWhenAddressChangedEnabled() const override;
|
bool isReannounceWhenAddressChangedEnabled() const override;
|
||||||
|
@ -387,6 +396,8 @@ namespace BitTorrent
|
||||||
void setUTPRateLimited(bool limited) override;
|
void setUTPRateLimited(bool limited) override;
|
||||||
MixedModeAlgorithm utpMixedMode() const override;
|
MixedModeAlgorithm utpMixedMode() const override;
|
||||||
void setUtpMixedMode(MixedModeAlgorithm mode) override;
|
void setUtpMixedMode(MixedModeAlgorithm mode) override;
|
||||||
|
int hostnameCacheTTL() const override;
|
||||||
|
void setHostnameCacheTTL(int value) override;
|
||||||
bool isIDNSupportEnabled() const override;
|
bool isIDNSupportEnabled() const override;
|
||||||
void setIDNSupportEnabled(bool enabled) override;
|
void setIDNSupportEnabled(bool enabled) override;
|
||||||
bool multiConnectionsPerIpEnabled() const override;
|
bool multiConnectionsPerIpEnabled() const override;
|
||||||
|
@ -423,7 +434,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
Torrent *getTorrent(const TorrentID &id) const override;
|
Torrent *getTorrent(const TorrentID &id) const override;
|
||||||
Torrent *findTorrent(const InfoHash &infoHash) const override;
|
Torrent *findTorrent(const InfoHash &infoHash) const override;
|
||||||
QVector<Torrent *> torrents() const override;
|
QList<Torrent *> torrents() const override;
|
||||||
qsizetype torrentsCount() const override;
|
qsizetype torrentsCount() const override;
|
||||||
const SessionStatus &status() const override;
|
const SessionStatus &status() const override;
|
||||||
const CacheStatus &cacheStatus() const override;
|
const CacheStatus &cacheStatus() const override;
|
||||||
|
@ -437,10 +448,15 @@ namespace BitTorrent
|
||||||
bool downloadMetadata(const TorrentDescriptor &torrentDescr) override;
|
bool downloadMetadata(const TorrentDescriptor &torrentDescr) override;
|
||||||
bool cancelDownloadMetadata(const TorrentID &id) override;
|
bool cancelDownloadMetadata(const TorrentID &id) override;
|
||||||
|
|
||||||
void increaseTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
void increaseTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
||||||
void decreaseTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
void decreaseTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
||||||
void topTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
void topTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
||||||
void bottomTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
void bottomTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
||||||
|
|
||||||
|
QString lastExternalIPv4Address() const override;
|
||||||
|
QString lastExternalIPv6Address() const override;
|
||||||
|
|
||||||
|
qint64 freeDiskSpace() const override;
|
||||||
|
|
||||||
// Torrent interface
|
// Torrent interface
|
||||||
void handleTorrentResumeDataRequested(const TorrentImpl *torrent);
|
void handleTorrentResumeDataRequested(const TorrentImpl *torrent);
|
||||||
|
@ -456,34 +472,43 @@ namespace BitTorrent
|
||||||
void handleTorrentStarted(TorrentImpl *torrent);
|
void handleTorrentStarted(TorrentImpl *torrent);
|
||||||
void handleTorrentChecked(TorrentImpl *torrent);
|
void handleTorrentChecked(TorrentImpl *torrent);
|
||||||
void handleTorrentFinished(TorrentImpl *torrent);
|
void handleTorrentFinished(TorrentImpl *torrent);
|
||||||
void handleTorrentTrackersAdded(TorrentImpl *torrent, const QVector<TrackerEntry> &newTrackers);
|
void handleTorrentTrackersAdded(TorrentImpl *torrent, const QList<TrackerEntry> &newTrackers);
|
||||||
void handleTorrentTrackersRemoved(TorrentImpl *torrent, const QStringList &deletedTrackers);
|
void handleTorrentTrackersRemoved(TorrentImpl *torrent, const QStringList &deletedTrackers);
|
||||||
void handleTorrentTrackersChanged(TorrentImpl *torrent);
|
void handleTorrentTrackersChanged(TorrentImpl *torrent);
|
||||||
void handleTorrentUrlSeedsAdded(TorrentImpl *torrent, const QVector<QUrl> &newUrlSeeds);
|
void handleTorrentUrlSeedsAdded(TorrentImpl *torrent, const QList<QUrl> &newUrlSeeds);
|
||||||
void handleTorrentUrlSeedsRemoved(TorrentImpl *torrent, const QVector<QUrl> &urlSeeds);
|
void handleTorrentUrlSeedsRemoved(TorrentImpl *torrent, const QList<QUrl> &urlSeeds);
|
||||||
void handleTorrentResumeDataReady(TorrentImpl *torrent, const LoadTorrentParams &data);
|
void handleTorrentResumeDataReady(TorrentImpl *torrent, LoadTorrentParams data);
|
||||||
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
||||||
void handleTorrentStorageMovingStateChanged(TorrentImpl *torrent);
|
void handleTorrentStorageMovingStateChanged(TorrentImpl *torrent);
|
||||||
|
|
||||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context);
|
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context);
|
||||||
|
|
||||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
lt::torrent_handle reloadTorrent(const lt::torrent_handle ¤tHandle, lt::add_torrent_params params);
|
||||||
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
|
||||||
|
QFuture<FileSearchResult> findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||||
|
|
||||||
void enablePortMapping();
|
void enablePortMapping();
|
||||||
void disablePortMapping();
|
void disablePortMapping();
|
||||||
void addMappedPorts(const QSet<quint16> &ports);
|
void addMappedPorts(const QSet<quint16> &ports);
|
||||||
void removeMappedPorts(const QSet<quint16> &ports);
|
void removeMappedPorts(const QSet<quint16> &ports);
|
||||||
|
|
||||||
QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint) const;
|
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
void invoke(Func &&func)
|
void invoke(Func &&func)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, std::forward<Func>(func), Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, std::forward<Func>(func), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void invokeAsync(std::function<void ()> func);
|
template <typename Func>
|
||||||
|
void invokeAsync(Func &&func)
|
||||||
|
{
|
||||||
|
m_asyncWorker->start(std::forward<Func>(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAddTrackersFromURLEnabled() const override;
|
||||||
|
void setAddTrackersFromURLEnabled(bool enabled) override;
|
||||||
|
QString additionalTrackersURL() const override;
|
||||||
|
void setAdditionalTrackersURL(const QString &url) override;
|
||||||
|
QString additionalTrackersFromURL() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void addTorrentAlertsReceived(qsizetype count);
|
void addTorrentAlertsReceived(qsizetype count);
|
||||||
|
@ -495,7 +520,6 @@ namespace BitTorrent
|
||||||
void generateResumeData();
|
void generateResumeData();
|
||||||
void handleIPFilterParsed(int ruleCount);
|
void handleIPFilterParsed(int ruleCount);
|
||||||
void handleIPFilterError();
|
void handleIPFilterError();
|
||||||
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
|
|
||||||
void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage);
|
void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -541,7 +565,6 @@ namespace BitTorrent
|
||||||
void populateAdditionalTrackers();
|
void populateAdditionalTrackers();
|
||||||
void enableIPFilter();
|
void enableIPFilter();
|
||||||
void disableIPFilter();
|
void disableIPFilter();
|
||||||
void processTrackerStatuses();
|
|
||||||
void processTorrentShareLimits(TorrentImpl *torrent);
|
void processTorrentShareLimits(TorrentImpl *torrent);
|
||||||
void populateExcludedFileNamesRegExpList();
|
void populateExcludedFileNamesRegExpList();
|
||||||
void prepareStartup();
|
void prepareStartup();
|
||||||
|
@ -555,8 +578,7 @@ namespace BitTorrent
|
||||||
void updateSeedingLimitTimer();
|
void updateSeedingLimitTimer();
|
||||||
void exportTorrentFile(const Torrent *torrent, const Path &folderPath);
|
void exportTorrentFile(const Torrent *torrent, const Path &folderPath);
|
||||||
|
|
||||||
void handleAlert(const lt::alert *alert);
|
void handleAlert(lt::alert *alert);
|
||||||
void dispatchTorrentAlert(const lt::torrent_alert *alert);
|
|
||||||
void handleAddTorrentAlert(const lt::add_torrent_alert *alert);
|
void handleAddTorrentAlert(const lt::add_torrent_alert *alert);
|
||||||
void handleStateUpdateAlert(const lt::state_update_alert *alert);
|
void handleStateUpdateAlert(const lt::state_update_alert *alert);
|
||||||
void handleMetadataReceivedAlert(const lt::metadata_received_alert *alert);
|
void handleMetadataReceivedAlert(const lt::metadata_received_alert *alert);
|
||||||
|
@ -583,15 +605,28 @@ namespace BitTorrent
|
||||||
void handleTrackerAlert(const lt::tracker_alert *alert);
|
void handleTrackerAlert(const lt::tracker_alert *alert);
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
void handleTorrentConflictAlert(const lt::torrent_conflict_alert *alert);
|
void handleTorrentConflictAlert(const lt::torrent_conflict_alert *alert);
|
||||||
|
void handleFilePrioAlert(const lt::file_prio_alert *alert);
|
||||||
#endif
|
#endif
|
||||||
|
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *alert);
|
||||||
|
void handleFileCompletedAlert(const lt::file_completed_alert *alert);
|
||||||
|
void handleFileRenamedAlert(const lt::file_renamed_alert *alert);
|
||||||
|
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *alert);
|
||||||
|
void handlePerformanceAlert(const lt::performance_alert *alert) const;
|
||||||
|
void handleSaveResumeDataAlert(lt::save_resume_data_alert *alert);
|
||||||
|
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *alert);
|
||||||
|
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *alert);
|
||||||
|
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *alert);
|
||||||
|
|
||||||
TorrentImpl *createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
TorrentImpl *createTorrent(const lt::torrent_handle &nativeHandle, LoadTorrentParams params);
|
||||||
|
TorrentImpl *getTorrent(const lt::torrent_handle &nativeHandle) const;
|
||||||
|
|
||||||
void saveResumeData();
|
void saveResumeData();
|
||||||
void saveTorrentsQueue();
|
void saveTorrentsQueue();
|
||||||
void removeTorrentsQueue();
|
void removeTorrentsQueue();
|
||||||
|
|
||||||
std::vector<lt::alert *> getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const;
|
void populateAdditionalTrackersFromURL();
|
||||||
|
|
||||||
|
void fetchPendingAlerts(lt::time_duration time = lt::time_duration::zero());
|
||||||
|
|
||||||
void moveTorrentStorage(const MoveStorageJob &job) const;
|
void moveTorrentStorage(const MoveStorageJob &job) const;
|
||||||
void handleMoveTorrentStorageJobFinished(const Path &newPath);
|
void handleMoveTorrentStorageJobFinished(const Path &newPath);
|
||||||
|
@ -605,10 +640,13 @@ namespace BitTorrent
|
||||||
void saveStatistics() const;
|
void saveStatistics() const;
|
||||||
void loadStatistics();
|
void loadStatistics();
|
||||||
|
|
||||||
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
|
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle);
|
||||||
|
|
||||||
void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {});
|
void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {});
|
||||||
|
|
||||||
|
void setAdditionalTrackersFromURL(const QString &trackers);
|
||||||
|
void updateTrackersFromURL();
|
||||||
|
|
||||||
CachedSettingValue<QString> m_DHTBootstrapNodes;
|
CachedSettingValue<QString> m_DHTBootstrapNodes;
|
||||||
CachedSettingValue<bool> m_isDHTEnabled;
|
CachedSettingValue<bool> m_isDHTEnabled;
|
||||||
CachedSettingValue<bool> m_isLSDEnabled;
|
CachedSettingValue<bool> m_isLSDEnabled;
|
||||||
|
@ -654,6 +692,7 @@ namespace BitTorrent
|
||||||
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
|
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
|
||||||
CachedSettingValue<bool> m_includeOverheadInLimits;
|
CachedSettingValue<bool> m_includeOverheadInLimits;
|
||||||
CachedSettingValue<QString> m_announceIP;
|
CachedSettingValue<QString> m_announceIP;
|
||||||
|
CachedSettingValue<int> m_announcePort;
|
||||||
CachedSettingValue<int> m_maxConcurrentHTTPAnnounces;
|
CachedSettingValue<int> m_maxConcurrentHTTPAnnounces;
|
||||||
CachedSettingValue<bool> m_isReannounceWhenAddressChangedEnabled;
|
CachedSettingValue<bool> m_isReannounceWhenAddressChangedEnabled;
|
||||||
CachedSettingValue<int> m_stopTrackerTimeout;
|
CachedSettingValue<int> m_stopTrackerTimeout;
|
||||||
|
@ -664,6 +703,7 @@ namespace BitTorrent
|
||||||
CachedSettingValue<BTProtocol> m_btProtocol;
|
CachedSettingValue<BTProtocol> m_btProtocol;
|
||||||
CachedSettingValue<bool> m_isUTPRateLimited;
|
CachedSettingValue<bool> m_isUTPRateLimited;
|
||||||
CachedSettingValue<MixedModeAlgorithm> m_utpMixedMode;
|
CachedSettingValue<MixedModeAlgorithm> m_utpMixedMode;
|
||||||
|
CachedSettingValue<int> m_hostnameCacheTTL;
|
||||||
CachedSettingValue<bool> m_IDNSupportEnabled;
|
CachedSettingValue<bool> m_IDNSupportEnabled;
|
||||||
CachedSettingValue<bool> m_multiConnectionsPerIpEnabled;
|
CachedSettingValue<bool> m_multiConnectionsPerIpEnabled;
|
||||||
CachedSettingValue<bool> m_validateHTTPSTrackerCertificate;
|
CachedSettingValue<bool> m_validateHTTPSTrackerCertificate;
|
||||||
|
@ -671,6 +711,8 @@ namespace BitTorrent
|
||||||
CachedSettingValue<bool> m_blockPeersOnPrivilegedPorts;
|
CachedSettingValue<bool> m_blockPeersOnPrivilegedPorts;
|
||||||
CachedSettingValue<bool> m_isAddTrackersEnabled;
|
CachedSettingValue<bool> m_isAddTrackersEnabled;
|
||||||
CachedSettingValue<QString> m_additionalTrackers;
|
CachedSettingValue<QString> m_additionalTrackers;
|
||||||
|
CachedSettingValue<bool> m_isAddTrackersFromURLEnabled;
|
||||||
|
CachedSettingValue<QString> m_additionalTrackersURL;
|
||||||
CachedSettingValue<qreal> m_globalMaxRatio;
|
CachedSettingValue<qreal> m_globalMaxRatio;
|
||||||
CachedSettingValue<int> m_globalMaxSeedingMinutes;
|
CachedSettingValue<int> m_globalMaxSeedingMinutes;
|
||||||
CachedSettingValue<int> m_globalMaxInactiveSeedingMinutes;
|
CachedSettingValue<int> m_globalMaxInactiveSeedingMinutes;
|
||||||
|
@ -692,6 +734,7 @@ namespace BitTorrent
|
||||||
CachedSettingValue<bool> m_isBandwidthSchedulerEnabled;
|
CachedSettingValue<bool> m_isBandwidthSchedulerEnabled;
|
||||||
CachedSettingValue<bool> m_isPerformanceWarningEnabled;
|
CachedSettingValue<bool> m_isPerformanceWarningEnabled;
|
||||||
CachedSettingValue<int> m_saveResumeDataInterval;
|
CachedSettingValue<int> m_saveResumeDataInterval;
|
||||||
|
CachedSettingValue<int> m_saveStatisticsInterval;
|
||||||
CachedSettingValue<int> m_shutdownTimeout;
|
CachedSettingValue<int> m_shutdownTimeout;
|
||||||
CachedSettingValue<int> m_port;
|
CachedSettingValue<int> m_port;
|
||||||
CachedSettingValue<bool> m_sslEnabled;
|
CachedSettingValue<bool> m_sslEnabled;
|
||||||
|
@ -743,6 +786,9 @@ namespace BitTorrent
|
||||||
bool m_IPFilteringConfigured = false;
|
bool m_IPFilteringConfigured = false;
|
||||||
mutable bool m_listenInterfaceConfigured = false;
|
mutable bool m_listenInterfaceConfigured = false;
|
||||||
|
|
||||||
|
QString m_additionalTrackersFromURL;
|
||||||
|
QTimer *m_updateTrackersFromURLTimer = nullptr;
|
||||||
|
|
||||||
bool m_isRestored = false;
|
bool m_isRestored = false;
|
||||||
bool m_isPaused = isStartPaused();
|
bool m_isPaused = isStartPaused();
|
||||||
|
|
||||||
|
@ -752,8 +798,9 @@ namespace BitTorrent
|
||||||
const bool m_wasPexEnabled = m_isPeXEnabled;
|
const bool m_wasPexEnabled = m_isPeXEnabled;
|
||||||
|
|
||||||
int m_numResumeData = 0;
|
int m_numResumeData = 0;
|
||||||
QVector<TrackerEntry> m_additionalTrackerEntries;
|
QList<TrackerEntry> m_additionalTrackerEntries;
|
||||||
QVector<QRegularExpression> m_excludedFileNamesRegExpList;
|
QList<TrackerEntry> m_additionalTrackerEntriesFromURL;
|
||||||
|
QList<QRegularExpression> m_excludedFileNamesRegExpList;
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
mutable QElapsedTimer m_statisticsLastUpdateTimer;
|
mutable QElapsedTimer m_statisticsLastUpdateTimer;
|
||||||
|
@ -778,22 +825,26 @@ namespace BitTorrent
|
||||||
FileSearcher *m_fileSearcher = nullptr;
|
FileSearcher *m_fileSearcher = nullptr;
|
||||||
TorrentContentRemover *m_torrentContentRemover = nullptr;
|
TorrentContentRemover *m_torrentContentRemover = nullptr;
|
||||||
|
|
||||||
|
using AddTorrentAlertHandler = std::function<void (const lt::add_torrent_alert *alert)>;
|
||||||
|
QList<AddTorrentAlertHandler> m_addTorrentAlertHandlers;
|
||||||
|
|
||||||
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
|
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
|
||||||
|
|
||||||
QHash<TorrentID, TorrentImpl *> m_torrents;
|
QHash<TorrentID, TorrentImpl *> m_torrents;
|
||||||
QHash<TorrentID, TorrentImpl *> m_hybridTorrentsByAltID;
|
QHash<TorrentID, TorrentImpl *> m_hybridTorrentsByAltID;
|
||||||
QHash<TorrentID, LoadTorrentParams> m_loadingTorrents;
|
|
||||||
QHash<TorrentID, RemovingTorrentData> m_removingTorrents;
|
QHash<TorrentID, RemovingTorrentData> m_removingTorrents;
|
||||||
QHash<TorrentID, TorrentID> m_changedTorrentIDs;
|
QHash<TorrentID, TorrentID> m_changedTorrentIDs;
|
||||||
QMap<QString, CategoryOptions> m_categories;
|
QMap<QString, CategoryOptions> m_categories;
|
||||||
TagSet m_tags;
|
TagSet m_tags;
|
||||||
|
|
||||||
|
std::vector<lt::alert *> m_alerts; // make it a class variable so it can preserve its allocated `capacity`
|
||||||
qsizetype m_receivedAddTorrentAlertsCount = 0;
|
qsizetype m_receivedAddTorrentAlertsCount = 0;
|
||||||
QList<Torrent *> m_loadedTorrents;
|
QList<Torrent *> m_loadedTorrents;
|
||||||
|
|
||||||
// This field holds amounts of peers reported by trackers in their responses to announces
|
// This field holds amounts of peers reported by trackers in their responses to announces
|
||||||
// (torrent.tracker_name.tracker_local_endpoint.protocol_version.num_peers)
|
// (torrent.tracker_name.tracker_local_endpoint.protocol_version.num_peers)
|
||||||
QHash<lt::torrent_handle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>>> m_updatedTrackerStatuses;
|
QHash<lt::torrent_handle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>>> m_updatedTrackerStatuses;
|
||||||
|
QMutex m_updatedTrackerStatusesMutex;
|
||||||
|
|
||||||
// I/O errored torrents
|
// I/O errored torrents
|
||||||
QSet<TorrentID> m_recentErroredTorrents;
|
QSet<TorrentID> m_recentErroredTorrents;
|
||||||
|
@ -807,7 +858,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
QList<MoveStorageJob> m_moveStorageQueue;
|
QList<MoveStorageJob> m_moveStorageQueue;
|
||||||
|
|
||||||
QString m_lastExternalIP;
|
QString m_lastExternalIPv4Address;
|
||||||
|
QString m_lastExternalIPv6Address;
|
||||||
|
|
||||||
bool m_needUpgradeDownloadPath = false;
|
bool m_needUpgradeDownloadPath = false;
|
||||||
|
|
||||||
|
@ -817,13 +869,13 @@ namespace BitTorrent
|
||||||
bool m_isPortMappingEnabled = false;
|
bool m_isPortMappingEnabled = false;
|
||||||
QHash<quint16, std::vector<lt::port_mapping_t>> m_mappedPorts;
|
QHash<quint16, std::vector<lt::port_mapping_t>> m_mappedPorts;
|
||||||
|
|
||||||
QTimer *m_wakeupCheckTimer = nullptr;
|
QElapsedTimer m_wakeupCheckTimestamp;
|
||||||
QDateTime m_wakeupCheckTimestamp;
|
|
||||||
|
|
||||||
QList<TorrentImpl *> m_pendingFinishedTorrents;
|
QList<TorrentImpl *> m_pendingFinishedTorrents;
|
||||||
|
|
||||||
QDateTime m_qNow;
|
FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr;
|
||||||
lt::clock_type::time_point m_ltNow;
|
QTimer *m_freeDiskSpaceCheckingTimer = nullptr;
|
||||||
|
qint64 m_freeDiskSpace = -1;
|
||||||
|
|
||||||
friend void Session::initInstance();
|
friend void Session::initInstance();
|
||||||
friend void Session::freeInstance();
|
friend void Session::freeInstance();
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
|
|
||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
|
@ -51,9 +53,7 @@ namespace BitTorrent
|
||||||
const int Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME = -2;
|
const int Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME = -2;
|
||||||
const int Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT = -1;
|
const int Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT = -1;
|
||||||
|
|
||||||
const qreal Torrent::MAX_RATIO = 9999;
|
const qreal Torrent::MAX_RATIO = std::numeric_limits<qreal>::infinity();
|
||||||
const int Torrent::MAX_SEEDING_TIME = 525600;
|
|
||||||
const int Torrent::MAX_INACTIVE_SEEDING_TIME = 525600;
|
|
||||||
|
|
||||||
TorrentID Torrent::id() const
|
TorrentID Torrent::id() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -45,6 +45,8 @@ class QByteArray;
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
|
template <typename T> class QFuture;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
enum class DownloadPriority;
|
enum class DownloadPriority;
|
||||||
|
@ -132,8 +134,6 @@ namespace BitTorrent
|
||||||
static const int NO_INACTIVE_SEEDING_TIME_LIMIT;
|
static const int NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||||
|
|
||||||
static const qreal MAX_RATIO;
|
static const qreal MAX_RATIO;
|
||||||
static const int MAX_SEEDING_TIME;
|
|
||||||
static const int MAX_INACTIVE_SEEDING_TIME;
|
|
||||||
|
|
||||||
using TorrentContentHandler::TorrentContentHandler;
|
using TorrentContentHandler::TorrentContentHandler;
|
||||||
|
|
||||||
|
@ -215,7 +215,15 @@ namespace BitTorrent
|
||||||
virtual int piecesCount() const = 0;
|
virtual int piecesCount() const = 0;
|
||||||
virtual int piecesHave() const = 0;
|
virtual int piecesHave() const = 0;
|
||||||
virtual qreal progress() const = 0;
|
virtual qreal progress() const = 0;
|
||||||
|
|
||||||
virtual QDateTime addedTime() const = 0;
|
virtual QDateTime addedTime() const = 0;
|
||||||
|
virtual QDateTime completedTime() const = 0;
|
||||||
|
virtual QDateTime lastSeenComplete() const = 0;
|
||||||
|
virtual qlonglong activeTime() const = 0;
|
||||||
|
virtual qlonglong finishedTime() const = 0;
|
||||||
|
virtual qlonglong timeSinceUpload() const = 0;
|
||||||
|
virtual qlonglong timeSinceDownload() const = 0;
|
||||||
|
virtual qlonglong timeSinceActivity() const = 0;
|
||||||
|
|
||||||
// Share limits
|
// Share limits
|
||||||
virtual qreal ratioLimit() const = 0;
|
virtual qreal ratioLimit() const = 0;
|
||||||
|
@ -249,13 +257,11 @@ namespace BitTorrent
|
||||||
virtual bool hasMissingFiles() const = 0;
|
virtual bool hasMissingFiles() const = 0;
|
||||||
virtual bool hasError() const = 0;
|
virtual bool hasError() const = 0;
|
||||||
virtual int queuePosition() const = 0;
|
virtual int queuePosition() const = 0;
|
||||||
virtual QVector<TrackerEntryStatus> trackers() const = 0;
|
virtual QList<TrackerEntryStatus> trackers() const = 0;
|
||||||
virtual QVector<QUrl> urlSeeds() const = 0;
|
virtual QList<QUrl> urlSeeds() const = 0;
|
||||||
virtual QString error() const = 0;
|
virtual QString error() const = 0;
|
||||||
virtual qlonglong totalDownload() const = 0;
|
virtual qlonglong totalDownload() const = 0;
|
||||||
virtual qlonglong totalUpload() const = 0;
|
virtual qlonglong totalUpload() const = 0;
|
||||||
virtual qlonglong activeTime() const = 0;
|
|
||||||
virtual qlonglong finishedTime() const = 0;
|
|
||||||
virtual qlonglong eta() const = 0;
|
virtual qlonglong eta() const = 0;
|
||||||
virtual int seedsCount() const = 0;
|
virtual int seedsCount() const = 0;
|
||||||
virtual int peersCount() const = 0;
|
virtual int peersCount() const = 0;
|
||||||
|
@ -263,21 +269,13 @@ namespace BitTorrent
|
||||||
virtual int totalSeedsCount() const = 0;
|
virtual int totalSeedsCount() const = 0;
|
||||||
virtual int totalPeersCount() const = 0;
|
virtual int totalPeersCount() const = 0;
|
||||||
virtual int totalLeechersCount() const = 0;
|
virtual int totalLeechersCount() const = 0;
|
||||||
virtual QDateTime lastSeenComplete() const = 0;
|
|
||||||
virtual QDateTime completedTime() const = 0;
|
|
||||||
virtual qlonglong timeSinceUpload() const = 0;
|
|
||||||
virtual qlonglong timeSinceDownload() const = 0;
|
|
||||||
virtual qlonglong timeSinceActivity() const = 0;
|
|
||||||
virtual int downloadLimit() const = 0;
|
virtual int downloadLimit() const = 0;
|
||||||
virtual int uploadLimit() const = 0;
|
virtual int uploadLimit() const = 0;
|
||||||
virtual bool superSeeding() const = 0;
|
virtual bool superSeeding() const = 0;
|
||||||
virtual bool isDHTDisabled() const = 0;
|
virtual bool isDHTDisabled() const = 0;
|
||||||
virtual bool isPEXDisabled() const = 0;
|
virtual bool isPEXDisabled() const = 0;
|
||||||
virtual bool isLSDDisabled() const = 0;
|
virtual bool isLSDDisabled() const = 0;
|
||||||
virtual QVector<PeerInfo> peers() const = 0;
|
|
||||||
virtual QBitArray pieces() const = 0;
|
virtual QBitArray pieces() const = 0;
|
||||||
virtual QBitArray downloadingPieces() const = 0;
|
|
||||||
virtual QVector<int> pieceAvailability() const = 0;
|
|
||||||
virtual qreal distributedCopies() const = 0;
|
virtual qreal distributedCopies() const = 0;
|
||||||
virtual qreal maxRatio() const = 0;
|
virtual qreal maxRatio() const = 0;
|
||||||
virtual int maxSeedingTime() const = 0;
|
virtual int maxSeedingTime() const = 0;
|
||||||
|
@ -306,11 +304,11 @@ namespace BitTorrent
|
||||||
virtual void setDHTDisabled(bool disable) = 0;
|
virtual void setDHTDisabled(bool disable) = 0;
|
||||||
virtual void setPEXDisabled(bool disable) = 0;
|
virtual void setPEXDisabled(bool disable) = 0;
|
||||||
virtual void setLSDDisabled(bool disable) = 0;
|
virtual void setLSDDisabled(bool disable) = 0;
|
||||||
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
|
virtual void addTrackers(QList<TrackerEntry> trackers) = 0;
|
||||||
virtual void removeTrackers(const QStringList &trackers) = 0;
|
virtual void removeTrackers(const QStringList &trackers) = 0;
|
||||||
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
|
virtual void replaceTrackers(QList<TrackerEntry> trackers) = 0;
|
||||||
virtual void addUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
virtual void addUrlSeeds(const QList<QUrl> &urlSeeds) = 0;
|
||||||
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
virtual void removeUrlSeeds(const QList<QUrl> &urlSeeds) = 0;
|
||||||
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
||||||
virtual void clearPeers() = 0;
|
virtual void clearPeers() = 0;
|
||||||
virtual void setMetadata(const TorrentInfo &torrentInfo) = 0;
|
virtual void setMetadata(const TorrentInfo &torrentInfo) = 0;
|
||||||
|
@ -324,10 +322,10 @@ namespace BitTorrent
|
||||||
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
||||||
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
||||||
|
|
||||||
virtual void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const = 0;
|
virtual QFuture<QList<PeerInfo>> fetchPeerInfo() const = 0;
|
||||||
virtual void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const = 0;
|
virtual QFuture<QList<QUrl>> fetchURLSeeds() const = 0;
|
||||||
virtual void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const = 0;
|
virtual QFuture<QList<int>> fetchPieceAvailability() const = 0;
|
||||||
virtual void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const = 0;
|
virtual QFuture<QBitArray> fetchDownloadingPieces() const = 0;
|
||||||
|
|
||||||
TorrentID id() const;
|
TorrentID id() const;
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2022-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -34,6 +34,8 @@
|
||||||
#include "abstractfilestorage.h"
|
#include "abstractfilestorage.h"
|
||||||
#include "downloadpriority.h"
|
#include "downloadpriority.h"
|
||||||
|
|
||||||
|
template <typename T> class QFuture;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
class TorrentContentHandler : public QObject, public AbstractFileStorage
|
class TorrentContentHandler : public QObject, public AbstractFileStorage
|
||||||
|
@ -44,18 +46,17 @@ namespace BitTorrent
|
||||||
virtual bool hasMetadata() const = 0;
|
virtual bool hasMetadata() const = 0;
|
||||||
virtual Path actualStorageLocation() const = 0;
|
virtual Path actualStorageLocation() const = 0;
|
||||||
virtual Path actualFilePath(int fileIndex) const = 0;
|
virtual Path actualFilePath(int fileIndex) const = 0;
|
||||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
virtual QList<DownloadPriority> filePriorities() const = 0;
|
||||||
virtual QVector<qreal> filesProgress() const = 0;
|
virtual QList<qreal> filesProgress() const = 0;
|
||||||
/**
|
/**
|
||||||
* @brief fraction of file pieces that are available at least from one peer
|
* @brief fraction of file pieces that are available at least from one peer
|
||||||
*
|
*
|
||||||
* This is not the same as torrrent availability, it is just a fraction of pieces
|
* This is not the same as torrrent availability, it is just a fraction of pieces
|
||||||
* that can be downloaded right now. It varies between 0 to 1.
|
* that can be downloaded right now. It varies between 0 to 1.
|
||||||
*/
|
*/
|
||||||
virtual QVector<qreal> availableFileFractions() const = 0;
|
virtual QFuture<QList<qreal>> fetchAvailableFileFractions() const = 0;
|
||||||
virtual void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const = 0;
|
|
||||||
|
|
||||||
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
|
virtual void prioritizeFiles(const QList<DownloadPriority> &priorities) = 0;
|
||||||
virtual void flushCache() const = 0;
|
virtual void flushCache() const = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,10 @@ BitTorrent::TorrentCreationManager::TorrentCreationManager(IApplication *app, QO
|
||||||
, m_maxTasks {SETTINGS_KEY(u"MaxTasks"_s), 256}
|
, m_maxTasks {SETTINGS_KEY(u"MaxTasks"_s), 256}
|
||||||
, m_numThreads {SETTINGS_KEY(u"NumThreads"_s), 1}
|
, m_numThreads {SETTINGS_KEY(u"NumThreads"_s), 1}
|
||||||
, m_tasks {std::make_unique<TaskSet>()}
|
, m_tasks {std::make_unique<TaskSet>()}
|
||||||
|
, m_threadPool(this)
|
||||||
{
|
{
|
||||||
|
m_threadPool.setObjectName("TorrentCreationManager m_threadPool");
|
||||||
|
|
||||||
if (m_numThreads > 0)
|
if (m_numThreads > 0)
|
||||||
m_threadPool.setMaxThreadCount(m_numThreads);
|
m_threadPool.setMaxThreadCount(m_numThreads);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <libtorrent/file_storage.hpp>
|
#include <libtorrent/file_storage.hpp>
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
|
||||||
|
#include <QtSystemDetection>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
@ -126,8 +127,17 @@ void TorrentCreator::run()
|
||||||
QDirIterator dirIter {m_params.sourcePath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
QDirIterator dirIter {m_params.sourcePath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
|
||||||
while (dirIter.hasNext())
|
while (dirIter.hasNext())
|
||||||
{
|
{
|
||||||
const QString filePath = dirIter.next();
|
const QFileInfo dirInfo = dirIter.nextFileInfo();
|
||||||
dirs.append(filePath);
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// .lnk to directory
|
||||||
|
// Windows users do not expect torrent creator to traverse into .lnk files so skip over them
|
||||||
|
if (dirInfo.isShortcut())
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QString dirPath = dirInfo.filePath();
|
||||||
|
dirs.append(dirPath);
|
||||||
}
|
}
|
||||||
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
|
||||||
|
|
||||||
|
@ -142,10 +152,25 @@ void TorrentCreator::run()
|
||||||
while (fileIter.hasNext())
|
while (fileIter.hasNext())
|
||||||
{
|
{
|
||||||
const QFileInfo fileInfo = fileIter.nextFileInfo();
|
const QFileInfo fileInfo = fileIter.nextFileInfo();
|
||||||
|
const Path filePath {fileInfo.filePath()};
|
||||||
|
qint64 fileSize = fileInfo.size();
|
||||||
|
|
||||||
const Path relFilePath = parentPath.relativePathOf(Path(fileInfo.filePath()));
|
#ifdef Q_OS_WIN
|
||||||
|
// .lnk to file
|
||||||
|
// libtorrent couldn't handle .lnk files on Windows
|
||||||
|
if (fileInfo.isShortcut())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// file symbolic link
|
||||||
|
// QFileInfo::size() failed to return the target file size
|
||||||
|
// and we need to redirect it manually
|
||||||
|
if (fileInfo.isSymbolicLink())
|
||||||
|
fileSize = QFileInfo(fileInfo.symLinkTarget()).size();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const Path relFilePath = parentPath.relativePathOf(filePath);
|
||||||
tmpNames.append(relFilePath.toString());
|
tmpNames.append(relFilePath.toString());
|
||||||
fileSizeMap[tmpNames.last()] = fileInfo.size();
|
fileSizeMap[tmpNames.last()] = fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
|
||||||
|
@ -161,8 +186,8 @@ void TorrentCreator::run()
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
|
lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
|
||||||
#else
|
#else
|
||||||
lt::create_torrent newTorrent(fs, m_params.pieceSize, m_params.paddedFileSizeLimit
|
lt::create_torrent newTorrent {fs, m_params.pieceSize, m_params.paddedFileSizeLimit
|
||||||
, (m_params.isAlignmentOptimized ? lt::create_torrent::optimize_alignment : lt::create_flags_t {}));
|
, (m_params.isAlignmentOptimized ? lt::create_torrent::optimize_alignment : lt::create_flags_t {})};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Add url seeds
|
// Add url seeds
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -35,9 +35,7 @@
|
||||||
#include <libtorrent/write_resume_data.hpp>
|
#include <libtorrent/write_resume_data.hpp>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDateTime>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
@ -143,11 +141,33 @@ catch (const lt::system_error &err)
|
||||||
return nonstd::make_unexpected(QString::fromLocal8Bit(err.what()));
|
return nonstd::make_unexpected(QString::fromLocal8Bit(err.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonstd::expected<QByteArray, QString> BitTorrent::TorrentDescriptor::saveToBuffer() const
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const lt::entry torrentEntry = lt::write_torrent_file(m_ltAddTorrentParams);
|
||||||
|
// usually torrent size should be smaller than 1 MB,
|
||||||
|
// however there are >100 MB v2/hybrid torrent files out in the wild
|
||||||
|
QByteArray buffer;
|
||||||
|
buffer.reserve(1024 * 1024);
|
||||||
|
lt::bencode(std::back_inserter(buffer), torrentEntry);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
catch (const lt::system_error &err)
|
||||||
|
{
|
||||||
|
return nonstd::make_unexpected(QString::fromLocal8Bit(err.what()));
|
||||||
|
}
|
||||||
|
|
||||||
BitTorrent::TorrentDescriptor::TorrentDescriptor(lt::add_torrent_params ltAddTorrentParams)
|
BitTorrent::TorrentDescriptor::TorrentDescriptor(lt::add_torrent_params ltAddTorrentParams)
|
||||||
: m_ltAddTorrentParams {std::move(ltAddTorrentParams)}
|
: m_ltAddTorrentParams {std::move(ltAddTorrentParams)}
|
||||||
{
|
{
|
||||||
if (m_ltAddTorrentParams.ti && m_ltAddTorrentParams.ti->is_valid())
|
if (m_ltAddTorrentParams.ti && m_ltAddTorrentParams.ti->is_valid())
|
||||||
|
{
|
||||||
m_info.emplace(*m_ltAddTorrentParams.ti);
|
m_info.emplace(*m_ltAddTorrentParams.ti);
|
||||||
|
if (m_ltAddTorrentParams.ti->creation_date() > 0)
|
||||||
|
m_creationDate = QDateTime::fromSecsSinceEpoch(m_ltAddTorrentParams.ti->creation_date());
|
||||||
|
m_creator = QString::fromStdString(m_ltAddTorrentParams.ti->creator());
|
||||||
|
m_comment = QString::fromStdString(m_ltAddTorrentParams.ti->comment());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BitTorrent::InfoHash BitTorrent::TorrentDescriptor::infoHash() const
|
BitTorrent::InfoHash BitTorrent::TorrentDescriptor::infoHash() const
|
||||||
|
@ -166,18 +186,17 @@ QString BitTorrent::TorrentDescriptor::name() const
|
||||||
|
|
||||||
QDateTime BitTorrent::TorrentDescriptor::creationDate() const
|
QDateTime BitTorrent::TorrentDescriptor::creationDate() const
|
||||||
{
|
{
|
||||||
return ((m_ltAddTorrentParams.ti->creation_date() != 0)
|
return m_creationDate;
|
||||||
? QDateTime::fromSecsSinceEpoch(m_ltAddTorrentParams.ti->creation_date()) : QDateTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BitTorrent::TorrentDescriptor::creator() const
|
QString BitTorrent::TorrentDescriptor::creator() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(m_ltAddTorrentParams.ti->creator());
|
return m_creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BitTorrent::TorrentDescriptor::comment() const
|
QString BitTorrent::TorrentDescriptor::comment() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(m_ltAddTorrentParams.ti->comment());
|
return m_comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::optional<BitTorrent::TorrentInfo> &BitTorrent::TorrentDescriptor::info() const
|
const std::optional<BitTorrent::TorrentInfo> &BitTorrent::TorrentDescriptor::info() const
|
||||||
|
@ -204,9 +223,9 @@ void BitTorrent::TorrentDescriptor::setTorrentInfo(TorrentInfo torrentInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<BitTorrent::TrackerEntry> BitTorrent::TorrentDescriptor::trackers() const
|
QList<BitTorrent::TrackerEntry> BitTorrent::TorrentDescriptor::trackers() const
|
||||||
{
|
{
|
||||||
QVector<TrackerEntry> ret;
|
QList<TrackerEntry> ret;
|
||||||
ret.reserve(static_cast<decltype(ret)::size_type>(m_ltAddTorrentParams.trackers.size()));
|
ret.reserve(static_cast<decltype(ret)::size_type>(m_ltAddTorrentParams.trackers.size()));
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
for (const std::string &tracker : m_ltAddTorrentParams.trackers)
|
for (const std::string &tracker : m_ltAddTorrentParams.trackers)
|
||||||
|
@ -215,9 +234,9 @@ QVector<BitTorrent::TrackerEntry> BitTorrent::TorrentDescriptor::trackers() cons
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<QUrl> BitTorrent::TorrentDescriptor::urlSeeds() const
|
QList<QUrl> BitTorrent::TorrentDescriptor::urlSeeds() const
|
||||||
{
|
{
|
||||||
QVector<QUrl> urlSeeds;
|
QList<QUrl> urlSeeds;
|
||||||
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(m_ltAddTorrentParams.url_seeds.size()));
|
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(m_ltAddTorrentParams.url_seeds.size()));
|
||||||
|
|
||||||
for (const std::string &nativeURLSeed : m_ltAddTorrentParams.url_seeds)
|
for (const std::string &nativeURLSeed : m_ltAddTorrentParams.url_seeds)
|
||||||
|
|
|
@ -33,16 +33,15 @@
|
||||||
#include <libtorrent/add_torrent_params.hpp>
|
#include <libtorrent/add_torrent_params.hpp>
|
||||||
|
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
|
#include <QDateTime>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "base/3rdparty/expected.hpp"
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "torrentdescriptor.h"
|
|
||||||
#include "torrentinfo.h"
|
#include "torrentinfo.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QDateTime;
|
|
||||||
class QString;
|
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
|
@ -60,8 +59,8 @@ namespace BitTorrent
|
||||||
QDateTime creationDate() const;
|
QDateTime creationDate() const;
|
||||||
QString creator() const;
|
QString creator() const;
|
||||||
QString comment() const;
|
QString comment() const;
|
||||||
QVector<TrackerEntry> trackers() const;
|
QList<TrackerEntry> trackers() const;
|
||||||
QVector<QUrl> urlSeeds() const;
|
QList<QUrl> urlSeeds() const;
|
||||||
const std::optional<TorrentInfo> &info() const;
|
const std::optional<TorrentInfo> &info() const;
|
||||||
|
|
||||||
void setTorrentInfo(TorrentInfo torrentInfo);
|
void setTorrentInfo(TorrentInfo torrentInfo);
|
||||||
|
@ -70,6 +69,7 @@ namespace BitTorrent
|
||||||
static nonstd::expected<TorrentDescriptor, QString> loadFromFile(const Path &path) noexcept;
|
static nonstd::expected<TorrentDescriptor, QString> loadFromFile(const Path &path) noexcept;
|
||||||
static nonstd::expected<TorrentDescriptor, QString> parse(const QString &str) noexcept;
|
static nonstd::expected<TorrentDescriptor, QString> parse(const QString &str) noexcept;
|
||||||
nonstd::expected<void, QString> saveToFile(const Path &path) const;
|
nonstd::expected<void, QString> saveToFile(const Path &path) const;
|
||||||
|
nonstd::expected<QByteArray, QString> saveToBuffer() const;
|
||||||
|
|
||||||
const lt::add_torrent_params <AddTorrentParams() const;
|
const lt::add_torrent_params <AddTorrentParams() const;
|
||||||
|
|
||||||
|
@ -78,6 +78,9 @@ namespace BitTorrent
|
||||||
|
|
||||||
lt::add_torrent_params m_ltAddTorrentParams;
|
lt::add_torrent_params m_ltAddTorrentParams;
|
||||||
std::optional<TorrentInfo> m_info;
|
std::optional<TorrentInfo> m_info;
|
||||||
|
QDateTime m_creationDate;
|
||||||
|
QString m_creator;
|
||||||
|
QString m_comment;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,11 +41,11 @@
|
||||||
#include <QBitArray>
|
#include <QBitArray>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
|
@ -94,8 +94,7 @@ namespace BitTorrent
|
||||||
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
TorrentImpl(SessionImpl *session, const lt::torrent_handle &nativeHandle, LoadTorrentParams params);
|
||||||
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
|
||||||
~TorrentImpl() override;
|
~TorrentImpl() override;
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
@ -138,7 +137,15 @@ namespace BitTorrent
|
||||||
int piecesCount() const override;
|
int piecesCount() const override;
|
||||||
int piecesHave() const override;
|
int piecesHave() const override;
|
||||||
qreal progress() const override;
|
qreal progress() const override;
|
||||||
|
|
||||||
QDateTime addedTime() const override;
|
QDateTime addedTime() const override;
|
||||||
|
QDateTime completedTime() const override;
|
||||||
|
QDateTime lastSeenComplete() const override;
|
||||||
|
qlonglong activeTime() const override;
|
||||||
|
qlonglong finishedTime() const override;
|
||||||
|
qlonglong timeSinceUpload() const override;
|
||||||
|
qlonglong timeSinceDownload() const override;
|
||||||
|
qlonglong timeSinceActivity() const override;
|
||||||
|
|
||||||
qreal ratioLimit() const override;
|
qreal ratioLimit() const override;
|
||||||
void setRatioLimit(qreal limit) override;
|
void setRatioLimit(qreal limit) override;
|
||||||
|
@ -154,7 +161,7 @@ namespace BitTorrent
|
||||||
qlonglong fileSize(int index) const override;
|
qlonglong fileSize(int index) const override;
|
||||||
PathList filePaths() const override;
|
PathList filePaths() const override;
|
||||||
PathList actualFilePaths() const override;
|
PathList actualFilePaths() const override;
|
||||||
QVector<DownloadPriority> filePriorities() const override;
|
QList<DownloadPriority> filePriorities() const override;
|
||||||
|
|
||||||
TorrentInfo info() const override;
|
TorrentInfo info() const override;
|
||||||
bool isFinished() const override;
|
bool isFinished() const override;
|
||||||
|
@ -176,36 +183,26 @@ namespace BitTorrent
|
||||||
bool hasMissingFiles() const override;
|
bool hasMissingFiles() const override;
|
||||||
bool hasError() const override;
|
bool hasError() const override;
|
||||||
int queuePosition() const override;
|
int queuePosition() const override;
|
||||||
QVector<TrackerEntryStatus> trackers() const override;
|
QList<TrackerEntryStatus> trackers() const override;
|
||||||
QVector<QUrl> urlSeeds() const override;
|
QList<QUrl> urlSeeds() const override;
|
||||||
QString error() const override;
|
QString error() const override;
|
||||||
qlonglong totalDownload() const override;
|
qlonglong totalDownload() const override;
|
||||||
qlonglong totalUpload() const override;
|
qlonglong totalUpload() const override;
|
||||||
qlonglong activeTime() const override;
|
|
||||||
qlonglong finishedTime() const override;
|
|
||||||
qlonglong eta() const override;
|
qlonglong eta() const override;
|
||||||
QVector<qreal> filesProgress() const override;
|
QList<qreal> filesProgress() const override;
|
||||||
int seedsCount() const override;
|
int seedsCount() const override;
|
||||||
int peersCount() const override;
|
int peersCount() const override;
|
||||||
int leechsCount() const override;
|
int leechsCount() const override;
|
||||||
int totalSeedsCount() const override;
|
int totalSeedsCount() const override;
|
||||||
int totalPeersCount() const override;
|
int totalPeersCount() const override;
|
||||||
int totalLeechersCount() const override;
|
int totalLeechersCount() const override;
|
||||||
QDateTime lastSeenComplete() const override;
|
|
||||||
QDateTime completedTime() const override;
|
|
||||||
qlonglong timeSinceUpload() const override;
|
|
||||||
qlonglong timeSinceDownload() const override;
|
|
||||||
qlonglong timeSinceActivity() const override;
|
|
||||||
int downloadLimit() const override;
|
int downloadLimit() const override;
|
||||||
int uploadLimit() const override;
|
int uploadLimit() const override;
|
||||||
bool superSeeding() const override;
|
bool superSeeding() const override;
|
||||||
bool isDHTDisabled() const override;
|
bool isDHTDisabled() const override;
|
||||||
bool isPEXDisabled() const override;
|
bool isPEXDisabled() const override;
|
||||||
bool isLSDDisabled() const override;
|
bool isLSDDisabled() const override;
|
||||||
QVector<PeerInfo> peers() const override;
|
|
||||||
QBitArray pieces() const override;
|
QBitArray pieces() const override;
|
||||||
QBitArray downloadingPieces() const override;
|
|
||||||
QVector<int> pieceAvailability() const override;
|
|
||||||
qreal distributedCopies() const override;
|
qreal distributedCopies() const override;
|
||||||
qreal maxRatio() const override;
|
qreal maxRatio() const override;
|
||||||
int maxSeedingTime() const override;
|
int maxSeedingTime() const override;
|
||||||
|
@ -219,7 +216,6 @@ namespace BitTorrent
|
||||||
int connectionsCount() const override;
|
int connectionsCount() const override;
|
||||||
int connectionsLimit() const override;
|
int connectionsLimit() const override;
|
||||||
qlonglong nextAnnounce() const override;
|
qlonglong nextAnnounce() const override;
|
||||||
QVector<qreal> availableFileFractions() const override;
|
|
||||||
|
|
||||||
void setName(const QString &name) override;
|
void setName(const QString &name) override;
|
||||||
void setSequentialDownload(bool enable) override;
|
void setSequentialDownload(bool enable) override;
|
||||||
|
@ -230,7 +226,7 @@ namespace BitTorrent
|
||||||
void forceDHTAnnounce() override;
|
void forceDHTAnnounce() override;
|
||||||
void forceRecheck() override;
|
void forceRecheck() override;
|
||||||
void renameFile(int index, const Path &path) override;
|
void renameFile(int index, const Path &path) override;
|
||||||
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
void prioritizeFiles(const QList<DownloadPriority> &priorities) override;
|
||||||
void setUploadLimit(int limit) override;
|
void setUploadLimit(int limit) override;
|
||||||
void setDownloadLimit(int limit) override;
|
void setDownloadLimit(int limit) override;
|
||||||
void setSuperSeeding(bool enable) override;
|
void setSuperSeeding(bool enable) override;
|
||||||
|
@ -238,11 +234,11 @@ namespace BitTorrent
|
||||||
void setPEXDisabled(bool disable) override;
|
void setPEXDisabled(bool disable) override;
|
||||||
void setLSDDisabled(bool disable) override;
|
void setLSDDisabled(bool disable) override;
|
||||||
void flushCache() const override;
|
void flushCache() const override;
|
||||||
void addTrackers(QVector<TrackerEntry> trackers) override;
|
void addTrackers(QList<TrackerEntry> trackers) override;
|
||||||
void removeTrackers(const QStringList &trackers) override;
|
void removeTrackers(const QStringList &trackers) override;
|
||||||
void replaceTrackers(QVector<TrackerEntry> trackers) override;
|
void replaceTrackers(QList<TrackerEntry> trackers) override;
|
||||||
void addUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
void addUrlSeeds(const QList<QUrl> &urlSeeds) override;
|
||||||
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
void removeUrlSeeds(const QList<QUrl> &urlSeeds) override;
|
||||||
bool connectPeer(const PeerAddress &peerAddress) override;
|
bool connectPeer(const PeerAddress &peerAddress) override;
|
||||||
void clearPeers() override;
|
void clearPeers() override;
|
||||||
void setMetadata(const TorrentInfo &torrentInfo) override;
|
void setMetadata(const TorrentInfo &torrentInfo) override;
|
||||||
|
@ -257,19 +253,29 @@ namespace BitTorrent
|
||||||
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
||||||
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
||||||
|
|
||||||
void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const override;
|
QFuture<QList<PeerInfo>> fetchPeerInfo() const override;
|
||||||
void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const override;
|
QFuture<QList<QUrl>> fetchURLSeeds() const override;
|
||||||
void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const override;
|
QFuture<QList<int>> fetchPieceAvailability() const override;
|
||||||
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
|
QFuture<QBitArray> fetchDownloadingPieces() const override;
|
||||||
void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const override;
|
QFuture<QList<qreal>> fetchAvailableFileFractions() const override;
|
||||||
|
|
||||||
bool needSaveResumeData() const;
|
bool needSaveResumeData() const;
|
||||||
|
|
||||||
// Session interface
|
// Session interface
|
||||||
lt::torrent_handle nativeHandle() const;
|
lt::torrent_handle nativeHandle() const;
|
||||||
|
|
||||||
void handleAlert(const lt::alert *a);
|
int fileIndexFromNative(lt::file_index_t nativeFileIndex) const;
|
||||||
|
|
||||||
void handleStateUpdate(const lt::torrent_status &nativeStatus);
|
void handleStateUpdate(const lt::torrent_status &nativeStatus);
|
||||||
|
void handleFastResumeRejected();
|
||||||
|
void handleFileCompleted(lt::file_index_t nativeFileIndex);
|
||||||
|
void handleFileError(FileErrorInfo fileError);
|
||||||
|
void handleFileRenamed(lt::file_index_t nativeFileIndex, const Path &newActualFilePath, const Path &oldActualFilePath);
|
||||||
|
void handleFileRenameFailed(lt::file_index_t nativeFileIndex);
|
||||||
|
void handleMetadataReceived();
|
||||||
|
void handleSaveResumeData(lt::add_torrent_params params);
|
||||||
|
void handleTorrentChecked();
|
||||||
|
void handleTorrentFinished();
|
||||||
void handleQueueingModeChanged();
|
void handleQueueingModeChanged();
|
||||||
void handleCategoryOptionsChanged();
|
void handleCategoryOptionsChanged();
|
||||||
void handleAppendExtensionToggled();
|
void handleAppendExtensionToggled();
|
||||||
|
@ -277,7 +283,6 @@ namespace BitTorrent
|
||||||
void requestResumeData(lt::resume_data_flags_t flags = {});
|
void requestResumeData(lt::resume_data_flags_t flags = {});
|
||||||
void deferredRequestResumeData();
|
void deferredRequestResumeData();
|
||||||
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
|
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
|
||||||
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
|
||||||
TrackerEntryStatus updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo);
|
TrackerEntryStatus updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo);
|
||||||
void resetTrackerEntryStatuses();
|
void resetTrackerEntryStatuses();
|
||||||
|
|
||||||
|
@ -290,23 +295,6 @@ namespace BitTorrent
|
||||||
void updateProgress();
|
void updateProgress();
|
||||||
void updateState();
|
void updateState();
|
||||||
|
|
||||||
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
|
|
||||||
void handleFileCompletedAlert(const lt::file_completed_alert *p);
|
|
||||||
void handleFileErrorAlert(const lt::file_error_alert *p);
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
|
||||||
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);
|
|
||||||
void handlePerformanceAlert(const lt::performance_alert *p) const;
|
|
||||||
void handleSaveResumeDataAlert(const lt::save_resume_data_alert *p);
|
|
||||||
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p);
|
|
||||||
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *p);
|
|
||||||
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *p);
|
|
||||||
void handleTorrentPausedAlert(const lt::torrent_paused_alert *p);
|
|
||||||
void handleTorrentResumedAlert(const lt::torrent_resumed_alert *p);
|
|
||||||
|
|
||||||
bool isMoveInProgress() const;
|
bool isMoveInProgress() const;
|
||||||
|
|
||||||
void setAutoManaged(bool enable);
|
void setAutoManaged(bool enable);
|
||||||
|
@ -319,29 +307,36 @@ namespace BitTorrent
|
||||||
void manageActualFilePaths();
|
void manageActualFilePaths();
|
||||||
void applyFirstLastPiecePriority(bool enabled);
|
void applyFirstLastPiecePriority(bool enabled);
|
||||||
|
|
||||||
void prepareResumeData(const lt::add_torrent_params ¶ms);
|
void prepareResumeData(lt::add_torrent_params resumeData);
|
||||||
void endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames);
|
void endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames);
|
||||||
void reload();
|
void reload();
|
||||||
|
|
||||||
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
||||||
|
|
||||||
template <typename Func, typename Callback>
|
template <typename Func>
|
||||||
void invokeAsync(Func func, Callback resultHandler) const;
|
QFuture<std::invoke_result_t<Func>> invokeAsync(Func &&func) const;
|
||||||
|
|
||||||
SessionImpl *const m_session = nullptr;
|
SessionImpl *const m_session = nullptr;
|
||||||
lt::session *m_nativeSession = nullptr;
|
|
||||||
lt::torrent_handle m_nativeHandle;
|
lt::torrent_handle m_nativeHandle;
|
||||||
mutable lt::torrent_status m_nativeStatus;
|
mutable lt::torrent_status m_nativeStatus;
|
||||||
TorrentState m_state = TorrentState::Unknown;
|
TorrentState m_state = TorrentState::Unknown;
|
||||||
TorrentInfo m_torrentInfo;
|
TorrentInfo m_torrentInfo;
|
||||||
PathList m_filePaths;
|
PathList m_filePaths;
|
||||||
QHash<lt::file_index_t, int> m_indexMap;
|
QHash<lt::file_index_t, int> m_indexMap;
|
||||||
QVector<DownloadPriority> m_filePriorities;
|
QList<DownloadPriority> m_filePriorities;
|
||||||
QBitArray m_completedFiles;
|
QBitArray m_completedFiles;
|
||||||
SpeedMonitor m_payloadRateMonitor;
|
SpeedMonitor m_payloadRateMonitor;
|
||||||
|
|
||||||
InfoHash m_infoHash;
|
InfoHash m_infoHash;
|
||||||
|
|
||||||
|
QDateTime m_creationDate;
|
||||||
|
QString m_creator;
|
||||||
|
QString m_comment;
|
||||||
|
|
||||||
|
QDateTime m_addedTime;
|
||||||
|
QDateTime m_completedTime;
|
||||||
|
QDateTime m_lastSeenComplete;
|
||||||
|
|
||||||
// m_moveFinishedTriggers is activated only when the following conditions are met:
|
// m_moveFinishedTriggers is activated only when the following conditions are met:
|
||||||
// all file rename jobs complete, all file move jobs complete
|
// all file rename jobs complete, all file move jobs complete
|
||||||
QQueue<EventTrigger> m_moveFinishedTriggers;
|
QQueue<EventTrigger> m_moveFinishedTriggers;
|
||||||
|
@ -352,8 +347,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
|
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
|
||||||
|
|
||||||
QVector<TrackerEntryStatus> m_trackerEntryStatuses;
|
QList<TrackerEntryStatus> m_trackerEntryStatuses;
|
||||||
QVector<QUrl> m_urlSeeds;
|
QList<QUrl> m_urlSeeds;
|
||||||
FileErrorInfo m_lastFileError;
|
FileErrorInfo m_lastFileError;
|
||||||
|
|
||||||
// Persistent data
|
// Persistent data
|
||||||
|
@ -378,13 +373,13 @@ namespace BitTorrent
|
||||||
|
|
||||||
bool m_unchecked = false;
|
bool m_unchecked = false;
|
||||||
|
|
||||||
lt::add_torrent_params m_ltAddTorrentParams;
|
mutable lt::add_torrent_params m_ltAddTorrentParams;
|
||||||
|
|
||||||
int m_downloadLimit = 0;
|
int m_downloadLimit = 0;
|
||||||
int m_uploadLimit = 0;
|
int m_uploadLimit = 0;
|
||||||
|
|
||||||
QBitArray m_pieces;
|
QBitArray m_pieces;
|
||||||
QVector<std::int64_t> m_filesProgress;
|
QList<std::int64_t> m_filesProgress;
|
||||||
|
|
||||||
bool m_deferredRequestResumeDataInvoked = false;
|
bool m_deferredRequestResumeDataInvoked = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -28,24 +28,15 @@
|
||||||
|
|
||||||
#include "torrentinfo.h"
|
#include "torrentinfo.h"
|
||||||
|
|
||||||
#include <libtorrent/create_torrent.hpp>
|
|
||||||
#include <libtorrent/error_code.hpp>
|
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "base/utils/io.h"
|
|
||||||
#include "base/utils/misc.h"
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
|
|
||||||
|
@ -82,66 +73,6 @@ bool TorrentInfo::isValid() const
|
||||||
return (m_nativeInfo != nullptr);
|
return (m_nativeInfo != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data) noexcept
|
|
||||||
{
|
|
||||||
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
|
|
||||||
// used in `torrent_info()` constructor
|
|
||||||
const auto *pref = Preferences::instance();
|
|
||||||
|
|
||||||
lt::error_code ec;
|
|
||||||
const lt::bdecode_node node = lt::bdecode(data, ec
|
|
||||||
, nullptr, pref->getBdecodeDepthLimit(), pref->getBdecodeTokenLimit());
|
|
||||||
if (ec)
|
|
||||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
|
||||||
|
|
||||||
const lt::torrent_info nativeInfo {node, ec};
|
|
||||||
if (ec)
|
|
||||||
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
|
|
||||||
|
|
||||||
return TorrentInfo(nativeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const Path &path) noexcept
|
|
||||||
{
|
|
||||||
QByteArray data;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const qint64 torrentSizeLimit = Preferences::instance()->getTorrentFileSizeLimit();
|
|
||||||
const auto readResult = Utils::IO::readFile(path, torrentSizeLimit);
|
|
||||||
if (!readResult)
|
|
||||||
return nonstd::make_unexpected(readResult.error().message);
|
|
||||||
data = readResult.value();
|
|
||||||
}
|
|
||||||
catch (const std::bad_alloc &e)
|
|
||||||
{
|
|
||||||
return nonstd::make_unexpected(tr("Failed to allocate memory when reading file. File: \"%1\". Error: \"%2\"")
|
|
||||||
.arg(path.toString(), QString::fromLocal8Bit(e.what())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return load(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
nonstd::expected<void, QString> TorrentInfo::saveToFile(const Path &path) const
|
|
||||||
{
|
|
||||||
if (!isValid())
|
|
||||||
return nonstd::make_unexpected(tr("Invalid metadata"));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto torrentCreator = lt::create_torrent(*m_nativeInfo);
|
|
||||||
const lt::entry torrentEntry = torrentCreator.generate();
|
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, torrentEntry);
|
|
||||||
if (!result)
|
|
||||||
return result.get_unexpected();
|
|
||||||
}
|
|
||||||
catch (const lt::system_error &err)
|
|
||||||
{
|
|
||||||
return nonstd::make_unexpected(QString::fromLocal8Bit(err.what()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoHash TorrentInfo::infoHash() const
|
InfoHash TorrentInfo::infoHash() const
|
||||||
{
|
{
|
||||||
if (!isValid()) return {};
|
if (!isValid()) return {};
|
||||||
|
@ -160,28 +91,6 @@ QString TorrentInfo::name() const
|
||||||
return QString::fromStdString(m_nativeInfo->orig_files().name());
|
return QString::fromStdString(m_nativeInfo->orig_files().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime TorrentInfo::creationDate() const
|
|
||||||
{
|
|
||||||
if (!isValid()) return {};
|
|
||||||
|
|
||||||
const std::time_t date = m_nativeInfo->creation_date();
|
|
||||||
return ((date != 0) ? QDateTime::fromSecsSinceEpoch(date) : QDateTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentInfo::creator() const
|
|
||||||
{
|
|
||||||
if (!isValid()) return {};
|
|
||||||
|
|
||||||
return QString::fromStdString(m_nativeInfo->creator());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentInfo::comment() const
|
|
||||||
{
|
|
||||||
if (!isValid()) return {};
|
|
||||||
|
|
||||||
return QString::fromStdString(m_nativeInfo->comment());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentInfo::isPrivate() const
|
bool TorrentInfo::isPrivate() const
|
||||||
{
|
{
|
||||||
if (!isValid()) return false;
|
if (!isValid()) return false;
|
||||||
|
@ -270,43 +179,7 @@ qlonglong TorrentInfo::fileOffset(const int index) const
|
||||||
return m_nativeInfo->orig_files().file_offset(m_nativeIndexes[index]);
|
return m_nativeInfo->orig_files().file_offset(m_nativeIndexes[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<TrackerEntry> TorrentInfo::trackers() const
|
QByteArray TorrentInfo::rawData() const
|
||||||
{
|
|
||||||
if (!isValid()) return {};
|
|
||||||
|
|
||||||
const std::vector<lt::announce_entry> trackers = m_nativeInfo->trackers();
|
|
||||||
|
|
||||||
QVector<TrackerEntry> ret;
|
|
||||||
ret.reserve(static_cast<decltype(ret)::size_type>(trackers.size()));
|
|
||||||
for (const lt::announce_entry &tracker : trackers)
|
|
||||||
ret.append({.url = QString::fromStdString(tracker.url), .tier = tracker.tier});
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<QUrl> TorrentInfo::urlSeeds() const
|
|
||||||
{
|
|
||||||
if (!isValid()) return {};
|
|
||||||
|
|
||||||
const std::vector<lt::web_seed_entry> &nativeWebSeeds = m_nativeInfo->web_seeds();
|
|
||||||
|
|
||||||
QVector<QUrl> urlSeeds;
|
|
||||||
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(nativeWebSeeds.size()));
|
|
||||||
|
|
||||||
for (const lt::web_seed_entry &webSeed : nativeWebSeeds)
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 20100
|
|
||||||
if (webSeed.type == lt::web_seed_entry::url_seed)
|
|
||||||
urlSeeds.append(QUrl(QString::fromStdString(webSeed.url)));
|
|
||||||
#else
|
|
||||||
urlSeeds.append(QUrl(QString::fromStdString(webSeed.url)));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlSeeds;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray TorrentInfo::metadata() const
|
|
||||||
{
|
{
|
||||||
if (!isValid()) return {};
|
if (!isValid()) return {};
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
@ -320,7 +193,7 @@ QByteArray TorrentInfo::metadata() const
|
||||||
PathList TorrentInfo::filesForPiece(const int pieceIndex) const
|
PathList TorrentInfo::filesForPiece(const int pieceIndex) const
|
||||||
{
|
{
|
||||||
// no checks here because fileIndicesForPiece() will return an empty list
|
// no checks here because fileIndicesForPiece() will return an empty list
|
||||||
const QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
const QList<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||||
|
|
||||||
PathList res;
|
PathList res;
|
||||||
res.reserve(fileIndices.size());
|
res.reserve(fileIndices.size());
|
||||||
|
@ -330,14 +203,14 @@ PathList TorrentInfo::filesForPiece(const int pieceIndex) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
|
QList<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
|
||||||
{
|
{
|
||||||
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
|
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const std::vector<lt::file_slice> files = m_nativeInfo->map_block(
|
const std::vector<lt::file_slice> files = m_nativeInfo->map_block(
|
||||||
lt::piece_index_t {pieceIndex}, 0, m_nativeInfo->piece_size(lt::piece_index_t {pieceIndex}));
|
lt::piece_index_t {pieceIndex}, 0, m_nativeInfo->piece_size(lt::piece_index_t {pieceIndex}));
|
||||||
QVector<int> res;
|
QList<int> res;
|
||||||
res.reserve(static_cast<decltype(res)::size_type>(files.size()));
|
res.reserve(static_cast<decltype(res)::size_type>(files.size()));
|
||||||
for (const lt::file_slice &fileSlice : files)
|
for (const lt::file_slice &fileSlice : files)
|
||||||
{
|
{
|
||||||
|
@ -349,13 +222,13 @@ QVector<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<QByteArray> TorrentInfo::pieceHashes() const
|
QList<QByteArray> TorrentInfo::pieceHashes() const
|
||||||
{
|
{
|
||||||
if (!isValid())
|
if (!isValid())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const int count = piecesCount();
|
const int count = piecesCount();
|
||||||
QVector<QByteArray> hashes;
|
QList<QByteArray> hashes;
|
||||||
hashes.reserve(count);
|
hashes.reserve(count);
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
|
@ -366,16 +239,7 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
|
||||||
|
|
||||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const Path &filePath) const
|
TorrentInfo::PieceRange TorrentInfo::filePieces(const Path &filePath) const
|
||||||
{
|
{
|
||||||
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
return filePieces(fileIndex(filePath));
|
||||||
return {};
|
|
||||||
|
|
||||||
const int index = fileIndex(filePath);
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
qDebug() << "Filename" << filePath.toString() << "was not found in torrent" << name();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return filePieces(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
|
TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
|
||||||
|
@ -384,10 +248,7 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if ((fileIndex < 0) || (fileIndex >= filesCount()))
|
if ((fileIndex < 0) || (fileIndex >= filesCount()))
|
||||||
{
|
|
||||||
qDebug() << "File index (" << fileIndex << ") is out of range for torrent" << name();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
const lt::file_storage &files = m_nativeInfo->orig_files();
|
const lt::file_storage &files = m_nativeInfo->orig_files();
|
||||||
const auto fileSize = files.file_size(m_nativeIndexes[fileIndex]);
|
const auto fileSize = files.file_size(m_nativeIndexes[fileIndex]);
|
||||||
|
@ -450,7 +311,7 @@ std::shared_ptr<lt::torrent_info> TorrentInfo::nativeInfo() const
|
||||||
return std::make_shared<lt::torrent_info>(*m_nativeInfo);
|
return std::make_shared<lt::torrent_info>(*m_nativeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<lt::file_index_t> TorrentInfo::nativeIndexes() const
|
QList<lt::file_index_t> TorrentInfo::nativeIndexes() const
|
||||||
{
|
{
|
||||||
return m_nativeIndexes;
|
return m_nativeIndexes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -32,9 +32,8 @@
|
||||||
|
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QVector>
|
#include <QList>
|
||||||
|
|
||||||
#include "base/3rdparty/expected.hpp"
|
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
|
@ -50,26 +49,17 @@ namespace BitTorrent
|
||||||
|
|
||||||
class TorrentInfo
|
class TorrentInfo
|
||||||
{
|
{
|
||||||
Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentInfo() = default;
|
TorrentInfo() = default;
|
||||||
TorrentInfo(const TorrentInfo &other) = default;
|
TorrentInfo(const TorrentInfo &other) = default;
|
||||||
|
|
||||||
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
explicit TorrentInfo(const lt::torrent_info &nativeInfo);
|
||||||
|
|
||||||
static nonstd::expected<TorrentInfo, QString> load(const QByteArray &data) noexcept;
|
|
||||||
static nonstd::expected<TorrentInfo, QString> loadFromFile(const Path &path) noexcept;
|
|
||||||
nonstd::expected<void, QString> saveToFile(const Path &path) const;
|
|
||||||
|
|
||||||
TorrentInfo &operator=(const TorrentInfo &other);
|
TorrentInfo &operator=(const TorrentInfo &other);
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
InfoHash infoHash() const;
|
InfoHash infoHash() const;
|
||||||
QString name() const;
|
QString name() const;
|
||||||
QDateTime creationDate() const;
|
|
||||||
QString creator() const;
|
|
||||||
QString comment() const;
|
|
||||||
bool isPrivate() const;
|
bool isPrivate() const;
|
||||||
qlonglong totalSize() const;
|
qlonglong totalSize() const;
|
||||||
int filesCount() const;
|
int filesCount() const;
|
||||||
|
@ -80,12 +70,9 @@ namespace BitTorrent
|
||||||
PathList filePaths() const;
|
PathList filePaths() const;
|
||||||
qlonglong fileSize(int index) const;
|
qlonglong fileSize(int index) const;
|
||||||
qlonglong fileOffset(int index) const;
|
qlonglong fileOffset(int index) const;
|
||||||
QVector<TrackerEntry> trackers() const;
|
|
||||||
QVector<QUrl> urlSeeds() const;
|
|
||||||
QByteArray metadata() const;
|
|
||||||
PathList filesForPiece(int pieceIndex) const;
|
PathList filesForPiece(int pieceIndex) const;
|
||||||
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
QList<int> fileIndicesForPiece(int pieceIndex) const;
|
||||||
QVector<QByteArray> pieceHashes() const;
|
QList<QByteArray> pieceHashes() const;
|
||||||
|
|
||||||
using PieceRange = IndexRange<int>;
|
using PieceRange = IndexRange<int>;
|
||||||
// returns pair of the first and the last pieces into which
|
// returns pair of the first and the last pieces into which
|
||||||
|
@ -93,10 +80,12 @@ namespace BitTorrent
|
||||||
PieceRange filePieces(const Path &filePath) const;
|
PieceRange filePieces(const Path &filePath) const;
|
||||||
PieceRange filePieces(int fileIndex) const;
|
PieceRange filePieces(int fileIndex) const;
|
||||||
|
|
||||||
|
QByteArray rawData() const;
|
||||||
|
|
||||||
bool matchesInfoHash(const InfoHash &otherInfoHash) const;
|
bool matchesInfoHash(const InfoHash &otherInfoHash) const;
|
||||||
|
|
||||||
std::shared_ptr<lt::torrent_info> nativeInfo() const;
|
std::shared_ptr<lt::torrent_info> nativeInfo() const;
|
||||||
QVector<lt::file_index_t> nativeIndexes() const;
|
QList<lt::file_index_t> nativeIndexes() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// returns file index or -1 if fileName is not found
|
// returns file index or -1 if fileName is not found
|
||||||
|
@ -106,7 +95,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
// internal indexes of files (payload only, excluding any .pad files)
|
// internal indexes of files (payload only, excluding any .pad files)
|
||||||
// by which they are addressed in libtorrent
|
// by which they are addressed in libtorrent
|
||||||
QVector<lt::file_index_t> m_nativeIndexes;
|
QList<lt::file_index_t> m_nativeIndexes;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -380,7 +380,7 @@ void Tracker::registerPeer(const TrackerAnnounceRequest &announceReq)
|
||||||
{
|
{
|
||||||
// Reached max size, remove a random torrent
|
// Reached max size, remove a random torrent
|
||||||
if (m_torrents.size() >= MAX_TORRENTS)
|
if (m_torrents.size() >= MAX_TORRENTS)
|
||||||
m_torrents.erase(m_torrents.begin());
|
m_torrents.erase(m_torrents.cbegin());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_torrents[announceReq.torrentID].setPeer(announceReq.peer);
|
m_torrents[announceReq.torrentID].setPeer(announceReq.peer);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -28,10 +28,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "announcetimepoint.h"
|
||||||
|
|
||||||
class QStringView;
|
class QStringView;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
|
@ -40,7 +41,6 @@ namespace BitTorrent
|
||||||
{
|
{
|
||||||
NotContacted = 1,
|
NotContacted = 1,
|
||||||
Working = 2,
|
Working = 2,
|
||||||
Updating = 3,
|
|
||||||
NotWorking = 4,
|
NotWorking = 4,
|
||||||
TrackerError = 5,
|
TrackerError = 5,
|
||||||
Unreachable = 6
|
Unreachable = 6
|
||||||
|
@ -51,6 +51,7 @@ namespace BitTorrent
|
||||||
QString name {};
|
QString name {};
|
||||||
int btVersion = 1;
|
int btVersion = 1;
|
||||||
|
|
||||||
|
bool isUpdating = false;
|
||||||
TrackerEndpointState state = TrackerEndpointState::NotContacted;
|
TrackerEndpointState state = TrackerEndpointState::NotContacted;
|
||||||
QString message {};
|
QString message {};
|
||||||
|
|
||||||
|
@ -59,8 +60,8 @@ namespace BitTorrent
|
||||||
int numLeeches = -1;
|
int numLeeches = -1;
|
||||||
int numDownloaded = -1;
|
int numDownloaded = -1;
|
||||||
|
|
||||||
QDateTime nextAnnounceTime {};
|
AnnounceTimePoint nextAnnounceTime {};
|
||||||
QDateTime minAnnounceTime {};
|
AnnounceTimePoint minAnnounceTime {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TrackerEntryStatus
|
struct TrackerEntryStatus
|
||||||
|
@ -68,6 +69,7 @@ namespace BitTorrent
|
||||||
QString url {};
|
QString url {};
|
||||||
int tier = 0;
|
int tier = 0;
|
||||||
|
|
||||||
|
bool isUpdating = false;
|
||||||
TrackerEndpointState state = TrackerEndpointState::NotContacted;
|
TrackerEndpointState state = TrackerEndpointState::NotContacted;
|
||||||
QString message {};
|
QString message {};
|
||||||
|
|
||||||
|
@ -76,8 +78,8 @@ namespace BitTorrent
|
||||||
int numLeeches = -1;
|
int numLeeches = -1;
|
||||||
int numDownloaded = -1;
|
int numDownloaded = -1;
|
||||||
|
|
||||||
QDateTime nextAnnounceTime {};
|
AnnounceTimePoint nextAnnounceTime {};
|
||||||
QDateTime minAnnounceTime {};
|
AnnounceTimePoint minAnnounceTime {};
|
||||||
|
|
||||||
QHash<std::pair<QString, int>, TrackerEndpointStatus> endpoints {};
|
QHash<std::pair<QString, int>, TrackerEndpointStatus> endpoints {};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2023-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
|
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -29,16 +29,24 @@
|
||||||
|
|
||||||
#include "freediskspacechecker.h"
|
#include "freediskspacechecker.h"
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
qint64 FreeDiskSpaceChecker::lastResult() const
|
FreeDiskSpaceChecker::FreeDiskSpaceChecker(const Path &pathToCheck)
|
||||||
|
: m_pathToCheck {pathToCheck}
|
||||||
{
|
{
|
||||||
return m_lastResult;
|
}
|
||||||
|
|
||||||
|
Path FreeDiskSpaceChecker::pathToCheck() const
|
||||||
|
{
|
||||||
|
return m_pathToCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeDiskSpaceChecker::setPathToCheck(const Path &newPathToCheck)
|
||||||
|
{
|
||||||
|
m_pathToCheck = newPathToCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeDiskSpaceChecker::check()
|
void FreeDiskSpaceChecker::check()
|
||||||
{
|
{
|
||||||
m_lastResult = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath());
|
emit checked(Utils::Fs::freeDiskSpaceOnPath(m_pathToCheck));
|
||||||
emit checked(m_lastResult);
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2023-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
|
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -31,15 +31,18 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
class FreeDiskSpaceChecker final : public QObject
|
class FreeDiskSpaceChecker final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(FreeDiskSpaceChecker)
|
Q_DISABLE_COPY_MOVE(FreeDiskSpaceChecker)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using QObject::QObject;
|
FreeDiskSpaceChecker(const Path &pathToCheck);
|
||||||
|
|
||||||
qint64 lastResult() const;
|
Path pathToCheck() const;
|
||||||
|
void setPathToCheck(const Path &newPathToCheck);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void check();
|
void check();
|
||||||
|
@ -48,5 +51,5 @@ signals:
|
||||||
void checked(qint64 freeSpaceSize);
|
void checked(qint64 freeSpaceSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qint64 m_lastResult = 0;
|
Path m_pathToCheck;
|
||||||
};
|
};
|
|
@ -155,12 +155,16 @@ void Connection::read()
|
||||||
sendResponse(resp);
|
sendResponse(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||||
|
m_receivedData.slice(result.frameSize);
|
||||||
|
#else
|
||||||
m_receivedData.remove(0, result.frameSize);
|
m_receivedData.remove(0, result.frameSize);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_UNREACHABLE();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|