mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-02 13:48:05 -06:00
Compare commits
165 Commits
release-5.
...
v5_0_x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
805c80bf51 | ||
|
|
4b794a9930 | ||
|
|
96b27a313e | ||
|
|
c39849bd3b | ||
|
|
daf7af0d7b | ||
|
|
a6809efbbb | ||
|
|
ddd8fbd34e | ||
|
|
13fcd3d635 | ||
|
|
3c6ff0097f | ||
|
|
03dc089148 | ||
|
|
100ee5dbe0 | ||
|
|
310a9d8e1a | ||
|
|
677cabcbdf | ||
|
|
b86079974c | ||
|
|
ca6a89e238 | ||
|
|
0132b17af6 | ||
|
|
505c1e1c0a | ||
|
|
ecde201ec5 | ||
|
|
730bf957a4 | ||
|
|
069cd029eb | ||
|
|
375e6800e9 | ||
|
|
09fb92466a | ||
|
|
69321f0e94 | ||
|
|
f39e066672 | ||
|
|
6a5ea93c92 | ||
|
|
35dce07c63 | ||
|
|
0188e11dd7 | ||
|
|
1dc348539b | ||
|
|
241a0e91bf | ||
|
|
68f7295500 | ||
|
|
53adb7bfa8 | ||
|
|
6128f6eecc | ||
|
|
d156a44f8d | ||
|
|
c3c7f28bad | ||
|
|
9ac14cdf9f | ||
|
|
b899ea8c40 | ||
|
|
0d7c367332 | ||
|
|
22826499d5 | ||
|
|
dbfd830b56 | ||
|
|
ad3348b95f | ||
|
|
44b08fcb74 | ||
|
|
71b752baf3 | ||
|
|
15b6091261 | ||
|
|
abe457389d | ||
|
|
abce4cd1bc | ||
|
|
2bfb336905 | ||
|
|
2dee65fa52 | ||
|
|
423b3ed9bf | ||
|
|
3454f064f0 | ||
|
|
ac9ca4f452 | ||
|
|
09899a7d0d | ||
|
|
9ab3c573dc | ||
|
|
993eb25323 | ||
|
|
1e27e6504e | ||
|
|
330dce6aa2 | ||
|
|
39b965af48 | ||
|
|
5e105b0348 | ||
|
|
f2b2a2b034 | ||
|
|
10499dffe9 | ||
|
|
eea01b94a3 | ||
|
|
374951f6f2 | ||
|
|
6d6f9bc619 | ||
|
|
84ee620fdc | ||
|
|
6079b25419 | ||
|
|
fe24bc825b | ||
|
|
94136262a8 | ||
|
|
f52947e27e | ||
|
|
315e88aee9 | ||
|
|
565c6d843a | ||
|
|
9104351c89 | ||
|
|
e58b0a65d2 | ||
|
|
878d829904 | ||
|
|
063f77bc6c | ||
|
|
2a4077414f | ||
|
|
2a44253802 | ||
|
|
4712eba0dc | ||
|
|
983b7814aa | ||
|
|
e082a21751 | ||
|
|
7dd1d1bac8 | ||
|
|
49f57b1049 | ||
|
|
fbf68a0649 | ||
|
|
39229dc06a | ||
|
|
bb314e1555 | ||
|
|
a3a8b15828 | ||
|
|
b579afe1aa | ||
|
|
93096dba56 | ||
|
|
6379c33964 | ||
|
|
84372de675 | ||
|
|
403b7c7c35 | ||
|
|
b2fab43865 | ||
|
|
387821267f | ||
|
|
dd7ef8e934 | ||
|
|
cce295faeb | ||
|
|
db5479ea01 | ||
|
|
e1216c4c9a | ||
|
|
f4a0868426 | ||
|
|
59a5fcf7d0 | ||
|
|
f9a2b02a8d | ||
|
|
04f6a565f3 | ||
|
|
3e96048ee4 | ||
|
|
d4ccf3001c | ||
|
|
64506f16bd | ||
|
|
24a7a835af | ||
|
|
93b9bf9552 | ||
|
|
f4125601de | ||
|
|
2d67729617 | ||
|
|
878ebbed41 | ||
|
|
c61c3d7cd8 | ||
|
|
978fbbdc0d | ||
|
|
63689cf763 | ||
|
|
cebc72d3cf | ||
|
|
a67bd271c6 | ||
|
|
a8cffbb205 | ||
|
|
7dfb0110d4 | ||
|
|
3ad8fcbdd2 | ||
|
|
195eae5f3d | ||
|
|
920ae26f7b | ||
|
|
09ed0d6b66 | ||
|
|
4f0cc8aa11 | ||
|
|
4d490c84e7 | ||
|
|
96607ce874 | ||
|
|
418edc7471 | ||
|
|
bd01b7c4df | ||
|
|
b0ac763048 | ||
|
|
127d2d6f0b | ||
|
|
4149609e78 | ||
|
|
78c549f83e | ||
|
|
a3a53e2e0e | ||
|
|
5aaa43e01d | ||
|
|
86745d7b07 | ||
|
|
210650a5ee | ||
|
|
fe93b6d0d8 | ||
|
|
e8b585acd8 | ||
|
|
cea20141a9 | ||
|
|
0f5a27ed50 | ||
|
|
c2cf898ccd | ||
|
|
5e5aa8a563 | ||
|
|
12a4c3fda2 | ||
|
|
5f50b701d2 | ||
|
|
9f20d9c3aa | ||
|
|
05e3130baa | ||
|
|
683492648f | ||
|
|
2f2e158877 | ||
|
|
e60e96cb0e | ||
|
|
5f31208bf1 | ||
|
|
fa58e58e70 | ||
|
|
671943a9a6 | ||
|
|
8bad80bcdd | ||
|
|
c44e300507 | ||
|
|
318a677e8f | ||
|
|
0246df790a | ||
|
|
782fbc1425 | ||
|
|
7deccd5592 | ||
|
|
4a36fe7278 | ||
|
|
1c5af96ad8 | ||
|
|
3bb47a5410 | ||
|
|
d7abeb4bf0 | ||
|
|
a19d623ead | ||
|
|
1ef21bc2b7 | ||
|
|
4687b4e8e4 | ||
|
|
d2e5163861 | ||
|
|
8a15ea8026 | ||
|
|
2b99554813 | ||
|
|
e6638f9c19 | ||
|
|
ec6eac2ba1 |
18
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
18
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -7,17 +7,19 @@ 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 (**[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.
|
- (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.
|
||||||
- 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.
|
- 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.
|
||||||
- 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:
|
||||||
@@ -26,10 +28,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.6.6 x64
|
qBittorrent: 4.3.7 x64
|
||||||
Operating system: Windows 10 Pro x64 (22H2) 10.0.19045
|
Operating system: Windows 10 Pro 21H1/2009 x64
|
||||||
Qt: 6.4.3
|
Qt: 5.15.2
|
||||||
libtorrent-rasterbar: 1.2.19
|
libtorrent-rasterbar: 1.2.14
|
||||||
placeholder: |
|
placeholder: |
|
||||||
qBittorrent:
|
qBittorrent:
|
||||||
Operating system:
|
Operating system:
|
||||||
@@ -71,4 +73,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: true
|
required: false
|
||||||
|
|||||||
31
.github/workflows/ci_file_health.yaml
vendored
31
.github/workflows/ci_file_health.yaml
vendored
@@ -12,15 +12,11 @@ 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: Setup python
|
- name: Install tools
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "*"
|
python-version: "*"
|
||||||
@@ -36,7 +32,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.6/pandoc-3.6-linux-amd64.tar.gz"
|
"https://github.com/jgm/pandoc/releases/download/3.1.7/pandoc-3.1.7-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
|
||||||
@@ -46,26 +42,3 @@ 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 != "template-injection")
|
|
||||||
and (.ruleId != "unpinned-uses")'
|
|
||||||
IGNORE_ID='(.id != "template-injection")
|
|
||||||
and (.id != "unpinned-uses")'
|
|
||||||
zizmor \
|
|
||||||
--format sarif \
|
|
||||||
--pedantic \
|
|
||||||
./ \
|
|
||||||
| 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"
|
|
||||||
|
|||||||
17
.github/workflows/ci_macos.yaml
vendored
17
.github/workflows/ci_macos.yaml
vendored
@@ -2,7 +2,8 @@ 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 }}
|
||||||
@@ -12,13 +13,11 @@ 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.11", "1.2.20"]
|
libt_version: ["2.0.10", "1.2.19"]
|
||||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||||
qt_version: ["6.7.0"]
|
qt_version: ["6.7.0"]
|
||||||
|
|
||||||
@@ -29,8 +28,6 @@ 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
|
||||||
@@ -57,10 +54,10 @@ jobs:
|
|||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "86"
|
BOOST_MINOR_VERSION: "85"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
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_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_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"
|
||||||
@@ -93,7 +90,7 @@ 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_CXX_STANDARD=17 \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}" \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
@@ -102,7 +99,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build qBittorrent
|
- name: Build qBittorrent
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS -DQT_FORCE_ASSERTS -Werror -Wno-error=deprecated-declarations" \
|
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
cmake \
|
cmake \
|
||||||
-B build \
|
-B build \
|
||||||
|
|||||||
8
.github/workflows/ci_python.yaml
vendored
8
.github/workflows/ci_python.yaml
vendored
@@ -16,8 +16,6 @@ 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
|
||||||
@@ -36,7 +34,7 @@ jobs:
|
|||||||
- name: Lint code (auxiliary scripts)
|
- name: Lint code (auxiliary scripts)
|
||||||
run: |
|
run: |
|
||||||
pyflakes $PY_FILES
|
pyflakes $PY_FILES
|
||||||
bandit --skip B101,B314,B405 $PY_FILES
|
bandit --skip B314,B405 $PY_FILES
|
||||||
|
|
||||||
- name: Format code (auxiliary scripts)
|
- name: Format code (auxiliary scripts)
|
||||||
run: |
|
run: |
|
||||||
@@ -52,7 +50,7 @@ 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.9'
|
python-version: '3.7'
|
||||||
|
|
||||||
- name: Install tools (search engine)
|
- name: Install tools (search engine)
|
||||||
run: pip install bandit mypy pycodestyle pyflakes pyright
|
run: pip install bandit mypy pycodestyle pyflakes pyright
|
||||||
@@ -63,7 +61,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 \
|
||||||
|
|||||||
20
.github/workflows/ci_ubuntu.yaml
vendored
20
.github/workflows/ci_ubuntu.yaml
vendored
@@ -2,7 +2,9 @@ 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 }}
|
||||||
@@ -12,27 +14,22 @@ 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.11", "1.2.20"]
|
libt_version: ["2.0.10", "1.2.19"]
|
||||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||||
qt_version: ["6.5.2"]
|
qt_version: ["6.5.2"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
harden_flags: "-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS"
|
harden_flags: "-D_FORTIFY_SOURCE=2 -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: |
|
||||||
@@ -52,10 +49,10 @@ jobs:
|
|||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "77"
|
BOOST_MINOR_VERSION: "76"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
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_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_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"
|
||||||
@@ -88,7 +85,6 @@ 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 }}" \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
@@ -105,7 +101,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build qBittorrent
|
- name: Build qBittorrent
|
||||||
run: |
|
run: |
|
||||||
CXXFLAGS="$CXXFLAGS ${{ env.harden_flags }} -DQT_FORCE_ASSERTS -Werror" \
|
CXXFLAGS="$CXXFLAGS ${{ env.harden_flags }} -Werror" \
|
||||||
LDFLAGS="$LDFLAGS -gz" \
|
LDFLAGS="$LDFLAGS -gz" \
|
||||||
cmake \
|
cmake \
|
||||||
-B build \
|
-B build \
|
||||||
|
|||||||
12
.github/workflows/ci_webui.yaml
vendored
12
.github/workflows/ci_webui.yaml
vendored
@@ -2,7 +2,8 @@ name: CI - WebUI
|
|||||||
|
|
||||||
on: [pull_request, push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
permissions: {}
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@@ -12,8 +13,6 @@ jobs:
|
|||||||
ci:
|
ci:
|
||||||
name: Check
|
name: Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -22,8 +21,6 @@ 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
|
||||||
@@ -31,10 +28,7 @@ jobs:
|
|||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
|
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: |
|
run: npm install
|
||||||
npm install
|
|
||||||
npm ls
|
|
||||||
npm ls --all
|
|
||||||
|
|
||||||
- name: Lint code
|
- name: Lint code
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
|
|||||||
24
.github/workflows/ci_windows.yaml
vendored
24
.github/workflows/ci_windows.yaml
vendored
@@ -2,7 +2,8 @@ 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 }}
|
||||||
@@ -12,13 +13,11 @@ 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.11", "1.2.20"]
|
libt_version: ["2.0.10", "1.2.19"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
boost_path: "${{ github.workspace }}/../boost"
|
boost_path: "${{ github.workspace }}/../boost"
|
||||||
@@ -28,8 +27,6 @@ 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
|
||||||
@@ -81,10 +78,10 @@ jobs:
|
|||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "86"
|
BOOST_MINOR_VERSION: "85"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
$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_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_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 }}/.."
|
||||||
@@ -98,8 +95,7 @@ jobs:
|
|||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
version: "6.8.0"
|
version: "6.7.3"
|
||||||
arch: win64_msvc2022_64
|
|
||||||
archives: qtbase qtsvg qttools
|
archives: qtbase qtsvg qttools
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
@@ -118,7 +114,6 @@ jobs:
|
|||||||
-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" `
|
||||||
@@ -132,7 +127,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build qBittorrent
|
- name: Build qBittorrent
|
||||||
run: |
|
run: |
|
||||||
$env:CXXFLAGS+="/DQT_FORCE_ASSERTS /WX"
|
$env:CXXFLAGS+=" /WX"
|
||||||
cmake `
|
cmake `
|
||||||
-B build `
|
-B build `
|
||||||
-G "Ninja" `
|
-G "Ninja" `
|
||||||
@@ -191,11 +186,6 @@ jobs:
|
|||||||
name: qBittorrent-CI_Windows-x64_libtorrent-${{ matrix.libt_version }}
|
name: qBittorrent-CI_Windows-x64_libtorrent-${{ matrix.libt_version }}
|
||||||
path: upload
|
path: upload
|
||||||
|
|
||||||
- name: Install NSIS
|
|
||||||
uses: repolevedavaj/install-nsis@265e893c16602d8ccfb0a9ca44173b084078917c # v1.0.3
|
|
||||||
with:
|
|
||||||
nsis-version: '3.11'
|
|
||||||
|
|
||||||
- name: Create installer
|
- name: Create installer
|
||||||
run: |
|
run: |
|
||||||
7z x -o"dist/windows/" "dist/windows/NSISPlugins.zip"
|
7z x -o"dist/windows/" "dist/windows/NSISPlugins.zip"
|
||||||
|
|||||||
9
.github/workflows/coverity-scan.yaml
vendored
9
.github/workflows/coverity-scan.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
libt_version: ["2.0.11"]
|
libt_version: ["2.0.10"]
|
||||||
qbt_gui: ["GUI=ON"]
|
qbt_gui: ["GUI=ON"]
|
||||||
qt_version: ["6.5.2"]
|
qt_version: ["6.5.2"]
|
||||||
|
|
||||||
@@ -26,8 +26,6 @@ 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: |
|
||||||
@@ -39,10 +37,10 @@ jobs:
|
|||||||
- name: Install boost
|
- name: Install boost
|
||||||
env:
|
env:
|
||||||
BOOST_MAJOR_VERSION: "1"
|
BOOST_MAJOR_VERSION: "1"
|
||||||
BOOST_MINOR_VERSION: "86"
|
BOOST_MINOR_VERSION: "85"
|
||||||
BOOST_PATCH_VERSION: "0"
|
BOOST_PATCH_VERSION: "0"
|
||||||
run: |
|
run: |
|
||||||
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_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_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"
|
||||||
@@ -73,7 +71,6 @@ jobs:
|
|||||||
-B build \
|
-B build \
|
||||||
-G "Ninja" \
|
-G "Ninja" \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_CXX_STANDARD=20 \
|
|
||||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
-DBOOST_ROOT="${{ env.boost_path }}" \
|
||||||
-Ddeprecated-functions=OFF
|
-Ddeprecated-functions=OFF
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|||||||
@@ -16,5 +16,3 @@ ths = "ths"
|
|||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
BA = "BA"
|
BA = "BA"
|
||||||
helo = "helo"
|
helo = "helo"
|
||||||
Pn = "Pn"
|
|
||||||
UIU = "UIU"
|
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
#!/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.
|
|
||||||
|
|
||||||
from collections.abc import Callable, Sequence
|
|
||||||
from typing import Optional
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
import xml.etree.ElementTree as ElementTree
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
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,11 +26,9 @@
|
|||||||
# 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 collections.abc import Sequence
|
from typing import Optional, Sequence
|
||||||
from typing import Optional
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Optional[Sequence[str]] = None) -> int:
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
@@ -69,4 +67,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
exit(main())
|
||||||
|
|||||||
5
.github/workflows/stale_bot.yaml
vendored
5
.github/workflows/stale_bot.yaml
vendored
@@ -4,13 +4,12 @@ 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
4
.gitignore
vendored
@@ -41,3 +41,7 @@ src/icons/skin/build-icons/icons/*.png
|
|||||||
|
|
||||||
# CMake build directory
|
# CMake build directory
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
# Web UI tools
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
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
|
||||||
@@ -19,7 +13,7 @@ repos:
|
|||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
- repo: https://github.com/pre-commit/pre-commit-hooks.git
|
||||||
rev: v5.0.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-json
|
- id: check-json
|
||||||
name: Check JSON files
|
name: Check JSON files
|
||||||
@@ -69,17 +63,19 @@ repos:
|
|||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/codespell-project/codespell.git
|
- repo: https://github.com/codespell-project/codespell.git
|
||||||
rev: v2.4.0
|
rev: v2.2.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
name: Check spelling (codespell)
|
name: Check spelling (codespell)
|
||||||
args: ["--ignore-words-list", "additionals,categor,curren,fo,ist,ket,notin,searchin,sectionin,superseeding,te,ths"]
|
args: ["--ignore-words-list", "additionals,curren,fo,ist,ket,searchin,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/.*
|
||||||
@@ -88,7 +84,7 @@ repos:
|
|||||||
- ts
|
- ts
|
||||||
|
|
||||||
- repo: https://github.com/crate-ci/typos.git
|
- repo: https://github.com/crate-ci/typos.git
|
||||||
rev: v1.29.4
|
rev: v1.16.18
|
||||||
hooks:
|
hooks:
|
||||||
- id: typos
|
- id: typos
|
||||||
name: Check spelling (typos)
|
name: Check spelling (typos)
|
||||||
@@ -99,8 +95,11 @@ 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_v51x]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_v50x]
|
||||||
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_v51x]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_v50x]
|
||||||
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
|
||||||
|
|||||||
@@ -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.,
|
||||||
<https://fsf.org/>
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
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,7 +304,8 @@ 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, see <https://www.gnu.org/licenses/>.
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
@@ -328,8 +329,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 Moe Ghoul>, 1 April 1989
|
<signature of Ty Coon>, 1 April 1989
|
||||||
Moe Ghoul, President of Vice
|
Ty Coon, 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
|
||||||
|
|||||||
207
Changelog
207
Changelog
@@ -1,138 +1,45 @@
|
|||||||
Tue Nov 11th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.3
|
Sun Apr 13th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.5
|
||||||
- BUGFIX: Don't leave an empty folder when deleting or moving torrents (Ryu481)
|
- FEATURE: Add an advanced setting for setting the "Add New Torrent" dialog as modal (glassez)
|
||||||
- BUGFIX: Fix invalid Transifex links (rekayno)
|
- BUGFIX: Improve command line parameters serialization (glassez)
|
||||||
- BUGFIX: Don't fail because of existing files when exporting torrent files (glassez)
|
- BUGFIX: Declare missing color IDs for theming (glassez)
|
||||||
- BUGFIX: Allow equals character in the command line value (Mark Yu)
|
- WINDOWS: NSIS: Update Swedish translation (Daniel Nylander)
|
||||||
- BUGFIX: Fix "Save as .torrent file" button being visible before metadata retrieved (glassez)
|
|
||||||
- BUGFIX: Fix crash related with the processing order of libtorrent alerts (glassez)
|
|
||||||
- BUGFIX: Fix screen reader accessibility in torrent list (Andrew Johnson)
|
|
||||||
- BUGFIX: Improve tab key focus in hidable tab bar widget (Andrew Johnson)
|
|
||||||
- WEBUI: Fix http header affecting reverse proxy (Chocobo1)
|
|
||||||
- WEBAPI: Use native separators for path autofill suggestions (glassez)
|
|
||||||
- SEARCH: Plugin updater should work again now (Chocobo1)
|
|
||||||
- WINDOWS: Program update checker should work again now (Cloudflare related) (Chocobo1)
|
|
||||||
- WINDOWS: NSIS: Add Catalan and Kurdish translations (Ramon López i Cros, Halbast)
|
|
||||||
- LINUX: Fix crashes related to getrandom() on specific setups (Chocobo1)
|
|
||||||
- MACOS: Fix system language autodetection on MacOS (Ryu481)
|
|
||||||
|
|
||||||
Wed Jul 02nd 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.2
|
Tue Feb 18th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.4
|
||||||
- BUGFIX: Don't expose palette colors in UI theme editor since they are not customizable (glassez)
|
- BUGFIX: Fix cannot remove trackers via WebAPI (Chocobo1)
|
||||||
- BUGFIX: Add fallback to update mechanism (sledgehammer999)
|
- BUGFIX: Fix torrent content checkbox state under certain conditions (thalieht)
|
||||||
- WEBUI: Fix incorrectly backported changes (glassez)
|
- BUGFIX: Hide zero and infinity values in peer list only when that setting is set to `Always` (thalieht)
|
||||||
- WEBAPI: Trim leading whitespaces on Run External Program fields (Chocobo1)
|
- BUGFIX: Remove stopped torrent from "error" tracker filter (glassez)
|
||||||
- RSS/SEARCH: Prevent opening local files if web page is expected (glassez)
|
- WEBUI: Fix memory leak in context menus (skomerko)
|
||||||
- MACOS: Make qBittorrent quit on MacOS with main window closed (Ryu481)
|
- WEBAPI: Don't trim string parameters (glassez)
|
||||||
|
- WINDOWS: Handle Qt style options uniformly (glassez)
|
||||||
|
- WINDOWS: NSIS: Update Portuguese translation (Hugo Carvalho)
|
||||||
|
- MACOS: Avoid memory leak (Chocobo1)
|
||||||
|
|
||||||
Mon Jun 23rd 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.1
|
Mon Dec 16th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.3
|
||||||
- BUGFIX: Don't interpret wildcard pattern as filepath globbing (glassez)
|
- BUGFIX: Discard obsolete "state update" events after torrent is reloaded (glassez)
|
||||||
- BUGFIX: Fix appearance of search history length spinbox (glassez)
|
- BUGFIX: Fix incorrect SQL column definition (glassez)
|
||||||
- BUGFIX: Remove dubious seeding time max value (glassez)
|
- BUGFIX: Avoid redundant requests of announce entries from libtorrent (glassez)
|
||||||
- BUGFIX: Fix ratio handling (glassez)
|
- WEBUI: Fix removing tracker URL with '|' character (Thomas Piccirello)
|
||||||
- BUGFIX: Fix compilation with Qt 6.6.0 (glassez)
|
- WEBUI: Fix reloading page after login (Evgenii Ryshkov)
|
||||||
- WEBUI: Make General tab text selectable by default (dezza)
|
- WEBAPI: Fix incorrect key in torrent creator (Bartu Özen)
|
||||||
- WEBUI: Add versioning to local preferences (Chocobo1)
|
- RSS: Don't add duplicate episodes to previously matched (wavygecko)
|
||||||
- WEBUI: Make multi-rename search & replace fields use a monospace font (Atk)
|
- RSS: Use cached current time when parsing RSS feed (glassez)
|
||||||
- WEBUI: Fix wrong replacement sequence in IPv6 string (Chocobo1)
|
- WINDOWS: Don't follow symlink when creating torrents on Windows (Chocobo1)
|
||||||
- WEBUI: Fix memory leak (bolshoytoster)
|
- WINDOWS: NSIS: Update Italian translation (Giacomo411)
|
||||||
- WEBUI: Fix path autofill in set location and new category (tehcneko)
|
|
||||||
- RSS: Mark matched article as "read" if it refers to a duplicate torrent (glassez)
|
|
||||||
- WINDOWS: Update command line help message (KanishkaHalder1771)
|
|
||||||
- WINDOWS: NSIS: Don't require agreement on the license page (Chocobo1)
|
|
||||||
- LINUX: Fix preview not opening on Wayland (Isak05)
|
|
||||||
- LINUX: Add fallback for random number generator (Chocobo1)
|
|
||||||
|
|
||||||
Sun Apr 27th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.0
|
Sun Nov 17th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.2
|
||||||
- FEATURE: Enable customizing the save statistics time interval (Burnerelu)
|
- BUGFIX: Remove trackers from previous category when moved to new one (glassez)
|
||||||
- FEATURE: Add drag support to torrent content widget (Chocobo1)
|
- BUGFIX: Fix `.torrent` file could not be deleted when torrent is canceled (glassez)
|
||||||
- FEATURE: Display External IP Address in status bar (Thomas Piccirello)
|
- BUGFIX: Reset tracker entries when pausing the session (glassez)
|
||||||
- FEATURE: Use modern functions to get random numbers under Linux/Windows (security related) (Chocobo1)
|
- BUGFIX: Check real palette darkness to detect "dark theme" (glassez)
|
||||||
- FEATURE: Add eXact Length parameter when creating magnet URI (antanilol)
|
- BUGFIX: Correctly handle "torrent finished" events (glassez)
|
||||||
- FEATURE: Support fetching tracker list from URL (Thomas Piccirello)
|
- BUGFIX: Preserve initial torrent progress while checking resume data (glassez)
|
||||||
- FEATURE: Add `announce_port` support (Maxime Thiebaut)
|
- BUGFIX: Avoid reapplying Mark-of-the-Web when it already exists (Chocobo1)
|
||||||
- BUGFIX: Enable adaptive step size for upload and download limits (Harald Nordgren)
|
- BUGFIX: Don't apply Mark-of-the-Web on existing files (Chocobo1)
|
||||||
- BUGFIX: Add URL link for reverse proxy setup examples (Chocobo1)
|
- WEBUI: Add color scheme switcher (sledgehammer999)
|
||||||
- BUGFIX: Allow drop action only on transfer list (Chocobo1)
|
- SEARCH: Correctly delete the moved search tab (glassez)
|
||||||
- BUGFIX: Fix the tab order in dialogs (thalieht)
|
- WINDOWS: Correctly save and restore Qt style setting (glassez)
|
||||||
- BUGFIX: Fix filesize sorting in preview dialog (DoubleSpicy)
|
- WINDOWS: NSIS: update Luxembourgish, Simplified Chinese and Traditional Chinese translations (Ikko Eltociear Ashimine, 3gf8jv4dv)
|
||||||
- BUGFIX: Improve the speed icons in the status bar (Mahdi Hosseinzadeh)
|
|
||||||
- BUGFIX: Update link to news (tinyboxvk)
|
|
||||||
- BUGFIX: Fix tab stop order in various dialogs and UI elements (Chocobo1)
|
|
||||||
- BUGFIX: Make links accessible by keyboard (Chocobo1)
|
|
||||||
- BUGFIX: Make tab key switch focus (Chocobo1)
|
|
||||||
- BUGFIX: Revise DHT bootstrap node list (stalkerok, Chocobo1)
|
|
||||||
- BUGFIX: Return first tracker as fallback for "current tracker" (glassez)
|
|
||||||
- BUGFIX: Prevent crash when exiting app with `Add torrent` dialogs opened (glassez)
|
|
||||||
- BUGFIX: Fix torrent relocating files when switching to "manual" mode (glassez)
|
|
||||||
- BUGFIX: Prevent crash due to corrupted resume data (glassez)
|
|
||||||
- WEBUI: Improvements that should help with assistive technologies (Chocobo1)
|
|
||||||
- WEBUI: Internal refactoring to migrate away from MooTools and towards native browser APIs (Chocobo1, skomerko)
|
|
||||||
- WEBUI: Implement path autocompletion (Paweł Kotiuk)
|
|
||||||
- WEBUI: Implement double-click behavior controls (Hanabishi)
|
|
||||||
- WEBUI: Add ability to toggle alternating row colors in tables (skomerko)
|
|
||||||
- WEBUI: Improve visibility of unread RSS articles (skomerko)
|
|
||||||
- WEBUI: Remove deleted torrents even if they are currently filtered out (Carmelo Scandaliato)
|
|
||||||
- WEBUI: Highlight torrent category in context menu (skomerko)
|
|
||||||
- WEBUI: Implement 'Auto hide zero status filters' (skomerko)
|
|
||||||
- WEBUI: Allow to filter torrent list by save path (skomerko)
|
|
||||||
- WEBUI: Handle regex syntax error for torrent filtering (HamletDuFromage)
|
|
||||||
- WEBUI: Add missing icons (skomerko)
|
|
||||||
- WEBUI: Add link to 'List of alternative WebUI' wiki page in Options (Chocobo1)
|
|
||||||
- WEBUI: Improve properties panel, torrent deletion dialog, filter list, subcategories, torrent deletion, statistics window (skomerko)
|
|
||||||
- WEBUI: Allow to display only hostname in the Tracker column (skomerko)
|
|
||||||
- WEBUI: Show country/region name next to its flag (skomerko)
|
|
||||||
- WEBUI: Improve hash copy actions in context menu (skomerko)
|
|
||||||
- WEBUI: Support removing tracker from all torrents in WebUI/WebAPI (Thomas Piccirello)
|
|
||||||
- WEBUI: Display DHT information in the Status bar only when DHT is enabled (skomerko)
|
|
||||||
- WEBUI: Add 'Confirm torrent recheck' option (skomerko)
|
|
||||||
- WEBUI: Support managing web seeds (Thomas Piccirello)
|
|
||||||
- WEBUI: Add colors to log table rows (skomerko)
|
|
||||||
- WEBUI: Prevent text selection within tabs, menu items (skomerko)
|
|
||||||
- WEBUI: Use correct text and background colors in RSS details view (skomerko)
|
|
||||||
- WEBUI: Reduce padding in torrents table (skomerko)
|
|
||||||
- WEBUI: Add WebAPI/WebUI for managing cookies (Thomas Piccirello)
|
|
||||||
- WEBUI: Support updating RSS feed URL (Thomas Piccirello)
|
|
||||||
- WEBUI: Add 'Engine' column to Search table (skomerko)
|
|
||||||
- WEBUI: Add confirm dialog for Auto TMM (skomerko)
|
|
||||||
- WEBUI: Add context menu to search tabs (skomerko)
|
|
||||||
- WEBUI: Show file filter when Content tab selected on load (Thomas Piccirello)
|
|
||||||
- WEBUI: DHT, PeX and LSD rows are now always on top in Trackers table (skomerko)
|
|
||||||
- WEBUI: Clear properties panel when torrent no longer selected (skomerko)
|
|
||||||
- WEBUI: Support auto resizing table columns (Thomas Piccirello)
|
|
||||||
- WEBUI: Fix displaying RSS panel on load (Thomas Piccirello)
|
|
||||||
- WEBUI: Add tooltip to regex filter button (Patrik Elfström)
|
|
||||||
- WEBUI: Hide context menu when clicking on a table row (Patrik Elfström)
|
|
||||||
- WEBUI: Display torrent progress percentage in General tab (skomerko)
|
|
||||||
- WEBUI: Use thin scrollbars (skomerko)
|
|
||||||
- WEBUI: Show 'Rename...' context menu item only when one torrent is selected (skomerko)
|
|
||||||
- WEBUI: Display error when download fails (Thomas Piccirello)
|
|
||||||
- WEBUI: Add colors to 'Status' column in Trackers table (skomerko)
|
|
||||||
- WEBUI: Add missing icon to 'Queue' context menu item (skomerko)
|
|
||||||
- WEBUI: Change filter inputs to type search (Patrik Elfström)
|
|
||||||
- WEBUI: Allow to move state icon to name column in torrents table (skomerko)
|
|
||||||
- WEBUI: Fix bug where the 'Tracker editing' dialog displays incorrect data (skomerko)
|
|
||||||
- WEBUI: Maintain row highlight after rearranging table columns (skomerko)
|
|
||||||
- WEBUI: Fix preferences not applied in magnet handler (Chocobo1)
|
|
||||||
- WEBUI: Update sort icon after changing column order (skomerko)
|
|
||||||
- WEBUI: Show 'Edit tracker URL...' only when one tracker is selected (skomerko)
|
|
||||||
- WEBUI: Set status filter to 'All' if selected filter is no longer visible (skomerko)
|
|
||||||
- WEBAPI: Don't reannounce when removing tracker via WebAPI (Thomas Piccirello)
|
|
||||||
- WEBAPI: Add WebAPI for managing torrent webseeds (Thomas Piccirello)
|
|
||||||
- WEBAPI: Add `forced` parameter to `torrents/add` (Chris B)
|
|
||||||
- WEBAPI: Optionally include trackers list in torrent info response (ze0s)
|
|
||||||
- WEBAPI: Add new method `setTags` to upsert tags on torrents (ze0s)
|
|
||||||
- RSS: Resolve relative URLs within RSS article description (Zentino)
|
|
||||||
- SEARCH: Provide SSL context field (Chocobo1)
|
|
||||||
- SEARCH: Allow to refresh existing search (glassez)
|
|
||||||
- SEARCH: Allow multiple simultaneous searches (glassez)
|
|
||||||
- SEARCH: Store opened search tabs (glassez)
|
|
||||||
- SEARCH: Store search history (glassez)
|
|
||||||
- SEARCH: Migrate socks.py from SocksiPy to PySocks 1.7.1 (FredBill1)
|
|
||||||
- SEARCH: Bump Python version minimum requirement (Chocobo1)
|
|
||||||
- WINDOWS: Opt into Windows SegmentHeap (Andarwinux)
|
|
||||||
- WINDOWS: Allow to choose color scheme on Windows (glassez)
|
|
||||||
- WINDOWS: Verify hash of Python installer (Chocobo1)
|
|
||||||
- LINUX: Add support for Thunar file manager (algebnaly)
|
|
||||||
- MACOS: Fix shift-click selection on macOS (Luke Memet)
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -240,42 +147,6 @@ 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)
|
||||||
|
|||||||
2
INSTALL
2
INSTALL
@@ -18,7 +18,7 @@ qBittorrent - A BitTorrent client in C++ / Qt
|
|||||||
- CMake >= 3.16
|
- CMake >= 3.16
|
||||||
* Compile-time only
|
* Compile-time only
|
||||||
|
|
||||||
- Python >= 3.9.0
|
- Python >= 3.7.0
|
||||||
* Optional, run-time only
|
* Optional, run-time only
|
||||||
* Used by the bundled search engine
|
* Used by the bundled search engine
|
||||||
|
|
||||||
|
|||||||
18
SECURITY.md
18
SECURITY.md
@@ -1,18 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -47,9 +47,6 @@ 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
|
||||||
|
|||||||
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -55,7 +55,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>5.1.3</string>
|
<string>5.0.5</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
|||||||
@@ -161,8 +161,8 @@ 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
|
||||||
Comment[tr]=Dosyaları BitTorrent üzerinden indirin ve paylaşın
|
Comment[tr]=Dosyaları BitTorrent üzerinden indirin ve paylaşın
|
||||||
|
|||||||
@@ -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.1.3" date="2025-11-11"/>
|
<release version="5.0.5" date="2025-04-13"/>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
5
dist/windows/config.nsh
vendored
5
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.1.3"
|
!define /ifndef QBT_VERSION "5.0.5"
|
||||||
|
|
||||||
; 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:
|
||||||
@@ -111,8 +111,7 @@ 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_BUTTON $(^NextBtn)
|
!define MUI_LICENSEPAGE_CHECKBOX
|
||||||
!define MUI_LICENSEPAGE_TEXT_BOTTOM "$_CLICK"
|
|
||||||
!define MUI_LANGDLL_ALLLANGUAGES
|
!define MUI_LANGDLL_ALLLANGUAGES
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
|||||||
54
dist/windows/installer-translations/catalan.nsh
vendored
54
dist/windows/installer-translations/catalan.nsh
vendored
@@ -1,60 +1,60 @@
|
|||||||
;Installer strings
|
;Installer strings
|
||||||
|
|
||||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||||
LangString inst_qbt_req ${LANG_CATALAN} "qBittorrent (requerit)"
|
LangString inst_qbt_req ${LANG_CATALAN} "qBittorrent (required)"
|
||||||
;LangString inst_desktop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
;LangString inst_desktop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||||
LangString inst_desktop ${LANG_CATALAN} "Crea una drecera a l'escriptori"
|
LangString inst_desktop ${LANG_CATALAN} "Create Desktop Shortcut"
|
||||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||||
LangString inst_startmenu ${LANG_CATALAN} "Crea una drecera al menú d'inici"
|
LangString inst_startmenu ${LANG_CATALAN} "Create Start Menu Shortcut"
|
||||||
;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_CATALAN} "Inicia qBittorrent en iniciar Windows"
|
LangString inst_startup ${LANG_CATALAN} "Start qBittorrent on Windows start up"
|
||||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||||
LangString inst_torrent ${LANG_CATALAN} "Obre els fitxers .torrent amb qBittorrent"
|
LangString inst_torrent ${LANG_CATALAN} "Open .torrent files with 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_CATALAN} "Obre els enllaços magnètics amb qBittorrent"
|
LangString inst_magnet ${LANG_CATALAN} "Open magnet links with qBittorrent"
|
||||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||||
LangString inst_firewall ${LANG_CATALAN} "Afegir la regla del tallafoc del Windows"
|
LangString inst_firewall ${LANG_CATALAN} "Add Windows Firewall rule"
|
||||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||||
LangString inst_pathlimit ${LANG_CATALAN} "Deshabilita el límit de longitud del path de Windows (260 caràcters de limitació MAX_PATH, requereix Windows 10 1607 o posterior)"
|
LangString inst_pathlimit ${LANG_CATALAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||||
LangString inst_firewallinfo ${LANG_CATALAN} "S'està afegint la regla del tallafoc del Windows"
|
LangString inst_firewallinfo ${LANG_CATALAN} "Adding Windows Firewall rule"
|
||||||
;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_CATALAN} "qBittorrent s'està executant. Tanqueu l'aplicació abans d'instal·lar."
|
LangString inst_warning ${LANG_CATALAN} "qBittorrent is running. Please close the application before installing."
|
||||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||||
LangString inst_uninstall_question ${LANG_CATALAN} "La versió actual es desinstal·larà. La configuració de l'usuari i els torrents es mantindran intactes."
|
LangString inst_uninstall_question ${LANG_CATALAN} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||||
LangString inst_unist ${LANG_CATALAN} "Desinstal·lant la versió anterior."
|
LangString inst_unist ${LANG_CATALAN} "Uninstalling previous version."
|
||||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||||
LangString launch_qbt ${LANG_CATALAN} "Executa qBittorrent."
|
LangString launch_qbt ${LANG_CATALAN} "Launch 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_CATALAN} "Aquest instal·lador només funciona en versions Windows de 64 bits."
|
LangString inst_requires_64bit ${LANG_CATALAN} "This installer works only in 64-bit Windows versions."
|
||||||
;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_CATALAN} "Aquest instal·lador requereix almenys Windows 10 (1809) / Windows Server 2019."
|
LangString inst_requires_win10 ${LANG_CATALAN} "This installer requires at least 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_CATALAN} "Desinstal·lar qBittorrent"
|
LangString inst_uninstall_link_description ${LANG_CATALAN} "Uninstall qBittorrent"
|
||||||
|
|
||||||
;------------------------------------
|
;------------------------------------
|
||||||
;Uninstaller strings
|
;Uninstaller strings
|
||||||
|
|
||||||
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
||||||
LangString remove_files ${LANG_CATALAN} "Elimina els fitxers"
|
LangString remove_files ${LANG_CATALAN} "Remove files"
|
||||||
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
||||||
LangString remove_shortcuts ${LANG_CATALAN} "Elimina les draceres"
|
LangString remove_shortcuts ${LANG_CATALAN} "Remove shortcuts"
|
||||||
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
||||||
LangString remove_associations ${LANG_CATALAN} "Elimina les associacions de fitxers"
|
LangString remove_associations ${LANG_CATALAN} "Remove file associations"
|
||||||
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
||||||
LangString remove_registry ${LANG_CATALAN} "Elimina les claus del registre"
|
LangString remove_registry ${LANG_CATALAN} "Remove registry keys"
|
||||||
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
||||||
LangString remove_conf ${LANG_CATALAN} "Elimina els fitxers de configuració"
|
LangString remove_conf ${LANG_CATALAN} "Remove configuration files"
|
||||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||||
LangString remove_firewall ${LANG_CATALAN} "Elimina la regla del tallafoc del Windows"
|
LangString remove_firewall ${LANG_CATALAN} "Remove Windows Firewall rule"
|
||||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||||
LangString remove_firewallinfo ${LANG_CATALAN} "Eliminant la regla del tallafoc del Windows"
|
LangString remove_firewallinfo ${LANG_CATALAN} "Removing Windows Firewall rule"
|
||||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||||
LangString remove_cache ${LANG_CATALAN} "Elimina els torrents i les dades de la memòria cau"
|
LangString remove_cache ${LANG_CATALAN} "Remove torrents and cached 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_CATALAN} "qBittorrent s'està executant. Tanca l'aplicació abans de desinstal·lar-la."
|
LangString uninst_warning ${LANG_CATALAN} "qBittorrent is running. Please close the application before uninstalling."
|
||||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||||
LangString uninst_tor_warn ${LANG_CATALAN} "No s'està eliminant l'associació .torrent. Està associat a:"
|
LangString uninst_tor_warn ${LANG_CATALAN} "Not removing .torrent association. It is associated with:"
|
||||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||||
LangString uninst_mag_warn ${LANG_CATALAN} "No s'està eliminant l'associació magnètica. Està associat a:"
|
LangString uninst_mag_warn ${LANG_CATALAN} "Not removing magnet association. It is associated with:"
|
||||||
|
|||||||
54
dist/windows/installer-translations/kurdish.nsh
vendored
54
dist/windows/installer-translations/kurdish.nsh
vendored
@@ -1,60 +1,60 @@
|
|||||||
;Installer strings
|
;Installer strings
|
||||||
|
|
||||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||||
LangString inst_qbt_req ${LANG_KURDISH} "کیووبیتتۆرێنت (پێویستە)"
|
LangString inst_qbt_req ${LANG_KURDISH} "qBittorrent (required)"
|
||||||
;LangString inst_desktop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
;LangString inst_desktop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||||
LangString inst_desktop ${LANG_KURDISH} "قەدبڕێک دروست بکە لەسەر دێسکتۆپ"
|
LangString inst_desktop ${LANG_KURDISH} "Create Desktop Shortcut"
|
||||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||||
LangString inst_startmenu ${LANG_KURDISH} "قەدبڕێک دروست بکە لەسەر پێڕستی دەتپێک"
|
LangString inst_startmenu ${LANG_KURDISH} "Create Start Menu Shortcut"
|
||||||
;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_KURDISH} "لەگەڵ هەڵبوونی کۆمپیوتەرەکە کیووبیتتۆرێنت بکەرەوە"
|
LangString inst_startup ${LANG_KURDISH} "Start qBittorrent on Windows start up"
|
||||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||||
LangString inst_torrent ${LANG_KURDISH} "پەڕگەکانی .torrent بە کیووبیتتۆرێنت بکەرەوە"
|
LangString inst_torrent ${LANG_KURDISH} "Open .torrent files with 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_KURDISH} "بەسەرە موگناتیسییەکان بە کیووبیتتۆرێنت بکەرەوە"
|
LangString inst_magnet ${LANG_KURDISH} "Open magnet links with qBittorrent"
|
||||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||||
LangString inst_firewall ${LANG_KURDISH} "یاسای فایەروۆڵی ویندۆز زیاد بکە"
|
LangString inst_firewall ${LANG_KURDISH} "Add Windows Firewall rule"
|
||||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||||
LangString inst_pathlimit ${LANG_KURDISH} "سنووری درێژیی ڕێڕەوی ویندۆز ناچالاک بکە (سنووری ٢٦٠ پیتی، پێویستی بە ویندۆزی ١٠ و دواتر هەیە)"
|
LangString inst_pathlimit ${LANG_KURDISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||||
LangString inst_firewallinfo ${LANG_KURDISH} "یاسای فایەروۆڵی ویندۆز زیاد دەبێت"
|
LangString inst_firewallinfo ${LANG_KURDISH} "Adding Windows Firewall rule"
|
||||||
;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_KURDISH} "کیووبیتتۆرێنت کارایە. تکایە بەرنامەکە دابخەرەوە پێش دامەزراندن."
|
LangString inst_warning ${LANG_KURDISH} "qBittorrent is running. Please close the application before installing."
|
||||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||||
LangString inst_uninstall_question ${LANG_KURDISH} "ئەم وەشانەی ئێستا دەسڕدرێتەوە. ڕێکخستنەکانی بەکارهێنەر و تۆرێنتەکان وەک خۆیان دەمێننەوە."
|
LangString inst_uninstall_question ${LANG_KURDISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||||
LangString inst_unist ${LANG_KURDISH} "وەشانی پێشوو دەسڕدرێتەوە."
|
LangString inst_unist ${LANG_KURDISH} "Uninstalling previous version."
|
||||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||||
LangString launch_qbt ${LANG_KURDISH} "کیووبیتتۆرێنت کارا بکە."
|
LangString launch_qbt ${LANG_KURDISH} "Launch 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_KURDISH} "ئەم دامەزرێنەرە تەنیا لەسەر وەشانی ٦٤ بیتی ویندۆز کار دەکات."
|
LangString inst_requires_64bit ${LANG_KURDISH} "This installer works only in 64-bit Windows versions."
|
||||||
;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_KURDISH} "ئەم دامەزرێنەرە لانیکەم پێویستی بە ویندۆزی ١٠ یان ویندۆز سێرڤەری ٢٠١٩ هەیە."
|
LangString inst_requires_win10 ${LANG_KURDISH} "This installer requires at least 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_KURDISH} "کیووبیتتۆرێنت بسڕەوە"
|
LangString inst_uninstall_link_description ${LANG_KURDISH} "Uninstall qBittorrent"
|
||||||
|
|
||||||
;------------------------------------
|
;------------------------------------
|
||||||
;Uninstaller strings
|
;Uninstaller strings
|
||||||
|
|
||||||
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
||||||
LangString remove_files ${LANG_KURDISH} "پەڕگەکان بسڕەوە"
|
LangString remove_files ${LANG_KURDISH} "Remove files"
|
||||||
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
||||||
LangString remove_shortcuts ${LANG_KURDISH} "قەدبڕەکان بسڕەوە"
|
LangString remove_shortcuts ${LANG_KURDISH} "Remove shortcuts"
|
||||||
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
||||||
LangString remove_associations ${LANG_KURDISH} "پەیوەندیی پەڕگەکان بسڕەوە"
|
LangString remove_associations ${LANG_KURDISH} "Remove file associations"
|
||||||
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
||||||
LangString remove_registry ${LANG_KURDISH} "کلیلەکانی تۆمار بسڕەوە"
|
LangString remove_registry ${LANG_KURDISH} "Remove registry keys"
|
||||||
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
||||||
LangString remove_conf ${LANG_KURDISH} "پەڕگەکانی شێوەپێدان بسڕەوە"
|
LangString remove_conf ${LANG_KURDISH} "Remove configuration files"
|
||||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||||
LangString remove_firewall ${LANG_KURDISH} "یاسای فایەروۆڵی ویندۆز بسڕەوە"
|
LangString remove_firewall ${LANG_KURDISH} "Remove Windows Firewall rule"
|
||||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||||
LangString remove_firewallinfo ${LANG_KURDISH} "یاسای فایەروۆڵی ویندۆز دەسڕێتەوە"
|
LangString remove_firewallinfo ${LANG_KURDISH} "Removing Windows Firewall rule"
|
||||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||||
LangString remove_cache ${LANG_KURDISH} "تۆرێنت و داتای کاشکراو بسڕەوە"
|
LangString remove_cache ${LANG_KURDISH} "Remove torrents and cached 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_KURDISH} "کیووبیتتۆرێنت کارایە. تکایە بەرنامەکە دابخە پێش سڕینەوەی."
|
LangString uninst_warning ${LANG_KURDISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||||
LangString uninst_tor_warn ${LANG_KURDISH} "پەیوەندیی .torrent ناسڕێتەوە. پەیوەندیی هەیە بە:"
|
LangString uninst_tor_warn ${LANG_KURDISH} "Not removing .torrent association. It is associated with:"
|
||||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||||
LangString uninst_mag_warn ${LANG_KURDISH} "پەیوەندیی موگناتیسی ناسڕێتەوە. پەیوەیندیی هەیە بە:"
|
LangString uninst_mag_warn ${LANG_KURDISH} "Not removing magnet association. It is associated with:"
|
||||||
|
|||||||
@@ -1,44 +1,41 @@
|
|||||||
.\" Automatically generated by Pandoc 3.4
|
.\" Automatically generated by Pandoc 3.1.7
|
||||||
.\"
|
.\"
|
||||||
.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
|
\f[B]qBittorrent-nox\f[R] is an advanced command-line Bittorrent client
|
||||||
client written in C++ / Qt using the \f[B]libtorrent\-rasterbar\f[R]
|
written in C++ / Qt using the \f[B]libtorrent-rasterbar\f[R] library by
|
||||||
library by Arvid Norberg.
|
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.
|
\[lq]admin\[rq] with \[lq]adminadmin\[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
|
\f[B]\f[CB]--webui-port=x\f[B]\f[R] Changes Web UI port to x (default:
|
||||||
(default: 8080).
|
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 \c
|
Christophe Dumez <chris@qbittorrent.org>.
|
||||||
.MT chris@qbittorrent.org
|
|
||||||
.ME \c
|
|
||||||
\&.
|
|
||||||
|
|||||||
@@ -1,38 +1,35 @@
|
|||||||
.\" Automatically generated by Pandoc 3.4
|
.\" Automatically generated by Pandoc 3.1.7
|
||||||
.\"
|
.\"
|
||||||
.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
|
\f[B]\f[CB]--webui-port=x\f[B]\f[R] Changes Web UI port to x (default:
|
||||||
(default: 8080).
|
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 \c
|
Christophe Dumez <chris@qbittorrent.org>.
|
||||||
.MT chris@qbittorrent.org
|
|
||||||
.ME \c
|
|
||||||
\&.
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
.\" Automatically generated by Pandoc 3.4
|
.\" Automatically generated by Pandoc 3.1.7
|
||||||
.\"
|
.\"
|
||||||
.TH "QBITTORRENT\-NOX" "1" "16 января 2010" "Клиент сети БитТоррент для командной строки"
|
.TH "QBITTORRENT-NOX" "1" "16 января 2010" "Клиент сети БитТоррент для командной строки" ""
|
||||||
.SH НАЗВАНИЕ
|
.SH НАЗВАНИЕ
|
||||||
qBittorrent\-nox \[em] клиент сети БитТоррент для командной строки.
|
qBittorrent-nox \[em] клиент сети БитТоррент для командной строки.
|
||||||
.SH АВТОРЫ
|
.SH АВТОРЫ
|
||||||
Christophe Dumez \c
|
Christophe Dumez <chris@qbittorrent.org>.
|
||||||
.MT chris@qbittorrent.org
|
|
||||||
.ME \c
|
|
||||||
\&.
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
.\" Automatically generated by Pandoc 3.4
|
.\" Automatically generated by Pandoc 3.1.7
|
||||||
.\"
|
.\"
|
||||||
.TH "QBITTORRENT" "1" "16 января 2010" "Клиент сети БитТоррент"
|
.TH "QBITTORRENT" "1" "16 января 2010" "Клиент сети БитТоррент" ""
|
||||||
.SH НАЗВАНИЕ
|
.SH НАЗВАНИЕ
|
||||||
qBittorrent \[em] клиент сети БитТоррент.
|
qBittorrent \[em] клиент сети БитТоррент.
|
||||||
.SH АВТОРЫ
|
.SH АВТОРЫ
|
||||||
Christophe Dumez \c
|
Christophe Dumez <chris@qbittorrent.org>.
|
||||||
.MT chris@qbittorrent.org
|
|
||||||
.ME \c
|
|
||||||
\&.
|
|
||||||
|
|||||||
@@ -86,19 +86,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
|||||||
PROPERTIES
|
PROPERTIES
|
||||||
MACOSX_PACKAGE_LOCATION Resources
|
MACOSX_PACKAGE_LOCATION Resources
|
||||||
)
|
)
|
||||||
# Generate lproj folders for the translations
|
|
||||||
foreach(TS_FILE IN LISTS QBT_TS_FILES)
|
|
||||||
string(FIND "${TS_FILE}" "_" POS)
|
|
||||||
math(EXPR START "${POS} + 1")
|
|
||||||
string(SUBSTRING "${TS_FILE}" ${START} -1 LPROJ_FOLDER)
|
|
||||||
string(REPLACE ".ts" ".lproj" LPROJ_FOLDER "${LPROJ_FOLDER}")
|
|
||||||
# @ is not valid as a language code for a lproj folder on MacOS
|
|
||||||
string(REPLACE "@" "-" LPROJ_FOLDER "${LPROJ_FOLDER}")
|
|
||||||
add_custom_command(TARGET qbt_app POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
|
||||||
"$<TARGET_FILE_DIR:qbt_app>/../Resources/${LPROJ_FOLDER}"
|
|
||||||
)
|
|
||||||
endforeach()
|
|
||||||
# provide variables for substitution in dist/mac/Info.plist
|
# provide variables for substitution in dist/mac/Info.plist
|
||||||
get_target_property(EXECUTABLE_NAME qbt_app OUTPUT_NAME)
|
get_target_property(EXECUTABLE_NAME qbt_app OUTPUT_NAME)
|
||||||
# This variable name should be changed once qmake is no longer used. Refer to the discussion in PR #14813
|
# This variable name should be changed once qmake is no longer used. Refer to the discussion in PR #14813
|
||||||
|
|||||||
@@ -602,7 +602,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 sequence is different for Windows and other OS, this is intentional
|
// The processing sequenece 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();
|
||||||
@@ -920,10 +920,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 BitTorrent::AddTorrentError &reason)
|
, [this](const QString &source, const QString &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.message));
|
, tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason));
|
||||||
});
|
});
|
||||||
|
|
||||||
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#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>
|
||||||
@@ -61,7 +60,7 @@ namespace
|
|||||||
class Option
|
class Option
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
explicit constexpr Option(const QStringView name, const QChar shortcut = QChar::Null)
|
explicit constexpr Option(const char *name, char shortcut = 0)
|
||||||
: m_name {name}
|
: m_name {name}
|
||||||
, m_shortcut {shortcut}
|
, m_shortcut {shortcut}
|
||||||
{
|
{
|
||||||
@@ -69,23 +68,23 @@ namespace
|
|||||||
|
|
||||||
QString fullParameter() const
|
QString fullParameter() const
|
||||||
{
|
{
|
||||||
return u"--" + m_name.toString();
|
return u"--" + QString::fromLatin1(m_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString shortcutParameter() const
|
QString shortcutParameter() const
|
||||||
{
|
{
|
||||||
return u"-" + m_shortcut;
|
return u"-" + QChar::fromLatin1(m_shortcut);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasShortcut() const
|
bool hasShortcut() const
|
||||||
{
|
{
|
||||||
return !m_shortcut.isNull();
|
return m_shortcut != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString envVarName() const
|
QString envVarName() const
|
||||||
{
|
{
|
||||||
return u"QBT_"
|
return u"QBT_"
|
||||||
+ m_name.toString().toUpper().replace(u'-', u'_');
|
+ QString::fromLatin1(m_name).toUpper().replace(u'-', u'_');
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -100,15 +99,15 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QStringView m_name;
|
const char *m_name = nullptr;
|
||||||
const QChar m_shortcut;
|
const char m_shortcut;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Boolean option.
|
// Boolean option.
|
||||||
class BoolOption : protected Option
|
class BoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit constexpr BoolOption(const QStringView name, const QChar shortcut = QChar::Null)
|
explicit constexpr BoolOption(const char *name, char shortcut = 0)
|
||||||
: Option {name, shortcut}
|
: Option {name, shortcut}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -140,21 +139,19 @@ namespace
|
|||||||
struct StringOption : protected Option
|
struct StringOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit constexpr StringOption(const QStringView name)
|
explicit constexpr StringOption(const char *name)
|
||||||
: Option {name, QChar::Null}
|
: Option {name, 0}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString value(const QString &arg) const
|
QString value(const QString &arg) const
|
||||||
{
|
{
|
||||||
const qsizetype index = arg.indexOf(u'=');
|
QStringList parts = arg.split(u'=');
|
||||||
if (index == -1)
|
if (parts.size() == 2)
|
||||||
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "Parameter '%1' must follow syntax '%1=%2'",
|
return Utils::String::unquote(parts[1], u"'\""_s);
|
||||||
|
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "Parameter '%1' must follow syntax '%1=%2'",
|
||||||
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'")
|
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'")
|
||||||
.arg(fullParameter(), u"<value>"_s));
|
.arg(fullParameter(), u"<value>"_s));
|
||||||
|
|
||||||
const QStringView val = QStringView(arg).sliced(index + 1);
|
|
||||||
return Utils::String::unquote(val, u"'\""_s).toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString value(const QProcessEnvironment &env, const QString &defaultValue = {}) const
|
QString value(const QProcessEnvironment &env, const QString &defaultValue = {}) const
|
||||||
@@ -170,7 +167,7 @@ namespace
|
|||||||
|
|
||||||
friend bool operator==(const StringOption &option, const QString &arg)
|
friend bool operator==(const StringOption &option, const QString &arg)
|
||||||
{
|
{
|
||||||
return (arg == option.fullParameter()) || arg.startsWith(option.parameterAssignment());
|
return arg.startsWith(option.parameterAssignment());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -184,7 +181,7 @@ namespace
|
|||||||
class IntOption : protected StringOption
|
class IntOption : protected StringOption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit constexpr IntOption(const QStringView name)
|
explicit constexpr IntOption(const char *name)
|
||||||
: StringOption {name}
|
: StringOption {name}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -232,8 +229,8 @@ namespace
|
|||||||
class TriStateBoolOption : protected Option
|
class TriStateBoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr TriStateBoolOption(const QStringView name, const bool defaultValue)
|
constexpr TriStateBoolOption(const char *name, bool defaultValue)
|
||||||
: Option {name, QChar::Null}
|
: Option {name, 0}
|
||||||
, m_defaultValue(defaultValue)
|
, m_defaultValue(defaultValue)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -302,32 +299,31 @@ namespace
|
|||||||
return arg.section(u'=', 0, 0) == option.fullParameter();
|
return arg.section(u'=', 0, 0) == option.fullParameter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
bool m_defaultValue;
|
||||||
bool m_defaultValue = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr const BoolOption SHOW_HELP_OPTION {u"help", u'h'};
|
constexpr const BoolOption SHOW_HELP_OPTION {"help", 'h'};
|
||||||
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
|
||||||
constexpr const BoolOption SHOW_VERSION_OPTION {u"version", u'v'};
|
constexpr const BoolOption SHOW_VERSION_OPTION {"version", 'v'};
|
||||||
#endif
|
#endif
|
||||||
constexpr const BoolOption CONFIRM_LEGAL_NOTICE {u"confirm-legal-notice"};
|
constexpr const BoolOption CONFIRM_LEGAL_NOTICE {"confirm-legal-notice"};
|
||||||
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
||||||
constexpr const BoolOption DAEMON_OPTION {u"daemon", u'd'};
|
constexpr const BoolOption DAEMON_OPTION {"daemon", 'd'};
|
||||||
#else
|
#else
|
||||||
constexpr const BoolOption NO_SPLASH_OPTION {u"no-splash"};
|
constexpr const BoolOption NO_SPLASH_OPTION {"no-splash"};
|
||||||
#endif
|
#endif
|
||||||
constexpr const IntOption WEBUI_PORT_OPTION {u"webui-port"};
|
constexpr const IntOption WEBUI_PORT_OPTION {"webui-port"};
|
||||||
constexpr const IntOption TORRENTING_PORT_OPTION {u"torrenting-port"};
|
constexpr const IntOption TORRENTING_PORT_OPTION {"torrenting-port"};
|
||||||
constexpr const StringOption PROFILE_OPTION {u"profile"};
|
constexpr const StringOption PROFILE_OPTION {"profile"};
|
||||||
constexpr const StringOption CONFIGURATION_OPTION {u"configuration"};
|
constexpr const StringOption CONFIGURATION_OPTION {"configuration"};
|
||||||
constexpr const BoolOption RELATIVE_FASTRESUME {u"relative-fastresume"};
|
constexpr const BoolOption RELATIVE_FASTRESUME {"relative-fastresume"};
|
||||||
constexpr const StringOption SAVE_PATH_OPTION {u"save-path"};
|
constexpr const StringOption SAVE_PATH_OPTION {"save-path"};
|
||||||
constexpr const TriStateBoolOption STOPPED_OPTION {u"add-stopped", true};
|
constexpr const TriStateBoolOption STOPPED_OPTION {"add-stopped", true};
|
||||||
constexpr const BoolOption SKIP_HASH_CHECK_OPTION {u"skip-hash-check"};
|
constexpr const BoolOption SKIP_HASH_CHECK_OPTION {"skip-hash-check"};
|
||||||
constexpr const StringOption CATEGORY_OPTION {u"category"};
|
constexpr const StringOption CATEGORY_OPTION {"category"};
|
||||||
constexpr const BoolOption SEQUENTIAL_OPTION {u"sequential"};
|
constexpr const BoolOption SEQUENTIAL_OPTION {"sequential"};
|
||||||
constexpr const BoolOption FIRST_AND_LAST_OPTION {u"first-and-last"};
|
constexpr const BoolOption FIRST_AND_LAST_OPTION {"first-and-last"};
|
||||||
constexpr const TriStateBoolOption SKIP_DIALOG_OPTION {u"skip-dialog", true};
|
constexpr const TriStateBoolOption SKIP_DIALOG_OPTION {"skip-dialog", true};
|
||||||
}
|
}
|
||||||
|
|
||||||
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
|
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
|
||||||
@@ -493,12 +489,6 @@ 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'
|
||||||
|
|
||||||
@@ -550,7 +540,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'
|
||||||
+ noSplashCommand + u'\n'
|
+ u"QBT_NO_SPLASH=1 " + prgName + 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"
|
||||||
|
|||||||
@@ -74,7 +74,6 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
@@ -91,7 +90,7 @@ namespace QtLP_Private
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray ACK = QByteArrayLiteral("ack");
|
const char ACK[] = "ack";
|
||||||
|
|
||||||
QtLocalPeer::QtLocalPeer(const QString &path, QObject *parent)
|
QtLocalPeer::QtLocalPeer(const QString &path, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
@@ -170,7 +169,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(ACK.size()) == ACK);
|
res &= (socket.read(qstrlen(ACK)) == ACK);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -221,7 +220,7 @@ void QtLocalPeer::receiveConnection()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString message(QString::fromUtf8(uMsg));
|
QString message(QString::fromUtf8(uMsg));
|
||||||
socket->write(ACK);
|
socket->write(ACK, qstrlen(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;
|
||||||
QList<Qt::HANDLE> m_readMutexes;
|
QVector<Qt::HANDLE> m_readMutexes;
|
||||||
QString m_mutexName;
|
QString m_mutexName;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
1096
src/base/3rdparty/expected.hpp
vendored
1096
src/base/3rdparty/expected.hpp
vendored
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,7 @@ 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
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ void AddTorrentManager::onSessionTorrentAdded(BitTorrent::Torrent *torrent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason)
|
void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason)
|
||||||
{
|
{
|
||||||
if (const QString source = m_sourcesByInfoHash.take(infoHash); !source.isEmpty())
|
if (const QString source = m_sourcesByInfoHash.take(infoHash); !source.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -154,7 +154,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, {BitTorrent::AddTorrentError::Other, reason});
|
emit addTorrentFailed(source, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTorrentManager::handleDuplicateTorrent(const QString &source
|
void AddTorrentManager::handleDuplicateTorrent(const QString &source
|
||||||
@@ -187,7 +187,7 @@ void AddTorrentManager::handleDuplicateTorrent(const QString &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. Result: %3")
|
||||||
.arg(source, existingTorrent->name(), message));
|
.arg(source, existingTorrent->name(), message));
|
||||||
emit addTorrentFailed(source, {BitTorrent::AddTorrentError::DuplicateTorrent, message});
|
emit addTorrentFailed(source, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard)
|
void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard)
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "base/applicationcomponent.h"
|
#include "base/applicationcomponent.h"
|
||||||
#include "base/bittorrent/addtorrenterror.h"
|
|
||||||
#include "base/bittorrent/addtorrentparams.h"
|
#include "base/bittorrent/addtorrentparams.h"
|
||||||
#include "base/torrentfileguard.h"
|
#include "base/torrentfileguard.h"
|
||||||
|
|
||||||
@@ -67,7 +66,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 BitTorrent::AddTorrentError &reason);
|
void addTorrentFailed(const QString &source, const QString &reason);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
|
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
|
||||||
@@ -80,7 +79,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 BitTorrent::AddTorrentError &reason);
|
void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &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 <QList>
|
#include <QVector>
|
||||||
|
|
||||||
#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()));
|
||||||
|
|
||||||
QList<int> renamingFileIndexes;
|
QVector<int> renamingFileIndexes;
|
||||||
renamingFileIndexes.reserve(filesCount());
|
renamingFileIndexes.reserve(filesCount());
|
||||||
|
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
||||||
QList<DownloadPriority> filePriorities; // used if TorrentInfo is set
|
QVector<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;
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_UNREACHABLE();
|
Q_ASSERT(false);
|
||||||
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 QList<TorrentID> &queue) const;
|
void storeQueue(const QVector<TorrentID> &queue) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Path m_resumeDataDir;
|
const Path m_resumeDataDir;
|
||||||
@@ -131,11 +131,10 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
|
QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
|
||||||
{
|
{
|
||||||
return m_registeredTorrents;
|
return m_registeredTorrents;
|
||||||
}
|
}
|
||||||
@@ -147,7 +146,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, -1);
|
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, torrentSizeLimit);
|
||||||
if (!resumeDataReadResult)
|
if (!resumeDataReadResult)
|
||||||
return nonstd::make_unexpected(resumeDataReadResult.error().message);
|
return nonstd::make_unexpected(resumeDataReadResult.error().message);
|
||||||
|
|
||||||
@@ -290,11 +289,10 @@ 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)
|
||||||
@@ -322,8 +320,6 @@ 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)
|
||||||
@@ -358,7 +354,7 @@ void BitTorrent::BencodeResumeDataStorage::remove(const TorrentID &id) const
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::BencodeResumeDataStorage::storeQueue(const QList<TorrentID> &queue) const
|
void BitTorrent::BencodeResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
|
||||||
{
|
{
|
||||||
@@ -464,7 +460,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 QList<TorrentID> &queue) const
|
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<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 <QList>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
#include "base/utils/thread.h"
|
#include "base/utils/thread.h"
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
|
class QThread;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
@@ -48,18 +49,18 @@ namespace BitTorrent
|
|||||||
public:
|
public:
|
||||||
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
||||||
|
|
||||||
QList<TorrentID> registeredTorrents() const override;
|
QVector<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, const LoadTorrentParams &resumeData) const override;
|
||||||
void remove(const TorrentID &id) const override;
|
void remove(const TorrentID &id) const override;
|
||||||
void storeQueue(const QList<TorrentID> &queue) const override;
|
void storeQueue(const QVector<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;
|
||||||
|
|
||||||
QList<TorrentID> m_registeredTorrents;
|
QVector<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-2025 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2021-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -41,7 +41,6 @@
|
|||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QList>
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
@@ -49,6 +48,7 @@
|
|||||||
#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"
|
||||||
@@ -107,11 +107,11 @@ namespace
|
|||||||
class StoreQueueJob final : public Job
|
class StoreQueueJob final : public Job
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit StoreQueueJob(const QList<TorrentID> &queue);
|
explicit StoreQueueJob(const QVector<TorrentID> &queue);
|
||||||
void perform(QSqlDatabase db) override;
|
void perform(QSqlDatabase db) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QList<TorrentID> m_queue;
|
const QVector<TorrentID> m_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Column
|
struct Column
|
||||||
@@ -120,35 +120,36 @@ namespace
|
|||||||
QString placeholder;
|
QString placeholder;
|
||||||
};
|
};
|
||||||
|
|
||||||
Column makeColumn(const QString &columnName)
|
Column makeColumn(const char *columnName)
|
||||||
{
|
{
|
||||||
return {.name = columnName, .placeholder = (u':' + columnName)};
|
const QString name = QString::fromLatin1(columnName);
|
||||||
|
return {.name = name, .placeholder = (u':' + name)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Column DB_COLUMN_ID = makeColumn(u"id"_s);
|
const Column DB_COLUMN_ID = makeColumn("id");
|
||||||
const Column DB_COLUMN_TORRENT_ID = makeColumn(u"torrent_id"_s);
|
const Column DB_COLUMN_TORRENT_ID = makeColumn("torrent_id");
|
||||||
const Column DB_COLUMN_QUEUE_POSITION = makeColumn(u"queue_position"_s);
|
const Column DB_COLUMN_QUEUE_POSITION = makeColumn("queue_position");
|
||||||
const Column DB_COLUMN_NAME = makeColumn(u"name"_s);
|
const Column DB_COLUMN_NAME = makeColumn("name");
|
||||||
const Column DB_COLUMN_CATEGORY = makeColumn(u"category"_s);
|
const Column DB_COLUMN_CATEGORY = makeColumn("category");
|
||||||
const Column DB_COLUMN_TAGS = makeColumn(u"tags"_s);
|
const Column DB_COLUMN_TAGS = makeColumn("tags");
|
||||||
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn(u"target_save_path"_s);
|
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn("target_save_path");
|
||||||
const Column DB_COLUMN_DOWNLOAD_PATH = makeColumn(u"download_path"_s);
|
const Column DB_COLUMN_DOWNLOAD_PATH = makeColumn("download_path");
|
||||||
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn(u"content_layout"_s);
|
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
||||||
const Column DB_COLUMN_RATIO_LIMIT = makeColumn(u"ratio_limit"_s);
|
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
||||||
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn(u"seeding_time_limit"_s);
|
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
||||||
const Column DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT = makeColumn(u"inactive_seeding_time_limit"_s);
|
const Column DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT = makeColumn("inactive_seeding_time_limit");
|
||||||
const Column DB_COLUMN_SHARE_LIMIT_ACTION = makeColumn(u"share_limit_action"_s);
|
const Column DB_COLUMN_SHARE_LIMIT_ACTION = makeColumn("share_limit_action");
|
||||||
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn(u"has_outer_pieces_priority"_s);
|
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn("has_outer_pieces_priority");
|
||||||
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn(u"has_seed_status"_s);
|
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn("has_seed_status");
|
||||||
const Column DB_COLUMN_OPERATING_MODE = makeColumn(u"operating_mode"_s);
|
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
|
||||||
const Column DB_COLUMN_STOPPED = makeColumn(u"stopped"_s);
|
const Column DB_COLUMN_STOPPED = makeColumn("stopped");
|
||||||
const Column DB_COLUMN_STOP_CONDITION = makeColumn(u"stop_condition"_s);
|
const Column DB_COLUMN_STOP_CONDITION = makeColumn("stop_condition");
|
||||||
const Column DB_COLUMN_SSL_CERTIFICATE = makeColumn(u"ssl_certificate"_s);
|
const Column DB_COLUMN_SSL_CERTIFICATE = makeColumn("ssl_certificate");
|
||||||
const Column DB_COLUMN_SSL_PRIVATE_KEY = makeColumn(u"ssl_private_key"_s);
|
const Column DB_COLUMN_SSL_PRIVATE_KEY = makeColumn("ssl_private_key");
|
||||||
const Column DB_COLUMN_SSL_DH_PARAMS = makeColumn(u"ssl_dh_params"_s);
|
const Column DB_COLUMN_SSL_DH_PARAMS = makeColumn("ssl_dh_params");
|
||||||
const Column DB_COLUMN_RESUMEDATA = makeColumn(u"libtorrent_resume_data"_s);
|
const Column DB_COLUMN_RESUMEDATA = makeColumn("libtorrent_resume_data");
|
||||||
const Column DB_COLUMN_METADATA = makeColumn(u"metadata"_s);
|
const Column DB_COLUMN_METADATA = makeColumn("metadata");
|
||||||
const Column DB_COLUMN_VALUE = makeColumn(u"value"_s);
|
const Column DB_COLUMN_VALUE = makeColumn("value");
|
||||||
|
|
||||||
template <typename LTStr>
|
template <typename LTStr>
|
||||||
QString fromLTString(const LTStr &str)
|
QString fromLTString(const LTStr &str)
|
||||||
@@ -167,7 +168,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 QList<Column> &columns)
|
std::pair<QString, QString> joinColumns(const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
int namesSize = columns.size();
|
int namesSize = columns.size();
|
||||||
int valuesSize = columns.size();
|
int valuesSize = columns.size();
|
||||||
@@ -192,30 +193,104 @@ namespace
|
|||||||
return std::make_pair(names, values);
|
return std::make_pair(names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeInsertStatement(const QString &tableName, const QList<Column> &columns)
|
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u"INSERT INTO %1 (%2) VALUES (%3)"_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 QList<Column> &columns)
|
QString makeUpdateStatement(const QString &tableName, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u"UPDATE %1 SET (%2) = (%3)"_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 QList<Column> &columns)
|
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
const auto [names, values] = joinColumns(columns);
|
const auto [names, values] = joinColumns(columns);
|
||||||
return u" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)"_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 QString &definition)
|
QString makeColumnDefinition(const Column &column, const char *definition)
|
||||||
{
|
{
|
||||||
return u"%1 %2"_s.arg(quoted(column.name), definition);
|
return u"%1 %2"_s.arg(quoted(column.name), QString::fromLatin1(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
|
||||||
|
{
|
||||||
|
LoadTorrentParams resumeData;
|
||||||
|
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
||||||
|
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
||||||
|
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
||||||
|
if (!tagsData.isEmpty())
|
||||||
|
{
|
||||||
|
const QStringList tagList = tagsData.split(u',');
|
||||||
|
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
||||||
|
}
|
||||||
|
resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
||||||
|
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
||||||
|
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
||||||
|
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
|
||||||
|
resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt();
|
||||||
|
resumeData.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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +308,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
void store(const TorrentID &id, const LoadTorrentParams &resumeData);
|
void store(const TorrentID &id, const LoadTorrentParams &resumeData);
|
||||||
void remove(const TorrentID &id);
|
void remove(const TorrentID &id);
|
||||||
void storeQueue(const QList<TorrentID> &queue);
|
void storeQueue(const QVector<TorrentID> &queue);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addJob(std::unique_ptr<Job> job);
|
void addJob(std::unique_ptr<Job> job);
|
||||||
@@ -250,6 +325,7 @@ 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();
|
||||||
|
|
||||||
@@ -280,7 +356,7 @@ BitTorrent::DBResumeDataStorage::~DBResumeDataStorage()
|
|||||||
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
QVector<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));
|
||||||
@@ -291,7 +367,7 @@ QList<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents
|
|||||||
if (!query.exec(selectTorrentIDStatement))
|
if (!query.exec(selectTorrentIDStatement))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
QList<TorrentID> registeredTorrents;
|
QVector<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()));
|
||||||
@@ -337,7 +413,7 @@ void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) co
|
|||||||
m_asyncWorker->remove(id);
|
m_asyncWorker->remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::storeQueue(const QList<TorrentID> &queue) const
|
void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
|
||||||
{
|
{
|
||||||
m_asyncWorker->storeQueue(queue);
|
m_asyncWorker->storeQueue(queue);
|
||||||
}
|
}
|
||||||
@@ -362,7 +438,7 @@ void BitTorrent::DBResumeDataStorage::doLoadAll() const
|
|||||||
if (!query.exec(selectTorrentIDStatement))
|
if (!query.exec(selectTorrentIDStatement))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
QList<TorrentID> registeredTorrents;
|
QVector<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()));
|
||||||
@@ -436,9 +512,9 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
const QStringList tableMetaItems = {
|
const QStringList tableMetaItems = {
|
||||||
makeColumnDefinition(DB_COLUMN_ID, u"INTEGER PRIMARY KEY"_s),
|
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
|
||||||
makeColumnDefinition(DB_COLUMN_NAME, u"TEXT NOT NULL UNIQUE"_s),
|
makeColumnDefinition(DB_COLUMN_NAME, "TEXT NOT NULL UNIQUE"),
|
||||||
makeColumnDefinition(DB_COLUMN_VALUE, u"BLOB"_s)
|
makeColumnDefinition(DB_COLUMN_VALUE, "BLOB")
|
||||||
};
|
};
|
||||||
const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
|
const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
|
||||||
if (!query.exec(createTableMetaQuery))
|
if (!query.exec(createTableMetaQuery))
|
||||||
@@ -455,29 +531,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, u"INTEGER PRIMARY KEY"_s),
|
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
|
||||||
makeColumnDefinition(DB_COLUMN_TORRENT_ID, u"BLOB NOT NULL UNIQUE"_s),
|
makeColumnDefinition(DB_COLUMN_TORRENT_ID, "BLOB NOT NULL UNIQUE"),
|
||||||
makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, u"INTEGER NOT NULL DEFAULT -1"_s),
|
makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, "INTEGER NOT NULL DEFAULT -1"),
|
||||||
makeColumnDefinition(DB_COLUMN_NAME, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_NAME, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_CATEGORY, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_CATEGORY, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_TAGS, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_TAGS, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, u"TEXT NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, "TEXT NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, u"INTEGER NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, u"INTEGER NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, u"INTEGER NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_SHARE_LIMIT_ACTION, u"TEXT NOT NULL DEFAULT `Default`"_s),
|
makeColumnDefinition(DB_COLUMN_SHARE_LIMIT_ACTION, "TEXT NOT NULL DEFAULT `Default`"),
|
||||||
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, u"INTEGER NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, u"INTEGER NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, u"TEXT NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, "TEXT NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_STOPPED, u"INTEGER NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_STOPPED, "INTEGER NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_STOP_CONDITION, u"TEXT NOT NULL DEFAULT `None`"_s),
|
makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"),
|
||||||
makeColumnDefinition(DB_COLUMN_SSL_CERTIFICATE, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_SSL_CERTIFICATE, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_SSL_PRIVATE_KEY, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_SSL_PRIVATE_KEY, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_SSL_DH_PARAMS, u"TEXT"_s),
|
makeColumnDefinition(DB_COLUMN_SSL_DH_PARAMS, "TEXT"),
|
||||||
makeColumnDefinition(DB_COLUMN_RESUMEDATA, u"BLOB NOT NULL"_s),
|
makeColumnDefinition(DB_COLUMN_RESUMEDATA, "BLOB NOT NULL"),
|
||||||
makeColumnDefinition(DB_COLUMN_METADATA, u"BLOB"_s)
|
makeColumnDefinition(DB_COLUMN_METADATA, "BLOB")
|
||||||
};
|
};
|
||||||
const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
|
const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
|
||||||
if (!query.exec(createTableTorrentsQuery))
|
if (!query.exec(createTableTorrentsQuery))
|
||||||
@@ -515,7 +591,7 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto addColumn = [&query](const QString &table, const Column &column, const QString &definition)
|
const auto addColumn = [&query](const QString &table, const Column &column, const char *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))
|
||||||
@@ -527,10 +603,10 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (fromVersion <= 1)
|
if (fromVersion <= 1)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_DOWNLOAD_PATH, u"TEXT"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_DOWNLOAD_PATH, "TEXT");
|
||||||
|
|
||||||
if (fromVersion <= 2)
|
if (fromVersion <= 2)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_STOP_CONDITION, u"TEXT NOT NULL DEFAULT `None`"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`");
|
||||||
|
|
||||||
if (fromVersion <= 3)
|
if (fromVersion <= 3)
|
||||||
{
|
{
|
||||||
@@ -542,17 +618,17 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fromVersion <= 4)
|
if (fromVersion <= 4)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, u"INTEGER NOT NULL DEFAULT -2"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL DEFAULT -2");
|
||||||
|
|
||||||
if (fromVersion <= 5)
|
if (fromVersion <= 5)
|
||||||
{
|
{
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_CERTIFICATE, u"TEXT"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_CERTIFICATE, "TEXT");
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_PRIVATE_KEY, u"TEXT"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_PRIVATE_KEY, "TEXT");
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_DH_PARAMS, u"TEXT"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_DH_PARAMS, "TEXT");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromVersion <= 6)
|
if (fromVersion <= 6)
|
||||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, u"TEXT NOT NULL DEFAULT `Default`"_s);
|
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, "TEXT NOT NULL DEFAULT `Default`");
|
||||||
|
|
||||||
if (fromVersion == 7)
|
if (fromVersion == 7)
|
||||||
{
|
{
|
||||||
@@ -614,90 +690,6 @@ 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}
|
||||||
@@ -779,7 +771,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 QList<TorrentID> &queue)
|
void BitTorrent::DBResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue)
|
||||||
{
|
{
|
||||||
addJob(std::make_unique<StoreQueueJob>(queue));
|
addJob(std::make_unique<StoreQueueJob>(queue));
|
||||||
}
|
}
|
||||||
@@ -829,7 +821,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Column> columns {
|
QVector<Column> columns {
|
||||||
DB_COLUMN_TORRENT_ID,
|
DB_COLUMN_TORRENT_ID,
|
||||||
DB_COLUMN_NAME,
|
DB_COLUMN_NAME,
|
||||||
DB_COLUMN_CATEGORY,
|
DB_COLUMN_CATEGORY,
|
||||||
@@ -961,7 +953,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StoreQueueJob::StoreQueueJob(const QList<TorrentID> &queue)
|
StoreQueueJob::StoreQueueJob(const QVector<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-2025 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2021-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -31,9 +31,10 @@
|
|||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
|
#include "base/utils/thread.h"
|
||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
|
||||||
class QSqlQuery;
|
class QThread;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
@@ -46,12 +47,12 @@ namespace BitTorrent
|
|||||||
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
explicit DBResumeDataStorage(const Path &dbPath, QObject *parent = nullptr);
|
||||||
~DBResumeDataStorage() override;
|
~DBResumeDataStorage() override;
|
||||||
|
|
||||||
QList<TorrentID> registeredTorrents() const override;
|
QVector<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, const LoadTorrentParams &resumeData) const override;
|
||||||
void remove(const TorrentID &id) const override;
|
void remove(const TorrentID &id) const override;
|
||||||
void storeQueue(const QList<TorrentID> &queue) const override;
|
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doLoadAll() const override;
|
void doLoadAll() const override;
|
||||||
@@ -59,7 +60,8 @@ 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;
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ int FilterParserThread::parseDATFilterFile()
|
|||||||
return ruleCount;
|
return ruleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QList
|
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QVector
|
||||||
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 QList
|
std::vector<char> buffer(BUFFER_SIZE, 0); // seems a bit faster than QVector
|
||||||
qint64 bytesRead = 0;
|
qint64 bytesRead = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|||||||
@@ -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<QList<BitTorrent::TorrentID>>();
|
const int TORRENTIDLIST_TYPEID = qRegisterMetaType<QVector<BitTorrent::TorrentID>>();
|
||||||
|
|
||||||
BitTorrent::ResumeDataStorage::ResumeDataStorage(const Path &path, QObject *parent)
|
BitTorrent::ResumeDataStorage::ResumeDataStorage(const Path &path, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
@@ -56,7 +56,6 @@ 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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,17 +58,17 @@ namespace BitTorrent
|
|||||||
|
|
||||||
Path path() const;
|
Path path() const;
|
||||||
|
|
||||||
virtual QList<TorrentID> registeredTorrents() const = 0;
|
virtual QVector<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, const LoadTorrentParams &resumeData) const = 0;
|
||||||
virtual void remove(const TorrentID &id) const = 0;
|
virtual void remove(const TorrentID &id) const = 0;
|
||||||
virtual void storeQueue(const QList<TorrentID> &queue) const = 0;
|
virtual void storeQueue(const QVector<TorrentID> &queue) const = 0;
|
||||||
|
|
||||||
void loadAll() const;
|
void loadAll() const;
|
||||||
QList<LoadedResumeData> fetchLoadedResumeData() const;
|
QList<LoadedResumeData> fetchLoadedResumeData() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loadStarted(const QList<BitTorrent::TorrentID> &torrents);
|
void loadStarted(const QVector<BitTorrent::TorrentID> &torrents);
|
||||||
void loadFinished();
|
void loadFinished();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
|
|
||||||
#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"
|
||||||
@@ -238,12 +237,6 @@ 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;
|
||||||
@@ -265,8 +258,6 @@ 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;
|
||||||
@@ -393,8 +384,6 @@ 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;
|
||||||
@@ -458,7 +447,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 QList<Torrent *> torrents() const = 0;
|
virtual QVector<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;
|
||||||
@@ -472,17 +461,14 @@ 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 QList<TorrentID> &ids) = 0;
|
virtual void increaseTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
||||||
virtual void decreaseTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
virtual void decreaseTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
||||||
virtual void topTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
virtual void topTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
||||||
virtual void bottomTorrentsQueuePos(const QList<TorrentID> &ids) = 0;
|
virtual void bottomTorrentsQueuePos(const QVector<TorrentID> &ids) = 0;
|
||||||
|
|
||||||
virtual QString lastExternalIPv4Address() const = 0;
|
|
||||||
virtual QString lastExternalIPv6Address() const = 0;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void startupProgressUpdated(int progress);
|
void startupProgressUpdated(int progress);
|
||||||
void addTorrentFailed(const InfoHash &infoHash, const AddTorrentError &reason);
|
void addTorrentFailed(const InfoHash &infoHash, const QString &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);
|
||||||
@@ -509,12 +495,12 @@ 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 QList<Torrent *> &torrents);
|
void torrentsLoaded(const QVector<Torrent *> &torrents);
|
||||||
void torrentsUpdated(const QList<Torrent *> &torrents);
|
void torrentsUpdated(const QVector<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 QList<TrackerEntry> &trackers);
|
void trackersAdded(Torrent *torrent, const QVector<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);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "sessionimpl.h"
|
#include "sessionimpl.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
@@ -57,7 +58,6 @@
|
|||||||
#include <libtorrent/session_status.hpp>
|
#include <libtorrent/session_status.hpp>
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDeadlineTimer>
|
#include <QDeadlineTimer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -72,13 +72,13 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QThreadPool>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include "base/algorithm.h"
|
#include "base/algorithm.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/net/downloadmanager.h"
|
|
||||||
#include "base/net/proxyconfigurationmanager.h"
|
#include "base/net/proxyconfigurationmanager.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
@@ -88,7 +88,6 @@
|
|||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
#include "base/utils/number.h"
|
#include "base/utils/number.h"
|
||||||
#include "base/utils/random.h"
|
#include "base/utils/random.h"
|
||||||
#include "base/utils/string.h"
|
|
||||||
#include "base/version.h"
|
#include "base/version.h"
|
||||||
#include "bandwidthscheduler.h"
|
#include "bandwidthscheduler.h"
|
||||||
#include "bencoderesumedatastorage.h"
|
#include "bencoderesumedatastorage.h"
|
||||||
@@ -108,19 +107,19 @@
|
|||||||
#include "torrentimpl.h"
|
#include "torrentimpl.h"
|
||||||
#include "tracker.h"
|
#include "tracker.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
#include "trackerentrystatus.h"
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
const Path CATEGORIES_FILE_NAME {u"categories.json"_s};
|
const Path CATEGORIES_FILE_NAME {u"categories.json"_s};
|
||||||
const int MAX_PROCESSING_RESUMEDATA_COUNT = 50;
|
const int MAX_PROCESSING_RESUMEDATA_COUNT = 50;
|
||||||
|
const int STATISTICS_SAVE_INTERVAL = std::chrono::milliseconds(15min).count();
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const char PEER_ID[] = "qB";
|
const char PEER_ID[] = "qB";
|
||||||
const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2);
|
const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2);
|
||||||
const QString DEFAULT_DHT_BOOTSTRAP_NODES = u"dht.libtorrent.org:25401, dht.transmissionbt.com:6881, router.bittorrent.com:6881"_s;
|
const QString DEFAULT_DHT_BOOTSTRAP_NODES = u"dht.libtorrent.org:25401, dht.transmissionbt.com:6881, router.bittorrent.com:6881, router.utorrent.com:6881, dht.aelitis.com:6881"_s;
|
||||||
|
|
||||||
void torrentQueuePositionUp(const lt::torrent_handle &handle)
|
void torrentQueuePositionUp(const lt::torrent_handle &handle)
|
||||||
{
|
{
|
||||||
@@ -226,7 +225,7 @@ namespace
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Utils::String::fromLatin1(address.to_string());
|
return QString::fromLatin1(address.to_string().c_str());
|
||||||
}
|
}
|
||||||
catch (const std::exception &)
|
catch (const std::exception &)
|
||||||
{
|
{
|
||||||
@@ -300,15 +299,14 @@ namespace
|
|||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
case MoveStorageMode::FailIfExist:
|
case MoveStorageMode::FailIfExist:
|
||||||
return lt::move_flags_t::fail_if_exist;
|
return lt::move_flags_t::fail_if_exist;
|
||||||
case MoveStorageMode::KeepExistingFiles:
|
case MoveStorageMode::KeepExistingFiles:
|
||||||
return lt::move_flags_t::dont_replace;
|
return lt::move_flags_t::dont_replace;
|
||||||
case MoveStorageMode::Overwrite:
|
case MoveStorageMode::Overwrite:
|
||||||
return lt::move_flags_t::always_replace_files;
|
return lt::move_flags_t::always_replace_files;
|
||||||
default:
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,7 +443,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||||||
, m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY(u"IgnoreLimitsOnLAN"_s), false)
|
, m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY(u"IgnoreLimitsOnLAN"_s), false)
|
||||||
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY(u"IncludeOverheadInLimits"_s), false)
|
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY(u"IncludeOverheadInLimits"_s), false)
|
||||||
, m_announceIP(BITTORRENT_SESSION_KEY(u"AnnounceIP"_s))
|
, m_announceIP(BITTORRENT_SESSION_KEY(u"AnnounceIP"_s))
|
||||||
, m_announcePort(BITTORRENT_SESSION_KEY(u"AnnouncePort"_s), 0)
|
|
||||||
, m_maxConcurrentHTTPAnnounces(BITTORRENT_SESSION_KEY(u"MaxConcurrentHTTPAnnounces"_s), 50)
|
, m_maxConcurrentHTTPAnnounces(BITTORRENT_SESSION_KEY(u"MaxConcurrentHTTPAnnounces"_s), 50)
|
||||||
, m_isReannounceWhenAddressChangedEnabled(BITTORRENT_SESSION_KEY(u"ReannounceWhenAddressChanged"_s), false)
|
, m_isReannounceWhenAddressChangedEnabled(BITTORRENT_SESSION_KEY(u"ReannounceWhenAddressChanged"_s), false)
|
||||||
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY(u"StopTrackerTimeout"_s), 2)
|
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY(u"StopTrackerTimeout"_s), 2)
|
||||||
@@ -465,13 +462,9 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||||||
, m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY(u"BlockPeersOnPrivilegedPorts"_s), false)
|
, m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY(u"BlockPeersOnPrivilegedPorts"_s), false)
|
||||||
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY(u"AddTrackersEnabled"_s), false)
|
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY(u"AddTrackersEnabled"_s), false)
|
||||||
, m_additionalTrackers(BITTORRENT_SESSION_KEY(u"AdditionalTrackers"_s))
|
, m_additionalTrackers(BITTORRENT_SESSION_KEY(u"AdditionalTrackers"_s))
|
||||||
, m_isAddTrackersFromURLEnabled(BITTORRENT_SESSION_KEY(u"AddTrackersFromURLEnabled"_s), false)
|
, m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_s), -1, [](qreal r) { return r < 0 ? -1. : r;})
|
||||||
, m_additionalTrackersURL(BITTORRENT_SESSION_KEY(u"AdditionalTrackersURL"_s))
|
, m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_s), -1, lowerLimited(-1))
|
||||||
, m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_s), -1, [](qreal r) { return r < 0 ? -1. : r; })
|
, m_globalMaxInactiveSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxInactiveSeedingMinutes"_s), -1, lowerLimited(-1))
|
||||||
, m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_s)
|
|
||||||
, Torrent::NO_SEEDING_TIME_LIMIT, lowerLimited(Torrent::NO_SEEDING_TIME_LIMIT))
|
|
||||||
, m_globalMaxInactiveSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxInactiveSeedingMinutes"_s)
|
|
||||||
, Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT, lowerLimited(Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT))
|
|
||||||
, m_isAddTorrentToQueueTop(BITTORRENT_SESSION_KEY(u"AddTorrentToTopOfQueue"_s), false)
|
, m_isAddTorrentToQueueTop(BITTORRENT_SESSION_KEY(u"AddTorrentToTopOfQueue"_s), false)
|
||||||
, m_isAddTorrentStopped(BITTORRENT_SESSION_KEY(u"AddTorrentStopped"_s), false)
|
, m_isAddTorrentStopped(BITTORRENT_SESSION_KEY(u"AddTorrentStopped"_s), false)
|
||||||
, m_torrentStopCondition(BITTORRENT_SESSION_KEY(u"TorrentStopCondition"_s), Torrent::StopCondition::None)
|
, m_torrentStopCondition(BITTORRENT_SESSION_KEY(u"TorrentStopCondition"_s), Torrent::StopCondition::None)
|
||||||
@@ -490,7 +483,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||||||
, m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY(u"BandwidthSchedulerEnabled"_s), false)
|
, m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY(u"BandwidthSchedulerEnabled"_s), false)
|
||||||
, m_isPerformanceWarningEnabled(BITTORRENT_SESSION_KEY(u"PerformanceWarning"_s), false)
|
, m_isPerformanceWarningEnabled(BITTORRENT_SESSION_KEY(u"PerformanceWarning"_s), false)
|
||||||
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY(u"SaveResumeDataInterval"_s), 60)
|
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY(u"SaveResumeDataInterval"_s), 60)
|
||||||
, m_saveStatisticsInterval(BITTORRENT_SESSION_KEY(u"SaveStatisticsInterval"_s), 15)
|
|
||||||
, m_shutdownTimeout(BITTORRENT_SESSION_KEY(u"ShutdownTimeout"_s), -1)
|
, m_shutdownTimeout(BITTORRENT_SESSION_KEY(u"ShutdownTimeout"_s), -1)
|
||||||
, m_port(BITTORRENT_SESSION_KEY(u"Port"_s), -1)
|
, m_port(BITTORRENT_SESSION_KEY(u"Port"_s), -1)
|
||||||
, m_sslEnabled(BITTORRENT_SESSION_KEY(u"SSL/Enabled"_s), false)
|
, m_sslEnabled(BITTORRENT_SESSION_KEY(u"SSL/Enabled"_s), false)
|
||||||
@@ -545,9 +537,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||||||
{
|
{
|
||||||
// It is required to perform async access to libtorrent sequentially
|
// It is required to perform async access to libtorrent sequentially
|
||||||
m_asyncWorker->setMaxThreadCount(1);
|
m_asyncWorker->setMaxThreadCount(1);
|
||||||
m_asyncWorker->setObjectName("SessionImpl m_asyncWorker");
|
|
||||||
|
|
||||||
m_alerts.reserve(1024);
|
|
||||||
|
|
||||||
if (port() < 0)
|
if (port() < 0)
|
||||||
m_port = Utils::Random::rand(1024, 65535);
|
m_port = Utils::Random::rand(1024, 65535);
|
||||||
@@ -612,7 +601,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||||||
connect(m_ioThread.get(), &QThread::finished, m_torrentContentRemover, &QObject::deleteLater);
|
connect(m_ioThread.get(), &QThread::finished, m_torrentContentRemover, &QObject::deleteLater);
|
||||||
connect(m_torrentContentRemover, &TorrentContentRemover::jobFinished, this, &SessionImpl::torrentContentRemovingFinished);
|
connect(m_torrentContentRemover, &TorrentContentRemover::jobFinished, this, &SessionImpl::torrentContentRemovingFinished);
|
||||||
|
|
||||||
m_ioThread->setObjectName("SessionImpl m_ioThread");
|
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
|
|
||||||
initMetrics();
|
initMetrics();
|
||||||
@@ -625,15 +613,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||||||
enableTracker(isTrackerEnabled());
|
enableTracker(isTrackerEnabled());
|
||||||
|
|
||||||
prepareStartup();
|
prepareStartup();
|
||||||
|
|
||||||
m_updateTrackersFromURLTimer = new QTimer(this);
|
|
||||||
m_updateTrackersFromURLTimer->setInterval(24h);
|
|
||||||
connect(m_updateTrackersFromURLTimer, &QTimer::timeout, this, &SessionImpl::updateTrackersFromURL);
|
|
||||||
if (isAddTrackersFromURLEnabled())
|
|
||||||
{
|
|
||||||
updateTrackersFromURL();
|
|
||||||
m_updateTrackersFromURLTimer->start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionImpl::~SessionImpl()
|
SessionImpl::~SessionImpl()
|
||||||
@@ -681,7 +660,6 @@ SessionImpl::~SessionImpl()
|
|||||||
qDebug("Deleting libtorrent session...");
|
qDebug("Deleting libtorrent session...");
|
||||||
delete nativeSessionProxy;
|
delete nativeSessionProxy;
|
||||||
});
|
});
|
||||||
sessionTerminateThread->setObjectName("~SessionImpl sessionTerminateThread");
|
|
||||||
connect(sessionTerminateThread, &QThread::finished, sessionTerminateThread, &QObject::deleteLater);
|
connect(sessionTerminateThread, &QThread::finished, sessionTerminateThread, &QObject::deleteLater);
|
||||||
sessionTerminateThread->start();
|
sessionTerminateThread->start();
|
||||||
if (sessionTerminateThread->wait(shutdownDeadlineTimer))
|
if (sessionTerminateThread->wait(shutdownDeadlineTimer))
|
||||||
@@ -976,25 +954,23 @@ bool SessionImpl::editCategory(const QString &name, const CategoryOptions &optio
|
|||||||
if (options == currentOptions)
|
if (options == currentOptions)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
currentOptions = options;
|
||||||
|
storeCategories();
|
||||||
if (isDisableAutoTMMWhenCategorySavePathChanged())
|
if (isDisableAutoTMMWhenCategorySavePathChanged())
|
||||||
{
|
{
|
||||||
// This should be done before changing the category options
|
|
||||||
// to prevent the torrent from being moved at the new save path.
|
|
||||||
|
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
{
|
{
|
||||||
if (torrent->category() == name)
|
if (torrent->category() == name)
|
||||||
torrent->setAutoTMMEnabled(false);
|
torrent->setAutoTMMEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
currentOptions = options;
|
|
||||||
storeCategories();
|
|
||||||
|
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
|
||||||
{
|
{
|
||||||
if (torrent->category() == name)
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
torrent->handleCategoryOptionsChanged();
|
{
|
||||||
|
if (torrent->category() == name)
|
||||||
|
torrent->handleCategoryOptionsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit categoryOptionsChanged(name);
|
emit categoryOptionsChanged(name);
|
||||||
@@ -1222,7 +1198,7 @@ qreal SessionImpl::globalMaxRatio() const
|
|||||||
void SessionImpl::setGlobalMaxRatio(qreal ratio)
|
void SessionImpl::setGlobalMaxRatio(qreal ratio)
|
||||||
{
|
{
|
||||||
if (ratio < 0)
|
if (ratio < 0)
|
||||||
ratio = Torrent::NO_RATIO_LIMIT;
|
ratio = -1.;
|
||||||
|
|
||||||
if (ratio != globalMaxRatio())
|
if (ratio != globalMaxRatio())
|
||||||
{
|
{
|
||||||
@@ -1238,7 +1214,8 @@ int SessionImpl::globalMaxSeedingMinutes() const
|
|||||||
|
|
||||||
void SessionImpl::setGlobalMaxSeedingMinutes(int minutes)
|
void SessionImpl::setGlobalMaxSeedingMinutes(int minutes)
|
||||||
{
|
{
|
||||||
minutes = std::max(minutes, Torrent::NO_SEEDING_TIME_LIMIT);
|
if (minutes < 0)
|
||||||
|
minutes = -1;
|
||||||
|
|
||||||
if (minutes != globalMaxSeedingMinutes())
|
if (minutes != globalMaxSeedingMinutes())
|
||||||
{
|
{
|
||||||
@@ -1254,7 +1231,7 @@ int SessionImpl::globalMaxInactiveSeedingMinutes() const
|
|||||||
|
|
||||||
void SessionImpl::setGlobalMaxInactiveSeedingMinutes(int minutes)
|
void SessionImpl::setGlobalMaxInactiveSeedingMinutes(int minutes)
|
||||||
{
|
{
|
||||||
minutes = std::max(minutes, Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT);
|
minutes = std::max(minutes, -1);
|
||||||
|
|
||||||
if (minutes != globalMaxInactiveSeedingMinutes())
|
if (minutes != globalMaxInactiveSeedingMinutes())
|
||||||
{
|
{
|
||||||
@@ -1330,7 +1307,7 @@ void SessionImpl::prepareStartup()
|
|||||||
context->startupStorage = m_resumeDataStorage;
|
context->startupStorage = m_resumeDataStorage;
|
||||||
|
|
||||||
connect(context->startupStorage, &ResumeDataStorage::loadStarted, context
|
connect(context->startupStorage, &ResumeDataStorage::loadStarted, context
|
||||||
, [this, context](const QList<TorrentID> &torrents)
|
, [this, context](const QVector<TorrentID> &torrents)
|
||||||
{
|
{
|
||||||
context->totalResumeDataCount = torrents.size();
|
context->totalResumeDataCount = torrents.size();
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
@@ -1620,24 +1597,20 @@ void SessionImpl::endStartup(ResumeSessionContext *context)
|
|||||||
m_resumeDataTimer->start();
|
m_resumeDataTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wakeupCheckTimer = new QTimer(this);
|
m_wakeupCheckTimer = new QTimer(this);
|
||||||
connect(wakeupCheckTimer, &QTimer::timeout, this, [this]
|
connect(m_wakeupCheckTimer, &QTimer::timeout, this, [this]
|
||||||
{
|
{
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
|
const auto now = QDateTime::currentDateTime();
|
||||||
const bool hasSystemSlept = m_wakeupCheckTimestamp.durationElapsed() > 100s;
|
if (m_wakeupCheckTimestamp.secsTo(now) > 100)
|
||||||
#else
|
|
||||||
const bool hasSystemSlept = m_wakeupCheckTimestamp.elapsed() > std::chrono::milliseconds(100s).count();
|
|
||||||
#endif
|
|
||||||
if (hasSystemSlept)
|
|
||||||
{
|
{
|
||||||
LogMsg(tr("System wake-up event detected. Re-announcing to all the trackers..."));
|
LogMsg(tr("System wake-up event detected. Re-announcing to all the trackers..."));
|
||||||
reannounceToAllTrackers();
|
reannounceToAllTrackers();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_wakeupCheckTimestamp.start();
|
m_wakeupCheckTimestamp = now;
|
||||||
});
|
});
|
||||||
m_wakeupCheckTimestamp.start();
|
m_wakeupCheckTimestamp = QDateTime::currentDateTime();
|
||||||
wakeupCheckTimer->start(30s);
|
m_wakeupCheckTimer->start(30s);
|
||||||
|
|
||||||
m_isRestored = true;
|
m_isRestored = true;
|
||||||
emit startupProgressUpdated(100);
|
emit startupProgressUpdated(100);
|
||||||
@@ -2008,10 +1981,6 @@ lt::settings_pack SessionImpl::loadLTSettings() const
|
|||||||
settingsPack.set_bool(lt::settings_pack::rate_limit_ip_overhead, includeOverheadInLimits());
|
settingsPack.set_bool(lt::settings_pack::rate_limit_ip_overhead, includeOverheadInLimits());
|
||||||
// IP address to announce to trackers
|
// IP address to announce to trackers
|
||||||
settingsPack.set_str(lt::settings_pack::announce_ip, announceIP().toStdString());
|
settingsPack.set_str(lt::settings_pack::announce_ip, announceIP().toStdString());
|
||||||
#if LIBTORRENT_VERSION_NUM >= 20011
|
|
||||||
// Port to announce to trackers
|
|
||||||
settingsPack.set_int(lt::settings_pack::announce_port, announcePort());
|
|
||||||
#endif
|
|
||||||
// Max concurrent HTTP announces
|
// Max concurrent HTTP announces
|
||||||
settingsPack.set_int(lt::settings_pack::max_concurrent_http_announces, maxConcurrentHTTPAnnounces());
|
settingsPack.set_int(lt::settings_pack::max_concurrent_http_announces, maxConcurrentHTTPAnnounces());
|
||||||
// Stop tracker timeout
|
// Stop tracker timeout
|
||||||
@@ -2290,11 +2259,6 @@ void SessionImpl::populateAdditionalTrackers()
|
|||||||
m_additionalTrackerEntries = parseTrackerEntries(additionalTrackers());
|
m_additionalTrackerEntries = parseTrackerEntries(additionalTrackers());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::populateAdditionalTrackersFromURL()
|
|
||||||
{
|
|
||||||
m_additionalTrackerEntriesFromURL = parseTrackerEntries(additionalTrackersFromURL());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
||||||
{
|
{
|
||||||
if (!torrent->isFinished() || torrent->isForced())
|
if (!torrent->isFinished() || torrent->isForced())
|
||||||
@@ -2313,19 +2277,19 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
|||||||
QString description;
|
QString description;
|
||||||
|
|
||||||
if (const qreal ratio = torrent->realRatio();
|
if (const qreal ratio = torrent->realRatio();
|
||||||
(ratioLimit >= 0) && (ratio >= ratioLimit))
|
(ratioLimit >= 0) && (ratio <= Torrent::MAX_RATIO) && (ratio >= ratioLimit))
|
||||||
{
|
{
|
||||||
reached = true;
|
reached = true;
|
||||||
description = tr("Torrent reached the share ratio limit.");
|
description = tr("Torrent reached the share ratio limit.");
|
||||||
}
|
}
|
||||||
else if (const qlonglong seedingTimeInMinutes = torrent->finishedTime() / 60;
|
else if (const qlonglong seedingTimeInMinutes = torrent->finishedTime() / 60;
|
||||||
(seedingTimeLimit >= 0) && (seedingTimeInMinutes >= seedingTimeLimit))
|
(seedingTimeLimit >= 0) && (seedingTimeInMinutes <= Torrent::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit))
|
||||||
{
|
{
|
||||||
reached = true;
|
reached = true;
|
||||||
description = tr("Torrent reached the seeding time limit.");
|
description = tr("Torrent reached the seeding time limit.");
|
||||||
}
|
}
|
||||||
else if (const qlonglong inactiveSeedingTimeInMinutes = torrent->timeSinceActivity() / 60;
|
else if (const qlonglong inactiveSeedingTimeInMinutes = torrent->timeSinceActivity() / 60;
|
||||||
(inactiveSeedingTimeLimit >= 0) && (inactiveSeedingTimeInMinutes >= inactiveSeedingTimeLimit))
|
(inactiveSeedingTimeLimit >= 0) && (inactiveSeedingTimeInMinutes <= Torrent::MAX_INACTIVE_SEEDING_TIME) && (inactiveSeedingTimeInMinutes >= inactiveSeedingTimeLimit))
|
||||||
{
|
{
|
||||||
reached = true;
|
reached = true;
|
||||||
description = tr("Torrent reached the inactive seeding time limit.");
|
description = tr("Torrent reached the inactive seeding time limit.");
|
||||||
@@ -2536,7 +2500,7 @@ bool SessionImpl::cancelDownloadMetadata(const TorrentID &id)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::increaseTorrentsQueuePos(const QList<TorrentID> &ids)
|
void SessionImpl::increaseTorrentsQueuePos(const QVector<TorrentID> &ids)
|
||||||
{
|
{
|
||||||
using ElementType = std::pair<int, const TorrentImpl *>;
|
using ElementType = std::pair<int, const TorrentImpl *>;
|
||||||
std::priority_queue<ElementType
|
std::priority_queue<ElementType
|
||||||
@@ -2563,7 +2527,7 @@ void SessionImpl::increaseTorrentsQueuePos(const QList<TorrentID> &ids)
|
|||||||
m_torrentsQueueChanged = true;
|
m_torrentsQueueChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::decreaseTorrentsQueuePos(const QList<TorrentID> &ids)
|
void SessionImpl::decreaseTorrentsQueuePos(const QVector<TorrentID> &ids)
|
||||||
{
|
{
|
||||||
using ElementType = std::pair<int, const TorrentImpl *>;
|
using ElementType = std::pair<int, const TorrentImpl *>;
|
||||||
std::priority_queue<ElementType> torrentQueue;
|
std::priority_queue<ElementType> torrentQueue;
|
||||||
@@ -2591,7 +2555,7 @@ void SessionImpl::decreaseTorrentsQueuePos(const QList<TorrentID> &ids)
|
|||||||
m_torrentsQueueChanged = true;
|
m_torrentsQueueChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::topTorrentsQueuePos(const QList<TorrentID> &ids)
|
void SessionImpl::topTorrentsQueuePos(const QVector<TorrentID> &ids)
|
||||||
{
|
{
|
||||||
using ElementType = std::pair<int, const TorrentImpl *>;
|
using ElementType = std::pair<int, const TorrentImpl *>;
|
||||||
std::priority_queue<ElementType> torrentQueue;
|
std::priority_queue<ElementType> torrentQueue;
|
||||||
@@ -2616,7 +2580,7 @@ void SessionImpl::topTorrentsQueuePos(const QList<TorrentID> &ids)
|
|||||||
m_torrentsQueueChanged = true;
|
m_torrentsQueueChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::bottomTorrentsQueuePos(const QList<TorrentID> &ids)
|
void SessionImpl::bottomTorrentsQueuePos(const QVector<TorrentID> &ids)
|
||||||
{
|
{
|
||||||
using ElementType = std::pair<int, const TorrentImpl *>;
|
using ElementType = std::pair<int, const TorrentImpl *>;
|
||||||
std::priority_queue<ElementType
|
std::priority_queue<ElementType
|
||||||
@@ -2652,9 +2616,9 @@ void SessionImpl::handleTorrentResumeDataRequested(const TorrentImpl *torrent)
|
|||||||
++m_numResumeData;
|
++m_numResumeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Torrent *> SessionImpl::torrents() const
|
QVector<Torrent *> SessionImpl::torrents() const
|
||||||
{
|
{
|
||||||
QList<Torrent *> result;
|
QVector<Torrent *> result;
|
||||||
result.reserve(m_torrents.size());
|
result.reserve(m_torrents.size());
|
||||||
for (TorrentImpl *torrent : asConst(m_torrents))
|
for (TorrentImpl *torrent : asConst(m_torrents))
|
||||||
result << torrent;
|
result << torrent;
|
||||||
@@ -2754,10 +2718,7 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||||||
// We should not add the torrent if it is already
|
// We should not add the torrent if it is already
|
||||||
// processed or is pending to add to session
|
// processed or is pending to add to session
|
||||||
if (m_loadingTorrents.contains(id) || (infoHash.isHybrid() && m_loadingTorrents.contains(altID)))
|
if (m_loadingTorrents.contains(id) || (infoHash.isHybrid() && m_loadingTorrents.contains(altID)))
|
||||||
{
|
|
||||||
emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, tr("Duplicate torrent")});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (Torrent *torrent = findTorrent(infoHash))
|
if (Torrent *torrent = findTorrent(infoHash))
|
||||||
{
|
{
|
||||||
@@ -2771,20 +2732,16 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||||||
|
|
||||||
if (!isMergeTrackersEnabled())
|
if (!isMergeTrackersEnabled())
|
||||||
{
|
{
|
||||||
const QString message = tr("Merging of trackers is disabled");
|
|
||||||
LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2")
|
LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2")
|
||||||
.arg(torrent->name(), message));
|
.arg(torrent->name(), tr("Merging of trackers is disabled")));
|
||||||
emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, message});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isPrivate = torrent->isPrivate() || (hasMetadata && source.info()->isPrivate());
|
const bool isPrivate = torrent->isPrivate() || (hasMetadata && source.info()->isPrivate());
|
||||||
if (isPrivate)
|
if (isPrivate)
|
||||||
{
|
{
|
||||||
const QString message = tr("Trackers cannot be merged because it is a private torrent");
|
|
||||||
LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2")
|
LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2")
|
||||||
.arg(torrent->name(), message));
|
.arg(torrent->name(), tr("Trackers cannot be merged because it is a private torrent")));
|
||||||
emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, message});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2792,10 +2749,8 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||||||
torrent->addTrackers(source.trackers());
|
torrent->addTrackers(source.trackers());
|
||||||
torrent->addUrlSeeds(source.urlSeeds());
|
torrent->addUrlSeeds(source.urlSeeds());
|
||||||
|
|
||||||
const QString message = tr("Trackers are merged from new source");
|
|
||||||
LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2")
|
LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2")
|
||||||
.arg(torrent->name(), message));
|
.arg(torrent->name(), tr("Trackers are merged from new source")));
|
||||||
emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, message});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2923,21 +2878,6 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAddTrackersFromURLEnabled() && !(hasMetadata && p.ti->priv()))
|
|
||||||
{
|
|
||||||
const auto maxTierIter = std::max_element(p.tracker_tiers.cbegin(), p.tracker_tiers.cend());
|
|
||||||
const int baseTier = (maxTierIter != p.tracker_tiers.cend()) ? (*maxTierIter + 1) : 0;
|
|
||||||
|
|
||||||
p.trackers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerEntriesFromURL.size()));
|
|
||||||
p.tracker_tiers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerEntriesFromURL.size()));
|
|
||||||
p.tracker_tiers.resize(p.trackers.size(), 0);
|
|
||||||
for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerEntriesFromURL))
|
|
||||||
{
|
|
||||||
p.trackers.emplace_back(trackerEntry.url.toStdString());
|
|
||||||
p.tracker_tiers.emplace_back(Utils::Number::clampingAdd(trackerEntry.tier, baseTier));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.upload_limit = addTorrentParams.uploadLimit;
|
p.upload_limit = addTorrentParams.uploadLimit;
|
||||||
p.download_limit = addTorrentParams.downloadLimit;
|
p.download_limit = addTorrentParams.downloadLimit;
|
||||||
|
|
||||||
@@ -3072,6 +3012,11 @@ void SessionImpl::removeMappedPorts(const QSet<quint16> &ports)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionImpl::invokeAsync(std::function<void ()> func)
|
||||||
|
{
|
||||||
|
m_asyncWorker->start(std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
// Add a torrent to libtorrent session in hidden mode
|
// Add a torrent to libtorrent session in hidden mode
|
||||||
// and force it to download its metadata
|
// and force it to download its metadata
|
||||||
bool SessionImpl::downloadMetadata(const TorrentDescriptor &torrentDescr)
|
bool SessionImpl::downloadMetadata(const TorrentDescriptor &torrentDescr)
|
||||||
@@ -3151,7 +3096,7 @@ void SessionImpl::exportTorrentFile(const Torrent *torrent, const Path &folderPa
|
|||||||
while (newTorrentPath.exists())
|
while (newTorrentPath.exists())
|
||||||
{
|
{
|
||||||
// Append number to torrent name to make it unique
|
// Append number to torrent name to make it unique
|
||||||
torrentExportFilename = u"%1 (%2).torrent"_s.arg(validName).arg(++counter);
|
torrentExportFilename = u"%1 %2.torrent"_s.arg(validName).arg(++counter);
|
||||||
newTorrentPath = folderPath / Path(torrentExportFilename);
|
newTorrentPath = folderPath / Path(torrentExportFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3206,10 +3151,10 @@ void SessionImpl::saveResumeData()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPendingAlerts(waitTime);
|
const std::vector<lt::alert *> alerts = getPendingAlerts(waitTime);
|
||||||
|
|
||||||
bool hasWantedAlert = false;
|
bool hasWantedAlert = false;
|
||||||
for (const lt::alert *alert : m_alerts)
|
for (const lt::alert *alert : alerts)
|
||||||
{
|
{
|
||||||
if (const int alertType = alert->type();
|
if (const int alertType = alert->type();
|
||||||
(alertType == lt::save_resume_data_alert::alert_type) || (alertType == lt::save_resume_data_failed_alert::alert_type)
|
(alertType == lt::save_resume_data_alert::alert_type) || (alertType == lt::save_resume_data_failed_alert::alert_type)
|
||||||
@@ -3229,7 +3174,7 @@ void SessionImpl::saveResumeData()
|
|||||||
|
|
||||||
void SessionImpl::saveTorrentsQueue()
|
void SessionImpl::saveTorrentsQueue()
|
||||||
{
|
{
|
||||||
QList<TorrentID> queue;
|
QVector<TorrentID> queue;
|
||||||
for (const TorrentImpl *torrent : asConst(m_torrents))
|
for (const TorrentImpl *torrent : asConst(m_torrents))
|
||||||
{
|
{
|
||||||
if (const int queuePos = torrent->queuePosition(); queuePos >= 0)
|
if (const int queuePos = torrent->queuePosition(); queuePos >= 0)
|
||||||
@@ -3259,9 +3204,6 @@ void SessionImpl::setSavePath(const Path &path)
|
|||||||
|
|
||||||
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
||||||
{
|
{
|
||||||
// This should be done before changing the save path
|
|
||||||
// to prevent the torrent from being moved at the new save path.
|
|
||||||
|
|
||||||
QSet<QString> affectedCatogories {{}}; // includes default (unnamed) category
|
QSet<QString> affectedCatogories {{}}; // includes default (unnamed) category
|
||||||
for (auto it = m_categories.cbegin(); it != m_categories.cend(); ++it)
|
for (auto it = m_categories.cbegin(); it != m_categories.cend(); ++it)
|
||||||
{
|
{
|
||||||
@@ -3291,9 +3233,6 @@ void SessionImpl::setDownloadPath(const Path &path)
|
|||||||
|
|
||||||
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
||||||
{
|
{
|
||||||
// This should be done before changing the save path
|
|
||||||
// to prevent the torrent from being moved at the new save path.
|
|
||||||
|
|
||||||
QSet<QString> affectedCatogories {{}}; // includes default (unnamed) category
|
QSet<QString> affectedCatogories {{}}; // includes default (unnamed) category
|
||||||
for (auto it = m_categories.cbegin(); it != m_categories.cend(); ++it)
|
for (auto it = m_categories.cbegin(); it != m_categories.cend(); ++it)
|
||||||
{
|
{
|
||||||
@@ -3620,16 +3559,6 @@ void SessionImpl::setSaveResumeDataInterval(const int value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::minutes SessionImpl::saveStatisticsInterval() const
|
|
||||||
{
|
|
||||||
return std::chrono::minutes(m_saveStatisticsInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::setSaveStatisticsInterval(const std::chrono::minutes timeInMinutes)
|
|
||||||
{
|
|
||||||
m_saveStatisticsInterval = timeInMinutes.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int SessionImpl::shutdownTimeout() const
|
int SessionImpl::shutdownTimeout() const
|
||||||
{
|
{
|
||||||
return m_shutdownTimeout;
|
return m_shutdownTimeout;
|
||||||
@@ -3936,82 +3865,6 @@ void SessionImpl::setAdditionalTrackers(const QString &trackers)
|
|||||||
populateAdditionalTrackers();
|
populateAdditionalTrackers();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SessionImpl::isAddTrackersFromURLEnabled() const
|
|
||||||
{
|
|
||||||
return m_isAddTrackersFromURLEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::setAddTrackersFromURLEnabled(const bool enabled)
|
|
||||||
{
|
|
||||||
if (enabled != isAddTrackersFromURLEnabled())
|
|
||||||
{
|
|
||||||
m_isAddTrackersFromURLEnabled = enabled;
|
|
||||||
if (enabled)
|
|
||||||
{
|
|
||||||
updateTrackersFromURL();
|
|
||||||
m_updateTrackersFromURLTimer->start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_updateTrackersFromURLTimer->stop();
|
|
||||||
setAdditionalTrackersFromURL({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SessionImpl::additionalTrackersURL() const
|
|
||||||
{
|
|
||||||
return m_additionalTrackersURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::setAdditionalTrackersURL(const QString &url)
|
|
||||||
{
|
|
||||||
if (url != additionalTrackersURL())
|
|
||||||
{
|
|
||||||
m_additionalTrackersURL = url.trimmed();
|
|
||||||
if (isAddTrackersFromURLEnabled())
|
|
||||||
updateTrackersFromURL();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SessionImpl::additionalTrackersFromURL() const
|
|
||||||
{
|
|
||||||
return m_additionalTrackersFromURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::setAdditionalTrackersFromURL(const QString &trackers)
|
|
||||||
{
|
|
||||||
if (trackers != additionalTrackersFromURL())
|
|
||||||
{
|
|
||||||
m_additionalTrackersFromURL = trackers;
|
|
||||||
populateAdditionalTrackersFromURL();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::updateTrackersFromURL()
|
|
||||||
{
|
|
||||||
const QString url = additionalTrackersURL();
|
|
||||||
if (url.isEmpty())
|
|
||||||
{
|
|
||||||
setAdditionalTrackersFromURL({});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Net::DownloadManager::instance()->download(Net::DownloadRequest(url)
|
|
||||||
, Preferences::instance()->useProxyForGeneralPurposes(), this, [this](const Net::DownloadResult &result)
|
|
||||||
{
|
|
||||||
if (result.status == Net::DownloadStatus::Success)
|
|
||||||
{
|
|
||||||
setAdditionalTrackersFromURL(QString::fromUtf8(result.data));
|
|
||||||
LogMsg(tr("Tracker list updated"), Log::INFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMsg(tr("Failed to update tracker list. Reason: \"%1\"").arg(result.errorString), Log::WARNING);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SessionImpl::isIPFilteringEnabled() const
|
bool SessionImpl::isIPFilteringEnabled() const
|
||||||
{
|
{
|
||||||
return m_isIPFilteringEnabled;
|
return m_isIPFilteringEnabled;
|
||||||
@@ -4902,20 +4755,6 @@ void SessionImpl::setAnnounceIP(const QString &ip)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SessionImpl::announcePort() const
|
|
||||||
{
|
|
||||||
return m_announcePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::setAnnouncePort(const int port)
|
|
||||||
{
|
|
||||||
if (port != m_announcePort)
|
|
||||||
{
|
|
||||||
m_announcePort = port;
|
|
||||||
configureDeferred();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SessionImpl::maxConcurrentHTTPAnnounces() const
|
int SessionImpl::maxConcurrentHTTPAnnounces() const
|
||||||
{
|
{
|
||||||
return m_maxConcurrentHTTPAnnounces;
|
return m_maxConcurrentHTTPAnnounces;
|
||||||
@@ -5121,16 +4960,6 @@ void SessionImpl::setTrackerFilteringEnabled(const bool enabled)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SessionImpl::lastExternalIPv4Address() const
|
|
||||||
{
|
|
||||||
return m_lastExternalIPv4Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SessionImpl::lastExternalIPv6Address() const
|
|
||||||
{
|
|
||||||
return m_lastExternalIPv6Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SessionImpl::isListening() const
|
bool SessionImpl::isListening() const
|
||||||
{
|
{
|
||||||
return m_nativeSessionExtension->isSessionListening();
|
return m_nativeSessionExtension->isSessionListening();
|
||||||
@@ -5212,7 +5041,7 @@ void SessionImpl::handleTorrentSavingModeChanged(TorrentImpl *const torrent)
|
|||||||
emit torrentSavingModeChanged(torrent);
|
emit torrentSavingModeChanged(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentTrackersAdded(TorrentImpl *const torrent, const QList<TrackerEntry> &newTrackers)
|
void SessionImpl::handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector<TrackerEntry> &newTrackers)
|
||||||
{
|
{
|
||||||
for (const TrackerEntry &newTracker : newTrackers)
|
for (const TrackerEntry &newTracker : newTrackers)
|
||||||
LogMsg(tr("Added tracker to torrent. Torrent: \"%1\". Tracker: \"%2\"").arg(torrent->name(), newTracker.url));
|
LogMsg(tr("Added tracker to torrent. Torrent: \"%1\". Tracker: \"%2\"").arg(torrent->name(), newTracker.url));
|
||||||
@@ -5231,13 +5060,13 @@ void SessionImpl::handleTorrentTrackersChanged(TorrentImpl *const torrent)
|
|||||||
emit trackersChanged(torrent);
|
emit trackersChanged(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QList<QUrl> &newUrlSeeds)
|
void SessionImpl::handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector<QUrl> &newUrlSeeds)
|
||||||
{
|
{
|
||||||
for (const QUrl &newUrlSeed : newUrlSeeds)
|
for (const QUrl &newUrlSeed : newUrlSeeds)
|
||||||
LogMsg(tr("Added URL seed to torrent. Torrent: \"%1\". URL: \"%2\"").arg(torrent->name(), newUrlSeed.toString()));
|
LogMsg(tr("Added URL seed to torrent. Torrent: \"%1\". URL: \"%2\"").arg(torrent->name(), newUrlSeed.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QList<QUrl> &urlSeeds)
|
void SessionImpl::handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
for (const QUrl &urlSeed : urlSeeds)
|
for (const QUrl &urlSeed : urlSeeds)
|
||||||
LogMsg(tr("Removed URL seed from torrent. Torrent: \"%1\". URL: \"%2\"").arg(torrent->name(), urlSeed.toString()));
|
LogMsg(tr("Removed URL seed from torrent. Torrent: \"%1\". URL: \"%2\"").arg(torrent->name(), urlSeed.toString()));
|
||||||
@@ -5255,7 +5084,7 @@ void SessionImpl::handleTorrentStopped(TorrentImpl *const torrent)
|
|||||||
{
|
{
|
||||||
torrent->resetTrackerEntryStatuses();
|
torrent->resetTrackerEntryStatuses();
|
||||||
|
|
||||||
const QList<TrackerEntryStatus> trackers = torrent->trackers();
|
const QVector<TrackerEntryStatus> trackers = torrent->trackers();
|
||||||
QHash<QString, TrackerEntryStatus> updatedTrackers;
|
QHash<QString, TrackerEntryStatus> updatedTrackers;
|
||||||
updatedTrackers.reserve(trackers.size());
|
updatedTrackers.reserve(trackers.size());
|
||||||
|
|
||||||
@@ -5642,32 +5471,14 @@ void SessionImpl::handleIPFilterError()
|
|||||||
emit IPFilterParsed(true, 0);
|
emit IPFilterParsed(true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::fetchPendingAlerts(const lt::time_duration time)
|
std::vector<lt::alert *> SessionImpl::getPendingAlerts(const lt::time_duration time) const
|
||||||
{
|
{
|
||||||
if (time > lt::time_duration::zero())
|
if (time > lt::time_duration::zero())
|
||||||
m_nativeSession->wait_for_alert(time);
|
m_nativeSession->wait_for_alert(time);
|
||||||
|
|
||||||
m_alerts.clear();
|
std::vector<lt::alert *> alerts;
|
||||||
m_nativeSession->pop_alerts(&m_alerts);
|
m_nativeSession->pop_alerts(&alerts);
|
||||||
}
|
return alerts;
|
||||||
|
|
||||||
void SessionImpl::endAlertSequence(const int alertType, const qsizetype alertCount)
|
|
||||||
{
|
|
||||||
qDebug() << "End alert sequence. Alert:" << lt::alert_name(alertType) << "Count:" << alertCount;
|
|
||||||
|
|
||||||
if (alertType == lt::add_torrent_alert::alert_type)
|
|
||||||
{
|
|
||||||
emit addTorrentAlertsReceived(alertCount);
|
|
||||||
|
|
||||||
if (!m_loadedTorrents.isEmpty())
|
|
||||||
{
|
|
||||||
if (isRestored())
|
|
||||||
m_torrentsQueueChanged = true;
|
|
||||||
|
|
||||||
emit torrentsLoaded(m_loadedTorrents);
|
|
||||||
m_loadedTorrents.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentContentLayout SessionImpl::torrentContentLayout() const
|
TorrentContentLayout SessionImpl::torrentContentLayout() const
|
||||||
@@ -5683,29 +5494,36 @@ void SessionImpl::setTorrentContentLayout(const TorrentContentLayout value)
|
|||||||
// Read alerts sent by libtorrent session
|
// Read alerts sent by libtorrent session
|
||||||
void SessionImpl::readAlerts()
|
void SessionImpl::readAlerts()
|
||||||
{
|
{
|
||||||
fetchPendingAlerts();
|
// cache current datetime of Qt and libtorrent clocks in order
|
||||||
|
// to optimize conversion of time points from lt to Qt clocks
|
||||||
|
m_ltNow = lt::clock_type::now();
|
||||||
|
m_qNow = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
const std::vector<lt::alert *> alerts = getPendingAlerts();
|
||||||
|
|
||||||
Q_ASSERT(m_loadedTorrents.isEmpty());
|
Q_ASSERT(m_loadedTorrents.isEmpty());
|
||||||
|
Q_ASSERT(m_receivedAddTorrentAlertsCount == 0);
|
||||||
|
|
||||||
if (!isRestored())
|
if (!isRestored())
|
||||||
m_loadedTorrents.reserve(MAX_PROCESSING_RESUMEDATA_COUNT);
|
m_loadedTorrents.reserve(MAX_PROCESSING_RESUMEDATA_COUNT);
|
||||||
|
|
||||||
int previousAlertType = -1;
|
for (const lt::alert *a : alerts)
|
||||||
qsizetype alertSequenceSize = 0;
|
|
||||||
for (const lt::alert *a : m_alerts)
|
|
||||||
{
|
|
||||||
const int alertType = a->type();
|
|
||||||
if ((alertType != previousAlertType) && (previousAlertType != -1))
|
|
||||||
{
|
|
||||||
endAlertSequence(previousAlertType, alertSequenceSize);
|
|
||||||
alertSequenceSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAlert(a);
|
handleAlert(a);
|
||||||
++alertSequenceSize;
|
|
||||||
previousAlertType = alertType;
|
if (m_receivedAddTorrentAlertsCount > 0)
|
||||||
|
{
|
||||||
|
emit addTorrentAlertsReceived(m_receivedAddTorrentAlertsCount);
|
||||||
|
m_receivedAddTorrentAlertsCount = 0;
|
||||||
|
|
||||||
|
if (!m_loadedTorrents.isEmpty())
|
||||||
|
{
|
||||||
|
if (isRestored())
|
||||||
|
m_torrentsQueueChanged = true;
|
||||||
|
|
||||||
|
emit torrentsLoaded(m_loadedTorrents);
|
||||||
|
m_loadedTorrents.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
endAlertSequence(previousAlertType, alertSequenceSize);
|
|
||||||
|
|
||||||
// Some torrents may become "finished" after different alerts handling.
|
// Some torrents may become "finished" after different alerts handling.
|
||||||
processPendingFinishedTorrents();
|
processPendingFinishedTorrents();
|
||||||
@@ -5713,6 +5531,8 @@ void SessionImpl::readAlerts()
|
|||||||
|
|
||||||
void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert)
|
void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert)
|
||||||
{
|
{
|
||||||
|
++m_receivedAddTorrentAlertsCount;
|
||||||
|
|
||||||
if (alert->error)
|
if (alert->error)
|
||||||
{
|
{
|
||||||
const QString msg = QString::fromStdString(alert->message());
|
const QString msg = QString::fromStdString(alert->message());
|
||||||
@@ -5732,9 +5552,7 @@ void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert)
|
|||||||
if (const auto loadingTorrentsIter = m_loadingTorrents.find(TorrentID::fromInfoHash(infoHash))
|
if (const auto loadingTorrentsIter = m_loadingTorrents.find(TorrentID::fromInfoHash(infoHash))
|
||||||
; loadingTorrentsIter != m_loadingTorrents.end())
|
; loadingTorrentsIter != m_loadingTorrents.end())
|
||||||
{
|
{
|
||||||
const AddTorrentError::Kind errorKind = (alert->error == lt::errors::duplicate_torrent)
|
emit addTorrentFailed(infoHash, msg);
|
||||||
? AddTorrentError::DuplicateTorrent : AddTorrentError::Other;
|
|
||||||
emit addTorrentFailed(infoHash, {errorKind, msg});
|
|
||||||
m_loadingTorrents.erase(loadingTorrentsIter);
|
m_loadingTorrents.erase(loadingTorrentsIter);
|
||||||
}
|
}
|
||||||
else if (const auto downloadedMetadataIter = m_downloadedMetadata.find(TorrentID::fromInfoHash(infoHash))
|
else if (const auto downloadedMetadataIter = m_downloadedMetadata.find(TorrentID::fromInfoHash(infoHash))
|
||||||
@@ -5994,7 +5812,7 @@ void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed
|
|||||||
#else
|
#else
|
||||||
const auto torrentID = TorrentID::fromInfoHash(alert->info_hash);
|
const auto torrentID = TorrentID::fromInfoHash(alert->info_hash);
|
||||||
#endif
|
#endif
|
||||||
const auto errorMessage = alert->error ? Utils::String::fromLocal8Bit(alert->error.message()) : QString();
|
const auto errorMessage = alert->error ? QString::fromLocal8Bit(alert->error.message().c_str()) : QString();
|
||||||
handleRemovedTorrent(torrentID, errorMessage);
|
handleRemovedTorrent(torrentID, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6128,7 +5946,7 @@ void SessionImpl::handleUrlSeedAlert(const lt::url_seed_alert *alert)
|
|||||||
|
|
||||||
if (alert->error)
|
if (alert->error)
|
||||||
{
|
{
|
||||||
LogMsg(tr("URL seed connection failed. Torrent: \"%1\". URL: \"%2\". Error: \"%3\"")
|
LogMsg(tr("URL seed DNS lookup failed. Torrent: \"%1\". URL: \"%2\". Error: \"%3\"")
|
||||||
.arg(torrent->name(), QString::fromUtf8(alert->server_url()), QString::fromStdString(alert->message()))
|
.arg(torrent->name(), QString::fromUtf8(alert->server_url()), QString::fromStdString(alert->message()))
|
||||||
, Log::WARNING);
|
, Log::WARNING);
|
||||||
}
|
}
|
||||||
@@ -6152,7 +5970,7 @@ void SessionImpl::handleListenFailedAlert(const lt::listen_failed_alert *alert)
|
|||||||
const QString proto {toString(alert->socket_type)};
|
const QString proto {toString(alert->socket_type)};
|
||||||
LogMsg(tr("Failed to listen on IP. IP: \"%1\". Port: \"%2/%3\". Reason: \"%4\"")
|
LogMsg(tr("Failed to listen on IP. IP: \"%1\". Port: \"%2/%3\". Reason: \"%4\"")
|
||||||
.arg(toString(alert->address), proto, QString::number(alert->port)
|
.arg(toString(alert->address), proto, QString::number(alert->port)
|
||||||
, Utils::String::fromLocal8Bit(alert->error.message())), Log::CRITICAL);
|
, QString::fromLocal8Bit(alert->error.message().c_str())), Log::CRITICAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleExternalIPAlert(const lt::external_ip_alert *alert)
|
void SessionImpl::handleExternalIPAlert(const lt::external_ip_alert *alert)
|
||||||
@@ -6161,19 +5979,11 @@ void SessionImpl::handleExternalIPAlert(const lt::external_ip_alert *alert)
|
|||||||
LogMsg(tr("Detected external IP. IP: \"%1\"")
|
LogMsg(tr("Detected external IP. IP: \"%1\"")
|
||||||
.arg(externalIP), Log::INFO);
|
.arg(externalIP), Log::INFO);
|
||||||
|
|
||||||
const bool isIPv6 = alert->external_address.is_v6();
|
if (m_lastExternalIP != externalIP)
|
||||||
const bool isIPv4 = alert->external_address.is_v4();
|
|
||||||
if (isIPv6 && (externalIP != m_lastExternalIPv6Address))
|
|
||||||
{
|
{
|
||||||
if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIPv6Address.isEmpty())
|
if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIP.isEmpty())
|
||||||
reannounceToAllTrackers();
|
reannounceToAllTrackers();
|
||||||
m_lastExternalIPv6Address = externalIP;
|
m_lastExternalIP = externalIP;
|
||||||
}
|
|
||||||
else if (isIPv4 && (externalIP != m_lastExternalIPv4Address))
|
|
||||||
{
|
|
||||||
if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIPv4Address.isEmpty())
|
|
||||||
reannounceToAllTrackers();
|
|
||||||
m_lastExternalIPv4Address = externalIP;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6259,14 +6069,8 @@ void SessionImpl::handleSessionStatsAlert(const lt::session_stats_alert *alert)
|
|||||||
m_status.allTimeDownload = m_previouslyDownloaded + m_status.totalDownload;
|
m_status.allTimeDownload = m_previouslyDownloaded + m_status.totalDownload;
|
||||||
m_status.allTimeUpload = m_previouslyUploaded + m_status.totalUpload;
|
m_status.allTimeUpload = m_previouslyUploaded + m_status.totalUpload;
|
||||||
|
|
||||||
if (m_saveStatisticsInterval > 0)
|
if (m_statisticsLastUpdateTimer.hasExpired(STATISTICS_SAVE_INTERVAL))
|
||||||
{
|
saveStatistics();
|
||||||
const auto saveInterval = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::minutes(m_saveStatisticsInterval));
|
|
||||||
if (m_statisticsLastUpdateTimer.hasExpired(saveInterval.count()))
|
|
||||||
{
|
|
||||||
saveStatistics();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cacheStatus.totalUsedBuffers = stats[m_metricIndices.disk.diskBlocksInUse];
|
m_cacheStatus.totalUsedBuffers = stats[m_metricIndices.disk.diskBlocksInUse];
|
||||||
m_cacheStatus.jobQueueLength = stats[m_metricIndices.disk.queuedDiskJobs];
|
m_cacheStatus.jobQueueLength = stats[m_metricIndices.disk.queuedDiskJobs];
|
||||||
@@ -6340,7 +6144,7 @@ void SessionImpl::handleStorageMovedFailedAlert(const lt::storage_moved_failed_a
|
|||||||
|
|
||||||
void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
|
void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
|
||||||
{
|
{
|
||||||
QList<Torrent *> updatedTorrents;
|
QVector<Torrent *> updatedTorrents;
|
||||||
updatedTorrents.reserve(static_cast<decltype(updatedTorrents)::size_type>(alert->status.size()));
|
updatedTorrents.reserve(static_cast<decltype(updatedTorrents)::size_type>(alert->status.size()));
|
||||||
|
|
||||||
for (const lt::torrent_status &status : alert->status)
|
for (const lt::torrent_status &status : alert->status)
|
||||||
@@ -6378,7 +6182,7 @@ void SessionImpl::handleSocks5Alert(const lt::socks5_alert *alert) const
|
|||||||
const QString endpoint = (addr.is_v6() ? u"[%1]:%2"_s : u"%1:%2"_s)
|
const QString endpoint = (addr.is_v6() ? u"[%1]:%2"_s : u"%1:%2"_s)
|
||||||
.arg(QString::fromStdString(addr.to_string()), QString::number(alert->ip.port()));
|
.arg(QString::fromStdString(addr.to_string()), QString::number(alert->ip.port()));
|
||||||
LogMsg(tr("SOCKS5 proxy error. Address: %1. Message: \"%2\".")
|
LogMsg(tr("SOCKS5 proxy error. Address: %1. Message: \"%2\".")
|
||||||
.arg(endpoint, Utils::String::fromLocal8Bit(alert->error.message()))
|
.arg(endpoint, QString::fromLocal8Bit(alert->error.message().c_str()))
|
||||||
, Log::WARNING);
|
, Log::WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6491,7 +6295,7 @@ void SessionImpl::loadStatistics()
|
|||||||
|
|
||||||
void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle)
|
void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle)
|
||||||
{
|
{
|
||||||
invokeAsync([this, torrentHandle = std::move(torrentHandle)]
|
invokeAsync([this, torrentHandle = std::move(torrentHandle)]() mutable
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -6501,9 +6305,9 @@ void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle)
|
|||||||
QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers = m_updatedTrackerStatuses.take(torrentHandle);
|
QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers = m_updatedTrackerStatuses.take(torrentHandle);
|
||||||
updatedTrackerStatusesLocker.unlock();
|
updatedTrackerStatusesLocker.unlock();
|
||||||
|
|
||||||
invoke([this, infoHash = torrentHandle.info_hash(), nativeTrackers = std::move(nativeTrackers), updatedTrackers = std::move(updatedTrackers)]
|
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers), updatedTrackers = std::move(updatedTrackers)]
|
||||||
{
|
{
|
||||||
TorrentImpl *torrent = m_torrents.value(infoHash);
|
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
|
||||||
if (!torrent || torrent->isStopped())
|
if (!torrent || torrent->isStopped())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -6555,3 +6359,9 @@ void SessionImpl::handleRemovedTorrent(const TorrentID &torrentID, const QString
|
|||||||
|
|
||||||
m_removingTorrents.erase(removingTorrentDataIter);
|
m_removingTorrents.erase(removingTorrentDataIter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDateTime SessionImpl::fromLTTimePoint32(const lt::time_point32 &timePoint) const
|
||||||
|
{
|
||||||
|
const auto secsSinceNow = lt::duration_cast<lt::seconds>(timePoint - m_ltNow + lt::milliseconds(500)).count();
|
||||||
|
return m_qNow.addSecs(secsSinceNow);
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -38,14 +37,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 <QMutex>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QThreadPool>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
@@ -56,8 +55,11 @@
|
|||||||
#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;
|
||||||
|
|
||||||
@@ -233,8 +235,6 @@ 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;
|
||||||
@@ -361,8 +361,6 @@ 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;
|
||||||
@@ -426,7 +424,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;
|
||||||
QList<Torrent *> torrents() const override;
|
QVector<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;
|
||||||
@@ -440,13 +438,10 @@ 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 QList<TorrentID> &ids) override;
|
void increaseTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
||||||
void decreaseTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
void decreaseTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
||||||
void topTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
void topTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
||||||
void bottomTorrentsQueuePos(const QList<TorrentID> &ids) override;
|
void bottomTorrentsQueuePos(const QVector<TorrentID> &ids) override;
|
||||||
|
|
||||||
QString lastExternalIPv4Address() const override;
|
|
||||||
QString lastExternalIPv6Address() const override;
|
|
||||||
|
|
||||||
// Torrent interface
|
// Torrent interface
|
||||||
void handleTorrentResumeDataRequested(const TorrentImpl *torrent);
|
void handleTorrentResumeDataRequested(const TorrentImpl *torrent);
|
||||||
@@ -462,11 +457,11 @@ 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 QList<TrackerEntry> &newTrackers);
|
void handleTorrentTrackersAdded(TorrentImpl *torrent, const QVector<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 QList<QUrl> &newUrlSeeds);
|
void handleTorrentUrlSeedsAdded(TorrentImpl *torrent, const QVector<QUrl> &newUrlSeeds);
|
||||||
void handleTorrentUrlSeedsRemoved(TorrentImpl *torrent, const QList<QUrl> &urlSeeds);
|
void handleTorrentUrlSeedsRemoved(TorrentImpl *torrent, const QVector<QUrl> &urlSeeds);
|
||||||
void handleTorrentResumeDataReady(TorrentImpl *torrent, const LoadTorrentParams &data);
|
void handleTorrentResumeDataReady(TorrentImpl *torrent, const LoadTorrentParams &data);
|
||||||
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
void handleTorrentInfoHashChanged(TorrentImpl *torrent, const InfoHash &prevInfoHash);
|
||||||
void handleTorrentStorageMovingStateChanged(TorrentImpl *torrent);
|
void handleTorrentStorageMovingStateChanged(TorrentImpl *torrent);
|
||||||
@@ -481,23 +476,15 @@ namespace BitTorrent
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func>
|
void invokeAsync(std::function<void ()> 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);
|
||||||
@@ -604,10 +591,7 @@ namespace BitTorrent
|
|||||||
void saveTorrentsQueue();
|
void saveTorrentsQueue();
|
||||||
void removeTorrentsQueue();
|
void removeTorrentsQueue();
|
||||||
|
|
||||||
void populateAdditionalTrackersFromURL();
|
std::vector<lt::alert *> getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const;
|
||||||
|
|
||||||
void fetchPendingAlerts(lt::time_duration time = lt::time_duration::zero());
|
|
||||||
void endAlertSequence(int alertType, qsizetype alertCount);
|
|
||||||
|
|
||||||
void moveTorrentStorage(const MoveStorageJob &job) const;
|
void moveTorrentStorage(const MoveStorageJob &job) const;
|
||||||
void handleMoveTorrentStorageJobFinished(const Path &newPath);
|
void handleMoveTorrentStorageJobFinished(const Path &newPath);
|
||||||
@@ -625,9 +609,6 @@ namespace BitTorrent
|
|||||||
|
|
||||||
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;
|
||||||
@@ -673,7 +654,6 @@ 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;
|
||||||
@@ -691,8 +671,6 @@ 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;
|
||||||
@@ -714,7 +692,6 @@ 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;
|
||||||
@@ -766,9 +743,6 @@ 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();
|
||||||
|
|
||||||
@@ -778,9 +752,8 @@ namespace BitTorrent
|
|||||||
const bool m_wasPexEnabled = m_isPeXEnabled;
|
const bool m_wasPexEnabled = m_isPeXEnabled;
|
||||||
|
|
||||||
int m_numResumeData = 0;
|
int m_numResumeData = 0;
|
||||||
QList<TrackerEntry> m_additionalTrackerEntries;
|
QVector<TrackerEntry> m_additionalTrackerEntries;
|
||||||
QList<TrackerEntry> m_additionalTrackerEntriesFromURL;
|
QVector<QRegularExpression> m_excludedFileNamesRegExpList;
|
||||||
QList<QRegularExpression> m_excludedFileNamesRegExpList;
|
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
mutable QElapsedTimer m_statisticsLastUpdateTimer;
|
mutable QElapsedTimer m_statisticsLastUpdateTimer;
|
||||||
@@ -815,7 +788,6 @@ namespace BitTorrent
|
|||||||
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;
|
||||||
|
|
||||||
@@ -836,8 +808,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
QList<MoveStorageJob> m_moveStorageQueue;
|
QList<MoveStorageJob> m_moveStorageQueue;
|
||||||
|
|
||||||
QString m_lastExternalIPv4Address;
|
QString m_lastExternalIP;
|
||||||
QString m_lastExternalIPv6Address;
|
|
||||||
|
|
||||||
bool m_needUpgradeDownloadPath = false;
|
bool m_needUpgradeDownloadPath = false;
|
||||||
|
|
||||||
@@ -847,10 +818,14 @@ 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;
|
||||||
|
|
||||||
QElapsedTimer m_wakeupCheckTimestamp;
|
QTimer *m_wakeupCheckTimer = nullptr;
|
||||||
|
QDateTime m_wakeupCheckTimestamp;
|
||||||
|
|
||||||
QList<TorrentImpl *> m_pendingFinishedTorrents;
|
QList<TorrentImpl *> m_pendingFinishedTorrents;
|
||||||
|
|
||||||
|
QDateTime m_qNow;
|
||||||
|
lt::clock_type::time_point m_ltNow;
|
||||||
|
|
||||||
friend void Session::initInstance();
|
friend void Session::initInstance();
|
||||||
friend void Session::freeInstance();
|
friend void Session::freeInstance();
|
||||||
friend Session *Session::instance();
|
friend Session *Session::instance();
|
||||||
|
|||||||
@@ -29,8 +29,6 @@
|
|||||||
|
|
||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
@@ -53,7 +51,9 @@ 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 = std::numeric_limits<qreal>::infinity();
|
const qreal Torrent::MAX_RATIO = 9999;
|
||||||
|
const int Torrent::MAX_SEEDING_TIME = 525600;
|
||||||
|
const int Torrent::MAX_INACTIVE_SEEDING_TIME = 525600;
|
||||||
|
|
||||||
TorrentID Torrent::id() const
|
TorrentID Torrent::id() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ 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;
|
||||||
|
|
||||||
@@ -255,8 +257,8 @@ 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 QList<TrackerEntryStatus> trackers() const = 0;
|
virtual QVector<TrackerEntryStatus> trackers() const = 0;
|
||||||
virtual QList<QUrl> urlSeeds() const = 0;
|
virtual QVector<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;
|
||||||
@@ -273,10 +275,10 @@ namespace BitTorrent
|
|||||||
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 QList<PeerInfo> peers() const = 0;
|
virtual QVector<PeerInfo> peers() const = 0;
|
||||||
virtual QBitArray pieces() const = 0;
|
virtual QBitArray pieces() const = 0;
|
||||||
virtual QBitArray downloadingPieces() const = 0;
|
virtual QBitArray downloadingPieces() const = 0;
|
||||||
virtual QList<int> pieceAvailability() 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;
|
||||||
@@ -305,11 +307,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(QList<TrackerEntry> trackers) = 0;
|
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
|
||||||
virtual void removeTrackers(const QStringList &trackers) = 0;
|
virtual void removeTrackers(const QStringList &trackers) = 0;
|
||||||
virtual void replaceTrackers(QList<TrackerEntry> trackers) = 0;
|
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
|
||||||
virtual void addUrlSeeds(const QList<QUrl> &urlSeeds) = 0;
|
virtual void addUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
||||||
virtual void removeUrlSeeds(const QList<QUrl> &urlSeeds) = 0;
|
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
||||||
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
||||||
virtual void clearPeers() = 0;
|
virtual void clearPeers() = 0;
|
||||||
virtual void setMetadata(const TorrentInfo &torrentInfo) = 0;
|
virtual void setMetadata(const TorrentInfo &torrentInfo) = 0;
|
||||||
@@ -323,9 +325,9 @@ 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 (QList<PeerInfo>)> resultHandler) const = 0;
|
virtual void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const = 0;
|
||||||
virtual void fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler) const = 0;
|
virtual void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const = 0;
|
||||||
virtual void fetchPieceAvailability(std::function<void (QList<int>)> resultHandler) const = 0;
|
virtual void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const = 0;
|
||||||
virtual void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const = 0;
|
virtual void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const = 0;
|
||||||
|
|
||||||
TorrentID id() const;
|
TorrentID id() const;
|
||||||
|
|||||||
@@ -44,18 +44,18 @@ 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 QList<DownloadPriority> filePriorities() const = 0;
|
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||||
virtual QList<qreal> filesProgress() const = 0;
|
virtual QVector<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 QList<qreal> availableFileFractions() const = 0;
|
virtual QVector<qreal> availableFileFractions() const = 0;
|
||||||
virtual void fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const = 0;
|
virtual void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const = 0;
|
||||||
|
|
||||||
virtual void prioritizeFiles(const QList<DownloadPriority> &priorities) = 0;
|
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
|
||||||
virtual void flushCache() const = 0;
|
virtual void flushCache() const = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,10 +62,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,9 +207,9 @@ void BitTorrent::TorrentDescriptor::setTorrentInfo(TorrentInfo torrentInfo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<BitTorrent::TrackerEntry> BitTorrent::TorrentDescriptor::trackers() const
|
QVector<BitTorrent::TrackerEntry> BitTorrent::TorrentDescriptor::trackers() const
|
||||||
{
|
{
|
||||||
QList<TrackerEntry> ret;
|
QVector<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)
|
||||||
@@ -218,9 +218,9 @@ QList<BitTorrent::TrackerEntry> BitTorrent::TorrentDescriptor::trackers() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QUrl> BitTorrent::TorrentDescriptor::urlSeeds() const
|
QVector<QUrl> BitTorrent::TorrentDescriptor::urlSeeds() const
|
||||||
{
|
{
|
||||||
QList<QUrl> urlSeeds;
|
QVector<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)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#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;
|
||||||
@@ -59,8 +60,8 @@ namespace BitTorrent
|
|||||||
QDateTime creationDate() const;
|
QDateTime creationDate() const;
|
||||||
QString creator() const;
|
QString creator() const;
|
||||||
QString comment() const;
|
QString comment() const;
|
||||||
QList<TrackerEntry> trackers() const;
|
QVector<TrackerEntry> trackers() const;
|
||||||
QList<QUrl> urlSeeds() const;
|
QVector<QUrl> urlSeeds() const;
|
||||||
const std::optional<TorrentInfo> &info() const;
|
const std::optional<TorrentInfo> &info() const;
|
||||||
|
|
||||||
void setTorrentInfo(TorrentInfo torrentInfo);
|
void setTorrentInfo(TorrentInfo torrentInfo);
|
||||||
|
|||||||
@@ -63,7 +63,6 @@
|
|||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/string.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "downloadpriority.h"
|
#include "downloadpriority.h"
|
||||||
#include "extensiondata.h"
|
#include "extensiondata.h"
|
||||||
@@ -101,13 +100,16 @@ namespace
|
|||||||
if (const QString *endpointName = cache.object(ltTCPEndpoint))
|
if (const QString *endpointName = cache.object(ltTCPEndpoint))
|
||||||
return *endpointName;
|
return *endpointName;
|
||||||
|
|
||||||
const auto endpointName = Utils::String::fromLatin1((std::ostringstream() << ltTCPEndpoint).str());
|
const std::string tmp = (std::ostringstream() << ltTCPEndpoint).str();
|
||||||
|
const auto endpointName = QString::fromLatin1(tmp.c_str(), tmp.size());
|
||||||
cache.insert(ltTCPEndpoint, new QString(endpointName));
|
cache.insert(ltTCPEndpoint, new QString(endpointName));
|
||||||
return endpointName;
|
return endpointName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename FromLTTimePoint32Func>
|
||||||
void updateTrackerEntryStatus(TrackerEntryStatus &trackerEntryStatus, const lt::announce_entry &nativeEntry
|
void updateTrackerEntryStatus(TrackerEntryStatus &trackerEntryStatus, const lt::announce_entry &nativeEntry
|
||||||
, const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo)
|
, const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo
|
||||||
|
, const FromLTTimePoint32Func &fromLTTimePoint32)
|
||||||
{
|
{
|
||||||
Q_ASSERT(trackerEntryStatus.url == QString::fromStdString(nativeEntry.url));
|
Q_ASSERT(trackerEntryStatus.url == QString::fromStdString(nativeEntry.url));
|
||||||
|
|
||||||
@@ -144,8 +146,8 @@ namespace
|
|||||||
trackerEndpointStatus.numSeeds = ltAnnounceInfo.scrape_complete;
|
trackerEndpointStatus.numSeeds = ltAnnounceInfo.scrape_complete;
|
||||||
trackerEndpointStatus.numLeeches = ltAnnounceInfo.scrape_incomplete;
|
trackerEndpointStatus.numLeeches = ltAnnounceInfo.scrape_incomplete;
|
||||||
trackerEndpointStatus.numDownloaded = ltAnnounceInfo.scrape_downloaded;
|
trackerEndpointStatus.numDownloaded = ltAnnounceInfo.scrape_downloaded;
|
||||||
trackerEndpointStatus.nextAnnounceTime = ltAnnounceInfo.next_announce;
|
trackerEndpointStatus.nextAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.next_announce);
|
||||||
trackerEndpointStatus.minAnnounceTime = ltAnnounceInfo.min_announce;
|
trackerEndpointStatus.minAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.min_announce);
|
||||||
|
|
||||||
if (ltAnnounceInfo.updating)
|
if (ltAnnounceInfo.updating)
|
||||||
{
|
{
|
||||||
@@ -236,8 +238,8 @@ namespace
|
|||||||
trackerEntryStatus.numSeeds = -1;
|
trackerEntryStatus.numSeeds = -1;
|
||||||
trackerEntryStatus.numLeeches = -1;
|
trackerEntryStatus.numLeeches = -1;
|
||||||
trackerEntryStatus.numDownloaded = -1;
|
trackerEntryStatus.numDownloaded = -1;
|
||||||
trackerEntryStatus.nextAnnounceTime = {};
|
trackerEntryStatus.nextAnnounceTime = QDateTime();
|
||||||
trackerEntryStatus.minAnnounceTime = {};
|
trackerEntryStatus.minAnnounceTime = QDateTime();
|
||||||
trackerEntryStatus.message.clear();
|
trackerEntryStatus.message.clear();
|
||||||
|
|
||||||
for (const TrackerEndpointStatus &endpointStatus : asConst(trackerEntryStatus.endpoints))
|
for (const TrackerEndpointStatus &endpointStatus : asConst(trackerEntryStatus.endpoints))
|
||||||
@@ -249,7 +251,7 @@ namespace
|
|||||||
|
|
||||||
if (endpointStatus.state == trackerEntryStatus.state)
|
if (endpointStatus.state == trackerEntryStatus.state)
|
||||||
{
|
{
|
||||||
if ((trackerEntryStatus.nextAnnounceTime == AnnounceTimePoint()) || (trackerEntryStatus.nextAnnounceTime > endpointStatus.nextAnnounceTime))
|
if (!trackerEntryStatus.nextAnnounceTime.isValid() || (trackerEntryStatus.nextAnnounceTime > endpointStatus.nextAnnounceTime))
|
||||||
{
|
{
|
||||||
trackerEntryStatus.nextAnnounceTime = endpointStatus.nextAnnounceTime;
|
trackerEntryStatus.nextAnnounceTime = endpointStatus.nextAnnounceTime;
|
||||||
trackerEntryStatus.minAnnounceTime = endpointStatus.minAnnounceTime;
|
trackerEntryStatus.minAnnounceTime = endpointStatus.minAnnounceTime;
|
||||||
@@ -463,13 +465,7 @@ qlonglong TorrentImpl::wastedSize() const
|
|||||||
|
|
||||||
QString TorrentImpl::currentTracker() const
|
QString TorrentImpl::currentTracker() const
|
||||||
{
|
{
|
||||||
if (!m_nativeStatus.current_tracker.empty())
|
return QString::fromStdString(m_nativeStatus.current_tracker);
|
||||||
return QString::fromStdString(m_nativeStatus.current_tracker);
|
|
||||||
|
|
||||||
if (!m_trackerEntryStatuses.isEmpty())
|
|
||||||
return m_trackerEntryStatuses.constFirst().url;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Path TorrentImpl::savePath() const
|
Path TorrentImpl::savePath() const
|
||||||
@@ -630,12 +626,12 @@ Path TorrentImpl::makeUserPath(const Path &path) const
|
|||||||
return userPath;
|
return userPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<TrackerEntryStatus> TorrentImpl::trackers() const
|
QVector<TrackerEntryStatus> TorrentImpl::trackers() const
|
||||||
{
|
{
|
||||||
return m_trackerEntryStatuses;
|
return m_trackerEntryStatuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::addTrackers(QList<TrackerEntry> trackers)
|
void TorrentImpl::addTrackers(QVector<TrackerEntry> trackers)
|
||||||
{
|
{
|
||||||
trackers.removeIf([](const TrackerEntry &trackerEntry) { return trackerEntry.url.isEmpty(); });
|
trackers.removeIf([](const TrackerEntry &trackerEntry) { return trackerEntry.url.isEmpty(); });
|
||||||
|
|
||||||
@@ -648,7 +644,7 @@ void TorrentImpl::addTrackers(QList<TrackerEntry> trackers)
|
|||||||
if (newTrackerSet.isEmpty())
|
if (newTrackerSet.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
trackers = QList<TrackerEntry>(newTrackerSet.cbegin(), newTrackerSet.cend());
|
trackers = QVector<TrackerEntry>(newTrackerSet.cbegin(), newTrackerSet.cend());
|
||||||
for (const TrackerEntry &tracker : asConst(trackers))
|
for (const TrackerEntry &tracker : asConst(trackers))
|
||||||
{
|
{
|
||||||
m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier));
|
m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier));
|
||||||
@@ -684,13 +680,13 @@ void TorrentImpl::removeTrackers(const QStringList &trackers)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::replaceTrackers(QList<TrackerEntry> trackers)
|
void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers)
|
||||||
{
|
{
|
||||||
trackers.removeIf([](const TrackerEntry &trackerEntry) { return trackerEntry.url.isEmpty(); });
|
trackers.removeIf([](const TrackerEntry &trackerEntry) { return trackerEntry.url.isEmpty(); });
|
||||||
|
|
||||||
// Filter out duplicate trackers
|
// Filter out duplicate trackers
|
||||||
const auto uniqueTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend());
|
const auto uniqueTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend());
|
||||||
trackers = QList<TrackerEntry>(uniqueTrackers.cbegin(), uniqueTrackers.cend());
|
trackers = QVector<TrackerEntry>(uniqueTrackers.cbegin(), uniqueTrackers.cend());
|
||||||
std::sort(trackers.begin(), trackers.end()
|
std::sort(trackers.begin(), trackers.end()
|
||||||
, [](const TrackerEntry &left, const TrackerEntry &right) { return left.tier < right.tier; });
|
, [](const TrackerEntry &left, const TrackerEntry &right) { return left.tier < right.tier; });
|
||||||
|
|
||||||
@@ -715,12 +711,12 @@ void TorrentImpl::replaceTrackers(QList<TrackerEntry> trackers)
|
|||||||
m_session->handleTorrentTrackersChanged(this);
|
m_session->handleTorrentTrackersChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QUrl> TorrentImpl::urlSeeds() const
|
QVector<QUrl> TorrentImpl::urlSeeds() const
|
||||||
{
|
{
|
||||||
return m_urlSeeds;
|
return m_urlSeeds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::addUrlSeeds(const QList<QUrl> &urlSeeds)
|
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
m_session->invokeAsync([urlSeeds, session = m_session
|
m_session->invokeAsync([urlSeeds, session = m_session
|
||||||
, nativeHandle = m_nativeHandle
|
, nativeHandle = m_nativeHandle
|
||||||
@@ -729,12 +725,12 @@ void TorrentImpl::addUrlSeeds(const QList<QUrl> &urlSeeds)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||||
QList<QUrl> currentSeeds;
|
QVector<QUrl> currentSeeds;
|
||||||
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
||||||
for (const std::string &urlSeed : nativeSeeds)
|
for (const std::string &urlSeed : nativeSeeds)
|
||||||
currentSeeds.append(QString::fromStdString(urlSeed));
|
currentSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
|
||||||
QList<QUrl> addedUrlSeeds;
|
QVector<QUrl> addedUrlSeeds;
|
||||||
addedUrlSeeds.reserve(urlSeeds.size());
|
addedUrlSeeds.reserve(urlSeeds.size());
|
||||||
|
|
||||||
for (const QUrl &url : urlSeeds)
|
for (const QUrl &url : urlSeeds)
|
||||||
@@ -764,7 +760,7 @@ void TorrentImpl::addUrlSeeds(const QList<QUrl> &urlSeeds)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::removeUrlSeeds(const QList<QUrl> &urlSeeds)
|
void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
m_session->invokeAsync([urlSeeds, session = m_session
|
m_session->invokeAsync([urlSeeds, session = m_session
|
||||||
, nativeHandle = m_nativeHandle
|
, nativeHandle = m_nativeHandle
|
||||||
@@ -773,12 +769,12 @@ void TorrentImpl::removeUrlSeeds(const QList<QUrl> &urlSeeds)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||||
QList<QUrl> currentSeeds;
|
QVector<QUrl> currentSeeds;
|
||||||
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
||||||
for (const std::string &urlSeed : nativeSeeds)
|
for (const std::string &urlSeed : nativeSeeds)
|
||||||
currentSeeds.append(QString::fromStdString(urlSeed));
|
currentSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
|
||||||
QList<QUrl> removedUrlSeeds;
|
QVector<QUrl> removedUrlSeeds;
|
||||||
removedUrlSeeds.reserve(urlSeeds.size());
|
removedUrlSeeds.reserve(urlSeeds.size());
|
||||||
|
|
||||||
for (const QUrl &url : urlSeeds)
|
for (const QUrl &url : urlSeeds)
|
||||||
@@ -1035,7 +1031,7 @@ Path TorrentImpl::filePath(const int index) const
|
|||||||
|
|
||||||
Path TorrentImpl::actualFilePath(const int index) const
|
Path TorrentImpl::actualFilePath(const int index) const
|
||||||
{
|
{
|
||||||
const QList<lt::file_index_t> nativeIndexes = m_torrentInfo.nativeIndexes();
|
const QVector<lt::file_index_t> nativeIndexes = m_torrentInfo.nativeIndexes();
|
||||||
|
|
||||||
Q_ASSERT(index >= 0);
|
Q_ASSERT(index >= 0);
|
||||||
Q_ASSERT(index < nativeIndexes.size());
|
Q_ASSERT(index < nativeIndexes.size());
|
||||||
@@ -1070,7 +1066,7 @@ PathList TorrentImpl::actualFilePaths() const
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<DownloadPriority> TorrentImpl::filePriorities() const
|
QVector<DownloadPriority> TorrentImpl::filePriorities() const
|
||||||
{
|
{
|
||||||
return m_filePriorities;
|
return m_filePriorities;
|
||||||
}
|
}
|
||||||
@@ -1305,12 +1301,12 @@ int TorrentImpl::queuePosition() const
|
|||||||
QString TorrentImpl::error() const
|
QString TorrentImpl::error() const
|
||||||
{
|
{
|
||||||
if (m_nativeStatus.errc)
|
if (m_nativeStatus.errc)
|
||||||
return Utils::String::fromLocal8Bit(m_nativeStatus.errc.message());
|
return QString::fromLocal8Bit(m_nativeStatus.errc.message().c_str());
|
||||||
|
|
||||||
if (m_nativeStatus.flags & lt::torrent_flags::upload_mode)
|
if (m_nativeStatus.flags & lt::torrent_flags::upload_mode)
|
||||||
{
|
{
|
||||||
return tr("Couldn't write to file. Reason: \"%1\". Torrent is now in \"upload only\" mode.")
|
return tr("Couldn't write to file. Reason: \"%1\". Torrent is now in \"upload only\" mode.")
|
||||||
.arg(Utils::String::fromLocal8Bit(m_lastFileError.error.message()));
|
.arg(QString::fromLocal8Bit(m_lastFileError.error.message().c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -1376,7 +1372,7 @@ qlonglong TorrentImpl::eta() const
|
|||||||
return (wantedSize() - completedSize()) / speedAverage.download;
|
return (wantedSize() - completedSize()) / speedAverage.download;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<qreal> TorrentImpl::filesProgress() const
|
QVector<qreal> TorrentImpl::filesProgress() const
|
||||||
{
|
{
|
||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return {};
|
return {};
|
||||||
@@ -1387,9 +1383,9 @@ QList<qreal> TorrentImpl::filesProgress() const
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (m_completedFiles.count(true) == count)
|
if (m_completedFiles.count(true) == count)
|
||||||
return QList<qreal>(count, 1);
|
return QVector<qreal>(count, 1);
|
||||||
|
|
||||||
QList<qreal> result;
|
QVector<qreal> result;
|
||||||
result.reserve(count);
|
result.reserve(count);
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
@@ -1465,12 +1461,12 @@ bool TorrentImpl::isLSDDisabled() const
|
|||||||
return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::disable_lsd);
|
return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::disable_lsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<PeerInfo> TorrentImpl::peers() const
|
QVector<PeerInfo> TorrentImpl::peers() const
|
||||||
{
|
{
|
||||||
std::vector<lt::peer_info> nativePeers;
|
std::vector<lt::peer_info> nativePeers;
|
||||||
m_nativeHandle.get_peer_info(nativePeers);
|
m_nativeHandle.get_peer_info(nativePeers);
|
||||||
|
|
||||||
QList<PeerInfo> peers;
|
QVector<PeerInfo> peers;
|
||||||
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||||
|
|
||||||
for (const lt::peer_info &peer : nativePeers)
|
for (const lt::peer_info &peer : nativePeers)
|
||||||
@@ -1499,7 +1495,7 @@ QBitArray TorrentImpl::downloadingPieces() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> TorrentImpl::pieceAvailability() const
|
QVector<int> TorrentImpl::pieceAvailability() const
|
||||||
{
|
{
|
||||||
std::vector<int> avail;
|
std::vector<int> avail;
|
||||||
m_nativeHandle.piece_availability(avail);
|
m_nativeHandle.piece_availability(avail);
|
||||||
@@ -1549,8 +1545,7 @@ qreal TorrentImpl::realRatio() const
|
|||||||
|
|
||||||
const qreal ratio = upload / static_cast<qreal>(download);
|
const qreal ratio = upload / static_cast<qreal>(download);
|
||||||
Q_ASSERT(ratio >= 0);
|
Q_ASSERT(ratio >= 0);
|
||||||
|
return (ratio > MAX_RATIO) ? MAX_RATIO : ratio;
|
||||||
return ratio;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TorrentImpl::uploadPayloadRate() const
|
int TorrentImpl::uploadPayloadRate() const
|
||||||
@@ -1616,20 +1611,18 @@ bool TorrentImpl::setCategory(const QString &category)
|
|||||||
if (!category.isEmpty() && !m_session->categories().contains(category))
|
if (!category.isEmpty() && !m_session->categories().contains(category))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (m_session->isDisableAutoTMMWhenCategoryChanged())
|
|
||||||
{
|
|
||||||
// This should be done before changing the category name
|
|
||||||
// to prevent the torrent from being moved at the path of new category.
|
|
||||||
setAutoTMMEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString oldCategory = m_category;
|
const QString oldCategory = m_category;
|
||||||
m_category = category;
|
m_category = category;
|
||||||
deferredRequestResumeData();
|
deferredRequestResumeData();
|
||||||
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
||||||
|
|
||||||
if (m_useAutoTMM)
|
if (m_useAutoTMM)
|
||||||
adjustStorageLocation();
|
{
|
||||||
|
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
|
||||||
|
adjustStorageLocation();
|
||||||
|
else
|
||||||
|
setAutoTMMEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1781,7 +1774,11 @@ TrackerEntryStatus TorrentImpl::updateTrackerEntryStatus(const lt::announce_entr
|
|||||||
const QSet<int> btProtocols {1};
|
const QSet<int> btProtocols {1};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
::updateTrackerEntryStatus(*it, announceEntry, btProtocols, updateInfo);
|
const auto fromLTTimePoint32 = [this](const lt::time_point32 &timePoint)
|
||||||
|
{
|
||||||
|
return m_session->fromLTTimePoint32(timePoint);
|
||||||
|
};
|
||||||
|
::updateTrackerEntryStatus(*it, announceEntry, btProtocols, updateInfo, fromLTTimePoint32);
|
||||||
|
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
@@ -2151,7 +2148,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
|||||||
// URL seed list have been changed by libtorrent for some reason, so we need to update cached one.
|
// URL seed list have been changed by libtorrent for some reason, so we need to update cached one.
|
||||||
// Unfortunately, URL seed list containing in "resume data" is generated according to different rules
|
// Unfortunately, URL seed list containing in "resume data" is generated according to different rules
|
||||||
// than the list we usually cache, so we have to request it from the appropriate source.
|
// than the list we usually cache, so we have to request it from the appropriate source.
|
||||||
fetchURLSeeds([this](const QList<QUrl> &urlSeeds) { m_urlSeeds = urlSeeds; });
|
fetchURLSeeds([this](const QVector<QUrl> &urlSeeds) { m_urlSeeds = urlSeeds; });
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((m_maintenanceJob == MaintenanceJob::HandleMetadata) && p->params.ti)
|
if ((m_maintenanceJob == MaintenanceJob::HandleMetadata) && p->params.ti)
|
||||||
@@ -2277,7 +2274,7 @@ void TorrentImpl::handleSaveResumeDataFailedAlert(const lt::save_resume_data_fai
|
|||||||
if (p->error != lt::errors::resume_data_not_modified)
|
if (p->error != lt::errors::resume_data_not_modified)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Generate resume data failed. Torrent: \"%1\". Reason: \"%2\"")
|
LogMsg(tr("Generate resume data failed. Torrent: \"%1\". Reason: \"%2\"")
|
||||||
.arg(name(), Utils::String::fromLocal8Bit(p->error.message())), Log::CRITICAL);
|
.arg(name(), QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2365,7 +2362,7 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
|
|||||||
Q_ASSERT(fileIndex >= 0);
|
Q_ASSERT(fileIndex >= 0);
|
||||||
|
|
||||||
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
|
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
|
||||||
.arg(name(), filePath(fileIndex).toString(), Utils::String::fromLocal8Bit(p->error.message())), Log::WARNING);
|
.arg(name(), filePath(fileIndex).toString(), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
|
||||||
|
|
||||||
--m_renameCount;
|
--m_renameCount;
|
||||||
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
||||||
@@ -2544,7 +2541,7 @@ void TorrentImpl::adjustStorageLocation()
|
|||||||
|
|
||||||
void TorrentImpl::doRenameFile(const int index, const Path &path)
|
void TorrentImpl::doRenameFile(const int index, const Path &path)
|
||||||
{
|
{
|
||||||
const QList<lt::file_index_t> nativeIndexes = m_torrentInfo.nativeIndexes();
|
const QVector<lt::file_index_t> nativeIndexes = m_torrentInfo.nativeIndexes();
|
||||||
|
|
||||||
Q_ASSERT(index >= 0);
|
Q_ASSERT(index >= 0);
|
||||||
Q_ASSERT(index < nativeIndexes.size());
|
Q_ASSERT(index < nativeIndexes.size());
|
||||||
@@ -2713,6 +2710,8 @@ void TorrentImpl::setRatioLimit(qreal limit)
|
|||||||
{
|
{
|
||||||
if (limit < USE_GLOBAL_RATIO)
|
if (limit < USE_GLOBAL_RATIO)
|
||||||
limit = NO_RATIO_LIMIT;
|
limit = NO_RATIO_LIMIT;
|
||||||
|
else if (limit > MAX_RATIO)
|
||||||
|
limit = MAX_RATIO;
|
||||||
|
|
||||||
if (m_ratioLimit != limit)
|
if (m_ratioLimit != limit)
|
||||||
{
|
{
|
||||||
@@ -2726,6 +2725,8 @@ void TorrentImpl::setSeedingTimeLimit(int limit)
|
|||||||
{
|
{
|
||||||
if (limit < USE_GLOBAL_SEEDING_TIME)
|
if (limit < USE_GLOBAL_SEEDING_TIME)
|
||||||
limit = NO_SEEDING_TIME_LIMIT;
|
limit = NO_SEEDING_TIME_LIMIT;
|
||||||
|
else if (limit > MAX_SEEDING_TIME)
|
||||||
|
limit = MAX_SEEDING_TIME;
|
||||||
|
|
||||||
if (m_seedingTimeLimit != limit)
|
if (m_seedingTimeLimit != limit)
|
||||||
{
|
{
|
||||||
@@ -2739,6 +2740,8 @@ void TorrentImpl::setInactiveSeedingTimeLimit(int limit)
|
|||||||
{
|
{
|
||||||
if (limit < USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
if (limit < USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
||||||
limit = NO_INACTIVE_SEEDING_TIME_LIMIT;
|
limit = NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||||
|
else if (limit > MAX_INACTIVE_SEEDING_TIME)
|
||||||
|
limit = MAX_SEEDING_TIME;
|
||||||
|
|
||||||
if (m_inactiveSeedingTimeLimit != limit)
|
if (m_inactiveSeedingTimeLimit != limit)
|
||||||
{
|
{
|
||||||
@@ -2848,26 +2851,33 @@ QString TorrentImpl::createMagnetURI() const
|
|||||||
|
|
||||||
const SHA1Hash infoHash1 = infoHash().v1();
|
const SHA1Hash infoHash1 = infoHash().v1();
|
||||||
if (infoHash1.isValid())
|
if (infoHash1.isValid())
|
||||||
|
{
|
||||||
ret += u"xt=urn:btih:" + infoHash1.toString();
|
ret += u"xt=urn:btih:" + infoHash1.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (const SHA256Hash infoHash2 = infoHash().v2(); infoHash2.isValid())
|
const SHA256Hash infoHash2 = infoHash().v2();
|
||||||
|
if (infoHash2.isValid())
|
||||||
{
|
{
|
||||||
if (infoHash1.isValid())
|
if (infoHash1.isValid())
|
||||||
ret += u'&';
|
ret += u'&';
|
||||||
ret += u"xt=urn:btmh:1220" + infoHash2.toString();
|
ret += u"xt=urn:btmh:1220" + infoHash2.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const QString displayName = name(); displayName != id().toString())
|
const QString displayName = name();
|
||||||
|
if (displayName != id().toString())
|
||||||
|
{
|
||||||
ret += u"&dn=" + QString::fromLatin1(QUrl::toPercentEncoding(displayName));
|
ret += u"&dn=" + QString::fromLatin1(QUrl::toPercentEncoding(displayName));
|
||||||
|
}
|
||||||
if (hasMetadata())
|
|
||||||
ret += u"&xl=" + QString::number(totalSize());
|
|
||||||
|
|
||||||
for (const TrackerEntryStatus &tracker : asConst(trackers()))
|
for (const TrackerEntryStatus &tracker : asConst(trackers()))
|
||||||
|
{
|
||||||
ret += u"&tr=" + QString::fromLatin1(QUrl::toPercentEncoding(tracker.url));
|
ret += u"&tr=" + QString::fromLatin1(QUrl::toPercentEncoding(tracker.url));
|
||||||
|
}
|
||||||
|
|
||||||
for (const QUrl &urlSeed : asConst(urlSeeds()))
|
for (const QUrl &urlSeed : asConst(urlSeeds()))
|
||||||
ret += u"&ws=" + urlSeed.toString(QUrl::FullyEncoded);
|
{
|
||||||
|
ret += u"&ws=" + QString::fromLatin1(urlSeed.toEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -2925,15 +2935,15 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::fetchPeerInfo(std::function<void (QList<PeerInfo>)> resultHandler) const
|
void TorrentImpl::fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const
|
||||||
{
|
{
|
||||||
invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList<PeerInfo>
|
invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QVector<PeerInfo>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<lt::peer_info> nativePeers;
|
std::vector<lt::peer_info> nativePeers;
|
||||||
nativeHandle.get_peer_info(nativePeers);
|
nativeHandle.get_peer_info(nativePeers);
|
||||||
QList<PeerInfo> peers;
|
QVector<PeerInfo> peers;
|
||||||
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||||
for (const lt::peer_info &peer : nativePeers)
|
for (const lt::peer_info &peer : nativePeers)
|
||||||
peers.append(PeerInfo(peer, allPieces));
|
peers.append(PeerInfo(peer, allPieces));
|
||||||
@@ -2946,14 +2956,14 @@ void TorrentImpl::fetchPeerInfo(std::function<void (QList<PeerInfo>)> resultHand
|
|||||||
, std::move(resultHandler));
|
, std::move(resultHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler) const
|
void TorrentImpl::fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const
|
||||||
{
|
{
|
||||||
invokeAsync([nativeHandle = m_nativeHandle]() -> QList<QUrl>
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<QUrl>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const std::set<std::string> currentSeeds = nativeHandle.url_seeds();
|
const std::set<std::string> currentSeeds = nativeHandle.url_seeds();
|
||||||
QList<QUrl> urlSeeds;
|
QVector<QUrl> urlSeeds;
|
||||||
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
|
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
|
||||||
for (const std::string &urlSeed : currentSeeds)
|
for (const std::string &urlSeed : currentSeeds)
|
||||||
urlSeeds.append(QString::fromStdString(urlSeed));
|
urlSeeds.append(QString::fromStdString(urlSeed));
|
||||||
@@ -2966,15 +2976,15 @@ void TorrentImpl::fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler)
|
|||||||
, std::move(resultHandler));
|
, std::move(resultHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::fetchPieceAvailability(std::function<void (QList<int>)> resultHandler) const
|
void TorrentImpl::fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const
|
||||||
{
|
{
|
||||||
invokeAsync([nativeHandle = m_nativeHandle]() -> QList<int>
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<int>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<int> piecesAvailability;
|
std::vector<int> piecesAvailability;
|
||||||
nativeHandle.piece_availability(piecesAvailability);
|
nativeHandle.piece_availability(piecesAvailability);
|
||||||
return QList<int>(piecesAvailability.cbegin(), piecesAvailability.cend());
|
return QVector<int>(piecesAvailability.cbegin(), piecesAvailability.cend());
|
||||||
}
|
}
|
||||||
catch (const std::exception &) {}
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
@@ -3008,9 +3018,9 @@ void TorrentImpl::fetchDownloadingPieces(std::function<void (QBitArray)> resultH
|
|||||||
, std::move(resultHandler));
|
, std::move(resultHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const
|
void TorrentImpl::fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const
|
||||||
{
|
{
|
||||||
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList<qreal>
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QVector<qreal>
|
||||||
{
|
{
|
||||||
if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0))
|
if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0))
|
||||||
return {};
|
return {};
|
||||||
@@ -3022,9 +3032,9 @@ void TorrentImpl::fetchAvailableFileFractions(std::function<void (QList<qreal>)>
|
|||||||
const int filesCount = torrentInfo.filesCount();
|
const int filesCount = torrentInfo.filesCount();
|
||||||
// libtorrent returns empty array for seeding only torrents
|
// libtorrent returns empty array for seeding only torrents
|
||||||
if (piecesAvailability.empty())
|
if (piecesAvailability.empty())
|
||||||
return QList<qreal>(filesCount, -1);
|
return QVector<qreal>(filesCount, -1);
|
||||||
|
|
||||||
QList<qreal> result;
|
QVector<qreal> result;
|
||||||
result.reserve(filesCount);
|
result.reserve(filesCount);
|
||||||
for (int i = 0; i < filesCount; ++i)
|
for (int i = 0; i < filesCount; ++i)
|
||||||
{
|
{
|
||||||
@@ -3048,7 +3058,7 @@ void TorrentImpl::fetchAvailableFileFractions(std::function<void (QList<qreal>)>
|
|||||||
, std::move(resultHandler));
|
, std::move(resultHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::prioritizeFiles(const QList<DownloadPriority> &priorities)
|
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
||||||
{
|
{
|
||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return;
|
return;
|
||||||
@@ -3057,7 +3067,7 @@ void TorrentImpl::prioritizeFiles(const QList<DownloadPriority> &priorities)
|
|||||||
|
|
||||||
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
||||||
// 'torrent_finished_alert' and eg show tray notifications
|
// 'torrent_finished_alert' and eg show tray notifications
|
||||||
const QList<DownloadPriority> oldPriorities = filePriorities();
|
const QVector<DownloadPriority> oldPriorities = filePriorities();
|
||||||
for (int i = 0; i < oldPriorities.size(); ++i)
|
for (int i = 0; i < oldPriorities.size(); ++i)
|
||||||
{
|
{
|
||||||
if ((oldPriorities[i] == DownloadPriority::Ignored)
|
if ((oldPriorities[i] == DownloadPriority::Ignored)
|
||||||
@@ -3085,18 +3095,18 @@ void TorrentImpl::prioritizeFiles(const QList<DownloadPriority> &priorities)
|
|||||||
manageActualFilePaths();
|
manageActualFilePaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<qreal> TorrentImpl::availableFileFractions() const
|
QVector<qreal> TorrentImpl::availableFileFractions() const
|
||||||
{
|
{
|
||||||
Q_ASSERT(hasMetadata());
|
Q_ASSERT(hasMetadata());
|
||||||
|
|
||||||
const int filesCount = this->filesCount();
|
const int filesCount = this->filesCount();
|
||||||
if (filesCount <= 0) return {};
|
if (filesCount <= 0) return {};
|
||||||
|
|
||||||
const QList<int> piecesAvailability = pieceAvailability();
|
const QVector<int> piecesAvailability = pieceAvailability();
|
||||||
// libtorrent returns empty array for seeding only torrents
|
// libtorrent returns empty array for seeding only torrents
|
||||||
if (piecesAvailability.empty()) return QList<qreal>(filesCount, -1);
|
if (piecesAvailability.empty()) return QVector<qreal>(filesCount, -1);
|
||||||
|
|
||||||
QList<qreal> res;
|
QVector<qreal> res;
|
||||||
res.reserve(filesCount);
|
res.reserve(filesCount);
|
||||||
for (int i = 0; i < filesCount; ++i)
|
for (int i = 0; i < filesCount; ++i)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -162,7 +162,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;
|
||||||
QList<DownloadPriority> filePriorities() const override;
|
QVector<DownloadPriority> filePriorities() const override;
|
||||||
|
|
||||||
TorrentInfo info() const override;
|
TorrentInfo info() const override;
|
||||||
bool isFinished() const override;
|
bool isFinished() const override;
|
||||||
@@ -184,13 +184,13 @@ 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;
|
||||||
QList<TrackerEntryStatus> trackers() const override;
|
QVector<TrackerEntryStatus> trackers() const override;
|
||||||
QList<QUrl> urlSeeds() const override;
|
QVector<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 eta() const override;
|
qlonglong eta() const override;
|
||||||
QList<qreal> filesProgress() const override;
|
QVector<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;
|
||||||
@@ -203,10 +203,10 @@ namespace BitTorrent
|
|||||||
bool isDHTDisabled() const override;
|
bool isDHTDisabled() const override;
|
||||||
bool isPEXDisabled() const override;
|
bool isPEXDisabled() const override;
|
||||||
bool isLSDDisabled() const override;
|
bool isLSDDisabled() const override;
|
||||||
QList<PeerInfo> peers() const override;
|
QVector<PeerInfo> peers() const override;
|
||||||
QBitArray pieces() const override;
|
QBitArray pieces() const override;
|
||||||
QBitArray downloadingPieces() const override;
|
QBitArray downloadingPieces() const override;
|
||||||
QList<int> pieceAvailability() 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;
|
||||||
@@ -220,7 +220,7 @@ 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;
|
||||||
QList<qreal> availableFileFractions() 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;
|
||||||
@@ -231,7 +231,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 QList<DownloadPriority> &priorities) override;
|
void prioritizeFiles(const QVector<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;
|
||||||
@@ -239,11 +239,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(QList<TrackerEntry> trackers) override;
|
void addTrackers(QVector<TrackerEntry> trackers) override;
|
||||||
void removeTrackers(const QStringList &trackers) override;
|
void removeTrackers(const QStringList &trackers) override;
|
||||||
void replaceTrackers(QList<TrackerEntry> trackers) override;
|
void replaceTrackers(QVector<TrackerEntry> trackers) override;
|
||||||
void addUrlSeeds(const QList<QUrl> &urlSeeds) override;
|
void addUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
||||||
void removeUrlSeeds(const QList<QUrl> &urlSeeds) override;
|
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
||||||
bool connectPeer(const PeerAddress &peerAddress) override;
|
bool connectPeer(const PeerAddress &peerAddress) override;
|
||||||
void clearPeers() override;
|
void clearPeers() override;
|
||||||
void setMetadata(const TorrentInfo &torrentInfo) override;
|
void setMetadata(const TorrentInfo &torrentInfo) override;
|
||||||
@@ -258,11 +258,11 @@ 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 (QList<PeerInfo>)> resultHandler) const override;
|
void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const override;
|
||||||
void fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler) const override;
|
void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const override;
|
||||||
void fetchPieceAvailability(std::function<void (QList<int>)> resultHandler) const override;
|
void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const override;
|
||||||
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
|
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
|
||||||
void fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const override;
|
void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const override;
|
||||||
|
|
||||||
bool needSaveResumeData() const;
|
bool needSaveResumeData() const;
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ namespace BitTorrent
|
|||||||
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;
|
||||||
QList<DownloadPriority> m_filePriorities;
|
QVector<DownloadPriority> m_filePriorities;
|
||||||
QBitArray m_completedFiles;
|
QBitArray m_completedFiles;
|
||||||
SpeedMonitor m_payloadRateMonitor;
|
SpeedMonitor m_payloadRateMonitor;
|
||||||
|
|
||||||
@@ -361,8 +361,8 @@ namespace BitTorrent
|
|||||||
|
|
||||||
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
|
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
|
||||||
|
|
||||||
QList<TrackerEntryStatus> m_trackerEntryStatuses;
|
QVector<TrackerEntryStatus> m_trackerEntryStatuses;
|
||||||
QList<QUrl> m_urlSeeds;
|
QVector<QUrl> m_urlSeeds;
|
||||||
FileErrorInfo m_lastFileError;
|
FileErrorInfo m_lastFileError;
|
||||||
|
|
||||||
// Persistent data
|
// Persistent data
|
||||||
@@ -393,7 +393,7 @@ namespace BitTorrent
|
|||||||
int m_uploadLimit = 0;
|
int m_uploadLimit = 0;
|
||||||
|
|
||||||
QBitArray m_pieces;
|
QBitArray m_pieces;
|
||||||
QList<std::int64_t> m_filesProgress;
|
QVector<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-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -28,15 +28,24 @@
|
|||||||
|
|
||||||
#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"
|
||||||
|
|
||||||
@@ -73,6 +82,66 @@ 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 {};
|
||||||
@@ -91,6 +160,28 @@ 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;
|
||||||
@@ -179,7 +270,43 @@ 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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TorrentInfo::rawData() const
|
QVector<TrackerEntry> TorrentInfo::trackers() 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
|
||||||
@@ -193,7 +320,7 @@ QByteArray TorrentInfo::rawData() 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 QList<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
const QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||||
|
|
||||||
PathList res;
|
PathList res;
|
||||||
res.reserve(fileIndices.size());
|
res.reserve(fileIndices.size());
|
||||||
@@ -203,14 +330,14 @@ PathList TorrentInfo::filesForPiece(const int pieceIndex) const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
|
QVector<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}));
|
||||||
QList<int> res;
|
QVector<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)
|
||||||
{
|
{
|
||||||
@@ -222,13 +349,13 @@ QList<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QByteArray> TorrentInfo::pieceHashes() const
|
QVector<QByteArray> TorrentInfo::pieceHashes() const
|
||||||
{
|
{
|
||||||
if (!isValid())
|
if (!isValid())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const int count = piecesCount();
|
const int count = piecesCount();
|
||||||
QList<QByteArray> hashes;
|
QVector<QByteArray> hashes;
|
||||||
hashes.reserve(count);
|
hashes.reserve(count);
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
@@ -239,7 +366,16 @@ QList<QByteArray> TorrentInfo::pieceHashes() const
|
|||||||
|
|
||||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const Path &filePath) const
|
TorrentInfo::PieceRange TorrentInfo::filePieces(const Path &filePath) const
|
||||||
{
|
{
|
||||||
return filePieces(fileIndex(filePath));
|
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||||
|
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
|
||||||
@@ -248,7 +384,10 @@ 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]);
|
||||||
@@ -311,7 +450,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<lt::file_index_t> TorrentInfo::nativeIndexes() const
|
QVector<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-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015 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,8 +32,9 @@
|
|||||||
|
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QList>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
@@ -49,17 +50,26 @@ 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;
|
||||||
@@ -70,9 +80,12 @@ 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;
|
||||||
QList<int> fileIndicesForPiece(int pieceIndex) const;
|
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
||||||
QList<QByteArray> pieceHashes() const;
|
QVector<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
|
||||||
@@ -80,12 +93,10 @@ 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;
|
||||||
QList<lt::file_index_t> nativeIndexes() const;
|
QVector<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
|
||||||
@@ -95,7 +106,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
|
||||||
QList<lt::file_index_t> m_nativeIndexes;
|
QVector<lt::file_index_t> m_nativeIndexes;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -28,11 +28,10 @@
|
|||||||
|
|
||||||
#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
|
||||||
@@ -60,8 +59,8 @@ namespace BitTorrent
|
|||||||
int numLeeches = -1;
|
int numLeeches = -1;
|
||||||
int numDownloaded = -1;
|
int numDownloaded = -1;
|
||||||
|
|
||||||
AnnounceTimePoint nextAnnounceTime {};
|
QDateTime nextAnnounceTime {};
|
||||||
AnnounceTimePoint minAnnounceTime {};
|
QDateTime minAnnounceTime {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TrackerEntryStatus
|
struct TrackerEntryStatus
|
||||||
@@ -77,8 +76,8 @@ namespace BitTorrent
|
|||||||
int numLeeches = -1;
|
int numLeeches = -1;
|
||||||
int numDownloaded = -1;
|
int numDownloaded = -1;
|
||||||
|
|
||||||
AnnounceTimePoint nextAnnounceTime {};
|
QDateTime nextAnnounceTime {};
|
||||||
AnnounceTimePoint minAnnounceTime {};
|
QDateTime minAnnounceTime {};
|
||||||
|
|
||||||
QHash<std::pair<QString, int>, TrackerEndpointStatus> endpoints {};
|
QHash<std::pair<QString, int>, TrackerEndpointStatus> endpoints {};
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ void Connection::read()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_ASSERT(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ using QStringPair = std::pair<QString, QString>;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const QByteArray EOH = CRLF.repeated(2);
|
const QByteArray EOH = QByteArray(CRLF).repeated(2);
|
||||||
|
|
||||||
const QByteArrayView viewWithoutEndingWith(const QByteArrayView in, const QByteArrayView str)
|
const QByteArrayView viewWithoutEndingWith(const QByteArrayView in, const QByteArrayView str)
|
||||||
{
|
{
|
||||||
@@ -164,7 +164,7 @@ bool RequestParser::parseStartLines(const QStringView data)
|
|||||||
if (line.at(0).isSpace() && !requestLines.isEmpty())
|
if (line.at(0).isSpace() && !requestLines.isEmpty())
|
||||||
{
|
{
|
||||||
// continuation of previous line
|
// continuation of previous line
|
||||||
requestLines.last() += line;
|
requestLines.last() += line.toString();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -225,7 +225,9 @@ bool RequestParser::parseRequestLine(const QString &line)
|
|||||||
const QByteArrayView valueComponent = param.mid(eqCharPos + 1);
|
const QByteArrayView valueComponent = param.mid(eqCharPos + 1);
|
||||||
const QString paramName = QString::fromUtf8(
|
const QString paramName = QString::fromUtf8(
|
||||||
QByteArray::fromPercentEncoding(asQByteArray(nameComponent)).replace('+', ' '));
|
QByteArray::fromPercentEncoding(asQByteArray(nameComponent)).replace('+', ' '));
|
||||||
const QByteArray paramValue = QByteArray::fromPercentEncoding(asQByteArray(valueComponent)).replace('+', ' ');
|
const QByteArray paramValue = valueComponent.isNull()
|
||||||
|
? QByteArray("")
|
||||||
|
: QByteArray::fromPercentEncoding(asQByteArray(valueComponent)).replace('+', ' ');
|
||||||
|
|
||||||
m_request.query[paramName] = paramValue;
|
m_request.query[paramName] = paramValue;
|
||||||
}
|
}
|
||||||
@@ -335,7 +337,7 @@ bool RequestParser::parseFormData(const QByteArrayView data)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!parseHeaderLine(line, headersMap))
|
if (!parseHeaderLine(line.toString(), headersMap))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,8 +348,7 @@ bool RequestParser::parseFormData(const QByteArrayView data)
|
|||||||
|
|
||||||
if (headersMap.contains(filename))
|
if (headersMap.contains(filename))
|
||||||
{
|
{
|
||||||
m_request.files.append({.filename = headersMap[filename], .type = headersMap[HEADER_CONTENT_TYPE]
|
m_request.files.append({headersMap[filename], headersMap[HEADER_CONTENT_TYPE], payload.toByteArray()});
|
||||||
, .data = payload.toByteArray()});
|
|
||||||
}
|
}
|
||||||
else if (headersMap.contains(name))
|
else if (headersMap.contains(name))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,9 +37,8 @@
|
|||||||
|
|
||||||
#include <QtLogging>
|
#include <QtLogging>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QSslCertificate>
|
|
||||||
#include <QSslCipher>
|
#include <QSslCipher>
|
||||||
#include <QSslKey>
|
#include <QSslConfiguration>
|
||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@@ -101,12 +100,13 @@ using namespace Http;
|
|||||||
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||||
: QTcpServer(parent)
|
: QTcpServer(parent)
|
||||||
, m_requestHandler(requestHandler)
|
, m_requestHandler(requestHandler)
|
||||||
, m_sslConfig {QSslConfiguration::defaultConfiguration()}
|
|
||||||
{
|
{
|
||||||
setProxy(QNetworkProxy::NoProxy);
|
setProxy(QNetworkProxy::NoProxy);
|
||||||
|
|
||||||
m_sslConfig.setCiphers(safeCipherList());
|
QSslConfiguration sslConf {QSslConfiguration::defaultConfiguration()};
|
||||||
m_sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
|
sslConf.setProtocol(QSsl::TlsV1_2OrLater);
|
||||||
|
sslConf.setCiphers(safeCipherList());
|
||||||
|
QSslConfiguration::setDefaultConfiguration(sslConf);
|
||||||
|
|
||||||
auto *dropConnectionTimer = new QTimer(this);
|
auto *dropConnectionTimer = new QTimer(this);
|
||||||
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
|
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
|
||||||
@@ -115,7 +115,7 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
|||||||
|
|
||||||
void Server::incomingConnection(const qintptr socketDescriptor)
|
void Server::incomingConnection(const qintptr socketDescriptor)
|
||||||
{
|
{
|
||||||
std::unique_ptr<QTcpSocket> serverSocket = isHttps() ? std::make_unique<QSslSocket>(this) : std::make_unique<QTcpSocket>(this);
|
std::unique_ptr<QTcpSocket> serverSocket = m_https ? std::make_unique<QSslSocket>(this) : std::make_unique<QTcpSocket>(this);
|
||||||
if (!serverSocket->setSocketDescriptor(socketDescriptor))
|
if (!serverSocket->setSocketDescriptor(socketDescriptor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -127,10 +127,13 @@ void Server::incomingConnection(const qintptr socketDescriptor)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (isHttps())
|
if (m_https)
|
||||||
{
|
{
|
||||||
auto *sslSocket = static_cast<QSslSocket *>(serverSocket.get());
|
auto *sslSocket = static_cast<QSslSocket *>(serverSocket.get());
|
||||||
sslSocket->setSslConfiguration(m_sslConfig);
|
sslSocket->setProtocol(QSsl::SecureProtocols);
|
||||||
|
sslSocket->setPrivateKey(m_key);
|
||||||
|
sslSocket->setLocalCertificateChain(m_certificates);
|
||||||
|
sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
sslSocket->startServerEncryption();
|
sslSocket->startServerEncryption();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,17 +178,17 @@ bool Server::setupHttps(const QByteArray &certificates, const QByteArray &privat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sslConfig.setLocalCertificateChain(certs);
|
m_key = key;
|
||||||
m_sslConfig.setPrivateKey(key);
|
m_certificates = certs;
|
||||||
m_https = true;
|
m_https = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::disableHttps()
|
void Server::disableHttps()
|
||||||
{
|
{
|
||||||
m_sslConfig.setLocalCertificateChain({});
|
|
||||||
m_sslConfig.setPrivateKey({});
|
|
||||||
m_https = false;
|
m_https = false;
|
||||||
|
m_certificates.clear();
|
||||||
|
m_key.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::isHttps() const
|
bool Server::isHttps() const
|
||||||
|
|||||||
@@ -31,7 +31,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSslConfiguration>
|
#include <QSslCertificate>
|
||||||
|
#include <QSslKey>
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
@@ -62,6 +63,7 @@ namespace Http
|
|||||||
QSet<Connection *> m_connections; // for tracking persistent connections
|
QSet<Connection *> m_connections; // for tracking persistent connections
|
||||||
|
|
||||||
bool m_https = false;
|
bool m_https = false;
|
||||||
QSslConfiguration m_sslConfig;
|
QList<QSslCertificate> m_certificates;
|
||||||
|
QSslKey m_key;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,9 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QList>
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
|
||||||
@@ -60,7 +60,6 @@ namespace Http
|
|||||||
inline const QString HEADER_X_CONTENT_TYPE_OPTIONS = u"x-content-type-options"_s;
|
inline const QString HEADER_X_CONTENT_TYPE_OPTIONS = u"x-content-type-options"_s;
|
||||||
inline const QString HEADER_X_FORWARDED_FOR = u"x-forwarded-for"_s;
|
inline const QString HEADER_X_FORWARDED_FOR = u"x-forwarded-for"_s;
|
||||||
inline const QString HEADER_X_FORWARDED_HOST = u"x-forwarded-host"_s;
|
inline const QString HEADER_X_FORWARDED_HOST = u"x-forwarded-host"_s;
|
||||||
inline const QString HEADER_X_FORWARDED_PROTO = u"x-forwarded-proto"_s;
|
|
||||||
inline const QString HEADER_X_FRAME_OPTIONS = u"x-frame-options"_s;
|
inline const QString HEADER_X_FRAME_OPTIONS = u"x-frame-options"_s;
|
||||||
inline const QString HEADER_X_XSS_PROTECTION = u"x-xss-protection"_s;
|
inline const QString HEADER_X_XSS_PROTECTION = u"x-xss-protection"_s;
|
||||||
|
|
||||||
@@ -79,7 +78,7 @@ namespace Http
|
|||||||
inline const QString CONTENT_TYPE_FORM_DATA = u"multipart/form-data"_s;
|
inline const QString CONTENT_TYPE_FORM_DATA = u"multipart/form-data"_s;
|
||||||
|
|
||||||
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
|
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
|
||||||
inline const QByteArray CRLF = QByteArrayLiteral("\x0D\x0A");
|
inline const char CRLF[] = {0x0D, 0x0A, '\0'};
|
||||||
|
|
||||||
struct Environment
|
struct Environment
|
||||||
{
|
{
|
||||||
@@ -113,7 +112,7 @@ namespace Http
|
|||||||
HeaderMap headers;
|
HeaderMap headers;
|
||||||
QHash<QString, QByteArray> query;
|
QHash<QString, QByteArray> query;
|
||||||
QHash<QString, QString> posts;
|
QHash<QString, QString> posts;
|
||||||
QList<UploadedFile> files;
|
QVector<UploadedFile> files;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResponseStatus
|
struct ResponseStatus
|
||||||
|
|||||||
@@ -31,14 +31,14 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QList>
|
#include <QVector>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QList<T> loadFromBuffer(const boost::circular_buffer_space_optimized<T> &src, const int offset = 0)
|
QVector<T> loadFromBuffer(const boost::circular_buffer_space_optimized<T> &src, const int offset = 0)
|
||||||
{
|
{
|
||||||
QList<T> ret;
|
QVector<T> ret;
|
||||||
ret.reserve(static_cast<typename decltype(ret)::size_type>(src.size()) - offset);
|
ret.reserve(static_cast<typename decltype(ret)::size_type>(src.size()) - offset);
|
||||||
std::copy((src.begin() + offset), src.end(), std::back_inserter(ret));
|
std::copy((src.begin() + offset), src.end(), std::back_inserter(ret));
|
||||||
return ret;
|
return ret;
|
||||||
@@ -90,7 +90,7 @@ void Logger::addPeer(const QString &ip, const bool blocked, const QString &reaso
|
|||||||
emit newLogPeer(msg);
|
emit newLogPeer(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Log::Msg> Logger::getMessages(const int lastKnownId) const
|
QVector<Log::Msg> Logger::getMessages(const int lastKnownId) const
|
||||||
{
|
{
|
||||||
const QReadLocker locker(&m_lock);
|
const QReadLocker locker(&m_lock);
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ QList<Log::Msg> Logger::getMessages(const int lastKnownId) const
|
|||||||
return loadFromBuffer(m_messages, (size - diff));
|
return loadFromBuffer(m_messages, (size - diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Log::Peer> Logger::getPeers(const int lastKnownId) const
|
QVector<Log::Peer> Logger::getPeers(const int lastKnownId) const
|
||||||
{
|
{
|
||||||
const QReadLocker locker(&m_lock);
|
const QReadLocker locker(&m_lock);
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ public:
|
|||||||
|
|
||||||
void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL);
|
void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL);
|
||||||
void addPeer(const QString &ip, bool blocked, const QString &reason = {});
|
void addPeer(const QString &ip, bool blocked, const QString &reason = {});
|
||||||
QList<Log::Msg> getMessages(int lastKnownId = -1) const;
|
QVector<Log::Msg> getMessages(int lastKnownId = -1) const;
|
||||||
QList<Log::Peer> getPeers(int lastKnownId = -1) const;
|
QVector<Log::Peer> getPeers(int lastKnownId = -1) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newLogMessage(const Log::Msg &message);
|
void newLogMessage(const Log::Msg &message);
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ QString DNSUpdater::getUpdateUrl() const
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qWarning() << "Unrecognized Dynamic DNS service!";
|
qWarning() << "Unrecognized Dynamic DNS service!";
|
||||||
Q_UNREACHABLE();
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
url.setPath(u"/nic/update"_s);
|
url.setPath(u"/nic/update"_s);
|
||||||
@@ -305,7 +305,7 @@ QUrl DNSUpdater::getRegistrationUrl(const DNS::Service service)
|
|||||||
case DNS::Service::NoIP:
|
case DNS::Service::NoIP:
|
||||||
return {u"https://www.noip.com/remote-access"_s};
|
return {u"https://www.noip.com/remote-access"_s};
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -320,11 +320,14 @@ void Net::DownloadManager::processRequest(DownloadHandlerImpl *downloadHandler)
|
|||||||
|
|
||||||
const DownloadRequest downloadRequest = downloadHandler->downloadRequest();
|
const DownloadRequest downloadRequest = downloadHandler->downloadRequest();
|
||||||
QNetworkRequest request {downloadRequest.url()};
|
QNetworkRequest request {downloadRequest.url()};
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, (downloadRequest.userAgent().isEmpty()
|
|
||||||
? getBrowserUserAgent() : downloadRequest.userAgent().toUtf8()));
|
if (downloadRequest.userAgent().isEmpty())
|
||||||
|
request.setRawHeader("User-Agent", getBrowserUserAgent());
|
||||||
|
else
|
||||||
|
request.setRawHeader("User-Agent", downloadRequest.userAgent().toUtf8());
|
||||||
|
|
||||||
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
||||||
request.setRawHeader("Referer", request.url().toEncoded());
|
request.setRawHeader("Referer", request.url().toEncoded().data());
|
||||||
#ifdef QT_NO_COMPRESS
|
#ifdef QT_NO_COMPRESS
|
||||||
// The macro "QT_NO_COMPRESS" defined in QT will disable the zlib related features
|
// The macro "QT_NO_COMPRESS" defined in QT will disable the zlib related features
|
||||||
// and reply data auto-decompression in QT will also be disabled. But we can support
|
// and reply data auto-decompression in QT will also be disabled. But we can support
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
#include "geoipdatabase.h"
|
#include "geoipdatabase.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -42,7 +41,7 @@ namespace
|
|||||||
{
|
{
|
||||||
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
|
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
|
||||||
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
||||||
const QByteArray METADATA_BEGIN_MARK = QByteArrayLiteral("\xab\xcd\xefMaxMind.com");
|
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
|
||||||
const char DATA_SECTION_SEPARATOR[16] = {0};
|
const char DATA_SECTION_SEPARATOR[16] = {0};
|
||||||
|
|
||||||
enum class DataType
|
enum class DataType
|
||||||
@@ -310,7 +309,7 @@ QVariantHash GeoIPDatabase::readMetadata() const
|
|||||||
{
|
{
|
||||||
if (m_size > MAX_METADATA_SIZE)
|
if (m_size > MAX_METADATA_SIZE)
|
||||||
index += (m_size - MAX_METADATA_SIZE); // from begin of all data
|
index += (m_size - MAX_METADATA_SIZE); // from begin of all data
|
||||||
auto offset = static_cast<quint32>(index + METADATA_BEGIN_MARK.size());
|
auto offset = static_cast<quint32>(index + strlen(METADATA_BEGIN_MARK));
|
||||||
const QVariant metadata = readDataField(offset);
|
const QVariant metadata = readDataField(offset);
|
||||||
if (metadata.userType() == QMetaType::QVariantHash)
|
if (metadata.userType() == QMetaType::QVariantHash)
|
||||||
return metadata.toHash();
|
return metadata.toHash();
|
||||||
|
|||||||
@@ -456,15 +456,15 @@ void GeoIPManager::downloadFinished(const DownloadResult &result)
|
|||||||
Utils::Fs::mkpath(targetPath);
|
Utils::Fs::mkpath(targetPath);
|
||||||
|
|
||||||
const auto path = targetPath / Path(GEODB_FILENAME);
|
const auto path = targetPath / Path(GEODB_FILENAME);
|
||||||
const nonstd::expected<void, QString> saveResult = Utils::IO::saveToFile(path, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||||
if (saveResult)
|
if (result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Successfully updated IP geolocation database."), Log::INFO);
|
LogMsg(tr("Successfully updated IP geolocation database."), Log::INFO);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogMsg(tr("Couldn't save downloaded IP geolocation database file. Reason: %1")
|
LogMsg(tr("Couldn't save downloaded IP geolocation database file. Reason: %1")
|
||||||
.arg(saveResult.error()), Log::WARNING);
|
.arg(result.error()), Log::WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -565,11 +565,29 @@ void Smtp::logError(const QString &msg)
|
|||||||
|
|
||||||
QString Smtp::getCurrentDateTime() const
|
QString Smtp::getCurrentDateTime() const
|
||||||
{
|
{
|
||||||
// [rfc2822] 3.3. Date and Time Specification
|
// return date & time in the format specified in RFC 2822, section 3.3
|
||||||
const auto now = QDateTime::currentDateTime();
|
const QDateTime nowDateTime = QDateTime::currentDateTime();
|
||||||
const QLocale eng {QLocale::English};
|
const QDate nowDate = nowDateTime.date();
|
||||||
const QString weekday = eng.dayName(now.date().dayOfWeek(), QLocale::ShortFormat);
|
const QLocale eng(QLocale::English);
|
||||||
return (weekday + u", " + now.toString(Qt::RFC2822Date));
|
|
||||||
|
const QString timeStr = nowDateTime.time().toString(u"HH:mm:ss");
|
||||||
|
const QString weekDayStr = eng.dayName(nowDate.dayOfWeek(), QLocale::ShortFormat);
|
||||||
|
const QString dayStr = QString::number(nowDate.day());
|
||||||
|
const QString monthStr = eng.monthName(nowDate.month(), QLocale::ShortFormat);
|
||||||
|
const QString yearStr = QString::number(nowDate.year());
|
||||||
|
|
||||||
|
QDateTime tmp = nowDateTime;
|
||||||
|
tmp.setTimeSpec(Qt::UTC);
|
||||||
|
const int timeOffsetHour = nowDateTime.secsTo(tmp) / 3600;
|
||||||
|
const int timeOffsetMin = nowDateTime.secsTo(tmp) / 60 - (60 * timeOffsetHour);
|
||||||
|
const int timeOffset = timeOffsetHour * 100 + timeOffsetMin;
|
||||||
|
// buf size = 11 to avoid format truncation warnings from snprintf
|
||||||
|
char buf[11] = {0};
|
||||||
|
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
|
||||||
|
const auto timeOffsetStr = QString::fromUtf8(buf);
|
||||||
|
|
||||||
|
const QString ret = weekDayStr + u", " + dayStr + u' ' + monthStr + u' ' + yearStr + u' ' + timeStr + u' ' + timeOffsetStr;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smtp::error(QAbstractSocket::SocketError socketError)
|
void Smtp::error(QAbstractSocket::SocketError socketError)
|
||||||
|
|||||||
@@ -359,19 +359,6 @@ void Preferences::setStatusbarDisplayed(const bool displayed)
|
|||||||
setValue(u"Preferences/General/StatusbarDisplayed"_s, displayed);
|
setValue(u"Preferences/General/StatusbarDisplayed"_s, displayed);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isStatusbarExternalIPDisplayed() const
|
|
||||||
{
|
|
||||||
return value(u"Preferences/General/StatusbarExternalIPDisplayed"_s, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Preferences::setStatusbarExternalIPDisplayed(const bool displayed)
|
|
||||||
{
|
|
||||||
if (displayed == isStatusbarExternalIPDisplayed())
|
|
||||||
return;
|
|
||||||
|
|
||||||
setValue(u"Preferences/General/StatusbarExternalIPDisplayed"_s, displayed);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Preferences::isSplashScreenDisabled() const
|
bool Preferences::isSplashScreenDisabled() const
|
||||||
{
|
{
|
||||||
return value(u"Preferences/General/NoSplashScreen"_s, true);
|
return value(u"Preferences/General/NoSplashScreen"_s, true);
|
||||||
@@ -655,47 +642,6 @@ void Preferences::setSearchEnabled(const bool enabled)
|
|||||||
setValue(u"Preferences/Search/SearchEnabled"_s, enabled);
|
setValue(u"Preferences/Search/SearchEnabled"_s, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Preferences::searchHistoryLength() const
|
|
||||||
{
|
|
||||||
const int val = value(u"Search/HistoryLength"_s, 50);
|
|
||||||
return std::clamp(val, 0, 99);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Preferences::setSearchHistoryLength(const int length)
|
|
||||||
{
|
|
||||||
const int clampedLength = std::clamp(length, 0, 99);
|
|
||||||
if (clampedLength == searchHistoryLength())
|
|
||||||
return;
|
|
||||||
|
|
||||||
setValue(u"Search/HistoryLength"_s, clampedLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Preferences::storeOpenedSearchTabs() const
|
|
||||||
{
|
|
||||||
return value(u"Search/StoreOpenedSearchTabs"_s, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Preferences::setStoreOpenedSearchTabs(const bool enabled)
|
|
||||||
{
|
|
||||||
if (enabled == storeOpenedSearchTabs())
|
|
||||||
return;
|
|
||||||
|
|
||||||
setValue(u"Search/StoreOpenedSearchTabs"_s, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Preferences::storeOpenedSearchTabResults() const
|
|
||||||
{
|
|
||||||
return value(u"Search/StoreOpenedSearchTabResults"_s, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Preferences::setStoreOpenedSearchTabResults(const bool enabled)
|
|
||||||
{
|
|
||||||
if (enabled == storeOpenedSearchTabResults())
|
|
||||||
return;
|
|
||||||
|
|
||||||
setValue(u"Search/StoreOpenedSearchTabResults"_s, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Preferences::isWebUIEnabled() const
|
bool Preferences::isWebUIEnabled() const
|
||||||
{
|
{
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
@@ -740,11 +686,11 @@ void Preferences::setWebUIAuthSubnetWhitelistEnabled(const bool enabled)
|
|||||||
setValue(u"Preferences/WebUI/AuthSubnetWhitelistEnabled"_s, enabled);
|
setValue(u"Preferences/WebUI/AuthSubnetWhitelistEnabled"_s, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Utils::Net::Subnet> Preferences::getWebUIAuthSubnetWhitelist() const
|
QVector<Utils::Net::Subnet> Preferences::getWebUIAuthSubnetWhitelist() const
|
||||||
{
|
{
|
||||||
const auto subnets = value<QStringList>(u"Preferences/WebUI/AuthSubnetWhitelist"_s);
|
const auto subnets = value<QStringList>(u"Preferences/WebUI/AuthSubnetWhitelist"_s);
|
||||||
|
|
||||||
QList<Utils::Net::Subnet> ret;
|
QVector<Utils::Net::Subnet> ret;
|
||||||
ret.reserve(subnets.size());
|
ret.reserve(subnets.size());
|
||||||
|
|
||||||
for (const QString &rawSubnet : subnets)
|
for (const QString &rawSubnet : subnets)
|
||||||
|
|||||||
@@ -119,8 +119,6 @@ public:
|
|||||||
void setHideZeroComboValues(int n);
|
void setHideZeroComboValues(int n);
|
||||||
bool isStatusbarDisplayed() const;
|
bool isStatusbarDisplayed() const;
|
||||||
void setStatusbarDisplayed(bool displayed);
|
void setStatusbarDisplayed(bool displayed);
|
||||||
bool isStatusbarExternalIPDisplayed() const;
|
|
||||||
void setStatusbarExternalIPDisplayed(bool displayed);
|
|
||||||
bool isToolbarDisplayed() const;
|
bool isToolbarDisplayed() const;
|
||||||
void setToolbarDisplayed(bool displayed);
|
void setToolbarDisplayed(bool displayed);
|
||||||
bool isSplashScreenDisabled() const;
|
bool isSplashScreenDisabled() const;
|
||||||
@@ -172,14 +170,6 @@ public:
|
|||||||
bool isSearchEnabled() const;
|
bool isSearchEnabled() const;
|
||||||
void setSearchEnabled(bool enabled);
|
void setSearchEnabled(bool enabled);
|
||||||
|
|
||||||
// Search UI
|
|
||||||
int searchHistoryLength() const;
|
|
||||||
void setSearchHistoryLength(int length);
|
|
||||||
bool storeOpenedSearchTabs() const;
|
|
||||||
void setStoreOpenedSearchTabs(bool enabled);
|
|
||||||
bool storeOpenedSearchTabResults() const;
|
|
||||||
void setStoreOpenedSearchTabResults(bool enabled);
|
|
||||||
|
|
||||||
// HTTP Server
|
// HTTP Server
|
||||||
bool isWebUIEnabled() const;
|
bool isWebUIEnabled() const;
|
||||||
void setWebUIEnabled(bool enabled);
|
void setWebUIEnabled(bool enabled);
|
||||||
@@ -197,7 +187,7 @@ public:
|
|||||||
void setWebUILocalAuthEnabled(bool enabled);
|
void setWebUILocalAuthEnabled(bool enabled);
|
||||||
bool isWebUIAuthSubnetWhitelistEnabled() const;
|
bool isWebUIAuthSubnetWhitelistEnabled() const;
|
||||||
void setWebUIAuthSubnetWhitelistEnabled(bool enabled);
|
void setWebUIAuthSubnetWhitelistEnabled(bool enabled);
|
||||||
QList<Utils::Net::Subnet> getWebUIAuthSubnetWhitelist() const;
|
QVector<Utils::Net::Subnet> getWebUIAuthSubnetWhitelist() const;
|
||||||
void setWebUIAuthSubnetWhitelist(QStringList subnets);
|
void setWebUIAuthSubnetWhitelist(QStringList subnets);
|
||||||
QString getWebUIUsername() const;
|
QString getWebUIUsername() const;
|
||||||
void setWebUIUsername(const QString &username);
|
void setWebUIUsername(const QString &username);
|
||||||
|
|||||||
@@ -34,14 +34,14 @@
|
|||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QList>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
#include "rss_article.h"
|
#include "rss_article.h"
|
||||||
|
|
||||||
const int ARTICLEDATALIST_TYPEID = qRegisterMetaType<QList<QVariantHash>>();
|
const int ARTICLEDATALIST_TYPEID = qRegisterMetaType<QVector<QVariantHash>>();
|
||||||
|
|
||||||
void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
|
void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString
|
|||||||
emit loadingFinished(loadArticles(readResult.value(), url));
|
emit loadingFinished(loadArticles(readResult.value(), url));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QList<QVariantHash> &articlesData)
|
void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QVector<QVariantHash> &articlesData)
|
||||||
{
|
{
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
for (const QVariantHash &data : articlesData)
|
for (const QVariantHash &data : articlesData)
|
||||||
@@ -73,7 +73,7 @@ void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QList<Q
|
|||||||
arr << jsonObj;
|
arr << jsonObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(dataFileName, QJsonDocument(arr).toJson(QJsonDocument::Compact));
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(dataFileName, QJsonDocument(arr).toJson());
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Failed to save RSS feed in '%1', Reason: %2").arg(dataFileName.toString(), result.error())
|
LogMsg(tr("Failed to save RSS feed in '%1', Reason: %2").arg(dataFileName.toString(), result.error())
|
||||||
@@ -81,7 +81,7 @@ void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QList<Q
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QVariantHash> RSS::Private::FeedSerializer::loadArticles(const QByteArray &data, const QString &url)
|
QVector<QVariantHash> RSS::Private::FeedSerializer::loadArticles(const QByteArray &data, const QString &url)
|
||||||
{
|
{
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
const QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
@@ -98,7 +98,7 @@ QList<QVariantHash> RSS::Private::FeedSerializer::loadArticles(const QByteArray
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QVariantHash> result;
|
QVector<QVariantHash> result;
|
||||||
const QJsonArray jsonArr = jsonDoc.array();
|
const QJsonArray jsonArr = jsonDoc.array();
|
||||||
result.reserve(jsonArr.size());
|
result.reserve(jsonArr.size());
|
||||||
for (int i = 0; i < jsonArr.size(); ++i)
|
for (int i = 0; i < jsonArr.size(); ++i)
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ namespace RSS::Private
|
|||||||
using QObject::QObject;
|
using QObject::QObject;
|
||||||
|
|
||||||
void load(const Path &dataFileName, const QString &url);
|
void load(const Path &dataFileName, const QString &url);
|
||||||
void store(const Path &dataFileName, const QList<QVariantHash> &articlesData);
|
void store(const Path &dataFileName, const QVector<QVariantHash> &articlesData);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loadingFinished(const QList<QVariantHash> &articles);
|
void loadingFinished(const QVector<QVariantHash> &articles);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QVariantHash> loadArticles(const QByteArray &data, const QString &url);
|
QVector<QVariantHash> loadArticles(const QByteArray &data, const QString &url);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,16 +48,16 @@ const QString Article::KeyIsRead = u"isRead"_s;
|
|||||||
|
|
||||||
Article::Article(Feed *feed, const QVariantHash &varHash)
|
Article::Article(Feed *feed, const QVariantHash &varHash)
|
||||||
: QObject(feed)
|
: QObject(feed)
|
||||||
, m_feed {feed}
|
, m_feed(feed)
|
||||||
, m_guid {varHash.value(KeyId).toString()}
|
, m_guid(varHash.value(KeyId).toString())
|
||||||
, m_date {varHash.value(KeyDate).toDateTime()}
|
, m_date(varHash.value(KeyDate).toDateTime())
|
||||||
, m_title {varHash.value(KeyTitle).toString()}
|
, m_title(varHash.value(KeyTitle).toString())
|
||||||
, m_author {varHash.value(KeyAuthor).toString()}
|
, m_author(varHash.value(KeyAuthor).toString())
|
||||||
, m_description {varHash.value(KeyDescription).toString()}
|
, m_description(varHash.value(KeyDescription).toString())
|
||||||
, m_torrentURL {varHash.value(KeyTorrentURL).toString()}
|
, m_torrentURL(varHash.value(KeyTorrentURL).toString())
|
||||||
, m_link {varHash.value(KeyLink).toString()}
|
, m_link(varHash.value(KeyLink).toString())
|
||||||
, m_isRead {varHash.value(KeyIsRead, false).toBool()}
|
, m_isRead(varHash.value(KeyIsRead, false).toBool())
|
||||||
, m_data {varHash}
|
, m_data(varHash)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,11 +35,11 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
#include <QList>
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/addtorrentmanager.h"
|
#include "base/addtorrentmanager.h"
|
||||||
#include "base/asyncfilestorage.h"
|
#include "base/asyncfilestorage.h"
|
||||||
@@ -68,7 +68,7 @@ const QString RULES_FILE_NAME = u"download_rules.json"_s;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
QList<RSS::AutoDownloadRule> rulesFromJSON(const QByteArray &jsonData)
|
QVector<RSS::AutoDownloadRule> rulesFromJSON(const QByteArray &jsonData)
|
||||||
{
|
{
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &jsonError);
|
const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &jsonError);
|
||||||
@@ -79,7 +79,7 @@ namespace
|
|||||||
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
|
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
|
||||||
|
|
||||||
const QJsonObject jsonObj {jsonDoc.object()};
|
const QJsonObject jsonObj {jsonDoc.object()};
|
||||||
QList<RSS::AutoDownloadRule> rules;
|
QVector<RSS::AutoDownloadRule> rules;
|
||||||
for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it)
|
for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it)
|
||||||
{
|
{
|
||||||
const QJsonValue jsonVal {it.value()};
|
const QJsonValue jsonVal {it.value()};
|
||||||
@@ -123,7 +123,6 @@ AutoDownloader::AutoDownloader(IApplication *app)
|
|||||||
.arg(fileName.toString(), errorString), Log::CRITICAL);
|
.arg(fileName.toString(), errorString), Log::CRITICAL);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_ioThread->setObjectName("RSS::AutoDownloader m_ioThread");
|
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
|
|
||||||
connect(app->addTorrentManager(), &AddTorrentManager::torrentAdded
|
connect(app->addTorrentManager(), &AddTorrentManager::torrentAdded
|
||||||
@@ -375,24 +374,10 @@ void AutoDownloader::handleTorrentAdded(const QString &source)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoDownloader::handleAddTorrentFailed(const QString &source, const BitTorrent::AddTorrentError &error)
|
void AutoDownloader::handleAddTorrentFailed(const QString &source)
|
||||||
{
|
{
|
||||||
const auto job = m_waitingJobs.take(source);
|
m_waitingJobs.remove(source);
|
||||||
if (!job)
|
// TODO: Re-schedule job here.
|
||||||
return;
|
|
||||||
|
|
||||||
if (error.kind == BitTorrent::AddTorrentError::DuplicateTorrent)
|
|
||||||
{
|
|
||||||
if (Feed *feed = Session::instance()->feedByURL(job->feedURL))
|
|
||||||
{
|
|
||||||
if (Article *article = feed->articleByGUID(job->articleData.value(Article::KeyId).toString()))
|
|
||||||
article->markAsRead();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: Re-schedule job here.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoDownloader::handleNewArticle(const Article *article)
|
void AutoDownloader::handleNewArticle(const Article *article)
|
||||||
|
|||||||
@@ -37,11 +37,11 @@
|
|||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "base/applicationcomponent.h"
|
#include "base/applicationcomponent.h"
|
||||||
#include "base/bittorrent/addtorrenterror.h"
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
#include "base/utils/thread.h"
|
#include "base/utils/thread.h"
|
||||||
|
|
||||||
|
class QThread;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
@@ -112,7 +112,7 @@ namespace RSS
|
|||||||
private slots:
|
private slots:
|
||||||
void process();
|
void process();
|
||||||
void handleTorrentAdded(const QString &source);
|
void handleTorrentAdded(const QString &source);
|
||||||
void handleAddTorrentFailed(const QString &url, const BitTorrent::AddTorrentError &error);
|
void handleAddTorrentFailed(const QString &url);
|
||||||
void handleNewArticle(const Article *article);
|
void handleNewArticle(const Article *article);
|
||||||
void handleFeedURLChanged(Feed *feed, const QString &oldURL);
|
void handleFeedURLChanged(Feed *feed, const QString &oldURL);
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
#include <QList>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/asyncfilestorage.h"
|
#include "base/asyncfilestorage.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
@@ -301,7 +301,7 @@ void Feed::store()
|
|||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
m_savingTimer.stop();
|
m_savingTimer.stop();
|
||||||
|
|
||||||
QList<QVariantHash> articlesData;
|
QVector<QVariantHash> articlesData;
|
||||||
articlesData.reserve(m_articles.size());
|
articlesData.reserve(m_articles.size());
|
||||||
|
|
||||||
for (Article *article :asConst(m_articles))
|
for (Article *article :asConst(m_articles))
|
||||||
@@ -395,7 +395,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
QDateTime dummyPubDate {QDateTime::currentDateTime()};
|
QDateTime dummyPubDate {QDateTime::currentDateTime()};
|
||||||
QList<QVariantHash> newArticles;
|
QVector<QVariantHash> newArticles;
|
||||||
newArticles.reserve(loadedArticles.size());
|
newArticles.reserve(loadedArticles.size());
|
||||||
for (QVariantHash article : loadedArticles)
|
for (QVariantHash article : loadedArticles)
|
||||||
{
|
{
|
||||||
@@ -516,7 +516,7 @@ void Feed::handleArticleRead(Article *article)
|
|||||||
storeDeferred();
|
storeDeferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::handleArticleLoadFinished(QList<QVariantHash> articles)
|
void Feed::handleArticleLoadFinished(QVector<QVariantHash> articles)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_articles.isEmpty());
|
Q_ASSERT(m_articles.isEmpty());
|
||||||
Q_ASSERT(m_unreadCount == 0);
|
Q_ASSERT(m_unreadCount == 0);
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ namespace RSS
|
|||||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
void handleDownloadFinished(const Net::DownloadResult &result);
|
||||||
void handleParsingFinished(const Private::ParsingResult &result);
|
void handleParsingFinished(const Private::ParsingResult &result);
|
||||||
void handleArticleRead(Article *article);
|
void handleArticleRead(Article *article);
|
||||||
void handleArticleLoadFinished(QList<QVariantHash> articles);
|
void handleArticleLoadFinished(QVector<QVariantHash> articles);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void timerEvent(QTimerEvent *event) override;
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTimeZone>
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QXmlStreamEntityResolver>
|
#include <QXmlStreamEntityResolver>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
@@ -519,7 +518,7 @@ namespace
|
|||||||
return fallbackDate;
|
return fallbackDate;
|
||||||
|
|
||||||
const QTime qTime(hour, minute, second);
|
const QTime qTime(hour, minute, second);
|
||||||
QDateTime result(qDate, qTime, QTimeZone::UTC);
|
QDateTime result(qDate, qTime, Qt::UTC);
|
||||||
if (offset)
|
if (offset)
|
||||||
result = result.addSecs(-offset);
|
result = result.addSecs(-offset);
|
||||||
if (!result.isValid())
|
if (!result.isValid())
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ Session::Session()
|
|||||||
|
|
||||||
m_itemsByPath.insert(u""_s, new Folder); // root folder
|
m_itemsByPath.insert(u""_s, new Folder); // root folder
|
||||||
|
|
||||||
m_workingThread->setObjectName("RSS::Session m_workingThread");
|
|
||||||
m_workingThread->start();
|
m_workingThread->start();
|
||||||
load();
|
load();
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,10 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
@@ -145,7 +145,7 @@ void SearchHandler::readSearchOutput()
|
|||||||
lines.prepend(m_searchResultLineTruncated + lines.takeFirst());
|
lines.prepend(m_searchResultLineTruncated + lines.takeFirst());
|
||||||
m_searchResultLineTruncated = lines.takeLast().trimmed();
|
m_searchResultLineTruncated = lines.takeLast().trimmed();
|
||||||
|
|
||||||
QList<SearchResult> searchResultList;
|
QVector<SearchResult> searchResultList;
|
||||||
searchResultList.reserve(lines.size());
|
searchResultList.reserve(lines.size());
|
||||||
|
|
||||||
for (const QByteArray &line : asConst(lines))
|
for (const QByteArray &line : asConst(lines))
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void searchFinished(bool cancelled = false);
|
void searchFinished(bool cancelled = false);
|
||||||
void searchFailed();
|
void searchFailed();
|
||||||
void newSearchResults(const QList<SearchResult> &results);
|
void newSearchResults(const QVector<SearchResult> &results);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readSearchOutput();
|
void readSearchOutput();
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace
|
|||||||
QPointer<SearchPluginManager> SearchPluginManager::m_instance = nullptr;
|
QPointer<SearchPluginManager> SearchPluginManager::m_instance = nullptr;
|
||||||
|
|
||||||
SearchPluginManager::SearchPluginManager()
|
SearchPluginManager::SearchPluginManager()
|
||||||
: m_updateUrl(u"https://raw.githubusercontent.com/qbittorrent/search-plugins/refs/heads/master/nova3/engines/"_s)
|
: m_updateUrl(u"https://searchplugins.qbittorrent.org/nova3/engines/"_s)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_instance); // only one instance is allowed
|
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
@@ -359,7 +359,7 @@ SearchHandler *SearchPluginManager::startSearch(const QString &pattern, const QS
|
|||||||
// No search pattern entered
|
// No search pattern entered
|
||||||
Q_ASSERT(!pattern.isEmpty());
|
Q_ASSERT(!pattern.isEmpty());
|
||||||
|
|
||||||
return new SearchHandler(pattern, category, usedPlugins, this);
|
return new SearchHandler {pattern, category, usedPlugins, this};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SearchPluginManager::categoryFullName(const QString &categoryName)
|
QString SearchPluginManager::categoryFullName(const QString &categoryName)
|
||||||
@@ -367,14 +367,14 @@ QString SearchPluginManager::categoryFullName(const QString &categoryName)
|
|||||||
const QHash<QString, QString> categoryTable
|
const QHash<QString, QString> categoryTable
|
||||||
{
|
{
|
||||||
{u"all"_s, tr("All categories")},
|
{u"all"_s, tr("All categories")},
|
||||||
{u"anime"_s, tr("Anime")},
|
|
||||||
{u"books"_s, tr("Books")},
|
|
||||||
{u"games"_s, tr("Games")},
|
|
||||||
{u"movies"_s, tr("Movies")},
|
{u"movies"_s, tr("Movies")},
|
||||||
|
{u"tv"_s, tr("TV shows")},
|
||||||
{u"music"_s, tr("Music")},
|
{u"music"_s, tr("Music")},
|
||||||
{u"pictures"_s, tr("Pictures")},
|
{u"games"_s, tr("Games")},
|
||||||
|
{u"anime"_s, tr("Anime")},
|
||||||
{u"software"_s, tr("Software")},
|
{u"software"_s, tr("Software")},
|
||||||
{u"tv"_s, tr("TV shows")}
|
{u"pictures"_s, tr("Pictures")},
|
||||||
|
{u"books"_s, tr("Books")}
|
||||||
};
|
};
|
||||||
return categoryTable.value(categoryName);
|
return categoryTable.value(categoryName);
|
||||||
}
|
}
|
||||||
@@ -487,14 +487,14 @@ void SearchPluginManager::updateNova()
|
|||||||
const Path enginePath = engineLocation();
|
const Path enginePath = engineLocation();
|
||||||
|
|
||||||
QFile packageFile {(enginePath / Path(u"__init__.py"_s)).data()};
|
QFile packageFile {(enginePath / Path(u"__init__.py"_s)).data()};
|
||||||
if (packageFile.open(QIODevice::WriteOnly))
|
packageFile.open(QIODevice::WriteOnly);
|
||||||
packageFile.close();
|
packageFile.close();
|
||||||
|
|
||||||
Utils::Fs::mkdir(enginePath / Path(u"engines"_s));
|
Utils::Fs::mkdir(enginePath / Path(u"engines"_s));
|
||||||
|
|
||||||
QFile packageFile2 {(enginePath / Path(u"engines/__init__.py"_s)).data()};
|
QFile packageFile2 {(enginePath / Path(u"engines/__init__.py"_s)).data()};
|
||||||
if (packageFile2.open(QIODevice::WriteOnly))
|
packageFile2.open(QIODevice::WriteOnly);
|
||||||
packageFile2.close();
|
packageFile2.close();
|
||||||
|
|
||||||
// Copy search plugin files (if necessary)
|
// Copy search plugin files (if necessary)
|
||||||
const auto updateFile = [&enginePath](const Path &filename, const bool compareVersion)
|
const auto updateFile = [&enginePath](const Path &filename, const bool compareVersion)
|
||||||
|
|||||||
@@ -146,7 +146,6 @@ TorrentFilesWatcher::TorrentFilesWatcher(QObject *parent)
|
|||||||
|
|
||||||
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("TorrentFilesWatcher m_ioThread");
|
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/utils/thread.h"
|
#include "base/utils/thread.h"
|
||||||
|
|
||||||
|
class QThread;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Watches the configured directories for new .torrent files in order
|
* Watches the configured directories for new .torrent files in order
|
||||||
* to add torrents to BitTorrent session. Supports Network File System
|
* to add torrents to BitTorrent session. Supports Network File System
|
||||||
|
|||||||
@@ -205,11 +205,9 @@ bool TorrentFilter::matchState(const BitTorrent::Torrent *const torrent) const
|
|||||||
case Errored:
|
case Errored:
|
||||||
return torrent->isErrored();
|
return torrent->isErrored();
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_ASSERT(false);
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentFilter::matchHash(const BitTorrent::Torrent *const torrent) const
|
bool TorrentFilter::matchHash(const BitTorrent::Torrent *const torrent) const
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ bool Utils::ForeignApps::PythonInfo::isValid() const
|
|||||||
|
|
||||||
bool Utils::ForeignApps::PythonInfo::isSupportedVersion() const
|
bool Utils::ForeignApps::PythonInfo::isSupportedVersion() const
|
||||||
{
|
{
|
||||||
return (version >= Version {3, 9, 0});
|
return (version >= Version {3, 7, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
PythonInfo Utils::ForeignApps::pythonInfo()
|
PythonInfo Utils::ForeignApps::pythonInfo()
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QStorageInfo>
|
#include <QStorageInfo>
|
||||||
|
|
||||||
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,7 +105,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const Path &path)
|
|||||||
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
|
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QStringList tmpFileList = dir.entryList(QDir::Files | QDir::Hidden);
|
const QStringList tmpFileList = dir.entryList(QDir::Files);
|
||||||
|
|
||||||
// deleteFilesList contains unwanted files, usually created by the OS
|
// deleteFilesList contains unwanted files, usually created by the OS
|
||||||
// temp files on linux usually end with '~', e.g. `filename~`
|
// temp files on linux usually end with '~', e.g. `filename~`
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
#include <QStringView>
|
#include <QStringView>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
|
|
||||||
#include "base/net/downloadmanager.h"
|
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
@@ -213,14 +212,6 @@ bool Utils::Misc::isPreviewable(const Path &filePath)
|
|||||||
return multimediaExtensions.contains(filePath.extension().toUpper());
|
return multimediaExtensions.contains(filePath.extension().toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Misc::isTorrentLink(const QString &str)
|
|
||||||
{
|
|
||||||
return str.startsWith(u"magnet:", Qt::CaseInsensitive)
|
|
||||||
|| str.endsWith(TORRENT_FILE_EXTENSION, Qt::CaseInsensitive)
|
|
||||||
|| (!str.startsWith(u"file:", Qt::CaseInsensitive)
|
|
||||||
&& Net::DownloadManager::hasSupportedScheme(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap, const TimeResolution resolution)
|
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap, const TimeResolution resolution)
|
||||||
{
|
{
|
||||||
if (seconds < 0)
|
if (seconds < 0)
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ namespace Utils::Misc
|
|||||||
qint64 sizeInBytes(qreal size, SizeUnit unit);
|
qint64 sizeInBytes(qreal size, SizeUnit unit);
|
||||||
|
|
||||||
bool isPreviewable(const Path &filePath);
|
bool isPreviewable(const Path &filePath);
|
||||||
bool isTorrentLink(const QString &str);
|
|
||||||
|
|
||||||
// Take a number of seconds and return a user-friendly
|
// Take a number of seconds and return a user-friendly
|
||||||
// time duration like "1d 2h 10m".
|
// time duration like "1d 2h 10m".
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
|
||||||
@@ -54,7 +55,14 @@ namespace Utils
|
|||||||
return subnet;
|
return subnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isIPInSubnets(const QHostAddress &addr, const QList<Subnet> &subnets)
|
bool isLoopbackAddress(const QHostAddress &addr)
|
||||||
|
{
|
||||||
|
return (addr == QHostAddress::LocalHost)
|
||||||
|
|| (addr == QHostAddress::LocalHostIPv6)
|
||||||
|
|| (addr == QHostAddress(u"::ffff:127.0.0.1"_s));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIPInSubnets(const QHostAddress &addr, const QVector<Subnet> &subnets)
|
||||||
{
|
{
|
||||||
QHostAddress protocolEquivalentAddress;
|
QHostAddress protocolEquivalentAddress;
|
||||||
bool addrConversionOk = false;
|
bool addrConversionOk = false;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user