mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-31 12:48:04 -06:00
Compare commits
24 Commits
73c15e1f00
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5b5ba2065 | ||
|
|
14d2db185a | ||
|
|
351b065553 | ||
|
|
52a6e7229b | ||
|
|
be12be2b79 | ||
|
|
0bba3f342e | ||
|
|
ae7cc7445b | ||
|
|
e79be5853e | ||
|
|
b718cc52fa | ||
|
|
57c99b54c8 | ||
|
|
25224f6050 | ||
|
|
b5d16dfeee | ||
|
|
1c231ce014 | ||
|
|
93470f2080 | ||
|
|
260563d340 | ||
|
|
b792ecede5 | ||
|
|
19ebf67c74 | ||
|
|
56cd98e06e | ||
|
|
c25bd6aaea | ||
|
|
26e42abf32 | ||
|
|
5abf458e69 | ||
|
|
7451552e6a | ||
|
|
96161627d6 | ||
|
|
0f80d3a74a |
5
.github/workflows/ci_file_health.yaml
vendored
5
.github/workflows/ci_file_health.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -31,12 +31,13 @@ jobs:
|
||||
- name: Check doc
|
||||
env:
|
||||
pandoc_path: "${{ github.workspace }}/../pandoc"
|
||||
pandoc_version: "3.8.3"
|
||||
run: |
|
||||
# install pandoc
|
||||
curl \
|
||||
-L \
|
||||
-o "${{ runner.temp }}/pandoc.tar.gz" \
|
||||
"https://github.com/jgm/pandoc/releases/download/3.7.0.2/pandoc-3.7.0.2-linux-amd64.tar.gz"
|
||||
"https://github.com/jgm/pandoc/releases/download/${{ env.pandoc_version }}/pandoc-${{ env.pandoc_version }}-linux-amd64.tar.gz"
|
||||
tar -xf "${{ runner.temp }}/pandoc.tar.gz" -C "${{ github.workspace }}/.."
|
||||
mv "${{ github.workspace }}/.."/pandoc-* "${{ env.pandoc_path }}"
|
||||
# run pandoc
|
||||
|
||||
27
.github/workflows/ci_macos.yaml
vendored
27
.github/workflows/ci_macos.yaml
vendored
@@ -18,9 +18,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
libt_version: ["2.0.11", "1.2.20"]
|
||||
libtorrent:
|
||||
- version: "2.0.11"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "90"
|
||||
boost_patch_version: "0"
|
||||
- version: "1.2.20"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "86"
|
||||
boost_patch_version: "0"
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
qt_version: ["6.9.1"]
|
||||
qt_version: ["6.10.1"]
|
||||
|
||||
env:
|
||||
boost_path: "${{ github.workspace }}/../boost"
|
||||
@@ -28,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -55,13 +63,9 @@ jobs:
|
||||
max_size=1G
|
||||
|
||||
- name: Install boost
|
||||
env:
|
||||
BOOST_MAJOR_VERSION: "1"
|
||||
BOOST_MINOR_VERSION: "86"
|
||||
BOOST_PATCH_VERSION: "0"
|
||||
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_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_url="https://archives.boost.io/release/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/source/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
set +e
|
||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
||||
@@ -79,13 +83,14 @@ jobs:
|
||||
with:
|
||||
version: ${{ matrix.qt_version }}
|
||||
archives: qtbase qtdeclarative qtsvg qttools
|
||||
modules: qtimageformats
|
||||
# Not sure why Qt made a hard dependency on qtdeclarative, try removing it when Qt > 6.4.0
|
||||
cache: true
|
||||
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone \
|
||||
--branch v${{ matrix.libt_version }} \
|
||||
--branch v${{ matrix.libtorrent.version }} \
|
||||
--depth 1 \
|
||||
--recurse-submodules \
|
||||
https://github.com/arvidn/libtorrent.git \
|
||||
@@ -163,5 +168,5 @@ jobs:
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libtorrent.version }}_Qt-${{ matrix.qt_version }}
|
||||
path: upload
|
||||
|
||||
2
.github/workflows/ci_python.yaml
vendored
2
.github/workflows/ci_python.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
29
.github/workflows/ci_ubuntu.yaml
vendored
29
.github/workflows/ci_ubuntu.yaml
vendored
@@ -19,7 +19,15 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
libt_version: ["2.0.11", "1.2.20"]
|
||||
libtorrent:
|
||||
- version: "2.0.11"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "77"
|
||||
boost_patch_version: "0"
|
||||
- version: "1.2.20"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "77"
|
||||
boost_patch_version: "0"
|
||||
qbt_gui: ["GUI=ON", "GUI=OFF"]
|
||||
qt_version: ["6.6.3"]
|
||||
|
||||
@@ -30,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -50,13 +58,9 @@ jobs:
|
||||
max_size=1G
|
||||
|
||||
- name: Install boost
|
||||
env:
|
||||
BOOST_MAJOR_VERSION: "1"
|
||||
BOOST_MINOR_VERSION: "77"
|
||||
BOOST_PATCH_VERSION: "0"
|
||||
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_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_url="https://archives.boost.io/release/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/source/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
set +e
|
||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
||||
@@ -74,12 +78,13 @@ jobs:
|
||||
with:
|
||||
version: ${{ matrix.qt_version }}
|
||||
archives: icu qtbase qtdeclarative qtsvg qttools
|
||||
modules: qtimageformats
|
||||
cache: true
|
||||
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone \
|
||||
--branch v${{ matrix.libt_version }} \
|
||||
--branch v${{ matrix.libtorrent.version }} \
|
||||
--depth 1 \
|
||||
--recurse-submodules \
|
||||
https://github.com/arvidn/libtorrent.git \
|
||||
@@ -101,7 +106,7 @@ jobs:
|
||||
# to avoid scanning 3rdparty codebases, initialize it just before building qbt
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
if: startsWith(matrix.libt_version, 2) && (matrix.qbt_gui == 'GUI=ON')
|
||||
if: startsWith(matrix.libtorrent.version, 2) && (matrix.qbt_gui == 'GUI=ON')
|
||||
with:
|
||||
config-file: ./.github/workflows/helper/codeql/cpp.yaml
|
||||
languages: cpp
|
||||
@@ -132,7 +137,7 @@ jobs:
|
||||
|
||||
- name: Run CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
if: startsWith(matrix.libt_version, 2) && (matrix.qbt_gui == 'GUI=ON')
|
||||
if: startsWith(matrix.libtorrent.version, 2) && (matrix.qbt_gui == 'GUI=ON')
|
||||
with:
|
||||
category: ${{ github.base_ref || github.ref_name }}
|
||||
|
||||
@@ -179,5 +184,5 @@ jobs:
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: qBittorrent-CI_Ubuntu-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
name: qBittorrent-CI_Ubuntu-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libtorrent.version }}_Qt-${{ matrix.qt_version }}
|
||||
path: upload
|
||||
|
||||
2
.github/workflows/ci_webui.yaml
vendored
2
.github/workflows/ci_webui.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
39
.github/workflows/ci_windows.yaml
vendored
39
.github/workflows/ci_windows.yaml
vendored
@@ -10,7 +10,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: Build (${{ matrix.libt_version }}, ${{ matrix.config.arch }})
|
||||
name: Build (${{ matrix.libtorrent.version }}, ${{ matrix.config.arch }})
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
permissions:
|
||||
actions: write
|
||||
@@ -18,7 +18,15 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
libt_version: ["2.0.11", "1.2.20"]
|
||||
libtorrent:
|
||||
- version: "2.0.11"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "90"
|
||||
boost_patch_version: "0"
|
||||
- version: "1.2.20"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "86"
|
||||
boost_patch_version: "0"
|
||||
config:
|
||||
- os: windows-latest
|
||||
arch: x64
|
||||
@@ -31,7 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -86,13 +94,9 @@ jobs:
|
||||
$packages
|
||||
|
||||
- name: Install boost
|
||||
env:
|
||||
BOOST_MAJOR_VERSION: "1"
|
||||
BOOST_MINOR_VERSION: "86"
|
||||
BOOST_PATCH_VERSION: "0"
|
||||
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_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_url="https://archives.boost.io/release/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/source/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
$boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."
|
||||
if ($LastExitCode -ne 0)
|
||||
@@ -112,15 +116,16 @@ jobs:
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: "6.9.1"
|
||||
version: "6.10.1"
|
||||
arch: ${{ matrix.config.qt_arch }}
|
||||
archives: qtbase qtsvg qttools
|
||||
modules: qtimageformats
|
||||
cache: true
|
||||
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone `
|
||||
--branch v${{ matrix.libt_version }} `
|
||||
--branch v${{ matrix.libtorrent.version }} `
|
||||
--depth 1 `
|
||||
--recurse-submodules `
|
||||
https://github.com/arvidn/libtorrent.git `
|
||||
@@ -182,8 +187,16 @@ jobs:
|
||||
mkdir upload/qBittorrent/plugins/iconengines
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/iconengines/qsvgicon.dll" upload/qBittorrent/plugins/iconengines
|
||||
mkdir upload/qBittorrent/plugins/imageformats
|
||||
# include all imageformats dlls since CI (dev) build links dynamically
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qgif.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qicns.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qico.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qjpeg.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qsvg.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qtga.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qtiff.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qwbmp.dll" upload/qBittorrent/plugins/imageformats
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/imageformats/qwebp.dll" upload/qBittorrent/plugins/imageformats
|
||||
mkdir upload/qBittorrent/plugins/platforms
|
||||
copy "${{ env.Qt_ROOT_DIR }}/plugins/platforms/qwindows.dll" upload/qBittorrent/plugins/platforms
|
||||
mkdir upload/qBittorrent/plugins/sqldrivers
|
||||
@@ -202,7 +215,7 @@ jobs:
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: qBittorrent-CI_Windows-${{ matrix.config.arch }}_libtorrent-${{ matrix.libt_version }}
|
||||
name: qBittorrent-CI_Windows-${{ matrix.config.arch }}_libtorrent-${{ matrix.libtorrent.version }}
|
||||
path: upload
|
||||
|
||||
- name: Install NSIS
|
||||
@@ -218,5 +231,5 @@ jobs:
|
||||
- name: Upload installer
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: qBittorrent-CI_Windows-${{ matrix.config.arch }}_libtorrent-${{ matrix.libt_version }}-setup
|
||||
name: qBittorrent-CI_Windows-${{ matrix.config.arch }}_libtorrent-${{ matrix.libtorrent.version }}-setup
|
||||
path: dist/windows/qbittorrent_*_setup.exe
|
||||
|
||||
20
.github/workflows/coverity-scan.yaml
vendored
20
.github/workflows/coverity-scan.yaml
vendored
@@ -14,9 +14,13 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
libt_version: ["2.0.11"]
|
||||
libtorrent:
|
||||
- version: "2.0.11"
|
||||
boost_major_version: "1"
|
||||
boost_minor_version: "90"
|
||||
boost_patch_version: "0"
|
||||
qbt_gui: ["GUI=ON"]
|
||||
qt_version: ["6.9.1"]
|
||||
qt_version: ["6.10.1"]
|
||||
|
||||
env:
|
||||
boost_path: "${{ github.workspace }}/../boost"
|
||||
@@ -25,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -37,13 +41,9 @@ jobs:
|
||||
libssl-dev libxkbcommon-x11-dev libxcb-cursor-dev zlib1g-dev
|
||||
|
||||
- name: Install boost
|
||||
env:
|
||||
BOOST_MAJOR_VERSION: "1"
|
||||
BOOST_MINOR_VERSION: "88"
|
||||
BOOST_PATCH_VERSION: "0"
|
||||
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_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_url="https://archives.boost.io/release/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/source/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ matrix.libtorrent.boost_major_version }}.${{ matrix.libtorrent.boost_minor_version }}.${{ matrix.libtorrent.boost_patch_version }}/boost_${{ matrix.libtorrent.boost_major_version }}_${{ matrix.libtorrent.boost_minor_version }}_${{ matrix.libtorrent.boost_patch_version }}.tar.gz"
|
||||
set +e
|
||||
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
|
||||
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."; _exitCode="$?"
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
- name: Install libtorrent
|
||||
run: |
|
||||
git clone \
|
||||
--branch v${{ matrix.libt_version }} \
|
||||
--branch v${{ matrix.libtorrent.version }} \
|
||||
--depth 1 \
|
||||
--recurse-submodules \
|
||||
https://github.com/arvidn/libtorrent.git \
|
||||
|
||||
@@ -33,9 +33,9 @@ LangString inst_requires_win10 ${LANG_RUSSIAN} "Для работы этого
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_RUSSIAN} "Удалить qBittorrent"
|
||||
;LangString inst_arch_mismatch_x64_on_arm64 ${LANG_ENGLISH} "Error: This x64 version of qBittorrent cannot run on ARM64 systems. Please download the ARM64 installer."
|
||||
LangString inst_arch_mismatch_x64_on_arm64 ${LANG_RUSSIAN} "This x64 version of qBittorrent cannot run on ARM64 systems. Please download the ARM64 installer."
|
||||
LangString inst_arch_mismatch_x64_on_arm64 ${LANG_RUSSIAN} "Эта версия qBittorrent для x64 не может работать на системах ARM64. Пожалуйста, скачайте установщик для ARM64."
|
||||
;LangString inst_arch_mismatch_arm64_on_x64 ${LANG_ENGLISH} "Error: This ARM64 version of qBittorrent cannot run on x64 systems. Please download the x64 installer."
|
||||
LangString inst_arch_mismatch_arm64_on_x64 ${LANG_RUSSIAN} "This ARM64 version of qBittorrent cannot run on x64 systems. Please download the x64 installer."
|
||||
LangString inst_arch_mismatch_arm64_on_x64 ${LANG_RUSSIAN} "Эта версия qBittorrent для ARM64 не может работать на системах x64. Пожалуйста, скачайте установщик для x64."
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
|
||||
@@ -21,7 +21,7 @@ LangString inst_firewallinfo ${LANG_TURKISH} "Windows Güvenlik Duvarı kuralı
|
||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_TURKISH} "qBittorrent çalışıyor. Lütfen yüklemeden önce uygulamayı kapatın."
|
||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_TURKISH} "Şu anki sürüm kaldırılacaktır. Kullanıcı ayarları ve torrent'ler bozulmadan kalacaktır."
|
||||
LangString inst_uninstall_question ${LANG_TURKISH} "Şu anki sürüm kaldırılacak. Kullanıcı ayarları ve torrent'ler bozulmadan kalacaktır."
|
||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_TURKISH} "Önceki sürüm kaldırılıyor."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
@@ -33,9 +33,9 @@ LangString inst_requires_win10 ${LANG_TURKISH} "Bu yükleyici en az Windows 10
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_TURKISH} "qBittorrent'i kaldır"
|
||||
;LangString inst_arch_mismatch_x64_on_arm64 ${LANG_ENGLISH} "Error: This x64 version of qBittorrent cannot run on ARM64 systems. Please download the ARM64 installer."
|
||||
LangString inst_arch_mismatch_x64_on_arm64 ${LANG_TURKISH} "This x64 version of qBittorrent cannot run on ARM64 systems. Please download the ARM64 installer."
|
||||
LangString inst_arch_mismatch_x64_on_arm64 ${LANG_TURKISH} "qBittorrent'in bu x64 sürümü ARM64 sistemlerde çalışamaz. Lütfen ARM64 yükleyicisini indirin."
|
||||
;LangString inst_arch_mismatch_arm64_on_x64 ${LANG_ENGLISH} "Error: This ARM64 version of qBittorrent cannot run on x64 systems. Please download the x64 installer."
|
||||
LangString inst_arch_mismatch_arm64_on_x64 ${LANG_TURKISH} "This ARM64 version of qBittorrent cannot run on x64 systems. Please download the x64 installer."
|
||||
LangString inst_arch_mismatch_arm64_on_x64 ${LANG_TURKISH} "qBittorrent'in bu ARM64 sürümü x64 sistemlerde çalışamaz. Lütfen x64 yükleyicisini indirin."
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "base/bittorrent/sharelimitaction.h"
|
||||
#include "base/bittorrent/sharelimits.h"
|
||||
#include "base/bittorrent/torrentcontentlayout.h"
|
||||
#include "base/global.h"
|
||||
#include "base/logger.h"
|
||||
|
||||
@@ -34,7 +34,7 @@ add_library(qbt_base STATIC
|
||||
bittorrent/session.h
|
||||
bittorrent/sessionimpl.h
|
||||
bittorrent/sessionstatus.h
|
||||
bittorrent/sharelimitaction.h
|
||||
bittorrent/sharelimits.h
|
||||
bittorrent/speedmonitor.h
|
||||
bittorrent/sslparameters.h
|
||||
bittorrent/torrent.h
|
||||
|
||||
@@ -125,9 +125,9 @@ BitTorrent::AddTorrentParams BitTorrent::parseAddTorrentParams(const QJsonObject
|
||||
.useAutoTMM = getOptionalBool(jsonObj, PARAM_AUTOTMM),
|
||||
.uploadLimit = jsonObj.value(PARAM_UPLOADLIMIT).toInt(-1),
|
||||
.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1),
|
||||
.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(Torrent::USE_GLOBAL_SEEDING_TIME),
|
||||
.inactiveSeedingTimeLimit = jsonObj.value(PARAM_INACTIVESEEDINGTIMELIMIT).toInt(Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME),
|
||||
.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(Torrent::USE_GLOBAL_RATIO),
|
||||
.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(DEFAULT_SEEDING_TIME_LIMIT),
|
||||
.inactiveSeedingTimeLimit = jsonObj.value(PARAM_INACTIVESEEDINGTIMELIMIT).toInt(DEFAULT_SEEDING_TIME_LIMIT),
|
||||
.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(DEFAULT_RATIO_LIMIT),
|
||||
.shareLimitAction = getEnum<ShareLimitAction>(jsonObj, PARAM_SHARELIMITACTION, ShareLimitAction::Default),
|
||||
.sslParameters =
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "sharelimitaction.h"
|
||||
#include "sharelimits.h"
|
||||
#include "sslparameters.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
@@ -68,9 +68,9 @@ namespace BitTorrent
|
||||
std::optional<bool> useAutoTMM;
|
||||
int uploadLimit = -1;
|
||||
int downloadLimit = -1;
|
||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
int inactiveSeedingTimeLimit = Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
int seedingTimeLimit = DEFAULT_SEEDING_TIME_LIMIT;
|
||||
int inactiveSeedingTimeLimit = DEFAULT_SEEDING_TIME_LIMIT;
|
||||
qreal ratioLimit = DEFAULT_RATIO_LIMIT;
|
||||
ShareLimitAction shareLimitAction = ShareLimitAction::Default;
|
||||
SSLParameters sslParameters;
|
||||
|
||||
|
||||
@@ -239,8 +239,8 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
||||
torrentParams.comment = fromLTString(resumeDataRoot.dict_find_string_value("qBt-comment"));
|
||||
torrentParams.hasFinishedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus");
|
||||
torrentParams.firstLastPiecePriority = resumeDataRoot.dict_find_int_value("qBt-firstLastPiecePriority");
|
||||
torrentParams.seedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
torrentParams.inactiveSeedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-inactiveSeedingTimeLimit", Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME);
|
||||
torrentParams.seedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-seedingTimeLimit", DEFAULT_SEEDING_TIME_LIMIT);
|
||||
torrentParams.inactiveSeedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-inactiveSeedingTimeLimit", DEFAULT_SEEDING_TIME_LIMIT);
|
||||
torrentParams.shareLimitAction = Utils::String::toEnum(
|
||||
fromLTString(resumeDataRoot.dict_find_string_value("qBt-shareLimitAction")), ShareLimitAction::Default);
|
||||
|
||||
@@ -283,7 +283,7 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
||||
|
||||
const lt::string_view ratioLimitString = resumeDataRoot.dict_find_string_value("qBt-ratioLimit");
|
||||
if (ratioLimitString.empty())
|
||||
torrentParams.ratioLimit = resumeDataRoot.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
|
||||
torrentParams.ratioLimit = resumeDataRoot.dict_find_int_value("qBt-ratioLimit", DEFAULT_RATIO_LIMIT * 1000) / 1000.0;
|
||||
else
|
||||
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2021-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -32,10 +32,16 @@
|
||||
#include <QJsonValue>
|
||||
|
||||
#include "base/global.h"
|
||||
#include "base/utils/string.h"
|
||||
|
||||
const QString OPTION_SAVEPATH = u"save_path"_s;
|
||||
const QString OPTION_DOWNLOADPATH = u"download_path"_s;
|
||||
|
||||
const QString OPTION_RATIOLIMIT = u"ratio_limit"_s;
|
||||
const QString OPTION_SEEDINGTIMELIMIT = u"seeding_time_limit"_s;
|
||||
const QString OPTION_INACTIVESEEDINGTIMELIMIT = u"inactive_seeding_time_limit"_s;
|
||||
const QString OPTION_SHARELIMITACTION = u"share_limit_action"_s;
|
||||
|
||||
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
||||
{
|
||||
CategoryOptions options;
|
||||
@@ -47,6 +53,11 @@ BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObj
|
||||
else if (downloadPathValue.isString())
|
||||
options.downloadPath = {true, Path(downloadPathValue.toString())};
|
||||
|
||||
options.ratioLimit = jsonObj.value(OPTION_RATIOLIMIT).toDouble(DEFAULT_RATIO_LIMIT);
|
||||
options.seedingTimeLimit = jsonObj.value(OPTION_SEEDINGTIMELIMIT).toInt(DEFAULT_SEEDING_TIME_LIMIT);
|
||||
options.inactiveSeedingTimeLimit = jsonObj.value(OPTION_INACTIVESEEDINGTIMELIMIT).toInt(DEFAULT_SEEDING_TIME_LIMIT);
|
||||
options.shareLimitAction = Utils::String::toEnum<ShareLimitAction>(jsonObj.value(OPTION_SHARELIMITACTION).toString(), ShareLimitAction::Default);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -63,12 +74,10 @@ QJsonObject BitTorrent::CategoryOptions::toJSON() const
|
||||
|
||||
return {
|
||||
{OPTION_SAVEPATH, savePath.data()},
|
||||
{OPTION_DOWNLOADPATH, downloadPathValue}
|
||||
{OPTION_DOWNLOADPATH, downloadPathValue},
|
||||
{OPTION_RATIOLIMIT, ratioLimit},
|
||||
{OPTION_SEEDINGTIMELIMIT, seedingTimeLimit},
|
||||
{OPTION_INACTIVESEEDINGTIMELIMIT, inactiveSeedingTimeLimit},
|
||||
{OPTION_SHARELIMITACTION, Utils::String::fromEnum(shareLimitAction)}
|
||||
};
|
||||
}
|
||||
|
||||
bool BitTorrent::operator==(const BitTorrent::CategoryOptions &left, const BitTorrent::CategoryOptions &right)
|
||||
{
|
||||
return ((left.savePath == right.savePath)
|
||||
&& (left.downloadPath == right.downloadPath));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2021-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "base/path.h"
|
||||
#include "downloadpathoption.h"
|
||||
#include "sharelimits.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
@@ -44,9 +45,14 @@ namespace BitTorrent
|
||||
Path savePath;
|
||||
std::optional<DownloadPathOption> downloadPath;
|
||||
|
||||
qreal ratioLimit = DEFAULT_RATIO_LIMIT;
|
||||
int seedingTimeLimit = DEFAULT_SEEDING_TIME_LIMIT;
|
||||
int inactiveSeedingTimeLimit = DEFAULT_SEEDING_TIME_LIMIT;
|
||||
ShareLimitAction shareLimitAction = ShareLimitAction::Default;
|
||||
|
||||
static CategoryOptions fromJSON(const QJsonObject &jsonObj);
|
||||
QJsonObject toJSON() const;
|
||||
};
|
||||
|
||||
bool operator==(const CategoryOptions &left, const CategoryOptions &right);
|
||||
friend bool operator==(const CategoryOptions &, const CategoryOptions &) = default;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "sharelimitaction.h"
|
||||
#include "sharelimits.h"
|
||||
#include "sslparameters.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
@@ -61,9 +61,9 @@ namespace BitTorrent
|
||||
|
||||
bool addToQueueTop = false; // only for new torrents
|
||||
|
||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
int inactiveSeedingTimeLimit = Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||
qreal ratioLimit = DEFAULT_RATIO_LIMIT;
|
||||
int seedingTimeLimit = DEFAULT_SEEDING_TIME_LIMIT;
|
||||
int inactiveSeedingTimeLimit = DEFAULT_SEEDING_TIME_LIMIT;
|
||||
ShareLimitAction shareLimitAction = ShareLimitAction::Default;
|
||||
|
||||
SSLParameters sslParameters;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "addtorrenterror.h"
|
||||
#include "addtorrentparams.h"
|
||||
#include "categoryoptions.h"
|
||||
#include "sharelimitaction.h"
|
||||
#include "sharelimits.h"
|
||||
#include "torrentcontentremoveoption.h"
|
||||
#include "trackerentry.h"
|
||||
#include "trackerentrystatus.h"
|
||||
@@ -158,15 +158,17 @@ namespace BitTorrent
|
||||
|
||||
virtual QStringList categories() const = 0;
|
||||
virtual CategoryOptions categoryOptions(const QString &categoryName) const = 0;
|
||||
virtual bool setCategoryOptions(const QString &categoryName, const CategoryOptions &options) = 0;
|
||||
virtual Path categorySavePath(const QString &categoryName) const = 0;
|
||||
virtual Path categorySavePath(const QString &categoryName, const CategoryOptions &options) const = 0;
|
||||
virtual Path categoryDownloadPath(const QString &categoryName) const = 0;
|
||||
virtual Path categoryDownloadPath(const QString &categoryName, const CategoryOptions &options) const = 0;
|
||||
virtual qreal categoryRatioLimit(const QString &categoryName) const = 0;
|
||||
virtual int categorySeedingTimeLimit(const QString &categoryName) const = 0;
|
||||
virtual int categoryInactiveSeedingTimeLimit(const QString &categoryName) const = 0;
|
||||
virtual ShareLimitAction categoryShareLimitAction(const QString &categoryName) const = 0;
|
||||
virtual bool addCategory(const QString &name, const CategoryOptions &options = {}) = 0;
|
||||
virtual bool editCategory(const QString &name, const CategoryOptions &options) = 0;
|
||||
virtual bool removeCategory(const QString &name) = 0;
|
||||
virtual bool isSubcategoriesEnabled() const = 0;
|
||||
virtual void setSubcategoriesEnabled(bool value) = 0;
|
||||
virtual bool useCategoryPathsInManualMode() const = 0;
|
||||
virtual void setUseCategoryPathsInManualMode(bool value) = 0;
|
||||
|
||||
@@ -385,8 +387,8 @@ namespace BitTorrent
|
||||
virtual void setOutgoingPortsMax(int max) = 0;
|
||||
virtual int UPnPLeaseDuration() const = 0;
|
||||
virtual void setUPnPLeaseDuration(int duration) = 0;
|
||||
virtual int peerToS() const = 0;
|
||||
virtual void setPeerToS(int value) = 0;
|
||||
virtual int peerDSCP() const = 0;
|
||||
virtual void setPeerDSCP(int value) = 0;
|
||||
virtual bool ignoreLimitsOnLAN() const = 0;
|
||||
virtual void setIgnoreLimitsOnLAN(bool ignore) = 0;
|
||||
virtual bool includeOverheadInLimits() const = 0;
|
||||
|
||||
@@ -484,7 +484,7 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
, m_outgoingPortsMin(BITTORRENT_SESSION_KEY(u"OutgoingPortsMin"_s), 0)
|
||||
, m_outgoingPortsMax(BITTORRENT_SESSION_KEY(u"OutgoingPortsMax"_s), 0)
|
||||
, m_UPnPLeaseDuration(BITTORRENT_SESSION_KEY(u"UPnPLeaseDuration"_s), 0)
|
||||
, m_peerToS(BITTORRENT_SESSION_KEY(u"PeerToS"_s), 0x04)
|
||||
, m_peerDSCP(BITTORRENT_SESSION_KEY(u"PeerToS"_s), 0x01)
|
||||
, m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY(u"IgnoreLimitsOnLAN"_s), false)
|
||||
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY(u"IncludeOverheadInLimits"_s), false)
|
||||
, m_announceIP(BITTORRENT_SESSION_KEY(u"AnnounceIP"_s))
|
||||
@@ -513,9 +513,9 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
, m_additionalTrackersURL(BITTORRENT_SESSION_KEY(u"AdditionalTrackersURL"_s))
|
||||
, m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_s), -1, [](qreal r) { return r < 0 ? -1. : r; })
|
||||
, m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_s)
|
||||
, Torrent::NO_SEEDING_TIME_LIMIT, lowerLimited(Torrent::NO_SEEDING_TIME_LIMIT))
|
||||
, NO_SEEDING_TIME_LIMIT, lowerLimited(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))
|
||||
, NO_SEEDING_TIME_LIMIT, lowerLimited(NO_SEEDING_TIME_LIMIT))
|
||||
, m_isAddTorrentToQueueTop(BITTORRENT_SESSION_KEY(u"AddTorrentToTopOfQueue"_s), false)
|
||||
, m_isAddTorrentStopped(BITTORRENT_SESSION_KEY(u"AddTorrentStopped"_s), false)
|
||||
, m_torrentStopCondition(BITTORRENT_SESSION_KEY(u"TorrentStopCondition"_s), Torrent::StopCondition::None)
|
||||
@@ -555,7 +555,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
, m_savePath(BITTORRENT_SESSION_KEY(u"DefaultSavePath"_s), specialFolderLocation(SpecialFolder::Downloads))
|
||||
, m_downloadPath(BITTORRENT_SESSION_KEY(u"TempPath"_s), (savePath() / Path(u"temp"_s)))
|
||||
, m_isDownloadPathEnabled(BITTORRENT_SESSION_KEY(u"TempPathEnabled"_s), false)
|
||||
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY(u"SubcategoriesEnabled"_s), false)
|
||||
, m_useCategoryPathsInManualMode(BITTORRENT_SESSION_KEY(u"UseCategoryPathsInManualMode"_s), false)
|
||||
, m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY(u"DisableAutoTMMByDefault"_s), true)
|
||||
, m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY(u"DisableAutoTMMTriggers/CategoryChanged"_s), false)
|
||||
@@ -626,11 +625,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
enableBandwidthScheduler();
|
||||
|
||||
loadCategories();
|
||||
if (isSubcategoriesEnabled())
|
||||
{
|
||||
// if subcategories support changed manually
|
||||
m_categories = expandCategories(m_categories);
|
||||
}
|
||||
|
||||
const QStringList storedTags = m_storedTags.get();
|
||||
for (const QString &tagStr : storedTags)
|
||||
@@ -938,15 +932,8 @@ Path SessionImpl::categorySavePath(const QString &categoryName, const CategoryOp
|
||||
if (path.isEmpty())
|
||||
{
|
||||
// use implicit save path
|
||||
if (isSubcategoriesEnabled())
|
||||
{
|
||||
path = Utils::Fs::toValidPath(subcategoryName(categoryName));
|
||||
basePath = categorySavePath(parentCategoryName(categoryName));
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Utils::Fs::toValidPath(categoryName);
|
||||
}
|
||||
path = Utils::Fs::toValidPath(subcategoryName(categoryName));
|
||||
basePath = categorySavePath(parentCategoryName(categoryName));
|
||||
}
|
||||
|
||||
return (path.isAbsolute() ? path : (basePath / path));
|
||||
@@ -966,8 +953,7 @@ Path SessionImpl::categoryDownloadPath(const QString &categoryName, const Catego
|
||||
if (categoryName.isEmpty())
|
||||
return downloadPath();
|
||||
|
||||
const bool useSubcategories = isSubcategoriesEnabled();
|
||||
const QString name = useSubcategories ? subcategoryName(categoryName) : categoryName;
|
||||
const QString name = subcategoryName(categoryName);
|
||||
const Path path = !downloadPathOption.path.isEmpty()
|
||||
? downloadPathOption.path
|
||||
: Utils::Fs::toValidPath(name); // use implicit download path
|
||||
@@ -975,7 +961,7 @@ Path SessionImpl::categoryDownloadPath(const QString &categoryName, const Catego
|
||||
if (path.isAbsolute())
|
||||
return path;
|
||||
|
||||
const QString parentName = useSubcategories ? parentCategoryName(categoryName) : QString();
|
||||
const QString parentName = parentCategoryName(categoryName);
|
||||
CategoryOptions parentOptions = categoryOptions(parentName);
|
||||
// Even if download path of parent category is disabled (directly or by inheritance)
|
||||
// we need to construct the one as if it would be enabled.
|
||||
@@ -986,6 +972,62 @@ Path SessionImpl::categoryDownloadPath(const QString &categoryName, const Catego
|
||||
return (basePath / path);
|
||||
}
|
||||
|
||||
qreal SessionImpl::categoryRatioLimit(const QString &categoryName) const
|
||||
{
|
||||
if (categoryName.isEmpty())
|
||||
return globalMaxRatio();
|
||||
|
||||
if (const auto ratioLimit = categoryOptions(categoryName).ratioLimit;
|
||||
ratioLimit != DEFAULT_RATIO_LIMIT)
|
||||
{
|
||||
return ratioLimit;
|
||||
}
|
||||
|
||||
return categoryRatioLimit(parentCategoryName(categoryName));
|
||||
}
|
||||
|
||||
int SessionImpl::categorySeedingTimeLimit(const QString &categoryName) const
|
||||
{
|
||||
if (categoryName.isEmpty())
|
||||
return globalMaxSeedingMinutes();
|
||||
|
||||
if (const auto seedingTimeLimit = categoryOptions(categoryName).seedingTimeLimit;
|
||||
seedingTimeLimit != DEFAULT_SEEDING_TIME_LIMIT)
|
||||
{
|
||||
return seedingTimeLimit;
|
||||
}
|
||||
|
||||
return categorySeedingTimeLimit(parentCategoryName(categoryName));
|
||||
}
|
||||
|
||||
int SessionImpl::categoryInactiveSeedingTimeLimit(const QString &categoryName) const
|
||||
{
|
||||
if (categoryName.isEmpty())
|
||||
return globalMaxInactiveSeedingMinutes();
|
||||
|
||||
if (const auto inactiveSeedingTimeLimit = categoryOptions(categoryName).inactiveSeedingTimeLimit;
|
||||
inactiveSeedingTimeLimit != DEFAULT_SEEDING_TIME_LIMIT)
|
||||
{
|
||||
return inactiveSeedingTimeLimit;
|
||||
}
|
||||
|
||||
return categoryInactiveSeedingTimeLimit(parentCategoryName(categoryName));
|
||||
}
|
||||
|
||||
ShareLimitAction SessionImpl::categoryShareLimitAction(const QString &categoryName) const
|
||||
{
|
||||
if (categoryName.isEmpty())
|
||||
return shareLimitAction();
|
||||
|
||||
if (const auto shareLimitAction = categoryOptions(categoryName).shareLimitAction;
|
||||
shareLimitAction != ShareLimitAction::Default)
|
||||
{
|
||||
return shareLimitAction;
|
||||
}
|
||||
|
||||
return categoryShareLimitAction(parentCategoryName(categoryName));
|
||||
}
|
||||
|
||||
DownloadPathOption SessionImpl::resolveCategoryDownloadPathOption(const QString &categoryName, const std::optional<DownloadPathOption> &option) const
|
||||
{
|
||||
if (categoryName.isEmpty())
|
||||
@@ -994,7 +1036,7 @@ DownloadPathOption SessionImpl::resolveCategoryDownloadPathOption(const QString
|
||||
if (option.has_value())
|
||||
return *option;
|
||||
|
||||
const QString parentName = isSubcategoriesEnabled() ? parentCategoryName(categoryName) : QString();
|
||||
const QString parentName = parentCategoryName(categoryName);
|
||||
return resolveCategoryDownloadPathOption(parentName, categoryOptions(parentName).downloadPath);
|
||||
}
|
||||
|
||||
@@ -1006,15 +1048,12 @@ bool SessionImpl::addCategory(const QString &name, const CategoryOptions &option
|
||||
if (!isValidCategoryName(name) || m_categories.contains(name))
|
||||
return false;
|
||||
|
||||
if (isSubcategoriesEnabled())
|
||||
for (const QString &parent : asConst(expandCategory(name)))
|
||||
{
|
||||
for (const QString &parent : asConst(expandCategory(name)))
|
||||
if ((parent != name) && !m_categories.contains(parent))
|
||||
{
|
||||
if ((parent != name) && !m_categories.contains(parent))
|
||||
{
|
||||
m_categories[parent] = {};
|
||||
emit categoryAdded(parent);
|
||||
}
|
||||
m_categories[parent] = {};
|
||||
emit categoryAdded(parent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,9 +1064,9 @@ bool SessionImpl::addCategory(const QString &name, const CategoryOptions &option
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionImpl::editCategory(const QString &name, const CategoryOptions &options)
|
||||
bool SessionImpl::setCategoryOptions(const QString &categoryName, const CategoryOptions &options)
|
||||
{
|
||||
const auto it = m_categories.find(name);
|
||||
const auto it = m_categories.find(categoryName);
|
||||
if (it == m_categories.end())
|
||||
return false;
|
||||
|
||||
@@ -1035,14 +1074,15 @@ bool SessionImpl::editCategory(const QString &name, const CategoryOptions &optio
|
||||
if (options == currentOptions)
|
||||
return false;
|
||||
|
||||
if (isDisableAutoTMMWhenCategorySavePathChanged())
|
||||
if (isDisableAutoTMMWhenCategorySavePathChanged()
|
||||
&& ((options.savePath != currentOptions.savePath) || (options.downloadPath != currentOptions.downloadPath)))
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
if (torrent->category() == name)
|
||||
if (torrent->category() == categoryName)
|
||||
torrent->setAutoTMMEnabled(false);
|
||||
}
|
||||
}
|
||||
@@ -1052,39 +1092,37 @@ bool SessionImpl::editCategory(const QString &name, const CategoryOptions &optio
|
||||
|
||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||
{
|
||||
if (torrent->category() == name)
|
||||
if (torrent->category() == categoryName)
|
||||
torrent->handleCategoryOptionsChanged();
|
||||
}
|
||||
|
||||
emit categoryOptionsChanged(name);
|
||||
emit categoryOptionsChanged(categoryName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionImpl::removeCategory(const QString &name)
|
||||
{
|
||||
const QString parentCategory = parentCategoryName(name);
|
||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||
{
|
||||
if (torrent->belongsToCategory(name))
|
||||
torrent->setCategory(u""_s);
|
||||
torrent->setCategory(parentCategory);
|
||||
}
|
||||
|
||||
// remove stored category and its subcategories if exist
|
||||
bool result = false;
|
||||
if (isSubcategoriesEnabled())
|
||||
// remove subcategories
|
||||
const QString test = name + u'/';
|
||||
Algorithm::removeIf(m_categories, [this, &test, &result](const QString &category, const CategoryOptions &)
|
||||
{
|
||||
// remove subcategories
|
||||
const QString test = name + u'/';
|
||||
Algorithm::removeIf(m_categories, [this, &test, &result](const QString &category, const CategoryOptions &)
|
||||
if (category.startsWith(test))
|
||||
{
|
||||
if (category.startsWith(test))
|
||||
{
|
||||
result = true;
|
||||
emit categoryRemoved(category);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
result = true;
|
||||
emit categoryRemoved(category);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
result = (m_categories.remove(name) > 0) || result;
|
||||
|
||||
@@ -1098,32 +1136,6 @@ bool SessionImpl::removeCategory(const QString &name)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SessionImpl::isSubcategoriesEnabled() const
|
||||
{
|
||||
return m_isSubcategoriesEnabled;
|
||||
}
|
||||
|
||||
void SessionImpl::setSubcategoriesEnabled(const bool value)
|
||||
{
|
||||
if (isSubcategoriesEnabled() == value) return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
// expand categories to include all parent categories
|
||||
m_categories = expandCategories(m_categories);
|
||||
// update stored categories
|
||||
storeCategories();
|
||||
}
|
||||
else
|
||||
{
|
||||
// reload categories
|
||||
loadCategories();
|
||||
}
|
||||
|
||||
m_isSubcategoriesEnabled = value;
|
||||
emit subcategoriesSupportChanged();
|
||||
}
|
||||
|
||||
bool SessionImpl::useCategoryPathsInManualMode() const
|
||||
{
|
||||
return m_useCategoryPathsInManualMode;
|
||||
@@ -1281,7 +1293,7 @@ qreal SessionImpl::globalMaxRatio() const
|
||||
void SessionImpl::setGlobalMaxRatio(qreal ratio)
|
||||
{
|
||||
if (ratio < 0)
|
||||
ratio = Torrent::NO_RATIO_LIMIT;
|
||||
ratio = NO_RATIO_LIMIT;
|
||||
|
||||
if (ratio != globalMaxRatio())
|
||||
{
|
||||
@@ -1297,7 +1309,7 @@ int SessionImpl::globalMaxSeedingMinutes() const
|
||||
|
||||
void SessionImpl::setGlobalMaxSeedingMinutes(int minutes)
|
||||
{
|
||||
minutes = std::max(minutes, Torrent::NO_SEEDING_TIME_LIMIT);
|
||||
minutes = std::max(minutes, NO_SEEDING_TIME_LIMIT);
|
||||
|
||||
if (minutes != globalMaxSeedingMinutes())
|
||||
{
|
||||
@@ -1313,7 +1325,7 @@ int SessionImpl::globalMaxInactiveSeedingMinutes() const
|
||||
|
||||
void SessionImpl::setGlobalMaxInactiveSeedingMinutes(int minutes)
|
||||
{
|
||||
minutes = std::max(minutes, Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT);
|
||||
minutes = std::max(minutes, NO_SEEDING_TIME_LIMIT);
|
||||
|
||||
if (minutes != globalMaxInactiveSeedingMinutes())
|
||||
{
|
||||
@@ -2057,7 +2069,7 @@ lt::settings_pack SessionImpl::loadLTSettings() const
|
||||
// UPnP lease duration
|
||||
settingsPack.set_int(lt::settings_pack::upnp_lease_duration, UPnPLeaseDuration());
|
||||
// Type of service
|
||||
settingsPack.set_int(lt::settings_pack::peer_tos, peerToS());
|
||||
settingsPack.set_int(lt::settings_pack::peer_dscp, peerDSCP());
|
||||
// Include overhead in transfer limits
|
||||
settingsPack.set_bool(lt::settings_pack::rate_limit_ip_overhead, includeOverheadInLimits());
|
||||
// IP address to announce to trackers
|
||||
@@ -2356,14 +2368,9 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
||||
if (!torrent->isFinished() || torrent->isForced())
|
||||
return;
|
||||
|
||||
const auto effectiveLimit = []<typename T>(const T limit, const T useGlobalLimit, const T globalLimit) -> T
|
||||
{
|
||||
return (limit == useGlobalLimit) ? globalLimit : limit;
|
||||
};
|
||||
|
||||
const qreal ratioLimit = effectiveLimit(torrent->ratioLimit(), Torrent::USE_GLOBAL_RATIO, globalMaxRatio());
|
||||
const int seedingTimeLimit = effectiveLimit(torrent->seedingTimeLimit(), Torrent::USE_GLOBAL_SEEDING_TIME, globalMaxSeedingMinutes());
|
||||
const int inactiveSeedingTimeLimit = effectiveLimit(torrent->inactiveSeedingTimeLimit(), Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME, globalMaxInactiveSeedingMinutes());
|
||||
const qreal ratioLimit = torrent->effectiveRatioLimit();
|
||||
const int seedingTimeLimit = torrent->effectiveSeedingTimeLimit();
|
||||
const int inactiveSeedingTimeLimit = torrent->effectiveInactiveSeedingTimeLimit();
|
||||
|
||||
bool reached = false;
|
||||
QString description;
|
||||
@@ -2390,7 +2397,7 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
||||
if (reached)
|
||||
{
|
||||
const QString torrentName = tr("Torrent: \"%1\".").arg(torrent->name());
|
||||
const ShareLimitAction shareLimitAction = (torrent->shareLimitAction() == ShareLimitAction::Default) ? m_shareLimitAction : torrent->shareLimitAction();
|
||||
const ShareLimitAction shareLimitAction = torrent->effectiveShareLimitAction();
|
||||
|
||||
if (shareLimitAction == ShareLimitAction::Remove)
|
||||
{
|
||||
@@ -4882,17 +4889,17 @@ void SessionImpl::setUPnPLeaseDuration(const int duration)
|
||||
}
|
||||
}
|
||||
|
||||
int SessionImpl::peerToS() const
|
||||
int SessionImpl::peerDSCP() const
|
||||
{
|
||||
return m_peerToS;
|
||||
return m_peerDSCP;
|
||||
}
|
||||
|
||||
void SessionImpl::setPeerToS(const int value)
|
||||
void SessionImpl::setPeerDSCP(const int value)
|
||||
{
|
||||
if (value == m_peerToS)
|
||||
if (value == m_peerDSCP)
|
||||
return;
|
||||
|
||||
m_peerToS = value;
|
||||
m_peerDSCP = value;
|
||||
configureDeferred();
|
||||
}
|
||||
|
||||
@@ -5218,9 +5225,9 @@ bool SessionImpl::isKnownTorrent(const InfoHash &infoHash) const
|
||||
|
||||
void SessionImpl::updateSeedingLimitTimer()
|
||||
{
|
||||
if ((globalMaxRatio() == Torrent::NO_RATIO_LIMIT) && !hasPerTorrentRatioLimit()
|
||||
&& (globalMaxSeedingMinutes() == Torrent::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit()
|
||||
&& (globalMaxInactiveSeedingMinutes() == Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT) && !hasPerTorrentInactiveSeedingTimeLimit())
|
||||
if ((globalMaxRatio() == NO_RATIO_LIMIT) && !hasPerTorrentRatioLimit()
|
||||
&& (globalMaxSeedingMinutes() == NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit()
|
||||
&& (globalMaxInactiveSeedingMinutes() == NO_SEEDING_TIME_LIMIT) && !hasPerTorrentInactiveSeedingTimeLimit())
|
||||
{
|
||||
if (m_seedingLimitTimer->isActive())
|
||||
m_seedingLimitTimer->stop();
|
||||
@@ -5584,6 +5591,8 @@ void SessionImpl::loadCategories()
|
||||
const auto categoryOptions = CategoryOptions::fromJSON(it.value().toObject());
|
||||
m_categories[categoryName] = categoryOptions;
|
||||
}
|
||||
|
||||
m_categories = expandCategories(m_categories);
|
||||
}
|
||||
|
||||
bool SessionImpl::hasPerTorrentRatioLimit() const
|
||||
|
||||
@@ -149,15 +149,17 @@ namespace BitTorrent
|
||||
|
||||
QStringList categories() const override;
|
||||
CategoryOptions categoryOptions(const QString &categoryName) const override;
|
||||
bool setCategoryOptions(const QString &categoryName, const CategoryOptions &options) override;
|
||||
Path categorySavePath(const QString &categoryName) const override;
|
||||
Path categorySavePath(const QString &categoryName, const CategoryOptions &options) const override;
|
||||
Path categoryDownloadPath(const QString &categoryName) const override;
|
||||
Path categoryDownloadPath(const QString &categoryName, const CategoryOptions &options) const override;
|
||||
qreal categoryRatioLimit(const QString &categoryName) const override;
|
||||
int categorySeedingTimeLimit(const QString &categoryName) const override;
|
||||
int categoryInactiveSeedingTimeLimit(const QString &categoryName) const override;
|
||||
ShareLimitAction categoryShareLimitAction(const QString &categoryName) const override;
|
||||
bool addCategory(const QString &name, const CategoryOptions &options = {}) override;
|
||||
bool editCategory(const QString &name, const CategoryOptions &options) override;
|
||||
bool removeCategory(const QString &name) override;
|
||||
bool isSubcategoriesEnabled() const override;
|
||||
void setSubcategoriesEnabled(bool value) override;
|
||||
bool useCategoryPathsInManualMode() const override;
|
||||
void setUseCategoryPathsInManualMode(bool value) override;
|
||||
|
||||
@@ -359,8 +361,8 @@ namespace BitTorrent
|
||||
void setOutgoingPortsMax(int max) override;
|
||||
int UPnPLeaseDuration() const override;
|
||||
void setUPnPLeaseDuration(int duration) override;
|
||||
int peerToS() const override;
|
||||
void setPeerToS(int value) override;
|
||||
int peerDSCP() const override;
|
||||
void setPeerDSCP(int value) override;
|
||||
bool ignoreLimitsOnLAN() const override;
|
||||
void setIgnoreLimitsOnLAN(bool ignore) override;
|
||||
bool includeOverheadInLimits() const override;
|
||||
@@ -690,7 +692,7 @@ namespace BitTorrent
|
||||
CachedSettingValue<int> m_outgoingPortsMin;
|
||||
CachedSettingValue<int> m_outgoingPortsMax;
|
||||
CachedSettingValue<int> m_UPnPLeaseDuration;
|
||||
CachedSettingValue<int> m_peerToS;
|
||||
CachedSettingValue<int> m_peerDSCP;
|
||||
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
|
||||
CachedSettingValue<bool> m_includeOverheadInLimits;
|
||||
CachedSettingValue<QString> m_announceIP;
|
||||
@@ -754,7 +756,6 @@ namespace BitTorrent
|
||||
CachedSettingValue<Path> m_savePath;
|
||||
CachedSettingValue<Path> m_downloadPath;
|
||||
CachedSettingValue<bool> m_isDownloadPathEnabled;
|
||||
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
||||
CachedSettingValue<bool> m_useCategoryPathsInManualMode;
|
||||
CachedSettingValue<bool> m_isAutoTMMDisabledByDefault;
|
||||
CachedSettingValue<bool> m_isDisableAutoTMMWhenCategoryChanged;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -33,6 +33,12 @@
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
inline const qreal DEFAULT_RATIO_LIMIT = -2;
|
||||
inline const qreal NO_RATIO_LIMIT = -1;
|
||||
|
||||
inline const int DEFAULT_SEEDING_TIME_LIMIT = -2;
|
||||
inline const int NO_SEEDING_TIME_LIMIT = -1;
|
||||
|
||||
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
||||
// since `Q_NAMESPACE` cannot be used when the same namespace resides at different files.
|
||||
// https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -44,15 +44,6 @@ namespace BitTorrent
|
||||
|
||||
// Torrent
|
||||
|
||||
const qreal Torrent::USE_GLOBAL_RATIO = -2;
|
||||
const qreal Torrent::NO_RATIO_LIMIT = -1;
|
||||
|
||||
const int Torrent::USE_GLOBAL_SEEDING_TIME = -2;
|
||||
const int Torrent::NO_SEEDING_TIME_LIMIT = -1;
|
||||
|
||||
const int Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME = -2;
|
||||
const int Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT = -1;
|
||||
|
||||
const qreal Torrent::MAX_RATIO = std::numeric_limits<qreal>::infinity();
|
||||
|
||||
TorrentID Torrent::id() const
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "base/3rdparty/expected.hpp"
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/tagset.h"
|
||||
#include "sharelimitaction.h"
|
||||
#include "sharelimits.h"
|
||||
#include "torrentannouncestatus.h"
|
||||
#include "torrentcontenthandler.h"
|
||||
|
||||
@@ -125,15 +125,6 @@ namespace BitTorrent
|
||||
};
|
||||
Q_ENUM(StopCondition)
|
||||
|
||||
static const qreal USE_GLOBAL_RATIO;
|
||||
static const qreal NO_RATIO_LIMIT;
|
||||
|
||||
static const int USE_GLOBAL_SEEDING_TIME;
|
||||
static const int NO_SEEDING_TIME_LIMIT;
|
||||
|
||||
static const int USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||
static const int NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||
|
||||
static const qreal MAX_RATIO;
|
||||
|
||||
using TorrentContentHandler::TorrentContentHandler;
|
||||
@@ -236,6 +227,10 @@ namespace BitTorrent
|
||||
virtual void setInactiveSeedingTimeLimit(int limit) = 0;
|
||||
virtual ShareLimitAction shareLimitAction() const = 0;
|
||||
virtual void setShareLimitAction(ShareLimitAction action) = 0;
|
||||
virtual qreal effectiveRatioLimit() const = 0;
|
||||
virtual int effectiveSeedingTimeLimit() const = 0;
|
||||
virtual int effectiveInactiveSeedingTimeLimit() const = 0;
|
||||
virtual ShareLimitAction effectiveShareLimitAction() const = 0;
|
||||
|
||||
virtual PathList filePaths() const = 0;
|
||||
virtual PathList actualFilePaths() const = 0;
|
||||
@@ -279,9 +274,6 @@ namespace BitTorrent
|
||||
virtual bool isLSDDisabled() const = 0;
|
||||
virtual QBitArray pieces() const = 0;
|
||||
virtual qreal distributedCopies() const = 0;
|
||||
virtual qreal maxRatio() const = 0;
|
||||
virtual int maxSeedingTime() const = 0;
|
||||
virtual int maxInactiveSeedingTime() const = 0;
|
||||
virtual qreal realRatio() const = 0;
|
||||
virtual qreal popularity() const = 0;
|
||||
virtual int uploadPayloadRate() const = 0;
|
||||
|
||||
@@ -700,6 +700,7 @@ void TorrentImpl::removeTrackers(const QStringList &trackers)
|
||||
if (!removedTrackers.isEmpty())
|
||||
{
|
||||
m_nativeHandle.replace_trackers(nativeTrackers);
|
||||
m_announceStatus.reset();
|
||||
|
||||
deferredRequestResumeData();
|
||||
m_session->handleTorrentTrackersRemoved(this, removedTrackers);
|
||||
@@ -727,6 +728,7 @@ void TorrentImpl::replaceTrackers(QList<TrackerEntry> trackers)
|
||||
}
|
||||
|
||||
m_nativeHandle.replace_trackers(nativeTrackers);
|
||||
m_announceStatus.reset();
|
||||
|
||||
// Clear the peer list if it's a private torrent since
|
||||
// we do not want to keep connecting with peers from old tracker.
|
||||
@@ -934,7 +936,7 @@ bool TorrentImpl::belongsToCategory(const QString &category) const
|
||||
if (m_category == category)
|
||||
return true;
|
||||
|
||||
return (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + u'/'));
|
||||
return m_category.startsWith(category + u'/');
|
||||
}
|
||||
|
||||
TagSet TorrentImpl::tags() const
|
||||
@@ -1338,16 +1340,16 @@ qlonglong TorrentImpl::eta() const
|
||||
|
||||
if (isFinished())
|
||||
{
|
||||
const qreal maxRatioValue = maxRatio();
|
||||
const int maxSeedingTimeValue = maxSeedingTime();
|
||||
const int maxInactiveSeedingTimeValue = maxInactiveSeedingTime();
|
||||
if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0) && (maxInactiveSeedingTimeValue < 0)) return MAX_ETA;
|
||||
const qreal maxRatioValue = effectiveRatioLimit();
|
||||
const int maxSeedingTimeValue = effectiveSeedingTimeLimit();
|
||||
const int maxInactiveSeedingTimeValue = effectiveInactiveSeedingTimeLimit();
|
||||
if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0) && (maxInactiveSeedingTimeValue < 0))
|
||||
return MAX_ETA;
|
||||
|
||||
qlonglong ratioEta = MAX_ETA;
|
||||
|
||||
if ((speedAverage.upload > 0) && (maxRatioValue >= 0))
|
||||
{
|
||||
|
||||
qlonglong realDL = totalDownload();
|
||||
if (realDL <= 0)
|
||||
realDL = wantedSize();
|
||||
@@ -1479,30 +1481,38 @@ qreal TorrentImpl::distributedCopies() const
|
||||
return m_nativeStatus.distributed_copies;
|
||||
}
|
||||
|
||||
qreal TorrentImpl::maxRatio() const
|
||||
qreal TorrentImpl::effectiveRatioLimit() const
|
||||
{
|
||||
if (m_ratioLimit == USE_GLOBAL_RATIO)
|
||||
return m_session->globalMaxRatio();
|
||||
if (m_ratioLimit == DEFAULT_RATIO_LIMIT)
|
||||
return m_session->categoryRatioLimit(category());
|
||||
|
||||
return m_ratioLimit;
|
||||
}
|
||||
|
||||
int TorrentImpl::maxSeedingTime() const
|
||||
int TorrentImpl::effectiveSeedingTimeLimit() const
|
||||
{
|
||||
if (m_seedingTimeLimit == USE_GLOBAL_SEEDING_TIME)
|
||||
return m_session->globalMaxSeedingMinutes();
|
||||
if (m_seedingTimeLimit == DEFAULT_SEEDING_TIME_LIMIT)
|
||||
return m_session->categorySeedingTimeLimit(category());
|
||||
|
||||
return m_seedingTimeLimit;
|
||||
}
|
||||
|
||||
int TorrentImpl::maxInactiveSeedingTime() const
|
||||
int TorrentImpl::effectiveInactiveSeedingTimeLimit() const
|
||||
{
|
||||
if (m_inactiveSeedingTimeLimit == USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
||||
return m_session->globalMaxInactiveSeedingMinutes();
|
||||
if (m_inactiveSeedingTimeLimit == DEFAULT_SEEDING_TIME_LIMIT)
|
||||
return m_session->categoryInactiveSeedingTimeLimit(category());
|
||||
|
||||
return m_inactiveSeedingTimeLimit;
|
||||
}
|
||||
|
||||
ShareLimitAction TorrentImpl::effectiveShareLimitAction() const
|
||||
{
|
||||
if (m_shareLimitAction == ShareLimitAction::Default)
|
||||
return m_session->categoryShareLimitAction(category());
|
||||
|
||||
return m_shareLimitAction;
|
||||
}
|
||||
|
||||
qreal TorrentImpl::realRatio() const
|
||||
{
|
||||
const int64_t upload = m_nativeStatus.all_time_upload;
|
||||
@@ -2623,7 +2633,7 @@ void TorrentImpl::updateProgress()
|
||||
|
||||
void TorrentImpl::setRatioLimit(qreal limit)
|
||||
{
|
||||
if (limit < USE_GLOBAL_RATIO)
|
||||
if (limit < DEFAULT_RATIO_LIMIT)
|
||||
limit = NO_RATIO_LIMIT;
|
||||
|
||||
if (m_ratioLimit != limit)
|
||||
@@ -2636,7 +2646,7 @@ void TorrentImpl::setRatioLimit(qreal limit)
|
||||
|
||||
void TorrentImpl::setSeedingTimeLimit(int limit)
|
||||
{
|
||||
if (limit < USE_GLOBAL_SEEDING_TIME)
|
||||
if (limit < DEFAULT_SEEDING_TIME_LIMIT)
|
||||
limit = NO_SEEDING_TIME_LIMIT;
|
||||
|
||||
if (m_seedingTimeLimit != limit)
|
||||
@@ -2649,8 +2659,8 @@ void TorrentImpl::setSeedingTimeLimit(int limit)
|
||||
|
||||
void TorrentImpl::setInactiveSeedingTimeLimit(int limit)
|
||||
{
|
||||
if (limit < USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
||||
limit = NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||
if (limit < DEFAULT_SEEDING_TIME_LIMIT)
|
||||
limit = NO_SEEDING_TIME_LIMIT;
|
||||
|
||||
if (m_inactiveSeedingTimeLimit != limit)
|
||||
{
|
||||
|
||||
@@ -156,6 +156,10 @@ namespace BitTorrent
|
||||
void setInactiveSeedingTimeLimit(int limit) override;
|
||||
ShareLimitAction shareLimitAction() const override;
|
||||
void setShareLimitAction(ShareLimitAction action) override;
|
||||
qreal effectiveRatioLimit() const override;
|
||||
int effectiveSeedingTimeLimit() const override;
|
||||
int effectiveInactiveSeedingTimeLimit() const override;
|
||||
ShareLimitAction effectiveShareLimitAction() const override;
|
||||
|
||||
Path filePath(int index) const override;
|
||||
Path actualFilePath(int index) const override;
|
||||
@@ -205,9 +209,6 @@ namespace BitTorrent
|
||||
bool isLSDDisabled() const override;
|
||||
QBitArray pieces() const override;
|
||||
qreal distributedCopies() const override;
|
||||
qreal maxRatio() const override;
|
||||
int maxSeedingTime() const override;
|
||||
int maxInactiveSeedingTime() const override;
|
||||
qreal realRatio() const override;
|
||||
qreal popularity() const override;
|
||||
int uploadPayloadRate() const override;
|
||||
|
||||
@@ -54,6 +54,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QS
|
||||
const QStringList params
|
||||
{
|
||||
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
|
||||
Utils::ForeignApps::PYTHON_UTF8_MODE_FLAG,
|
||||
(SearchPluginManager::engineLocation() / Path(u"nova2dl.py"_s)).toString(),
|
||||
pluginName,
|
||||
url
|
||||
|
||||
@@ -102,6 +102,7 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co
|
||||
const QStringList params
|
||||
{
|
||||
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
|
||||
Utils::ForeignApps::PYTHON_UTF8_MODE_FLAG,
|
||||
(SearchPluginManager::engineLocation() / Path(u"nova2.py"_s)).toString(),
|
||||
m_usedPlugins.join(u','),
|
||||
m_category
|
||||
|
||||
@@ -554,6 +554,7 @@ void SearchPluginManager::update()
|
||||
const QStringList params
|
||||
{
|
||||
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
|
||||
Utils::ForeignApps::PYTHON_UTF8_MODE_FLAG,
|
||||
(engineLocation() / Path(u"/nova2.py"_s)).toString(),
|
||||
u"--capabilities"_s
|
||||
};
|
||||
|
||||
@@ -434,9 +434,9 @@ void TorrentFilesWatcher::Worker::processFolder(const Path &path, const Path &wa
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_failedTorrents.value(path).contains(filePath))
|
||||
if (!m_failedTorrents.value(watchedFolderPath).contains(filePath))
|
||||
{
|
||||
m_failedTorrents[path][filePath] = 0;
|
||||
m_failedTorrents[watchedFolderPath][filePath] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,7 +471,7 @@ void TorrentFilesWatcher::Worker::processFailedTorrents()
|
||||
BitTorrent::AddTorrentParams addTorrentParams = options.addTorrentParams;
|
||||
if (torrentPath != watchedFolderPath)
|
||||
{
|
||||
const Path subdirPath = watchedFolderPath.relativePathOf(torrentPath);
|
||||
const Path subdirPath = watchedFolderPath.relativePathOf(torrentPath).parentPath();
|
||||
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
|
||||
if (useAutoTMM)
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
namespace Utils::ForeignApps
|
||||
{
|
||||
inline const QString PYTHON_ISOLATE_MODE_FLAG = u"-I"_s;
|
||||
inline const QString PYTHON_UTF8_MODE_FLAG = u"-Xutf8=1"_s;
|
||||
|
||||
struct PythonInfo
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2023-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
|
||||
@@ -190,6 +190,7 @@ void AddTorrentParamsWidget::populate()
|
||||
}
|
||||
|
||||
populateDefaultPaths();
|
||||
resetShareLimitsWidgetDefaults();
|
||||
});
|
||||
|
||||
m_ui->savePathEdit->disconnect(this);
|
||||
@@ -241,7 +242,7 @@ void AddTorrentParamsWidget::populate()
|
||||
|
||||
m_ui->startTorrentComboBox->disconnect(this);
|
||||
m_ui->startTorrentComboBox->setCurrentIndex(m_addTorrentParams.addStopped
|
||||
? m_ui->startTorrentComboBox->findData(!*m_addTorrentParams.addStopped) : 0);
|
||||
? m_ui->startTorrentComboBox->findData(!*m_addTorrentParams.addStopped) : 0);
|
||||
connect(m_ui->startTorrentComboBox, &QComboBox::currentIndexChanged, this, [this]
|
||||
{
|
||||
const QVariant data = m_ui->startTorrentComboBox->currentData();
|
||||
@@ -270,6 +271,7 @@ void AddTorrentParamsWidget::populate()
|
||||
m_addTorrentParams.addToQueueTop = data.toBool();
|
||||
});
|
||||
|
||||
resetShareLimitsWidgetDefaults();
|
||||
m_ui->torrentShareLimitsWidget->setRatioLimit(m_addTorrentParams.ratioLimit);
|
||||
m_ui->torrentShareLimitsWidget->setSeedingTimeLimit(m_addTorrentParams.seedingTimeLimit);
|
||||
m_ui->torrentShareLimitsWidget->setInactiveSeedingTimeLimit(m_addTorrentParams.inactiveSeedingTimeLimit);
|
||||
@@ -418,3 +420,11 @@ void AddTorrentParamsWidget::populateSavePathOptions()
|
||||
|
||||
populateDefaultPaths();
|
||||
}
|
||||
|
||||
void AddTorrentParamsWidget::resetShareLimitsWidgetDefaults()
|
||||
{
|
||||
const auto *btSession = BitTorrent::Session::instance();
|
||||
m_ui->torrentShareLimitsWidget->setDefaults((m_addTorrentParams.category.isEmpty() ? TorrentShareLimitsWidget::UsedDefaults::Global : TorrentShareLimitsWidget::UsedDefaults::Category)
|
||||
, btSession->categoryRatioLimit(m_addTorrentParams.category), btSession->categorySeedingTimeLimit(m_addTorrentParams.category)
|
||||
, btSession->categoryInactiveSeedingTimeLimit(m_addTorrentParams.category), btSession->categoryShareLimitAction(m_addTorrentParams.category));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2023-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
|
||||
@@ -57,7 +57,7 @@ private:
|
||||
void populateDefaultPaths();
|
||||
void populateDefaultDownloadPath();
|
||||
void populateSavePathOptions();
|
||||
|
||||
void resetShareLimitsWidgetDefaults();
|
||||
|
||||
Ui::AddTorrentParamsWidget *m_ui;
|
||||
BitTorrent::AddTorrentParams m_addTorrentParams;
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace
|
||||
OUTGOING_PORT_MIN,
|
||||
OUTGOING_PORT_MAX,
|
||||
UPNP_LEASE_DURATION,
|
||||
PEER_TOS,
|
||||
PEER_DSCP,
|
||||
UTP_MIX_MODE,
|
||||
HOSTNAME_CACHE_TTL,
|
||||
IDN_SUPPORT,
|
||||
@@ -276,7 +276,7 @@ void AdvancedSettings::saveAdvancedSettings() const
|
||||
// UPnP lease duration
|
||||
session->setUPnPLeaseDuration(m_spinBoxUPnPLeaseDuration.value());
|
||||
// Type of service
|
||||
session->setPeerToS(m_spinBoxPeerToS.value());
|
||||
session->setPeerDSCP(m_spinBoxPeerDSCP.value());
|
||||
// uTP-TCP mixed mode
|
||||
session->setUtpMixedMode(m_comboBoxUtpMixedMode.currentData().value<BitTorrent::MixedModeAlgorithm>());
|
||||
// Hostname resolver cache TTL
|
||||
@@ -723,11 +723,11 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
addRow(UPNP_LEASE_DURATION, (tr("UPnP lease duration [0: permanent lease]") + u' ' + makeLink(u"https://www.libtorrent.org/reference-Settings.html#upnp_lease_duration", u"(?)"))
|
||||
, &m_spinBoxUPnPLeaseDuration);
|
||||
// Type of service
|
||||
m_spinBoxPeerToS.setMinimum(0);
|
||||
m_spinBoxPeerToS.setMaximum(255);
|
||||
m_spinBoxPeerToS.setValue(session->peerToS());
|
||||
addRow(PEER_TOS, (tr("Type of service (ToS) for connections to peers") + u' ' + makeLink(u"https://www.libtorrent.org/reference-Settings.html#peer_tos", u"(?)"))
|
||||
, &m_spinBoxPeerToS);
|
||||
m_spinBoxPeerDSCP.setMinimum(0);
|
||||
m_spinBoxPeerDSCP.setMaximum(255);
|
||||
m_spinBoxPeerDSCP.setValue(session->peerDSCP());
|
||||
addRow(PEER_DSCP, (tr("Differentiated Services Code Point (DSCP) for connections to peers") + u' ' + makeLink(u"https://www.libtorrent.org/reference-Settings.html#peer_dscp", u"(?)"))
|
||||
, &m_spinBoxPeerDSCP);
|
||||
// uTP-TCP mixed mode
|
||||
m_comboBoxUtpMixedMode.addItem(tr("Prefer TCP"), QVariant::fromValue(BitTorrent::MixedModeAlgorithm::TCP));
|
||||
m_comboBoxUtpMixedMode.addItem(tr("Peer proportional (throttles TCP)"), QVariant::fromValue(BitTorrent::MixedModeAlgorithm::Proportional));
|
||||
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
|
||||
QSpinBox m_spinBoxSaveResumeDataInterval, m_spinBoxSaveStatisticsInterval, m_spinBoxTorrentFileSizeLimit, m_spinBoxBdecodeDepthLimit, m_spinBoxBdecodeTokenLimit,
|
||||
m_spinBoxAsyncIOThreads, m_spinBoxFilePoolSize, m_spinBoxCheckingMemUsage, m_spinBoxDiskQueueSize,
|
||||
m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxPeerToS, m_spinBoxHostnameCacheTTL,
|
||||
m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxPeerDSCP, m_spinBoxHostnameCacheTTL,
|
||||
m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark,
|
||||
m_spinBoxSendBufferWatermarkFactor, m_spinBoxConnectionSpeed, m_spinBoxSocketSendBufferSize, m_spinBoxSocketReceiveBufferSize, m_spinBoxSocketBacklogSize,
|
||||
m_spinBoxAnnouncePort, m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout, m_spinBoxSessionShutdownTimeout,
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#include <QTranslator>
|
||||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/sharelimitaction.h"
|
||||
#include "base/bittorrent/sharelimits.h"
|
||||
#include "base/exceptions.h"
|
||||
#include "base/global.h"
|
||||
#include "base/net/downloadmanager.h"
|
||||
@@ -625,7 +625,6 @@ void OptionsDialog::loadDownloadsTabOptions()
|
||||
m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged());
|
||||
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
|
||||
|
||||
m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled());
|
||||
m_ui->checkUseCategoryPaths->setChecked(session->useCategoryPathsInManualMode());
|
||||
|
||||
m_ui->textSavePath->setDialogCaption(tr("Choose a save directory"));
|
||||
@@ -730,7 +729,6 @@ void OptionsDialog::loadDownloadsTabOptions()
|
||||
connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||
|
||||
connect(m_ui->checkUseSubcategories, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkUseCategoryPaths, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
|
||||
connect(m_ui->textSavePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||
@@ -802,7 +800,6 @@ void OptionsDialog::saveDownloadsTabOptions() const
|
||||
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
|
||||
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
|
||||
|
||||
session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
|
||||
session->setUseCategoryPathsInManualMode(m_ui->checkUseCategoryPaths->isChecked());
|
||||
|
||||
session->setSavePath(Path(m_ui->textSavePath->selectedPath()));
|
||||
|
||||
@@ -1381,13 +1381,6 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkUseSubcategories">
|
||||
<property name="text">
|
||||
<string>Use Subcategories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkUseCategoryPaths">
|
||||
<property name="toolTip">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2017, 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2017-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
|
||||
@@ -32,11 +32,12 @@
|
||||
#include <QPushButton>
|
||||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "torrentsharelimitswidget.h"
|
||||
#include "ui_torrentcategorydialog.h"
|
||||
|
||||
TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
|
||||
: QDialog {parent}
|
||||
: QDialog(parent)
|
||||
, m_ui {new Ui::TorrentCategoryDialog}
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
@@ -56,6 +57,15 @@ TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
|
||||
|
||||
connect(m_ui->textCategoryName, &QLineEdit::textChanged, this, &TorrentCategoryDialog::categoryNameChanged);
|
||||
connect(m_ui->comboUseDownloadPath, &QComboBox::currentIndexChanged, this, &TorrentCategoryDialog::useDownloadPathChanged);
|
||||
|
||||
resetShareLimitsWidgetDefaults();
|
||||
}
|
||||
|
||||
TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent, const QString &categoryName, const BitTorrent::CategoryOptions &categoryOptions)
|
||||
: TorrentCategoryDialog(parent)
|
||||
{
|
||||
setCategoryName(categoryName);
|
||||
setCategoryOptions(categoryOptions);
|
||||
}
|
||||
|
||||
TorrentCategoryDialog::~TorrentCategoryDialog()
|
||||
@@ -73,8 +83,7 @@ QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &pa
|
||||
newCategoryName += u'/';
|
||||
newCategoryName += tr("New Category");
|
||||
|
||||
TorrentCategoryDialog dialog {parent};
|
||||
dialog.setCategoryName(newCategoryName);
|
||||
TorrentCategoryDialog dialog {parent, newCategoryName, {}};
|
||||
while (dialog.exec() == TorrentCategoryDialog::Accepted)
|
||||
{
|
||||
newCategoryName = dialog.categoryName();
|
||||
@@ -110,14 +119,12 @@ void TorrentCategoryDialog::editCategory(QWidget *parent, const QString &categor
|
||||
|
||||
Q_ASSERT(Session::instance()->categories().contains(categoryName));
|
||||
|
||||
auto *dialog = new TorrentCategoryDialog(parent);
|
||||
auto *dialog = new TorrentCategoryDialog(parent, categoryName, Session::instance()->categoryOptions(categoryName));
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->setCategoryNameEditable(false);
|
||||
dialog->setCategoryName(categoryName);
|
||||
dialog->setCategoryOptions(Session::instance()->categoryOptions(categoryName));
|
||||
connect(dialog, &TorrentCategoryDialog::accepted, parent, [dialog, categoryName]()
|
||||
{
|
||||
Session::instance()->editCategory(categoryName, dialog->categoryOptions());
|
||||
Session::instance()->setCategoryOptions(categoryName, dialog->categoryOptions());
|
||||
});
|
||||
dialog->open();
|
||||
}
|
||||
@@ -149,6 +156,11 @@ BitTorrent::CategoryOptions TorrentCategoryDialog::categoryOptions() const
|
||||
else if (m_ui->comboUseDownloadPath->currentIndex() == 2)
|
||||
categoryOptions.downloadPath = {false, {}};
|
||||
|
||||
categoryOptions.ratioLimit = m_ui->torrentShareLimitsWidget->ratioLimit().value_or(BitTorrent::DEFAULT_RATIO_LIMIT);
|
||||
categoryOptions.seedingTimeLimit = m_ui->torrentShareLimitsWidget->seedingTimeLimit().value_or(BitTorrent::DEFAULT_SEEDING_TIME_LIMIT);
|
||||
categoryOptions.inactiveSeedingTimeLimit = m_ui->torrentShareLimitsWidget->inactiveSeedingTimeLimit().value_or(BitTorrent::DEFAULT_SEEDING_TIME_LIMIT);
|
||||
categoryOptions.shareLimitAction = m_ui->torrentShareLimitsWidget->shareLimitAction().value_or(BitTorrent::ShareLimitAction::Default);
|
||||
|
||||
return categoryOptions;
|
||||
}
|
||||
|
||||
@@ -165,6 +177,11 @@ void TorrentCategoryDialog::setCategoryOptions(const BitTorrent::CategoryOptions
|
||||
m_ui->comboUseDownloadPath->setCurrentIndex(0);
|
||||
m_ui->comboDownloadPath->setSelectedPath({});
|
||||
}
|
||||
|
||||
m_ui->torrentShareLimitsWidget->setRatioLimit(categoryOptions.ratioLimit);
|
||||
m_ui->torrentShareLimitsWidget->setSeedingTimeLimit(categoryOptions.seedingTimeLimit);
|
||||
m_ui->torrentShareLimitsWidget->setInactiveSeedingTimeLimit(categoryOptions.inactiveSeedingTimeLimit);
|
||||
m_ui->torrentShareLimitsWidget->setShareLimitAction(categoryOptions.shareLimitAction);
|
||||
}
|
||||
|
||||
void TorrentCategoryDialog::categoryNameChanged(const QString &categoryName)
|
||||
@@ -177,6 +194,13 @@ void TorrentCategoryDialog::categoryNameChanged(const QString &categoryName)
|
||||
if (useDownloadPath)
|
||||
m_ui->comboDownloadPath->setPlaceholder(btSession->categoryDownloadPath(categoryName, categoryOptions()));
|
||||
|
||||
const QString parentCategoryName = BitTorrent::Session::parentCategoryName(categoryName);
|
||||
if (m_parentCategoryName != parentCategoryName)
|
||||
{
|
||||
m_parentCategoryName = parentCategoryName;
|
||||
resetShareLimitsWidgetDefaults();
|
||||
}
|
||||
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!categoryName.isEmpty());
|
||||
}
|
||||
|
||||
@@ -195,3 +219,11 @@ void TorrentCategoryDialog::useDownloadPathChanged(const int index)
|
||||
const Path categoryPath = btSession->categoryDownloadPath(categoryName, categoryOptions());
|
||||
m_ui->comboDownloadPath->setPlaceholder(useDownloadPath ? categoryPath : Path());
|
||||
}
|
||||
|
||||
void TorrentCategoryDialog::resetShareLimitsWidgetDefaults()
|
||||
{
|
||||
const auto *btSession = BitTorrent::Session::instance();
|
||||
m_ui->torrentShareLimitsWidget->setDefaults((m_parentCategoryName.isEmpty() ? TorrentShareLimitsWidget::UsedDefaults::Global : TorrentShareLimitsWidget::UsedDefaults::Category)
|
||||
, btSession->categoryRatioLimit(m_parentCategoryName), btSession->categorySeedingTimeLimit(m_parentCategoryName)
|
||||
, btSession->categoryInactiveSeedingTimeLimit(m_parentCategoryName), btSession->categoryShareLimitAction(m_parentCategoryName));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2017, 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2017-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
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
static void editCategory(QWidget *parent, const QString &categoryName);
|
||||
|
||||
explicit TorrentCategoryDialog(QWidget *parent = nullptr);
|
||||
TorrentCategoryDialog(QWidget *parent, const QString &categoryName, const BitTorrent::CategoryOptions &categoryOptions);
|
||||
~TorrentCategoryDialog() override;
|
||||
|
||||
void setCategoryNameEditable(bool editable);
|
||||
@@ -65,6 +66,9 @@ private slots:
|
||||
void useDownloadPathChanged(int index);
|
||||
|
||||
private:
|
||||
void resetShareLimitsWidgetDefaults();
|
||||
|
||||
Ui::TorrentCategoryDialog *m_ui = nullptr;
|
||||
Path m_lastEnteredDownloadPath;
|
||||
QString m_parentCategoryName;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>493</width>
|
||||
<height>208</height>
|
||||
<height>268</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -140,6 +140,18 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="torrentShareLimitsBox">
|
||||
<property name="title">
|
||||
<string>Torrent share limits</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="torrentShareLimitsBoxLayout">
|
||||
<item>
|
||||
<widget class="TorrentShareLimitsWidget" name="torrentShareLimitsWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
@@ -172,6 +184,12 @@
|
||||
<header>gui/fspathedit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TorrentShareLimitsWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/torrentsharelimitswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -112,7 +112,7 @@ QString TorrentContentModelItem::displayData(const int column) const
|
||||
case BitTorrent::DownloadPriority::Mixed:
|
||||
return tr("Mixed", "Mixed (priorities");
|
||||
case BitTorrent::DownloadPriority::Ignored:
|
||||
return tr("Not downloaded");
|
||||
return tr("Do not download", "Do not download (priority)");
|
||||
case BitTorrent::DownloadPriority::High:
|
||||
return tr("High", "High (priority)");
|
||||
case BitTorrent::DownloadPriority::Maximum:
|
||||
|
||||
@@ -31,13 +31,17 @@
|
||||
|
||||
#include "torrentcreatordialog.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QMimeData>
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/torrentcreator.h"
|
||||
#include "base/bittorrent/torrentdescriptor.h"
|
||||
#include "base/global.h"
|
||||
#include "base/utils/fs.h"
|
||||
@@ -57,6 +61,37 @@ namespace
|
||||
#else
|
||||
const QFileDialog::Options FILE_DIALOG_OPTIONS {};
|
||||
#endif
|
||||
|
||||
class PieceCalculationThread final : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(PieceCalculationThread)
|
||||
|
||||
public:
|
||||
using CalcFunc = std::function<int ()>;
|
||||
|
||||
explicit PieceCalculationThread(CalcFunc calc, QObject *parent = nullptr)
|
||||
: QThread(parent)
|
||||
, m_calc {std::move(calc)}
|
||||
{
|
||||
}
|
||||
|
||||
~PieceCalculationThread() override
|
||||
{
|
||||
wait();
|
||||
}
|
||||
|
||||
signals:
|
||||
void resultReady(int pieces);
|
||||
|
||||
private:
|
||||
void run() override
|
||||
{
|
||||
emit resultReady(m_calc());
|
||||
}
|
||||
|
||||
CalcFunc m_calc;
|
||||
};
|
||||
}
|
||||
|
||||
TorrentCreatorDialog::TorrentCreatorDialog(QWidget *parent, const Path &defaultPath)
|
||||
@@ -98,7 +133,7 @@ TorrentCreatorDialog::TorrentCreatorDialog(QWidget *parent, const Path &defaultP
|
||||
connect(m_ui->addFolderButton, &QPushButton::clicked, this, &TorrentCreatorDialog::onAddFolderButtonClicked);
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &TorrentCreatorDialog::onCreateButtonClicked);
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(m_ui->buttonCalcTotalPieces, &QPushButton::clicked, this, &TorrentCreatorDialog::updatePiecesCount);
|
||||
connect(m_ui->buttonCalcTotalPieces, &QPushButton::clicked, this, &TorrentCreatorDialog::onCalculatePiecesButtonClicked);
|
||||
connect(m_ui->checkStartSeeding, &QCheckBox::clicked, m_ui->checkIgnoreShareLimits, &QWidget::setEnabled);
|
||||
|
||||
loadSettings();
|
||||
@@ -192,6 +227,41 @@ void TorrentCreatorDialog::dragEnterEvent(QDragEnterEvent *event)
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void TorrentCreatorDialog::onCalculatePiecesButtonClicked()
|
||||
{
|
||||
m_ui->buttonCalcTotalPieces->setEnabled(false);
|
||||
m_ui->labelTotalPieces->setText(tr("Calculating..."));
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
PieceCalculationThread::CalcFunc calc = [path = m_ui->textInputPath->selectedPath()
|
||||
, pieceSize = getPieceSize()
|
||||
, torrentFormat = getTorrentFormat()]() -> int
|
||||
{
|
||||
return BitTorrent::TorrentCreator::calculateTotalPieces(path, pieceSize, torrentFormat);
|
||||
};
|
||||
#else
|
||||
PieceCalculationThread::CalcFunc calc = [path = m_ui->textInputPath->selectedPath()
|
||||
, pieceSize = getPieceSize()
|
||||
, isAlignmentOptimized = m_ui->checkOptimizeAlignment->isChecked()
|
||||
, paddedFileSizeLimit = getPaddedFileSizeLimit()]() -> int
|
||||
{
|
||||
return BitTorrent::TorrentCreator::calculateTotalPieces(path, pieceSize, isAlignmentOptimized, paddedFileSizeLimit);
|
||||
};
|
||||
#endif
|
||||
|
||||
// since the calculation (in libtorrent) cannot be interrupted, always let it run to completion and
|
||||
// not managed by `parent`
|
||||
auto *thread = new PieceCalculationThread(std::move(calc), nullptr);
|
||||
thread->setObjectName("PieceCalculationThread thread");
|
||||
connect(thread, &PieceCalculationThread::finished, thread, &QObject::deleteLater);
|
||||
connect(thread, &PieceCalculationThread::resultReady, this, [this](const int pieces)
|
||||
{
|
||||
m_ui->labelTotalPieces->setText(QString::number(pieces));
|
||||
m_ui->buttonCalcTotalPieces->setEnabled(true);
|
||||
});
|
||||
thread->start();
|
||||
}
|
||||
|
||||
// Main function that create a .torrent file
|
||||
void TorrentCreatorDialog::onCreateButtonClicked()
|
||||
{
|
||||
@@ -277,9 +347,9 @@ void TorrentCreatorDialog::handleCreationSuccess(const BitTorrent::TorrentCreato
|
||||
params.skipChecking = true;
|
||||
if (m_ui->checkIgnoreShareLimits->isChecked())
|
||||
{
|
||||
params.ratioLimit = BitTorrent::Torrent::NO_RATIO_LIMIT;
|
||||
params.seedingTimeLimit = BitTorrent::Torrent::NO_SEEDING_TIME_LIMIT;
|
||||
params.inactiveSeedingTimeLimit = BitTorrent::Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||
params.ratioLimit = BitTorrent::NO_RATIO_LIMIT;
|
||||
params.seedingTimeLimit = BitTorrent::NO_SEEDING_TIME_LIMIT;
|
||||
params.inactiveSeedingTimeLimit = BitTorrent::NO_SEEDING_TIME_LIMIT;
|
||||
}
|
||||
params.useAutoTMM = false; // otherwise if it is on by default, it will overwrite `savePath` to the default save path
|
||||
|
||||
@@ -298,20 +368,6 @@ void TorrentCreatorDialog::updateProgressBar(int progress)
|
||||
m_ui->progressBar->setValue(progress);
|
||||
}
|
||||
|
||||
void TorrentCreatorDialog::updatePiecesCount()
|
||||
{
|
||||
const Path path = m_ui->textInputPath->selectedPath();
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const int count = BitTorrent::TorrentCreator::calculateTotalPieces(
|
||||
path, getPieceSize(), getTorrentFormat());
|
||||
#else
|
||||
const bool isAlignmentOptimized = m_ui->checkOptimizeAlignment->isChecked();
|
||||
const int count = BitTorrent::TorrentCreator::calculateTotalPieces(path
|
||||
, getPieceSize(), isAlignmentOptimized, getPaddedFileSizeLimit());
|
||||
#endif
|
||||
m_ui->labelTotalPieces->setText(QString::number(count));
|
||||
}
|
||||
|
||||
void TorrentCreatorDialog::setInteractionEnabled(const bool enabled) const
|
||||
{
|
||||
m_ui->textInputPath->setEnabled(enabled);
|
||||
@@ -382,3 +438,5 @@ void TorrentCreatorDialog::loadSettings()
|
||||
if (const QSize dialogSize = m_storeDialogSize; dialogSize.isValid())
|
||||
resize(dialogSize);
|
||||
}
|
||||
|
||||
#include "torrentcreatordialog.moc"
|
||||
|
||||
@@ -33,10 +33,15 @@
|
||||
#include <QDialog>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "base/bittorrent/torrentcreator.h"
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
enum class TorrentFormat;
|
||||
struct TorrentCreatorResult;
|
||||
}
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class TorrentCreatorDialog;
|
||||
@@ -54,7 +59,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void updateProgressBar(int progress);
|
||||
void updatePiecesCount();
|
||||
void onCalculatePiecesButtonClicked();
|
||||
void onCreateButtonClicked();
|
||||
void onAddFileButtonClicked();
|
||||
void onAddFolderButtonClicked();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2024-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2020 thalieht
|
||||
* Copyright (C) 2011 Christian Kandeler
|
||||
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||
@@ -289,7 +289,12 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QList<BitTorre
|
||||
, this, &TorrentOptionsDialog::handleDownSpeedLimitChanged);
|
||||
}
|
||||
|
||||
m_ui->torrentShareLimitsWidget->setDefaultLimits(session->globalMaxRatio(), session->globalMaxSeedingMinutes(), session->globalMaxInactiveSeedingMinutes());
|
||||
if (m_allSameCategory)
|
||||
{
|
||||
m_ui->torrentShareLimitsWidget->setDefaults((firstTorrentCategory.isEmpty() ? TorrentShareLimitsWidget::UsedDefaults::Global : TorrentShareLimitsWidget::UsedDefaults::Category)
|
||||
, session->categoryRatioLimit(firstTorrentCategory), session->categorySeedingTimeLimit(firstTorrentCategory)
|
||||
, session->categoryInactiveSeedingTimeLimit(firstTorrentCategory), session->categoryShareLimitAction(firstTorrentCategory));
|
||||
}
|
||||
if (allSameRatio)
|
||||
m_ui->torrentShareLimitsWidget->setRatioLimit(firstTorrentRatio);
|
||||
if (allSameSeedingTime)
|
||||
@@ -477,30 +482,37 @@ void TorrentOptionsDialog::accept()
|
||||
|
||||
void TorrentOptionsDialog::handleCategoryChanged([[maybe_unused]] const int index)
|
||||
{
|
||||
if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
|
||||
{
|
||||
if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
|
||||
{
|
||||
m_ui->savePath->setSelectedPath({});
|
||||
}
|
||||
else
|
||||
{
|
||||
const Path savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
|
||||
m_ui->savePath->setSelectedPath(savePath);
|
||||
const Path downloadPath = BitTorrent::Session::instance()->categoryDownloadPath(m_ui->comboCategory->currentText());
|
||||
m_ui->downloadPath->setSelectedPath(downloadPath);
|
||||
m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
|
||||
}
|
||||
}
|
||||
const auto *btSession = BitTorrent::Session::instance();
|
||||
|
||||
if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
|
||||
{
|
||||
if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
|
||||
m_ui->savePath->setSelectedPath({});
|
||||
|
||||
m_ui->comboCategory->clearEditText();
|
||||
m_ui->comboCategory->lineEdit()->setPlaceholderText(m_currentCategoriesString);
|
||||
|
||||
m_ui->torrentShareLimitsWidget->setDefaults(TorrentShareLimitsWidget::UsedDefaults::Global
|
||||
, BitTorrent::DEFAULT_RATIO_LIMIT, BitTorrent::DEFAULT_SEEDING_TIME_LIMIT
|
||||
, BitTorrent::DEFAULT_SEEDING_TIME_LIMIT, BitTorrent::ShareLimitAction::Default);
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString categoryName = m_ui->comboCategory->currentText();
|
||||
|
||||
if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
|
||||
{
|
||||
const Path savePath = btSession->categorySavePath(categoryName);
|
||||
m_ui->savePath->setSelectedPath(savePath);
|
||||
const Path downloadPath = btSession->categoryDownloadPath(categoryName);
|
||||
m_ui->downloadPath->setSelectedPath(downloadPath);
|
||||
m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
|
||||
}
|
||||
|
||||
m_ui->comboCategory->lineEdit()->setPlaceholderText(QString());
|
||||
m_ui->torrentShareLimitsWidget->setDefaults((categoryName.isEmpty() ? TorrentShareLimitsWidget::UsedDefaults::Global : TorrentShareLimitsWidget::UsedDefaults::Category)
|
||||
, btSession->categoryRatioLimit(categoryName), btSession->categorySeedingTimeLimit(categoryName)
|
||||
, btSession->categoryInactiveSeedingTimeLimit(categoryName), btSession->categoryShareLimitAction(categoryName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2024-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2020 thalieht
|
||||
* Copyright (C) 2011 Christian Kandeler
|
||||
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "base/bittorrent/sharelimitaction.h"
|
||||
#include "base/bittorrent/sharelimits.h"
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
|
||||
|
||||
@@ -52,6 +52,29 @@ namespace
|
||||
RemoveWithContentActionIndex,
|
||||
SuperSeedingActionIndex
|
||||
};
|
||||
|
||||
QString shareLimitActionName(const BitTorrent::ShareLimitAction shareLimitAction)
|
||||
{
|
||||
switch (shareLimitAction)
|
||||
{
|
||||
case BitTorrent::ShareLimitAction::Stop:
|
||||
return TorrentShareLimitsWidget::tr("Stop torrent");
|
||||
|
||||
case BitTorrent::ShareLimitAction::Remove:
|
||||
return TorrentShareLimitsWidget::tr("Remove torrent");
|
||||
|
||||
case BitTorrent::ShareLimitAction::RemoveWithContent:
|
||||
return TorrentShareLimitsWidget::tr("Remove torrent and its content");
|
||||
|
||||
case BitTorrent::ShareLimitAction::EnableSuperSeeding:
|
||||
return TorrentShareLimitsWidget::tr("Enable super seeding for torrent");
|
||||
|
||||
case BitTorrent::ShareLimitAction::Default:
|
||||
return TorrentShareLimitsWidget::tr("Default");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
TorrentShareLimitsWidget::TorrentShareLimitsWidget(QWidget *parent)
|
||||
@@ -60,6 +83,30 @@ TorrentShareLimitsWidget::TorrentShareLimitsWidget(QWidget *parent)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->comboBoxRatioMode->addItem({});
|
||||
m_ui->comboBoxRatioMode->addItem(tr("Unlimited"));
|
||||
m_ui->comboBoxRatioMode->addItem(tr("Set to"));
|
||||
m_ui->comboBoxRatioMode->setCurrentIndex(UninitializedModeIndex);
|
||||
|
||||
m_ui->comboBoxSeedingTimeMode->addItem({});
|
||||
m_ui->comboBoxSeedingTimeMode->addItem(tr("Unlimited"));
|
||||
m_ui->comboBoxSeedingTimeMode->addItem(tr("Set to"));
|
||||
m_ui->comboBoxSeedingTimeMode->setCurrentIndex(UninitializedModeIndex);
|
||||
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->addItem({});
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->addItem(tr("Unlimited"));
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->addItem(tr("Set to"));
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->setCurrentIndex(UninitializedModeIndex);
|
||||
|
||||
m_ui->comboBoxAction->addItem({});
|
||||
m_ui->comboBoxAction->addItem(shareLimitActionName(BitTorrent::ShareLimitAction::Stop));
|
||||
m_ui->comboBoxAction->addItem(shareLimitActionName(BitTorrent::ShareLimitAction::Remove));
|
||||
m_ui->comboBoxAction->addItem(shareLimitActionName(BitTorrent::ShareLimitAction::RemoveWithContent));
|
||||
m_ui->comboBoxAction->addItem(shareLimitActionName(BitTorrent::ShareLimitAction::EnableSuperSeeding));
|
||||
m_ui->comboBoxAction->setCurrentIndex(UninitializedActionIndex);
|
||||
|
||||
resetDefaultItemsText();
|
||||
|
||||
m_ui->spinBoxRatioValue->setEnabled(false);
|
||||
m_ui->spinBoxRatioValue->setMaximum(std::numeric_limits<int>::max());
|
||||
m_ui->spinBoxRatioValue->setSuffix({});
|
||||
@@ -71,9 +118,25 @@ TorrentShareLimitsWidget::TorrentShareLimitsWidget(QWidget *parent)
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setSuffix({});
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->clear();
|
||||
|
||||
connect(m_ui->comboBoxRatioMode, &QComboBox::currentIndexChanged, this, &TorrentShareLimitsWidget::refreshRatioLimitControls);
|
||||
connect(m_ui->comboBoxSeedingTimeMode, &QComboBox::currentIndexChanged, this, &TorrentShareLimitsWidget::refreshSeedingTimeLimitControls);
|
||||
connect(m_ui->comboBoxInactiveSeedingTimeMode, &QComboBox::currentIndexChanged, this, &TorrentShareLimitsWidget::refreshInactiveSeedingTimeLimitControls);
|
||||
int prevIndex = UninitializedModeIndex;
|
||||
connect(m_ui->comboBoxRatioMode, &QComboBox::currentIndexChanged, this
|
||||
, [this, prevIndex](const int currentIndex) mutable
|
||||
{
|
||||
onRatioLimitModeChanged(currentIndex, prevIndex);
|
||||
prevIndex = currentIndex;
|
||||
});
|
||||
connect(m_ui->comboBoxSeedingTimeMode, &QComboBox::currentIndexChanged, this
|
||||
, [this, prevIndex](const int currentIndex) mutable
|
||||
{
|
||||
onSeedingTimeLimitModeChanged(currentIndex, prevIndex);
|
||||
prevIndex = currentIndex;
|
||||
});
|
||||
connect(m_ui->comboBoxInactiveSeedingTimeMode, &QComboBox::currentIndexChanged, this
|
||||
, [this, prevIndex](const int currentIndex) mutable
|
||||
{
|
||||
onInactiveSeedingTimeLimitModeChanged(currentIndex, prevIndex);
|
||||
prevIndex = currentIndex;
|
||||
});
|
||||
}
|
||||
|
||||
TorrentShareLimitsWidget::~TorrentShareLimitsWidget()
|
||||
@@ -83,11 +146,11 @@ TorrentShareLimitsWidget::~TorrentShareLimitsWidget()
|
||||
|
||||
void TorrentShareLimitsWidget::setRatioLimit(const qreal ratioLimit)
|
||||
{
|
||||
if (ratioLimit == BitTorrent::Torrent::USE_GLOBAL_RATIO)
|
||||
if (ratioLimit == BitTorrent::DEFAULT_RATIO_LIMIT)
|
||||
{
|
||||
m_ui->comboBoxRatioMode->setCurrentIndex(DefaultModeIndex);
|
||||
}
|
||||
else if (ratioLimit == BitTorrent::Torrent::NO_RATIO_LIMIT)
|
||||
else if (ratioLimit == BitTorrent::NO_RATIO_LIMIT)
|
||||
{
|
||||
m_ui->comboBoxRatioMode->setCurrentIndex(UnlimitedModeIndex);
|
||||
}
|
||||
@@ -100,11 +163,11 @@ void TorrentShareLimitsWidget::setRatioLimit(const qreal ratioLimit)
|
||||
|
||||
void TorrentShareLimitsWidget::setSeedingTimeLimit(const int seedingTimeLimit)
|
||||
{
|
||||
if (seedingTimeLimit == BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME)
|
||||
if (seedingTimeLimit == BitTorrent::DEFAULT_SEEDING_TIME_LIMIT)
|
||||
{
|
||||
m_ui->comboBoxSeedingTimeMode->setCurrentIndex(DefaultModeIndex);
|
||||
}
|
||||
else if (seedingTimeLimit == BitTorrent::Torrent::NO_SEEDING_TIME_LIMIT)
|
||||
else if (seedingTimeLimit == BitTorrent::NO_SEEDING_TIME_LIMIT)
|
||||
{
|
||||
m_ui->comboBoxSeedingTimeMode->setCurrentIndex(UnlimitedModeIndex);
|
||||
}
|
||||
@@ -117,11 +180,11 @@ void TorrentShareLimitsWidget::setSeedingTimeLimit(const int seedingTimeLimit)
|
||||
|
||||
void TorrentShareLimitsWidget::setInactiveSeedingTimeLimit(const int inactiveSeedingTimeLimit)
|
||||
{
|
||||
if (inactiveSeedingTimeLimit == BitTorrent::Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME)
|
||||
if (inactiveSeedingTimeLimit == BitTorrent::DEFAULT_SEEDING_TIME_LIMIT)
|
||||
{
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->setCurrentIndex(DefaultModeIndex);
|
||||
}
|
||||
else if (inactiveSeedingTimeLimit == BitTorrent::Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT)
|
||||
else if (inactiveSeedingTimeLimit == BitTorrent::NO_SEEDING_TIME_LIMIT)
|
||||
{
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->setCurrentIndex(UnlimitedModeIndex);
|
||||
}
|
||||
@@ -155,25 +218,56 @@ void TorrentShareLimitsWidget::setShareLimitAction(const BitTorrent::ShareLimitA
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentShareLimitsWidget::setDefaultLimits(const qreal ratioLimit, const int seedingTimeLimit, const int inactiveSeedingTimeLimit)
|
||||
void TorrentShareLimitsWidget::setDefaults(UsedDefaults usedDefaults
|
||||
, const qreal ratioLimit, const int seedingTimeLimit
|
||||
, const int inactiveSeedingTimeLimit, const BitTorrent::ShareLimitAction action)
|
||||
{
|
||||
if (m_defaultRatioLimit != ratioLimit)
|
||||
m_defaultRatioLimit = ratioLimit;
|
||||
if (m_ui->comboBoxRatioMode->currentIndex() == DefaultModeIndex)
|
||||
{
|
||||
m_defaultRatioLimit = ratioLimit;
|
||||
refreshRatioLimitControls();
|
||||
if (m_defaultRatioLimit >= 0)
|
||||
{
|
||||
m_ui->spinBoxRatioValue->setValue(m_defaultRatioLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->spinBoxRatioValue->clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_defaultSeedingTimeLimit != seedingTimeLimit)
|
||||
m_defaultSeedingTimeLimit = seedingTimeLimit;
|
||||
if (m_ui->comboBoxSeedingTimeMode->currentIndex() == DefaultModeIndex)
|
||||
{
|
||||
m_defaultSeedingTimeLimit = seedingTimeLimit;
|
||||
refreshSeedingTimeLimitControls();
|
||||
if (m_defaultSeedingTimeLimit >= 0)
|
||||
{
|
||||
m_ui->spinBoxSeedingTimeValue->setValue(m_defaultSeedingTimeLimit);
|
||||
m_ui->spinBoxSeedingTimeValue->setSuffix(tr(" min"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->spinBoxSeedingTimeValue->setSuffix({});
|
||||
m_ui->spinBoxSeedingTimeValue->clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_defaultInactiveSeedingTimeLimit != inactiveSeedingTimeLimit)
|
||||
m_defaultInactiveSeedingTimeLimit = inactiveSeedingTimeLimit;
|
||||
if (m_ui->comboBoxInactiveSeedingTimeMode->currentIndex() == DefaultModeIndex)
|
||||
{
|
||||
m_defaultInactiveSeedingTimeLimit = inactiveSeedingTimeLimit;
|
||||
refreshInactiveSeedingTimeLimitControls();
|
||||
if (m_defaultInactiveSeedingTimeLimit >= 0)
|
||||
{
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setValue(m_defaultInactiveSeedingTimeLimit);
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setSuffix(tr(" min"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setSuffix({});
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->clear();
|
||||
}
|
||||
}
|
||||
|
||||
m_defaultShareLimitAction = action;
|
||||
m_usedDefaults = usedDefaults;
|
||||
resetDefaultItemsText();
|
||||
}
|
||||
|
||||
std::optional<qreal> TorrentShareLimitsWidget::ratioLimit() const
|
||||
@@ -181,9 +275,9 @@ std::optional<qreal> TorrentShareLimitsWidget::ratioLimit() const
|
||||
switch (m_ui->comboBoxRatioMode->currentIndex())
|
||||
{
|
||||
case DefaultModeIndex:
|
||||
return BitTorrent::Torrent::USE_GLOBAL_RATIO;
|
||||
return BitTorrent::DEFAULT_RATIO_LIMIT;
|
||||
case UnlimitedModeIndex:
|
||||
return BitTorrent::Torrent::NO_RATIO_LIMIT;
|
||||
return BitTorrent::NO_RATIO_LIMIT;
|
||||
case AssignedModeIndex:
|
||||
return m_ui->spinBoxRatioValue->value();
|
||||
default:
|
||||
@@ -196,9 +290,9 @@ std::optional<int> TorrentShareLimitsWidget::seedingTimeLimit() const
|
||||
switch (m_ui->comboBoxSeedingTimeMode->currentIndex())
|
||||
{
|
||||
case DefaultModeIndex:
|
||||
return BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
return BitTorrent::DEFAULT_SEEDING_TIME_LIMIT;
|
||||
case UnlimitedModeIndex:
|
||||
return BitTorrent::Torrent::NO_SEEDING_TIME_LIMIT;
|
||||
return BitTorrent::NO_SEEDING_TIME_LIMIT;
|
||||
case AssignedModeIndex:
|
||||
return m_ui->spinBoxSeedingTimeValue->value();
|
||||
default:
|
||||
@@ -211,9 +305,9 @@ std::optional<int> TorrentShareLimitsWidget::inactiveSeedingTimeLimit() const
|
||||
switch (m_ui->comboBoxInactiveSeedingTimeMode->currentIndex())
|
||||
{
|
||||
case DefaultModeIndex:
|
||||
return BitTorrent::Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||
return BitTorrent::DEFAULT_SEEDING_TIME_LIMIT;
|
||||
case UnlimitedModeIndex:
|
||||
return BitTorrent::Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT;
|
||||
return BitTorrent::NO_SEEDING_TIME_LIMIT;
|
||||
case AssignedModeIndex:
|
||||
return m_ui->spinBoxInactiveSeedingTimeValue->value();
|
||||
default:
|
||||
@@ -240,68 +334,97 @@ std::optional<BitTorrent::ShareLimitAction> TorrentShareLimitsWidget::shareLimit
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentShareLimitsWidget::refreshRatioLimitControls()
|
||||
void TorrentShareLimitsWidget::onRatioLimitModeChanged(const int currentIndex, const int previousIndex)
|
||||
{
|
||||
const auto index = m_ui->comboBoxRatioMode->currentIndex();
|
||||
m_ui->spinBoxRatioValue->setEnabled(currentIndex == AssignedModeIndex);
|
||||
|
||||
m_ui->spinBoxRatioValue->setEnabled(index == AssignedModeIndex);
|
||||
if (index == AssignedModeIndex)
|
||||
if (previousIndex == AssignedModeIndex)
|
||||
m_ratioLimit = m_ui->spinBoxRatioValue->value();
|
||||
|
||||
if (currentIndex == AssignedModeIndex)
|
||||
{
|
||||
m_ui->spinBoxRatioValue->setValue(m_ratioLimit);
|
||||
}
|
||||
else if ((index == DefaultModeIndex) && (m_defaultRatioLimit >= 0))
|
||||
else if ((currentIndex == DefaultModeIndex) && (m_defaultRatioLimit >= 0))
|
||||
{
|
||||
m_ui->spinBoxRatioValue->setValue(m_defaultRatioLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ratioLimit = m_ui->spinBoxRatioValue->value();
|
||||
m_ui->spinBoxRatioValue->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentShareLimitsWidget::refreshSeedingTimeLimitControls()
|
||||
void TorrentShareLimitsWidget::onSeedingTimeLimitModeChanged(const int currentIndex, const int previousIndex)
|
||||
{
|
||||
const auto index = m_ui->comboBoxSeedingTimeMode->currentIndex();
|
||||
m_ui->spinBoxSeedingTimeValue->setEnabled(currentIndex == AssignedModeIndex);
|
||||
|
||||
m_ui->spinBoxSeedingTimeValue->setEnabled(index == AssignedModeIndex);
|
||||
if (index == AssignedModeIndex)
|
||||
if (previousIndex == AssignedModeIndex)
|
||||
m_seedingTimeLimit = m_ui->spinBoxSeedingTimeValue->value();
|
||||
|
||||
if (currentIndex == AssignedModeIndex)
|
||||
{
|
||||
m_ui->spinBoxSeedingTimeValue->setValue(m_seedingTimeLimit);
|
||||
m_ui->spinBoxSeedingTimeValue->setSuffix(tr(" min"));
|
||||
}
|
||||
else if ((index == DefaultModeIndex) && (m_defaultSeedingTimeLimit >= 0))
|
||||
else if ((currentIndex == DefaultModeIndex) && (m_defaultSeedingTimeLimit >= 0))
|
||||
{
|
||||
m_ui->spinBoxSeedingTimeValue->setValue(m_defaultSeedingTimeLimit);
|
||||
m_ui->spinBoxSeedingTimeValue->setSuffix(tr(" min"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_seedingTimeLimit = m_ui->spinBoxSeedingTimeValue->value();
|
||||
m_ui->spinBoxSeedingTimeValue->setSuffix({});
|
||||
m_ui->spinBoxSeedingTimeValue->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentShareLimitsWidget::refreshInactiveSeedingTimeLimitControls()
|
||||
void TorrentShareLimitsWidget::onInactiveSeedingTimeLimitModeChanged(const int currentIndex, const int previousIndex)
|
||||
{
|
||||
const auto index = m_ui->comboBoxInactiveSeedingTimeMode->currentIndex();
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setEnabled(currentIndex == AssignedModeIndex);
|
||||
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setEnabled(index == AssignedModeIndex);
|
||||
if (index == AssignedModeIndex)
|
||||
if (previousIndex == AssignedModeIndex)
|
||||
m_inactiveSeedingTimeLimit = m_ui->spinBoxInactiveSeedingTimeValue->value();
|
||||
|
||||
if (currentIndex == AssignedModeIndex)
|
||||
{
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setValue(m_inactiveSeedingTimeLimit);
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setSuffix(tr(" min"));
|
||||
}
|
||||
else if ((index == DefaultModeIndex) && (m_defaultInactiveSeedingTimeLimit >= 0))
|
||||
else if ((currentIndex == DefaultModeIndex) && (m_defaultInactiveSeedingTimeLimit >= 0))
|
||||
{
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setValue(m_defaultInactiveSeedingTimeLimit);
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setSuffix(tr(" min"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inactiveSeedingTimeLimit = m_ui->spinBoxInactiveSeedingTimeValue->value();
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->setSuffix({});
|
||||
m_ui->spinBoxInactiveSeedingTimeValue->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentShareLimitsWidget::resetDefaultItemsText()
|
||||
{
|
||||
if (m_usedDefaults == UsedDefaults::Global)
|
||||
{
|
||||
m_ui->comboBoxRatioMode->setItemText(DefaultModeIndex, tr("Default"));
|
||||
m_ui->comboBoxSeedingTimeMode->setItemText(DefaultModeIndex, tr("Default"));
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->setItemText(DefaultModeIndex, tr("Default"));
|
||||
|
||||
m_ui->comboBoxAction->setItemText(DefaultActionIndex
|
||||
, (m_defaultShareLimitAction == BitTorrent::ShareLimitAction::Default)
|
||||
? tr("Default")
|
||||
: tr("Default (%1)", "Default (share limit action)").arg(shareLimitActionName(m_defaultShareLimitAction)));
|
||||
}
|
||||
else // TorrentOptions
|
||||
{
|
||||
m_ui->comboBoxRatioMode->setItemText(DefaultModeIndex, tr("From category"));
|
||||
m_ui->comboBoxSeedingTimeMode->setItemText(DefaultModeIndex, tr("From category"));
|
||||
m_ui->comboBoxInactiveSeedingTimeMode->setItemText(DefaultModeIndex, tr("From category"));
|
||||
|
||||
m_ui->comboBoxAction->setItemText(DefaultActionIndex
|
||||
, (m_defaultShareLimitAction == BitTorrent::ShareLimitAction::Default)
|
||||
? tr("From category")
|
||||
: tr("From category (%1)", "From category (share limit action)").arg(shareLimitActionName(m_defaultShareLimitAction)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "base/bittorrent/sharelimitaction.h"
|
||||
#include "base/bittorrent/sharelimits.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -45,6 +45,12 @@ class TorrentShareLimitsWidget final : public QWidget
|
||||
Q_DISABLE_COPY_MOVE(TorrentShareLimitsWidget)
|
||||
|
||||
public:
|
||||
enum class UsedDefaults
|
||||
{
|
||||
Global,
|
||||
Category
|
||||
};
|
||||
|
||||
explicit TorrentShareLimitsWidget(QWidget *parent = nullptr);
|
||||
~TorrentShareLimitsWidget() override;
|
||||
|
||||
@@ -53,7 +59,8 @@ public:
|
||||
void setInactiveSeedingTimeLimit(int inactiveSeedingTimeLimit);
|
||||
void setShareLimitAction(BitTorrent::ShareLimitAction action);
|
||||
|
||||
void setDefaultLimits(qreal ratioLimit, int seedingTimeLimit, int inactiveSeedingTimeLimit);
|
||||
void setDefaults(UsedDefaults usedDefaults, qreal ratioLimit, int seedingTimeLimit
|
||||
, int inactiveSeedingTimeLimit, BitTorrent::ShareLimitAction action);
|
||||
|
||||
std::optional<qreal> ratioLimit() const;
|
||||
std::optional<int> seedingTimeLimit() const;
|
||||
@@ -61,9 +68,10 @@ public:
|
||||
std::optional<BitTorrent::ShareLimitAction> shareLimitAction() const;
|
||||
|
||||
private:
|
||||
void refreshRatioLimitControls();
|
||||
void refreshSeedingTimeLimitControls();
|
||||
void refreshInactiveSeedingTimeLimitControls();
|
||||
void onRatioLimitModeChanged(int currentIndex, int previousIndex);
|
||||
void onSeedingTimeLimitModeChanged(int currentIndex, int previousIndex);
|
||||
void onInactiveSeedingTimeLimitModeChanged(int currentIndex, int previousIndex);
|
||||
void resetDefaultItemsText();
|
||||
|
||||
Ui::TorrentShareLimitsWidget *m_ui = nullptr;
|
||||
|
||||
@@ -74,4 +82,7 @@ private:
|
||||
int m_defaultSeedingTimeLimit = -1;
|
||||
int m_defaultInactiveSeedingTimeLimit = -1;
|
||||
qreal m_defaultRatioLimit = -1;
|
||||
BitTorrent::ShareLimitAction m_defaultShareLimitAction = BitTorrent::ShareLimitAction::Default;
|
||||
|
||||
UsedDefaults m_usedDefaults = UsedDefaults::Global;
|
||||
};
|
||||
|
||||
@@ -21,26 +21,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboBoxRatioMode">
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Unlimited</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set to</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="comboBoxRatioMode"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QDoubleSpinBox" name="spinBoxRatioValue">
|
||||
@@ -63,26 +44,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBoxSeedingTimeMode">
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Unlimited</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set to</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="comboBoxSeedingTimeMode"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QSpinBox" name="spinBoxSeedingTimeValue">
|
||||
@@ -108,26 +70,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="comboBoxInactiveSeedingTimeMode">
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Unlimited</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set to</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="comboBoxInactiveSeedingTimeMode"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QSpinBox" name="spinBoxInactiveSeedingTimeValue">
|
||||
@@ -157,36 +100,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBoxAction">
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stop torrent</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Remove torrent</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Remove torrent and its content</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enable super seeding for torrent</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="comboBoxAction"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="actionLayoutSpacer">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2016-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
|
||||
@@ -324,17 +324,17 @@ void CategoryFilterModel::categoryAdded(const QString &categoryName)
|
||||
{
|
||||
CategoryModelItem *parent = m_rootItem;
|
||||
|
||||
if (m_isSubcategoriesEnabled)
|
||||
{
|
||||
QStringList expanded = BitTorrent::Session::expandCategory(categoryName);
|
||||
if (expanded.count() > 1)
|
||||
parent = findItem(expanded[expanded.count() - 2]);
|
||||
}
|
||||
const QStringList expanded = BitTorrent::Session::expandCategory(categoryName);
|
||||
if (expanded.count() > 1)
|
||||
parent = findItem(expanded[expanded.count() - 2]);
|
||||
|
||||
Q_ASSERT(parent);
|
||||
if (!parent) [[unlikely]]
|
||||
return;
|
||||
|
||||
const int row = parent->childCount();
|
||||
beginInsertRows(index(parent), row, row);
|
||||
new CategoryModelItem(
|
||||
parent, m_isSubcategoriesEnabled ? shortName(categoryName) : categoryName);
|
||||
new CategoryModelItem(parent, shortName(categoryName));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@@ -426,7 +426,6 @@ void CategoryFilterModel::populate()
|
||||
|
||||
const auto *session = BitTorrent::Session::instance();
|
||||
const auto torrents = session->torrents();
|
||||
m_isSubcategoriesEnabled = session->isSubcategoriesEnabled();
|
||||
|
||||
// All torrents
|
||||
m_rootItem->addChild(CategoryModelItem::UID_ALL
|
||||
@@ -440,31 +439,19 @@ void CategoryFilterModel::populate()
|
||||
, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount));
|
||||
|
||||
using BitTorrent::Torrent;
|
||||
if (m_isSubcategoriesEnabled)
|
||||
for (const QString &categoryName : asConst(session->categories()))
|
||||
{
|
||||
for (const QString &categoryName : asConst(session->categories()))
|
||||
CategoryModelItem *parent = m_rootItem;
|
||||
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(categoryName)))
|
||||
{
|
||||
CategoryModelItem *parent = m_rootItem;
|
||||
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(categoryName)))
|
||||
const QString subcatName = shortName(subcat);
|
||||
if (!parent->hasChild(subcatName))
|
||||
{
|
||||
const QString subcatName = shortName(subcat);
|
||||
if (!parent->hasChild(subcatName))
|
||||
{
|
||||
const int torrentsCount = std::ranges::count_if(torrents
|
||||
, [&subcat](const Torrent *torrent) { return torrent->category() == subcat; });
|
||||
new CategoryModelItem(parent, subcatName, torrentsCount);
|
||||
}
|
||||
parent = parent->child(subcatName);
|
||||
const int torrentsCount = std::ranges::count_if(torrents
|
||||
, [&subcat](const Torrent *torrent) { return torrent->category() == subcat; });
|
||||
new CategoryModelItem(parent, subcatName, torrentsCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const QString &categoryName : asConst(session->categories()))
|
||||
{
|
||||
const int torrentsCount = std::ranges::count_if(torrents
|
||||
, [&categoryName](const Torrent *torrent) { return torrent->belongsToCategory(categoryName); });
|
||||
new CategoryModelItem(m_rootItem, categoryName, torrentsCount);
|
||||
parent = parent->child(subcatName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,9 +461,6 @@ CategoryModelItem *CategoryFilterModel::findItem(const QString &fullName) const
|
||||
if (fullName.isEmpty())
|
||||
return m_rootItem->childAt(1); // "Uncategorized" item
|
||||
|
||||
if (!m_isSubcategoriesEnabled)
|
||||
return m_rootItem->child(fullName);
|
||||
|
||||
CategoryModelItem *item = m_rootItem;
|
||||
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(fullName)))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2016-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
|
||||
@@ -72,6 +72,5 @@ private:
|
||||
QModelIndex index(CategoryModelItem *item) const;
|
||||
CategoryModelItem *findItem(const QString &fullName) const;
|
||||
|
||||
bool m_isSubcategoriesEnabled = false;
|
||||
CategoryModelItem *m_rootItem = nullptr;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2016-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
|
||||
@@ -72,9 +72,6 @@ CategoryFilterWidget::CategoryFilterWidget(QWidget *parent)
|
||||
#ifdef Q_OS_MACOS
|
||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
#endif
|
||||
m_defaultIndentation = indentation();
|
||||
if (!BitTorrent::Session::instance()->isSubcategoriesEnabled())
|
||||
setIndentation(0);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
setCurrentIndex(model()->index(0, 0));
|
||||
@@ -84,7 +81,18 @@ CategoryFilterWidget::CategoryFilterWidget(QWidget *parent)
|
||||
connect(this, &QWidget::customContextMenuRequested, this, &CategoryFilterWidget::showMenu);
|
||||
connect(selectionModel(), &QItemSelectionModel::currentRowChanged
|
||||
, this, &CategoryFilterWidget::onCurrentRowChanged);
|
||||
connect(model(), &QAbstractItemModel::modelReset, this, &CategoryFilterWidget::callUpdateGeometry);
|
||||
connect(model(), &QAbstractItemModel::rowsRemoved, this, [this]
|
||||
{
|
||||
adjustIndentation();
|
||||
updateGeometry();
|
||||
});
|
||||
connect(model(), &QAbstractItemModel::modelReset, this, [this]
|
||||
{
|
||||
adjustIndentation();
|
||||
updateGeometry();
|
||||
});
|
||||
|
||||
adjustIndentation();
|
||||
}
|
||||
|
||||
QString CategoryFilterWidget::currentCategory() const
|
||||
@@ -113,12 +121,8 @@ void CategoryFilterWidget::showMenu()
|
||||
const auto selectedRows = selectionModel()->selectedRows();
|
||||
if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first()))
|
||||
{
|
||||
if (BitTorrent::Session::instance()->isSubcategoriesEnabled())
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"list-add"_s), tr("Add subcategory...")
|
||||
, this, &CategoryFilterWidget::addSubcategory);
|
||||
}
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"list-add"_s), tr("Add subcategory...")
|
||||
, this, &CategoryFilterWidget::addSubcategory);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_s, u"document-edit"_s), tr("Edit category...")
|
||||
, this, &CategoryFilterWidget::editCategory);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_s, u"list-remove"_s), tr("Remove category")
|
||||
@@ -140,11 +144,6 @@ void CategoryFilterWidget::showMenu()
|
||||
|
||||
void CategoryFilterWidget::callUpdateGeometry()
|
||||
{
|
||||
if (!BitTorrent::Session::instance()->isSubcategoriesEnabled())
|
||||
setIndentation(0);
|
||||
else
|
||||
setIndentation(m_defaultIndentation);
|
||||
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -168,10 +167,13 @@ QSize CategoryFilterWidget::minimumSizeHint() const
|
||||
return size;
|
||||
}
|
||||
|
||||
void CategoryFilterWidget::rowsInserted(const QModelIndex &parent, int start, int end)
|
||||
void CategoryFilterWidget::rowsInserted(const QModelIndex &parent, const int start, const int end)
|
||||
{
|
||||
QTreeView::rowsInserted(parent, start, end);
|
||||
|
||||
if (parent.isValid())
|
||||
adjustIndentation();
|
||||
|
||||
// Expand all parents if the parent(s) of the node are not expanded.
|
||||
QModelIndex p = parent;
|
||||
while (p.isValid())
|
||||
@@ -184,6 +186,26 @@ void CategoryFilterWidget::rowsInserted(const QModelIndex &parent, int start, in
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
bool CategoryFilterWidget::hasAnySubcategory() const
|
||||
{
|
||||
const int rowCount = model()->rowCount();
|
||||
for (int row = 0; row < rowCount; ++row)
|
||||
{
|
||||
if (model()->hasChildren(model()->index(row, 0)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CategoryFilterWidget::adjustIndentation()
|
||||
{
|
||||
if (hasAnySubcategory())
|
||||
resetIndentation();
|
||||
else
|
||||
setIndentation(0);
|
||||
}
|
||||
|
||||
void CategoryFilterWidget::addCategory()
|
||||
{
|
||||
TorrentCategoryDialog::createCategory(this);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2016-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
|
||||
@@ -60,6 +60,6 @@ private:
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
void rowsInserted(const QModelIndex &parent, int start, int end) override;
|
||||
|
||||
int m_defaultIndentation;
|
||||
bool hasAnySubcategory() const;
|
||||
void adjustIndentation();
|
||||
};
|
||||
|
||||
@@ -49,18 +49,6 @@
|
||||
#include "transferlistwidget.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
enum ItemPos
|
||||
{
|
||||
StatusItemPos,
|
||||
CategoryItemPos,
|
||||
TagItemPos,
|
||||
TrackerStatusItemPos,
|
||||
TrackersItemPos
|
||||
};
|
||||
}
|
||||
|
||||
TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferListWidget *transferList, const bool downloadFavicon)
|
||||
: QWidget(parent)
|
||||
, m_transferList {transferList}
|
||||
@@ -80,7 +68,7 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi
|
||||
auto *item = new TransferListFiltersWidgetItem(tr("Status"), new StatusFilterWidget(this, transferList), this);
|
||||
item->setChecked(pref->getStatusFilterState());
|
||||
connect(item, &TransferListFiltersWidgetItem::toggled, pref, &Preferences::setStatusFilterState);
|
||||
mainWidgetLayout->insertWidget(StatusItemPos, item);
|
||||
mainWidgetLayout->addWidget(item);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -101,7 +89,7 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi
|
||||
m_transferList->applyCategoryFilter(enabled ? categoryFilterWidget->currentCategory() : QString());
|
||||
});
|
||||
connect(item, &TransferListFiltersWidgetItem::toggled, pref, &Preferences::setCategoryFilterState);
|
||||
mainWidgetLayout->insertWidget(CategoryItemPos, item);
|
||||
mainWidgetLayout->addWidget(item);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -122,16 +110,18 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi
|
||||
m_transferList->applyTagFilter(enabled ? tagFilterWidget->currentTag() : std::nullopt);
|
||||
});
|
||||
connect(item, &TransferListFiltersWidgetItem::toggled, pref, &Preferences::setTagFilterState);
|
||||
mainWidgetLayout->insertWidget(TagItemPos, item);
|
||||
mainWidgetLayout->addWidget(item);
|
||||
}
|
||||
|
||||
const int trackerStatusItemPos = mainWidgetLayout->count();
|
||||
|
||||
{
|
||||
m_trackersFilterWidget = new TrackersFilterWidget(this, transferList, downloadFavicon);
|
||||
|
||||
auto *item = new TransferListFiltersWidgetItem(tr("Trackers"), m_trackersFilterWidget, this);
|
||||
item->setChecked(pref->getTrackerFilterState());
|
||||
connect(item, &TransferListFiltersWidgetItem::toggled, pref, &Preferences::setTrackerFilterState);
|
||||
mainWidgetLayout->insertWidget(TrackersItemPos, item);
|
||||
mainWidgetLayout->addWidget(item);
|
||||
}
|
||||
|
||||
auto *scroll = new QScrollArea(this);
|
||||
@@ -144,17 +134,17 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi
|
||||
vLayout->setContentsMargins(0, 0, 0, 0);
|
||||
vLayout->addWidget(scroll);
|
||||
|
||||
const auto createTrackerStatusItem = [this, mainWidgetLayout, pref]
|
||||
const auto createTrackerStatusItem = [this, mainWidgetLayout, trackerStatusItemPos, pref]
|
||||
{
|
||||
auto *item = new TransferListFiltersWidgetItem(tr("Tracker status"), new TrackerStatusFilterWidget(this, m_transferList), this);
|
||||
item->setChecked(pref->getTrackerStatusFilterState());
|
||||
connect(item, &TransferListFiltersWidgetItem::toggled, pref, &Preferences::setTrackerStatusFilterState);
|
||||
mainWidgetLayout->insertWidget(TrackerStatusItemPos, item);
|
||||
mainWidgetLayout->insertWidget(trackerStatusItemPos, item);
|
||||
};
|
||||
|
||||
const auto removeTrackerStatusItem = [mainWidgetLayout]
|
||||
const auto removeTrackerStatusItem = [mainWidgetLayout, trackerStatusItemPos]
|
||||
{
|
||||
QLayoutItem *layoutItem = mainWidgetLayout->takeAt(TrackerStatusItemPos);
|
||||
QLayoutItem *layoutItem = mainWidgetLayout->takeAt(trackerStatusItemPos);
|
||||
delete layoutItem->widget();
|
||||
delete layoutItem;
|
||||
};
|
||||
|
||||
@@ -392,7 +392,7 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons
|
||||
case TR_RATIO:
|
||||
return ratioString(torrent->realRatio());
|
||||
case TR_RATIO_LIMIT:
|
||||
return ratioString(torrent->maxRatio());
|
||||
return ratioString(torrent->effectiveRatioLimit());
|
||||
case TR_POPULARITY:
|
||||
return ratioString(torrent->popularity());
|
||||
case TR_CATEGORY:
|
||||
@@ -509,7 +509,7 @@ QVariant TransferListModel::internalValue(const BitTorrent::Torrent *torrent, co
|
||||
case TR_COMPLETED:
|
||||
return torrent->completedSize();
|
||||
case TR_RATIO_LIMIT:
|
||||
return torrent->maxRatio();
|
||||
return torrent->effectiveRatioLimit();
|
||||
case TR_SEEN_COMPLETE_DATE:
|
||||
return torrent->lastSeenComplete();
|
||||
case TR_LAST_ACTIVITY:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# VERSION: 1.50
|
||||
# VERSION: 1.51
|
||||
|
||||
# Author:
|
||||
# Fabien Devaux <fab AT gnux DOT info>
|
||||
@@ -61,7 +61,7 @@ THREADED: bool = True
|
||||
try:
|
||||
MAX_THREADS: int = cpu_count()
|
||||
except NotImplementedError:
|
||||
MAX_THREADS = 1
|
||||
MAX_THREADS = 1 # pyright: ignore[reportConstantRedefinition]
|
||||
|
||||
Category = Enum('Category', ['all', 'anime', 'books', 'games', 'movies', 'music', 'pictures', 'software', 'tv'])
|
||||
|
||||
@@ -106,7 +106,7 @@ def list_engines() -> list[EngineModuleName]:
|
||||
Return list of all engines' module name
|
||||
"""
|
||||
|
||||
names = []
|
||||
names: list[EngineModuleName] = []
|
||||
|
||||
for engine_path in glob(path.join(path.dirname(__file__), 'engines', '*.py')):
|
||||
engine_module_name = path.basename(engine_path).split('.')[0].strip()
|
||||
|
||||
@@ -21,5 +21,8 @@ dev = [
|
||||
explicit_package_bases = true
|
||||
strict = true
|
||||
|
||||
[tool.pyright]
|
||||
typeCheckingMode = "strict"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["./"]
|
||||
|
||||
@@ -174,7 +174,6 @@ void AppController::preferencesAction()
|
||||
data[u"torrent_changed_tmm_enabled"_s] = !session->isDisableAutoTMMWhenCategoryChanged();
|
||||
data[u"save_path_changed_tmm_enabled"_s] = !session->isDisableAutoTMMWhenDefaultSavePathChanged();
|
||||
data[u"category_changed_tmm_enabled"_s] = !session->isDisableAutoTMMWhenCategorySavePathChanged();
|
||||
data[u"use_subcategories"] = session->isSubcategoriesEnabled();
|
||||
data[u"save_path"_s] = session->savePath().toString();
|
||||
data[u"temp_path_enabled"_s] = session->isDownloadPathEnabled();
|
||||
data[u"temp_path"_s] = session->downloadPath().toString();
|
||||
@@ -462,7 +461,7 @@ void AppController::preferencesAction()
|
||||
// UPnP lease duration
|
||||
data[u"upnp_lease_duration"_s] = session->UPnPLeaseDuration();
|
||||
// Type of service
|
||||
data[u"peer_tos"_s] = session->peerToS();
|
||||
data[u"peer_tos"_s] = session->peerDSCP();
|
||||
// uTP-TCP mixed mode
|
||||
data[u"utp_tcp_mixed_mode"_s] = static_cast<int>(session->utpMixedMode());
|
||||
// Hostname resolver cache TTL
|
||||
@@ -593,8 +592,6 @@ void AppController::setPreferencesAction()
|
||||
session->setDisableAutoTMMWhenDefaultSavePathChanged(!it.value().toBool());
|
||||
if (hasKey(u"category_changed_tmm_enabled"_s))
|
||||
session->setDisableAutoTMMWhenCategorySavePathChanged(!it.value().toBool());
|
||||
if (hasKey(u"use_subcategories"_s))
|
||||
session->setSubcategoriesEnabled(it.value().toBool());
|
||||
if (hasKey(u"save_path"_s))
|
||||
session->setSavePath(Path(it.value().toString()));
|
||||
if (hasKey(u"temp_path_enabled"_s))
|
||||
@@ -1117,7 +1114,7 @@ void AppController::setPreferencesAction()
|
||||
session->setUPnPLeaseDuration(it.value().toInt());
|
||||
// Type of service
|
||||
if (hasKey(u"peer_tos"_s))
|
||||
session->setPeerToS(it.value().toInt());
|
||||
session->setPeerDSCP(it.value().toInt());
|
||||
// uTP-TCP mixed mode
|
||||
if (hasKey(u"utp_tcp_mixed_mode"_s))
|
||||
session->setUtpMixedMode(static_cast<BitTorrent::MixedModeAlgorithm>(it.value().toInt()));
|
||||
|
||||
@@ -163,9 +163,9 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent)
|
||||
{KEY_TORRENT_AMOUNT_COMPLETED, torrent.completedSize()},
|
||||
{KEY_TORRENT_CONNECTIONS_COUNT, torrent.connectionsCount()},
|
||||
{KEY_TORRENT_CONNECTIONS_LIMIT, torrent.connectionsLimit()},
|
||||
{KEY_TORRENT_MAX_RATIO, torrent.maxRatio()},
|
||||
{KEY_TORRENT_MAX_SEEDING_TIME, torrent.maxSeedingTime()},
|
||||
{KEY_TORRENT_MAX_INACTIVE_SEEDING_TIME, torrent.maxInactiveSeedingTime()},
|
||||
{KEY_TORRENT_MAX_RATIO, torrent.effectiveRatioLimit()},
|
||||
{KEY_TORRENT_MAX_SEEDING_TIME, torrent.effectiveSeedingTimeLimit()},
|
||||
{KEY_TORRENT_MAX_INACTIVE_SEEDING_TIME, torrent.effectiveInactiveSeedingTimeLimit()},
|
||||
{KEY_TORRENT_RATIO, adjustRatio(torrent.realRatio())},
|
||||
{KEY_TORRENT_RATIO_LIMIT, torrent.ratioLimit()},
|
||||
{KEY_TORRENT_POPULARITY, torrent.popularity()},
|
||||
|
||||
@@ -56,7 +56,6 @@ namespace
|
||||
const QString KEY_SYNC_MAINDATA_QUEUEING = u"queueing"_s;
|
||||
const QString KEY_SYNC_MAINDATA_REFRESH_INTERVAL = u"refresh_interval"_s;
|
||||
const QString KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS = u"use_alt_speed_limits"_s;
|
||||
const QString KEY_SYNC_MAINDATA_USE_SUBCATEGORIES = u"use_subcategories"_s;
|
||||
|
||||
// Sync torrent peers keys
|
||||
const QString KEY_SYNC_TORRENT_PEERS_SHOW_FLAGS = u"show_flags"_s;
|
||||
@@ -617,7 +616,6 @@ void SyncController::makeMaindataSnapshot()
|
||||
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled();
|
||||
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
|
||||
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
|
||||
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_USE_SUBCATEGORIES] = session->isSubcategoriesEnabled();
|
||||
}
|
||||
|
||||
QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fullUpdate)
|
||||
@@ -771,7 +769,6 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
|
||||
serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled();
|
||||
serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
|
||||
serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
|
||||
serverState[KEY_SYNC_MAINDATA_USE_SUBCATEGORIES] = session->isSubcategoriesEnabled();
|
||||
if (const QVariantMap syncData = processMap(m_maindataSnapshot.serverState, serverState); !syncData.isEmpty())
|
||||
{
|
||||
m_maindataSyncBuf.serverState = syncData;
|
||||
|
||||
@@ -1066,9 +1066,9 @@ void TorrentsController::addAction()
|
||||
const QString torrentName = params()[u"rename"_s].trimmed();
|
||||
const int upLimit = parseInt(params()[u"upLimit"_s]).value_or(-1);
|
||||
const int dlLimit = parseInt(params()[u"dlLimit"_s]).value_or(-1);
|
||||
const double ratioLimit = parseDouble(params()[u"ratioLimit"_s]).value_or(BitTorrent::Torrent::USE_GLOBAL_RATIO);
|
||||
const int seedingTimeLimit = parseInt(params()[u"seedingTimeLimit"_s]).value_or(BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||
const int inactiveSeedingTimeLimit = parseInt(params()[u"inactiveSeedingTimeLimit"_s]).value_or(BitTorrent::Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME);
|
||||
const double ratioLimit = parseDouble(params()[u"ratioLimit"_s]).value_or(BitTorrent::DEFAULT_RATIO_LIMIT);
|
||||
const int seedingTimeLimit = parseInt(params()[u"seedingTimeLimit"_s]).value_or(BitTorrent::DEFAULT_SEEDING_TIME_LIMIT);
|
||||
const int inactiveSeedingTimeLimit = parseInt(params()[u"inactiveSeedingTimeLimit"_s]).value_or(BitTorrent::DEFAULT_SEEDING_TIME_LIMIT);
|
||||
const BitTorrent::ShareLimitAction shareLimitAction = Utils::String::toEnum(params()[u"shareLimitAction"_s], BitTorrent::ShareLimitAction::Default);
|
||||
const std::optional<bool> autoTMM = parseBool(params()[u"autoTMM"_s]);
|
||||
|
||||
@@ -1897,7 +1897,7 @@ void TorrentsController::editCategoryAction()
|
||||
categoryOptions.downloadPath = {useDownloadPath.value(), downloadPath};
|
||||
}
|
||||
|
||||
if (!BitTorrent::Session::instance()->editCategory(category, categoryOptions))
|
||||
if (!BitTorrent::Session::instance()->setCategoryOptions(category, categoryOptions))
|
||||
throw APIError(APIErrorType::Conflict, tr("Unable to edit category"));
|
||||
|
||||
setResult(QString());
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
inline const Utils::Version<3, 2> API_VERSION {2, 14, 1};
|
||||
inline const Utils::Version<3, 2> API_VERSION {2, 15, 0};
|
||||
|
||||
class APIController;
|
||||
class AuthController;
|
||||
|
||||
@@ -222,7 +222,6 @@ let alternativeSpeedLimits = false;
|
||||
let queueing_enabled = true;
|
||||
let serverSyncMainDataInterval = 1500;
|
||||
let customSyncMainDataInterval = null;
|
||||
let useSubcategories = true;
|
||||
const useAutoHideZeroStatusFilters = localPreferences.get("hide_zero_status_filters", "false") === "true";
|
||||
const displayFullURLTrackerColumn = localPreferences.get("full_url_tracker_column", "false") === "true";
|
||||
|
||||
@@ -634,7 +633,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||
categoryName: category,
|
||||
categoryCount: categoryData.torrents.size,
|
||||
nameSegments: category.split("/"),
|
||||
...(useSubcategories && {
|
||||
...({
|
||||
children: [],
|
||||
isRoot: true,
|
||||
forceExpand: localPreferences.get(`category_${category}_collapsed`) === null
|
||||
@@ -659,32 +658,25 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||
categoriesFragment.appendChild(createLink(CATEGORIES_ALL, "QBT_TR(All)QBT_TR[CONTEXT=CategoryFilterModel]", torrentsTable.getRowSize()));
|
||||
categoriesFragment.appendChild(createLink(CATEGORIES_UNCATEGORIZED, "QBT_TR(Uncategorized)QBT_TR[CONTEXT=CategoryFilterModel]", uncategorized));
|
||||
|
||||
if (useSubcategories) {
|
||||
categoryList.classList.add("subcategories");
|
||||
for (let i = 0; i < sortedCategories.length; ++i) {
|
||||
const category = sortedCategories[i];
|
||||
for (let j = (i + 1);
|
||||
((j < sortedCategories.length) && sortedCategories[j].categoryName.startsWith(`${category.categoryName}/`)); ++j) {
|
||||
const subcategory = sortedCategories[j];
|
||||
category.categoryCount += subcategory.categoryCount;
|
||||
category.forceExpand ||= subcategory.forceExpand;
|
||||
categoryList.classList.add("subcategories");
|
||||
for (let i = 0; i < sortedCategories.length; ++i) {
|
||||
const category = sortedCategories[i];
|
||||
for (let j = (i + 1);
|
||||
((j < sortedCategories.length) && sortedCategories[j].categoryName.startsWith(`${category.categoryName}/`)); ++j) {
|
||||
const subcategory = sortedCategories[j];
|
||||
category.categoryCount += subcategory.categoryCount;
|
||||
category.forceExpand ||= subcategory.forceExpand;
|
||||
|
||||
const isDirectSubcategory = (subcategory.nameSegments.length - category.nameSegments.length) === 1;
|
||||
if (isDirectSubcategory) {
|
||||
subcategory.isRoot = false;
|
||||
category.children.push(subcategory);
|
||||
}
|
||||
const isDirectSubcategory = (subcategory.nameSegments.length - category.nameSegments.length) === 1;
|
||||
if (isDirectSubcategory) {
|
||||
subcategory.isRoot = false;
|
||||
category.children.push(subcategory);
|
||||
}
|
||||
}
|
||||
for (const category of sortedCategories) {
|
||||
if (category.isRoot)
|
||||
createCategoryTree(category);
|
||||
}
|
||||
}
|
||||
else {
|
||||
categoryList.classList.remove("subcategories");
|
||||
for (const { categoryName, categoryCount } of sortedCategories)
|
||||
categoriesFragment.appendChild(createLink(categoryName, categoryName, categoryCount));
|
||||
for (const category of sortedCategories) {
|
||||
if (category.isRoot)
|
||||
createCategoryTree(category);
|
||||
}
|
||||
|
||||
categoryList.appendChild(categoriesFragment);
|
||||
@@ -1178,11 +1170,6 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||
updateAltSpeedIcon(alternativeSpeedLimits);
|
||||
}
|
||||
|
||||
if (useSubcategories !== serverState.use_subcategories) {
|
||||
useSubcategories = serverState.use_subcategories;
|
||||
updateCategoryList();
|
||||
}
|
||||
|
||||
serverSyncMainDataInterval = Math.max(serverState.refresh_interval, 500);
|
||||
};
|
||||
|
||||
|
||||
@@ -585,10 +585,7 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||
if ((id !== CATEGORIES_ALL) && (id !== CATEGORIES_UNCATEGORIZED)) {
|
||||
this.showItem("editCategory");
|
||||
this.showItem("deleteCategory");
|
||||
if (useSubcategories)
|
||||
this.showItem("createSubcategory");
|
||||
else
|
||||
this.hideItem("createSubcategory");
|
||||
this.showItem("createSubcategory");
|
||||
}
|
||||
else {
|
||||
this.hideItem("editCategory");
|
||||
|
||||
@@ -564,7 +564,8 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
column["force_hide"] = false;
|
||||
column["caption"] = caption;
|
||||
column["style"] = style;
|
||||
column["width"] = localPreferences.get(`column_${name}_width_${this.dynamicTableDivId}`, defaultWidth);
|
||||
if (defaultWidth !== -1)
|
||||
column["width"] = localPreferences.get(`column_${name}_width_${this.dynamicTableDivId}`, defaultWidth);
|
||||
column["dataProperties"] = [name];
|
||||
column["getRowValue"] = function(row, pos) {
|
||||
if (pos === undefined)
|
||||
@@ -1620,21 +1621,16 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
return false;
|
||||
break; // do nothing
|
||||
|
||||
default:
|
||||
if (!useSubcategories) {
|
||||
if (category !== row["full_data"].category)
|
||||
default: {
|
||||
const selectedCategory = window.qBittorrent.Client.categoryMap.get(category);
|
||||
if (selectedCategory !== undefined) {
|
||||
const selectedCategoryName = `${category}/`;
|
||||
const torrentCategoryName = `${row["full_data"].category}/`;
|
||||
if (!torrentCategoryName.startsWith(selectedCategoryName))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
const selectedCategory = window.qBittorrent.Client.categoryMap.get(category);
|
||||
if (selectedCategory !== undefined) {
|
||||
const selectedCategoryName = `${category}/`;
|
||||
const torrentCategoryName = `${row["full_data"].category}/`;
|
||||
if (!torrentCategoryName.startsWith(selectedCategoryName))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
@@ -2693,32 +2689,30 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
#filterNodes(root, filterTerms) {
|
||||
const ret = [];
|
||||
const stack = [root];
|
||||
const visited = [];
|
||||
|
||||
while (stack.length > 0) {
|
||||
const node = stack.at(-1);
|
||||
|
||||
if (node.isFolder && (!this.useVirtualList || !this.isCollapsed(node.rowId))) {
|
||||
const lastVisited = visited.at(-1);
|
||||
|
||||
if ((visited.length <= 0) || (lastVisited !== node)) {
|
||||
visited.push(node);
|
||||
if (node._visited === undefined) {
|
||||
node._visited = true;
|
||||
stack.push(...node.children);
|
||||
continue;
|
||||
}
|
||||
|
||||
// has children added or itself matches
|
||||
if (lastVisited.has_children_added || window.qBittorrent.Misc.containsAllTerms(node.name, filterTerms)) {
|
||||
// ready to check results from children at this point
|
||||
|
||||
if (node._hasChildrenAdded || window.qBittorrent.Misc.containsAllTerms(node.name, filterTerms)) {
|
||||
ret.push(this.getRow(node));
|
||||
delete node.has_children_added;
|
||||
delete node._hasChildrenAdded;
|
||||
|
||||
// propagate up
|
||||
const parent = node.root;
|
||||
if (parent !== undefined)
|
||||
parent.has_children_added = true;
|
||||
parent._hasChildrenAdded = true;
|
||||
}
|
||||
|
||||
visited.pop();
|
||||
delete node._visited;
|
||||
}
|
||||
else {
|
||||
if (window.qBittorrent.Misc.containsAllTerms(node[this.fileNameColumn], filterTerms)) {
|
||||
@@ -2726,7 +2720,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
|
||||
const parent = node.root;
|
||||
if (parent !== undefined)
|
||||
parent.has_children_added = true;
|
||||
parent._hasChildrenAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3098,9 +3092,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
return [...this.getRowValues()];
|
||||
}
|
||||
selectRow(rowId) {
|
||||
this.selectedRows.push(rowId);
|
||||
this.setRowClass();
|
||||
this.onSelectedRowChanged();
|
||||
super.selectRow(rowId);
|
||||
|
||||
let path = "";
|
||||
for (const row of this.getRowValues()) {
|
||||
@@ -3177,42 +3169,6 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
newColumn(name, style, caption, defaultWidth, defaultVisible) {
|
||||
const column = {};
|
||||
column["name"] = name;
|
||||
column["title"] = name;
|
||||
column["visible"] = defaultVisible;
|
||||
column["force_hide"] = false;
|
||||
column["caption"] = caption;
|
||||
column["style"] = style;
|
||||
if (defaultWidth !== -1)
|
||||
column["width"] = defaultWidth;
|
||||
|
||||
column["dataProperties"] = [name];
|
||||
column["getRowValue"] = function(row, pos) {
|
||||
if (pos === undefined)
|
||||
pos = 0;
|
||||
return row["full_data"][this.dataProperties[pos]];
|
||||
};
|
||||
column["compareRows"] = function(row1, row2) {
|
||||
const value1 = this.getRowValue(row1);
|
||||
const value2 = this.getRowValue(row2);
|
||||
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
||||
return compareNumbers(value1, value2);
|
||||
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
||||
};
|
||||
column["updateTd"] = function(td, row) {
|
||||
const value = this.getRowValue(row);
|
||||
td.textContent = value;
|
||||
td.title = value;
|
||||
};
|
||||
column["onResize"] = null;
|
||||
this.columns.push(column);
|
||||
this.columns[name] = column;
|
||||
|
||||
this.hiddenTableHeader.append(document.createElement("th"));
|
||||
this.fixedTableHeader.append(document.createElement("th"));
|
||||
}
|
||||
}
|
||||
|
||||
class RssArticleTable extends DynamicTable {
|
||||
@@ -3225,9 +3181,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
return [...this.getRowValues()];
|
||||
}
|
||||
selectRow(rowId) {
|
||||
this.selectedRows.push(rowId);
|
||||
this.setRowClass();
|
||||
this.onSelectedRowChanged();
|
||||
super.selectRow(rowId);
|
||||
|
||||
let articleId = "";
|
||||
let feedUid = "";
|
||||
@@ -3259,42 +3213,6 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
|
||||
return super.updateRow(tr, fullUpdate);
|
||||
}
|
||||
newColumn(name, style, caption, defaultWidth, defaultVisible) {
|
||||
const column = {};
|
||||
column["name"] = name;
|
||||
column["title"] = name;
|
||||
column["visible"] = defaultVisible;
|
||||
column["force_hide"] = false;
|
||||
column["caption"] = caption;
|
||||
column["style"] = style;
|
||||
if (defaultWidth !== -1)
|
||||
column["width"] = defaultWidth;
|
||||
|
||||
column["dataProperties"] = [name];
|
||||
column["getRowValue"] = function(row, pos) {
|
||||
if (pos === undefined)
|
||||
pos = 0;
|
||||
return row["full_data"][this.dataProperties[pos]];
|
||||
};
|
||||
column["compareRows"] = function(row1, row2) {
|
||||
const value1 = this.getRowValue(row1);
|
||||
const value2 = this.getRowValue(row2);
|
||||
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
||||
return compareNumbers(value1, value2);
|
||||
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
||||
};
|
||||
column["updateTd"] = function(td, row) {
|
||||
const value = this.getRowValue(row);
|
||||
td.textContent = value;
|
||||
td.title = value;
|
||||
};
|
||||
column["onResize"] = null;
|
||||
this.columns.push(column);
|
||||
this.columns[name] = column;
|
||||
|
||||
this.hiddenTableHeader.append(document.createElement("th"));
|
||||
this.fixedTableHeader.append(document.createElement("th"));
|
||||
}
|
||||
}
|
||||
|
||||
class RssDownloaderRulesTable extends DynamicTable {
|
||||
@@ -3342,46 +3260,8 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
window.qBittorrent.RssDownloader.renameRule(this.getRow(tr.rowId).full_data.name);
|
||||
});
|
||||
}
|
||||
newColumn(name, style, caption, defaultWidth, defaultVisible) {
|
||||
const column = {};
|
||||
column["name"] = name;
|
||||
column["title"] = name;
|
||||
column["visible"] = defaultVisible;
|
||||
column["force_hide"] = false;
|
||||
column["caption"] = caption;
|
||||
column["style"] = style;
|
||||
if (defaultWidth !== -1)
|
||||
column["width"] = defaultWidth;
|
||||
|
||||
column["dataProperties"] = [name];
|
||||
column["getRowValue"] = function(row, pos) {
|
||||
if (pos === undefined)
|
||||
pos = 0;
|
||||
return row["full_data"][this.dataProperties[pos]];
|
||||
};
|
||||
column["compareRows"] = function(row1, row2) {
|
||||
const value1 = this.getRowValue(row1);
|
||||
const value2 = this.getRowValue(row2);
|
||||
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
||||
return compareNumbers(value1, value2);
|
||||
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
||||
};
|
||||
column["updateTd"] = function(td, row) {
|
||||
const value = this.getRowValue(row);
|
||||
td.textContent = value;
|
||||
td.title = value;
|
||||
};
|
||||
column["onResize"] = null;
|
||||
this.columns.push(column);
|
||||
this.columns[name] = column;
|
||||
|
||||
this.hiddenTableHeader.append(document.createElement("th"));
|
||||
this.fixedTableHeader.append(document.createElement("th"));
|
||||
}
|
||||
selectRow(rowId) {
|
||||
this.selectedRows.push(rowId);
|
||||
this.setRowClass();
|
||||
this.onSelectedRowChanged();
|
||||
super.selectRow(rowId);
|
||||
|
||||
let name = "";
|
||||
for (const row of this.getRowValues()) {
|
||||
@@ -3427,42 +3307,6 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
getFilteredAndSortedRows() {
|
||||
return [...this.getRowValues()];
|
||||
}
|
||||
newColumn(name, style, caption, defaultWidth, defaultVisible) {
|
||||
const column = {};
|
||||
column["name"] = name;
|
||||
column["title"] = name;
|
||||
column["visible"] = defaultVisible;
|
||||
column["force_hide"] = false;
|
||||
column["caption"] = caption;
|
||||
column["style"] = style;
|
||||
if (defaultWidth !== -1)
|
||||
column["width"] = defaultWidth;
|
||||
|
||||
column["dataProperties"] = [name];
|
||||
column["getRowValue"] = function(row, pos) {
|
||||
if (pos === undefined)
|
||||
pos = 0;
|
||||
return row["full_data"][this.dataProperties[pos]];
|
||||
};
|
||||
column["compareRows"] = function(row1, row2) {
|
||||
const value1 = this.getRowValue(row1);
|
||||
const value2 = this.getRowValue(row2);
|
||||
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
||||
return compareNumbers(value1, value2);
|
||||
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
||||
};
|
||||
column["updateTd"] = function(td, row) {
|
||||
const value = this.getRowValue(row);
|
||||
td.textContent = value;
|
||||
td.title = value;
|
||||
};
|
||||
column["onResize"] = null;
|
||||
this.columns.push(column);
|
||||
this.columns[name] = column;
|
||||
|
||||
this.hiddenTableHeader.append(document.createElement("th"));
|
||||
this.fixedTableHeader.append(document.createElement("th"));
|
||||
}
|
||||
selectRow() {}
|
||||
}
|
||||
|
||||
@@ -3475,42 +3319,6 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||
getFilteredAndSortedRows() {
|
||||
return [...this.getRowValues()];
|
||||
}
|
||||
newColumn(name, style, caption, defaultWidth, defaultVisible) {
|
||||
const column = {};
|
||||
column["name"] = name;
|
||||
column["title"] = name;
|
||||
column["visible"] = defaultVisible;
|
||||
column["force_hide"] = false;
|
||||
column["caption"] = caption;
|
||||
column["style"] = style;
|
||||
if (defaultWidth !== -1)
|
||||
column["width"] = defaultWidth;
|
||||
|
||||
column["dataProperties"] = [name];
|
||||
column["getRowValue"] = function(row, pos) {
|
||||
if (pos === undefined)
|
||||
pos = 0;
|
||||
return row["full_data"][this.dataProperties[pos]];
|
||||
};
|
||||
column["compareRows"] = function(row1, row2) {
|
||||
const value1 = this.getRowValue(row1);
|
||||
const value2 = this.getRowValue(row2);
|
||||
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
||||
return compareNumbers(value1, value2);
|
||||
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
||||
};
|
||||
column["updateTd"] = function(td, row) {
|
||||
const value = this.getRowValue(row);
|
||||
td.textContent = value;
|
||||
td.title = value;
|
||||
};
|
||||
column["onResize"] = null;
|
||||
this.columns.push(column);
|
||||
this.columns[name] = column;
|
||||
|
||||
this.hiddenTableHeader.append(document.createElement("th"));
|
||||
this.fixedTableHeader.append(document.createElement("th"));
|
||||
}
|
||||
selectRow() {}
|
||||
updateRow(tr, fullUpdate) {
|
||||
const row = this.rows.get(tr.rowId);
|
||||
|
||||
@@ -229,10 +229,6 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="formRow">
|
||||
<input type="checkbox" id="use_subcategories_checkbox">
|
||||
<label for="use_subcategories_checkbox">QBT_TR(Use Subcategories)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||
</div>
|
||||
<div class="formRow">
|
||||
<input type="checkbox" id="categoryPathsManualModeCheckbox" title="QBT_TR(Resolve relative Save Path against appropriate Category path instead of Default one)QBT_TR[CONTEXT=OptionsDialog]">
|
||||
<label for="categoryPathsManualModeCheckbox">QBT_TR(Use Category paths in Manual Mode)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||
@@ -1565,10 +1561,10 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="peerToS">QBT_TR(Type of service (ToS) for connections to peers)QBT_TR[CONTEXT=OptionsDialog] <a href="https://www.libtorrent.org/reference-Settings.html#peer_tos" target="_blank">(?)</a></label>
|
||||
<label for="peerDSCP">QBT_TR(Differentiated Services Code Point (DSCP) for connections to peers)QBT_TR[CONTEXT=OptionsDialog] <a href="https://www.libtorrent.org/reference-Settings.html#peer_dscp" target="_blank">(?)</a></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="peerToS" style="width: 15em;">
|
||||
<input type="text" id="peerDSCP" style="width: 15em;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -2332,7 +2328,6 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||
document.getElementById("torrent_changed_tmm_combobox").value = pref.torrent_changed_tmm_enabled;
|
||||
document.getElementById("save_path_changed_tmm_combobox").value = pref.save_path_changed_tmm_enabled;
|
||||
document.getElementById("category_changed_tmm_combobox").value = pref.category_changed_tmm_enabled;
|
||||
document.getElementById("use_subcategories_checkbox").checked = pref.use_subcategories;
|
||||
document.getElementById("categoryPathsManualModeCheckbox").checked = pref.use_category_paths_in_manual_mode;
|
||||
document.getElementById("savepath_text").value = pref.save_path;
|
||||
document.getElementById("temppath_checkbox").checked = pref.temp_path_enabled;
|
||||
@@ -2673,7 +2668,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||
document.getElementById("outgoingPortsMin").value = pref.outgoing_ports_min;
|
||||
document.getElementById("outgoingPortsMax").value = pref.outgoing_ports_max;
|
||||
document.getElementById("UPnPLeaseDuration").value = pref.upnp_lease_duration;
|
||||
document.getElementById("peerToS").value = pref.peer_tos;
|
||||
document.getElementById("peerDSCP").value = pref.peer_tos;
|
||||
document.getElementById("utpTCPMixedModeAlgorithm").value = pref.utp_tcp_mixed_mode;
|
||||
document.getElementById("hostnameCacheTTL").value = pref.hostname_cache_ttl;
|
||||
document.getElementById("IDNSupportCheckbox").checked = pref.idn_support_enabled;
|
||||
@@ -2754,7 +2749,6 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||
settings["torrent_changed_tmm_enabled"] = (document.getElementById("torrent_changed_tmm_combobox").value === "true");
|
||||
settings["save_path_changed_tmm_enabled"] = (document.getElementById("save_path_changed_tmm_combobox").value === "true");
|
||||
settings["category_changed_tmm_enabled"] = (document.getElementById("category_changed_tmm_combobox").value === "true");
|
||||
settings["use_subcategories"] = document.getElementById("use_subcategories_checkbox").checked;
|
||||
settings["use_category_paths_in_manual_mode"] = document.getElementById("categoryPathsManualModeCheckbox").checked;
|
||||
settings["save_path"] = document.getElementById("savepath_text").value;
|
||||
settings["temp_path_enabled"] = document.getElementById("temppath_checkbox").checked;
|
||||
@@ -3162,12 +3156,12 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||
settings["outgoing_ports_max"] = Number(document.getElementById("outgoingPortsMax").value);
|
||||
settings["upnp_lease_duration"] = Number(document.getElementById("UPnPLeaseDuration").value);
|
||||
|
||||
const peerToS = Number(document.getElementById("peerToS").value);
|
||||
if (Number.isNaN(peerToS) || (peerToS < 0) || (peerToS > 255)) {
|
||||
alert("QBT_TR(Peer ToS must be between 0 and 255.)QBT_TR[CONTEXT=HttpServer]");
|
||||
const peerDSCP = Number(document.getElementById("peerDSCP").value);
|
||||
if (Number.isNaN(peerDSCP) || (peerDSCP < 0) || (peerDSCP > 255)) {
|
||||
alert("QBT_TR(Peer DSCP must be between 0 and 255.)QBT_TR[CONTEXT=HttpServer]");
|
||||
return;
|
||||
}
|
||||
settings["peer_tos"] = peerToS;
|
||||
settings["peer_tos"] = peerDSCP;
|
||||
|
||||
settings["utp_tcp_mixed_mode"] = Number(document.getElementById("utpTCPMixedModeAlgorithm").value);
|
||||
settings["hostname_cache_ttl"] = Number(document.getElementById("hostnameCacheTTL").value);
|
||||
@@ -3225,8 +3219,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||
window.parent.location.reload();
|
||||
window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage"));
|
||||
}).catch((error) => {
|
||||
// keep window open so user can reattempt saving
|
||||
alert("QBT_TR(Unable to save program preferences, qBittorrent is probably unreachable.)QBT_TR[CONTEXT=HttpServer]");
|
||||
window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage"));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user