1
mirror of https://github.com/qbittorrent/qBittorrent synced 2025-10-09 18:32:15 +02:00

Compare commits

...

73 Commits

Author SHA1 Message Date
sledgehammer999
cce295faeb Bump to 5.0.0 2024-09-29 20:53:45 +03:00
sledgehammer999
db5479ea01 Update Changelog 2024-09-29 20:49:43 +03:00
sledgehammer999
e1216c4c9a Sync translations from Transifex and run lupdate 2024-09-29 20:36:58 +03:00
sledgehammer999
f4a0868426 Make Program Updater choose the same build for download
We're probably stuck offering the duo of RC_1_2 and RC_2_0 for some
time in the future. So hardcode the choices and make the Program Updater
choose the variant the user currently uses.
2024-09-29 20:28:10 +03:00
sledgehammer999
59a5fcf7d0 Sync translations from Transifex and run lupdate 2024-09-13 11:10:38 +03:00
Vladimir Golovnev
f9a2b02a8d Backport changes to v5.0.x branch
PR #21241.
2024-09-12 08:42:52 +03:00
skomerko
04f6a565f3 WebUI: Provide 'Merge trackers to existing torrent' option
PR #21302.
2024-09-11 19:18:17 +03:00
Vladimir Golovnev
3e96048ee4 Apply "merge trackers" logic regardless of way the torrent is added
PR #21299.
2024-09-07 09:13:19 +04:00
Prince Gupta
d4ccf3001c Fix highlighted piece color
PR #20971.
2024-09-02 16:17:57 +03:00
skomerko
64506f16bd WebUI: Provide 'Use Category paths in Manual Mode' option
PR #21223.
2024-08-26 13:10:05 +03:00
sledgehammer999
24a7a835af Create new resources for this branch for Transifex 2024-08-26 01:11:05 +03:00
sledgehammer999
93b9bf9552 Sync translations from Transifex and run lupdate 2024-08-26 01:05:21 +03:00
Vladimir Golovnev
f4125601de Refresh search results colors once color scheme is changed
* Refresh search results colors once color scheme is changed
* Improve color of visited search result items

PR #21189.
Closes #21187.
2024-08-21 15:12:07 +03:00
sledgehammer999
2d67729617 Bump to v5.0.0rc1 2024-08-18 23:21:21 +03:00
sledgehammer999
878ebbed41 Update Changelog 2024-08-18 23:17:25 +03:00
Vladimir Golovnev
c61c3d7cd8 Backport changes to v5.0.x branch
PR #21164.
2024-08-16 07:17:21 +03:00
skomerko
978fbbdc0d WebUI: Always create generic filter items
PR #21188.
2024-08-15 20:37:19 +03:00
stalkerok
63689cf763 Add a flag about the connection peers are using NAT hole punching
PR #21052.
2024-08-15 20:33:45 +03:00
thalieht
cebc72d3cf WebUI: Add missing columns in transfer list
* Incomplete Save Path
* Info Hash v1
* Info Hash v2

PR #21158.
2024-08-15 20:32:40 +03:00
Vladimir Golovnev
a67bd271c6 Refresh pieces bar colors once color scheme is changed
PR #21183.
Closes #21155.
2024-08-13 09:37:47 +03:00
skomerko
a8cffbb205 WebUI: Clear trackerList on full update
Like other similar data structures, trackerList also need to be cleared in the event of a full sync update.

PR #21148.
2024-08-11 14:20:45 +03:00
Vladimir Golovnev
7dfb0110d4 Fix Incomplete Save Path cannot be changed for torrents without metadata
PR #21152.
Closes #21140.
2024-08-08 08:22:54 +03:00
Vladimir Golovnev
3ad8fcbdd2 Hide zero status filters when torrents removed
PR #21150.
Closes #21146.
2024-08-08 08:22:51 +03:00
Vladimir Golovnev
195eae5f3d Backport changes to v5.0.x branch
PR #20996.
2024-08-02 21:22:49 +03:00
Hanabishi
920ae26f7b WebUI: Fix Torrent Management Mode selector
PR #21053.
2024-07-15 17:40:17 +03:00
David Newhall
09ed0d6b66 WebAPI: Add root_path to torrent/info result
PR #21066.
Closes #21057.
2024-07-15 08:52:52 +03:00
Vladimir Golovnev
4f0cc8aa11 Fix incorrect sorting by "private" column
PR #21041.
2024-07-15 08:52:42 +03:00
ManiMatter
4d490c84e7 Add ability to display torrent "privateness" in UI
PR #20951.

---------

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
Co-authored-by: thalieht <thalieht@users.noreply.github.com>
2024-07-15 08:52:23 +03:00
Vladimir Golovnev
96607ce874 Prevent incorrect size from being used for creating array
PR #21050.
2024-07-12 08:51:08 +03:00
Vladimir Golovnev
418edc7471 Apply bulk changes to correct content widget items
PR #21006.
Closes #21001.
2024-07-08 16:51:33 +03:00
Vladimir Golovnev
bd01b7c4df WebUI: Correctly apply changed "save path" of RSS rules
PR #21030.
Closes #20141.
2024-07-08 10:18:02 +03:00
Vladimir Golovnev
b0ac763048 Show scroll bar in Torrent Tags dialog
PR #21026.
Closes #21022.
2024-07-07 16:10:07 +03:00
Vladimir Golovnev
127d2d6f0b Fix handling of tags containing '&' character
PR #21024.
Closes #20773.
2024-07-07 16:10:05 +03:00
Vladimir Golovnev
4149609e78 Allow to move content files to Trash instead of deleting them
PR #20252.
2024-07-07 16:09:48 +03:00
Vladimir Golovnev
78c549f83e Use custom storage when reloading torrent
PR #20998.
2024-07-07 16:07:22 +03:00
Thomas Piccirello
a3a53e2e0e WebUI: Fix preference name conflict
PR #20990.
2024-07-07 16:06:55 +03:00
Vladimir Golovnev
5aaa43e01d Restore ability to use server-side translation by custom WebUI
PR #20968.
2024-06-29 21:59:22 +03:00
Chocobo1
86745d7b07 GHA CI: use static versions of AppImage builder
It does not affect the produced artifacts. The only difference is the
tool itself won't depend on some specific OS image or library version.

PR #20983.
2024-06-25 21:13:20 +03:00
Thomas Piccirello
210650a5ee Use enabled search plugins by default in WebUI
PR #20969.
Closes #20558.
2024-06-25 21:13:20 +03:00
Chocobo1
fe93b6d0d8 Use proper casting
Previously `m_shutdownTimeout * 1000` was calculated in `int` and now it
is `qint64`.

PR #20982.
2024-06-25 21:13:19 +03:00
Chocobo1
e8b585acd8 Allow numeric types
The canonical type for `size_string` is `str`. However numeric types are also accepted in order
to accommodate poorly written plugins.

PR #20976.
2024-06-25 21:13:19 +03:00
vikas_c
cea20141a9 Show download progress for folders with zero byte size as 100 instead of 0
Fixes the download progress calculation for folders with zero size.
Previously, the progress would be Zero. Now, folders with zero size
show 100% progress.

PR #20567.
2024-06-25 21:13:19 +03:00
Chocobo1
0f5a27ed50 Improve connection handling
1. Previously unhandled connections will stay in pending state. It won't
be closed until timeout happened. This may lead to wasting system
resources. Now the (over-limit) connection is actively rejected.

2. When out-of-memory occurs here, reject the new connection instead of
throwing exception and crash.

3. Also clean up some unused bits.

PR #20961.
2024-06-25 21:13:18 +03:00
Vladimir Golovnev
c2cf898ccd Allow to use regular expression to filter torrent content
PR #20944.
Closes #19934.
2024-06-25 21:13:18 +03:00
Chocobo1
5e5aa8a563 Add type annotations
A few code are revised because the type checker (mypy) doesn't allow
changing types on a variable.

PR #20935.
2024-06-25 21:13:18 +03:00
ManiMatter
12a4c3fda2 WebAPI: Add "private" filter for 'info' endpoint
PR #20833.

---------

Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2024-06-25 21:13:17 +03:00
Vladimir Golovnev
5f50b701d2 Don't use custom "file icon provider" on Windows
PR #20936.
Closes #20908.
2024-06-25 21:13:17 +03:00
Chocobo1
9f20d9c3aa Revise Protocol column
Add "BT" (BitTorrent) to avoid confusion about which protocol it is referring to.
Also its value doesn't need to be translated.

PR #20897.
2024-06-25 21:13:17 +03:00
Vladimir Golovnev
05e3130baa Apply share limits when torrent downloading is finished
PR #20917.
Closes #20874.
2024-06-25 21:13:17 +03:00
Vladimir Golovnev
683492648f Apply filename filter to subfolder names as well
PR #20902.
Closes #14480.
2024-06-25 21:13:17 +03:00
Chocobo1
2f2e158877 WebUI: unify comment format 2024-06-25 21:13:16 +03:00
BurningMop
e60e96cb0e Add optional headers to search request
PR #20923.
2024-06-25 21:13:16 +03:00
Chocobo1
5f31208bf1 Add required manifest field
https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#assemblyidentity

PR #20907.
2024-06-25 21:13:16 +03:00
Chocobo1
fa58e58e70 WebUI: unify curly bracket usage 2024-06-25 21:13:16 +03:00
dependabot[bot]
671943a9a6 GHA CI: Bump Github Actions versions
PR #20913.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2024-06-25 21:13:16 +03:00
Chocobo1
8bad80bcdd Avoid redundant lookup
PR #20890.
2024-06-25 21:13:15 +03:00
thalieht
c44e300507 Increase default height of 'Share ratio limit' dialog in WebUI
PR #20866.
2024-06-25 21:13:15 +03:00
Chocobo1
318a677e8f Avoid creating redundant temporary file list
PR #20863.
2024-06-25 21:13:15 +03:00
Chocobo1
0246df790a Use Qt built-in methods 2024-06-25 21:13:15 +03:00
Chocobo1
782fbc1425 Use simpler conversion
The cookie value can only contain ASCII characters.
2024-06-25 21:13:15 +03:00
Chocobo1
7deccd5592 WebUI: add missing break 2024-06-25 21:13:14 +03:00
Chocobo1
4a36fe7278 WebUI: don't auto infer radix parameter 2024-06-25 21:13:14 +03:00
Chocobo1
1c5af96ad8 WebUI: simplify code 2024-06-25 21:13:14 +03:00
Chocobo1
3bb47a5410 WebUI: iterate over own properties only 2024-06-25 21:13:14 +03:00
Chocobo1
d7abeb4bf0 WebUI: use assignment operator shorthand 2024-06-25 21:13:14 +03:00
Chocobo1
a19d623ead WebUI: prefer arrow function in callbacks 2024-06-25 21:13:13 +03:00
Chocobo1
1ef21bc2b7 WebUI: enforce usage of const whenever possible 2024-06-25 21:13:13 +03:00
Chocobo1
4687b4e8e4 WebUI: enforce string quotes coding style 2024-06-25 19:33:20 +03:00
Thomas Piccirello
d2e5163861 WebUI: Restore previously used tab on load
This PR restores the users previously used tab (Transfer, Search, RSS, etc.) when the WebUI is reloaded.

PR #20705.
2024-06-25 19:30:10 +03:00
sledgehammer999
8a15ea8026 Merge pull request #20963 from sledgehammer999/revert_webui_i18n
Revert i18next
2024-06-25 03:02:24 +03:00
sledgehammer999
2b99554813 Update WebUI translation files 2024-06-17 02:07:15 +03:00
sledgehammer999
e6638f9c19 Revert "Use client side translation for public login page"
This reverts commit ac91c1348b.
2024-06-16 23:31:19 +03:00
sledgehammer999
ec6eac2ba1 Revert "Avoid leaking user locale preference to the web"
This reverts commit 66c34ddb6e.
2024-06-16 23:14:21 +03:00
314 changed files with 108126 additions and 94427 deletions

View File

@@ -23,7 +23,6 @@ jobs:
env:
boost_path: "${{ github.workspace }}/../boost"
openssl_root: "$(brew --prefix openssl@3)"
libtorrent_path: "${{ github.workspace }}/../libtorrent"
steps:
@@ -70,7 +69,7 @@ jobs:
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
- name: Install Qt
uses: jurplel/install-qt-action@v3
uses: jurplel/install-qt-action@v4
with:
version: ${{ matrix.qt_version }}
archives: qtbase qtdeclarative qtsvg qttools
@@ -94,8 +93,7 @@ jobs:
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBOOST_ROOT="${{ env.boost_path }}" \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}"
-Ddeprecated-functions=OFF
cmake --build build
sudo cmake --install build
@@ -109,7 +107,6 @@ jobs:
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBOOST_ROOT="${{ env.boost_path }}" \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}" \
-DTESTING=ON \
-DVERBOSE_CONFIGURE=ON \
-D${{ matrix.qbt_gui }}

View File

@@ -53,7 +53,7 @@ jobs:
python-version: '3.7'
- name: Install tools (search engine)
run: pip install bandit pycodestyle pyflakes
run: pip install bandit mypy pycodestyle pyflakes pyright
- name: Gather files (search engine)
run: |
@@ -61,6 +61,16 @@ jobs:
echo $PY_FILES
echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV"
- name: Check typings (search engine)
run: |
MYPYPATH="src/searchengine/nova3" \
mypy \
--follow-imports skip \
--strict \
$PY_FILES
pyright \
$PY_FILES
- name: Lint code (search engine)
run: |
pyflakes $PY_FILES

View File

@@ -64,7 +64,7 @@ jobs:
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
- name: Install Qt
uses: jurplel/install-qt-action@v3
uses: jurplel/install-qt-action@v4
with:
version: ${{ matrix.qt_version }}
archives: icu qtbase qtdeclarative qtsvg qttools
@@ -138,12 +138,12 @@ jobs:
curl \
-L \
-Z \
-O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \
-O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage \
-O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-static-x86_64.AppImage \
-O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-static-x86_64.AppImage \
-O https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage
chmod +x \
linuxdeploy-x86_64.AppImage \
linuxdeploy-plugin-qt-x86_64.AppImage \
linuxdeploy-static-x86_64.AppImage \
linuxdeploy-plugin-qt-static-x86_64.AppImage \
linuxdeploy-plugin-appimage-x86_64.AppImage
- name: Prepare files for AppImage
@@ -156,12 +156,12 @@ jobs:
- name: Package AppImage
run: |
./linuxdeploy-x86_64.AppImage --appdir qbittorrent --plugin qt
./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --plugin qt
rm qbittorrent/apprun-hooks/*
cp .github/workflows/helper/appimage/export_vars.sh qbittorrent/apprun-hooks/export_vars.sh
NO_APPSTREAM=1 \
OUTPUT=upload/qbittorrent-CI_Ubuntu_x86_64.AppImage \
./linuxdeploy-x86_64.AppImage --appdir qbittorrent --output appimage
./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --output appimage
- name: Upload build artifacts
uses: actions/upload-artifact@v4

View File

@@ -93,7 +93,7 @@ jobs:
move "${{ github.workspace }}/../boost_*" "${{ env.boost_path }}"
- name: Install Qt
uses: jurplel/install-qt-action@v3
uses: jurplel/install-qt-action@v4
with:
version: "6.7.0"
archives: qtbase qtsvg qttools
@@ -153,26 +153,26 @@ jobs:
copy build/qbittorrent.pdb upload/qBittorrent
copy dist/windows/qt.conf upload/qBittorrent
# runtimes
copy "${{ env.Qt6_DIR }}/bin/Qt6Core.dll" upload/qBittorrent
copy "${{ env.Qt6_DIR }}/bin/Qt6Gui.dll" upload/qBittorrent
copy "${{ env.Qt6_DIR }}/bin/Qt6Network.dll" upload/qBittorrent
copy "${{ env.Qt6_DIR }}/bin/Qt6Sql.dll" upload/qBittorrent
copy "${{ env.Qt6_DIR }}/bin/Qt6Svg.dll" upload/qBittorrent
copy "${{ env.Qt6_DIR }}/bin/Qt6Widgets.dll" upload/qBittorrent
copy "${{ env.Qt6_DIR }}/bin/Qt6Xml.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Core.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Gui.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Network.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Sql.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Svg.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Widgets.dll" upload/qBittorrent
copy "${{ env.Qt_ROOT_DIR }}/bin/Qt6Xml.dll" upload/qBittorrent
mkdir upload/qBittorrent/plugins/iconengines
copy "${{ env.Qt6_DIR }}/plugins/iconengines/qsvgicon.dll" upload/qBittorrent/plugins/iconengines
copy "${{ env.Qt_ROOT_DIR }}/plugins/iconengines/qsvgicon.dll" upload/qBittorrent/plugins/iconengines
mkdir upload/qBittorrent/plugins/imageformats
copy "${{ env.Qt6_DIR }}/plugins/imageformats/qico.dll" upload/qBittorrent/plugins/imageformats
copy "${{ env.Qt6_DIR }}/plugins/imageformats/qsvg.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/qsvg.dll" upload/qBittorrent/plugins/imageformats
mkdir upload/qBittorrent/plugins/platforms
copy "${{ env.Qt6_DIR }}/plugins/platforms/qwindows.dll" upload/qBittorrent/plugins/platforms
copy "${{ env.Qt_ROOT_DIR }}/plugins/platforms/qwindows.dll" upload/qBittorrent/plugins/platforms
mkdir upload/qBittorrent/plugins/sqldrivers
copy "${{ env.Qt6_DIR }}/plugins/sqldrivers/qsqlite.dll" upload/qBittorrent/plugins/sqldrivers
copy "${{ env.Qt_ROOT_DIR }}/plugins/sqldrivers/qsqlite.dll" upload/qBittorrent/plugins/sqldrivers
mkdir upload/qBittorrent/plugins/styles
copy "${{ env.Qt6_DIR }}/plugins/styles/qmodernwindowsstyle.dll" upload/qBittorrent/plugins/styles
copy "${{ env.Qt_ROOT_DIR }}/plugins/styles/qmodernwindowsstyle.dll" upload/qBittorrent/plugins/styles
mkdir upload/qBittorrent/plugins/tls
copy "${{ env.Qt6_DIR }}/plugins/tls/qschannelbackend.dll" upload/qBittorrent/plugins/tls
copy "${{ env.Qt_ROOT_DIR }}/plugins/tls/qschannelbackend.dll" upload/qBittorrent/plugins/tls
# cmake additionals
mkdir upload/cmake
copy build/compile_commands.json upload/cmake

View File

@@ -52,7 +52,7 @@ jobs:
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
- name: Install Qt
uses: jurplel/install-qt-action@v3
uses: jurplel/install-qt-action@v4
with:
version: ${{ matrix.qt_version }}
archives: icu qtbase qtdeclarative qtsvg qttools

View File

@@ -78,11 +78,7 @@ repos:
m4/.* |
src/base/3rdparty/.* |
src/searchengine/nova3/socks.py |
src/webui/www/private/lang/.* |
src/webui/www/private/scripts/lib/.* |
src/webui/www/public/lang/.* |
src/webui/www/public/scripts/lib/.* |
src/webui/www/transifex/.*
src/webui/www/private/scripts/lib/.*
)$
exclude_types:
- ts
@@ -106,11 +102,7 @@ repos:
m4/.* |
src/base/3rdparty/.* |
src/searchengine/nova3/socks.py |
src/webui/www/private/lang/.* |
src/webui/www/private/scripts/lib/.* |
src/webui/www/public/lang/.* |
src/webui/www/public/scripts/lib/.* |
src/webui/www/transifex/.*
src/webui/www/private/scripts/lib/.*
)$
exclude_types:
- svg

View File

@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_master]
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_v50x]
file_filter = src/lang/qbittorrent_<lang>.ts
source_file = src/lang/qbittorrent_en.ts
source_lang = en
@@ -9,7 +9,7 @@ type = QT
minimum_perc = 23
lang_map = pt: pt_PT, zh: zh_CN
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui]
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_v50x]
file_filter = src/webui/www/translations/webui_<lang>.ts
source_file = src/webui/www/translations/webui_en.ts
source_lang = en
@@ -17,14 +17,6 @@ type = QT
minimum_perc = 23
lang_map = pt: pt_PT, zh: zh_CN
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_json]
file_filter = src/webui/www/transifex/<lang>.json
source_file = src/webui/www/transifex/en.json
source_lang = en
type = KEYVALUEJSON
minimum_perc = 23
lang_map = pt: pt_PT, zh: zh_CN
[o:sledgehammer999:p:qbittorrent:r:qbittorrentdesktop_master]
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
source_lang = en

View File

@@ -1,4 +1,4 @@
Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.0
Sun Sep 29th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.0
- FEATURE: Support creating .torrent with larger piece size (Chocobo1)
- FEATURE: Improve tracker entries handling (glassez)
- FEATURE: Add separate filter item for tracker errors (glassez)
@@ -12,14 +12,30 @@ Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.0
- FEATURE: Enable Ctrl+F hotkey for more inputs (thalieht)
- FEATURE: Add seeding limits to RSS and Watched folders options UI (glassez)
- FEATURE: Subcategories implicitly follow the parent category options (glassez)
- FEATURE: Add support for SSL torrents (Chocobo1, Radu Carpa)
- FEATURE: Add option to name each qbittorrent instance (Chocobo1)
- FEATURE: Add button for sending test email (Thomas Piccirello)
- FEATURE: Allow torrents to override default share limit action (glassez)
- FEATURE: Use Start/Stop instead of Resume/Pause (thalieht)
- FEATURE: Add the Popularity metric (Aliaksei Urbanski)
- FEATURE: Focus on Download button if torrent link retrieved from the clipboard (glassez)
- FEATURE: Add ability to pause/resume entire BitTorrent session (glassez)
- FEATURE: Add an option to set BitTorrent session shutdown timeout (glassez)
- FEATURE: Apply "Excluded file names" to folder names as well (glassez)
- FEATURE: Allow to use regular expression to filter torrent content (glassez)
- FEATURE: Allow to move content files to Trash instead of deleting them (glassez)
- FEATURE: Add ability to display torrent "privateness" in UI (ManiMatter)
- FEATURE: Add a flag in `Peers` tab denoting a connection using NAT hole punching (stalkerok)
- BUGFIX: Display error message when unrecoverable error occurred (glassez)
- BUGFIX: Update size of selected files when selection is changed (glassez)
- BUGFIX: Normalize tags by trimming leading/trailing whitespace (glassez)
- BUGFIX: Correctly handle share limits in torrent options dialog (glassez)
- BUGFIX: Adjust tracker tier when adding additional trackers (Chocobo1)
- BUGFIX: Fix inconsistent naming between `Done/Progress` column (luzpaz)
- BUGFIX: Sanitize peer client names (Hanabishi)
- BUGFIX: Apply share limits immediately when torrent downloading is finished (glassez)
- BUGFIX: Show download progress for folders with zero byte size as 100 instead of 0 (vikas_c)
- BUGFIX: Fix highlighted piece color (Prince Gupta)
- BUGFIX: Apply "merge trackers" logic regardless of way the torrent is added (glassez)
- WEBUI: Improve WebUI responsiveness (Chocobo1)
- WEBUI: Do not exit the app when WebUI has failed to start (Hanabishi)
- WEBUI: Add `Moving` filter to side panel (xavier2k6)
@@ -28,14 +44,37 @@ Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.0
- WEBUI: Leave the fields empty when value is invalid (Chocobo1)
- WEBUI: Use natural sorting (Chocobo1)
- WEBUI: Improve WebUI login behavior (JayRet)
- WEBUI: Conditionally show filters sidebar (Thomas Piccirello)
- WEBUI: Add support for running concurrent searches (Thomas Piccirello)
- WEBUI: Improve accuracy of trackers list (Thomas Piccirello)
- WEBUI: Fix error when category doesn't exist (Thomas Piccirello)
- WEBUI: Improve table scrolling and selection on mobile (Thomas Piccirello)
- WEBUI: Restore search tabs on load (Thomas Piccirello)
- WEBUI: Restore previously used tab on load (Thomas Piccirello)
- WEBUI: Increase default height of `Share ratio limit` dialog (thalieht)
- WEBUI: Use enabled search plugins by default (Thomas Piccirello)
- WEBUI: Add columns `Incomplete Save Path`, `Info Hash v1`, `Info Hash v2` (thalieht)
- WEBUI: Always create generic filter items (skomerko)
- WEBUI: Provide `Use Category paths in Manual Mode` option (skomerko)
- WEBUI: Provide `Merge trackers to existing torrent` option (skomerko)
- WEBAPI: Fix wrong timestamp values (Chocobo1)
- WEBAPI: Send binary data with filename and mime type specified (glassez)
- WEBAPI: Expose API for the torrent creator (glassez, Radu Carpa)
- WEBAPI: Add support for SSL torrents (Chocobo1, Radu Carpa)
- WEBAPI: Provide endpoint for listing directory content (Paweł Kotiuk)
- WEBAPI: Provide "private" flag via "torrents/info" endpoint (ManiMatter)
- WEBAPI: Add a way to download .torrent file using search plugin (glassez)
- WEBAPI: Add "private" filter for "torrents/info" endpoint (ManiMatter)
- WEBAPI: Add root_path to "torrents/info" result (David Newhall)
- RSS: Show RSS feed title in HTML browser (Jay)
- RSS: Allow to set delay between requests to the same host (jNullj)
- SEARCH: Allow users to specify Python executable path (Chocobo1)
- SEARCH: Lazy load search plugins (milahu)
- SEARCH: Add date column to the built-in search engine (ducalex)
- SEARCH: Allow to rearrange search tabs (glassez)
- WINDOWS: Use Fusion style on Windows 10+. It has better compatibility with dark mode (glassez)
- WINDOWS: Allow to set qBittorrent as default program (glassez)
- WINDOWS: Don't access "Favorites" folder unexpectedly (glassez)
- LINUX: Add support for systemd power management (Chocobo1)
- LINUX: Add support for localized man pages (Victor Chernyakin)
- LINUX: Specify a locale if none is set (Chocobo1)

File diff suppressed because it is too large Load Diff

View File

@@ -62,6 +62,6 @@
<url type="contribute">https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md</url>
<content_rating type="oars-1.1"/>
<releases>
<release version="5.0.0~beta1" date="2024-03-19"/>
<release version="5.0.0" date="2024-09-29"/>
</releases>
</component>

View File

@@ -38,6 +38,8 @@ add_library(qbt_base STATIC
bittorrent/torrent.h
bittorrent/torrentcontenthandler.h
bittorrent/torrentcontentlayout.h
bittorrent/torrentcontentremoveoption.h
bittorrent/torrentcontentremover.h
bittorrent/torrentcreationmanager.h
bittorrent/torrentcreationtask.h
bittorrent/torrentcreator.h
@@ -145,6 +147,7 @@ add_library(qbt_base STATIC
bittorrent/sslparameters.cpp
bittorrent/torrent.cpp
bittorrent/torrentcontenthandler.cpp
bittorrent/torrentcontentremover.cpp
bittorrent/torrentcreationmanager.cpp
bittorrent/torrentcreationtask.cpp
bittorrent/torrentcreator.cpp

View File

@@ -40,7 +40,6 @@
#include <QRegularExpression>
#include <QThread>
#include "base/algorithm.h"
#include "base/exceptions.h"
#include "base/global.h"
#include "base/logger.h"

View File

@@ -240,11 +240,11 @@ void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage, const
lt::storage_interface *customStorageConstructor(const lt::storage_params &params, lt::file_pool &pool)
{
return new CustomStorage {params, pool};
return new CustomStorage(params, pool);
}
CustomStorage::CustomStorage(const lt::storage_params &params, lt::file_pool &filePool)
: lt::default_storage {params, filePool}
: lt::default_storage(params, filePool)
, m_savePath {params.path}
{
}

View File

@@ -367,6 +367,10 @@ void PeerInfo::determineFlags()
if (useUTPSocket())
updateFlags(u'P', C_UTP);
// h = Peer is using NAT hole punching
if (isHolepunched())
updateFlags(u'h', tr("Peer is using NAT hole punching"));
m_flags.chop(1);
m_flagsDescription.chop(1);
}

View File

@@ -37,17 +37,12 @@
#include "addtorrentparams.h"
#include "categoryoptions.h"
#include "sharelimitaction.h"
#include "torrentcontentremoveoption.h"
#include "trackerentry.h"
#include "trackerentrystatus.h"
class QString;
enum DeleteOption
{
DeleteTorrent,
DeleteTorrentAndFiles
};
namespace BitTorrent
{
class InfoHash;
@@ -58,6 +53,12 @@ namespace BitTorrent
struct CacheStatus;
struct SessionStatus;
enum class TorrentRemoveOption
{
KeepContent,
RemoveContent
};
// 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
@@ -425,7 +426,7 @@ namespace BitTorrent
virtual void setExcludedFileNamesEnabled(bool enabled) = 0;
virtual QStringList excludedFileNames() const = 0;
virtual void setExcludedFileNames(const QStringList &newList) = 0;
virtual bool isFilenameExcluded(const QString &fileName) const = 0;
virtual void applyFilenameFilter(const PathList &files, QList<BitTorrent::DownloadPriority> &priorities) = 0;
virtual QStringList bannedIPs() const = 0;
virtual void setBannedIPs(const QStringList &newList) = 0;
virtual ResumeDataStorageType resumeDataStorageType() const = 0;
@@ -434,6 +435,8 @@ namespace BitTorrent
virtual void setMergeTrackersEnabled(bool enabled) = 0;
virtual bool isStartPaused() const = 0;
virtual void setStartPaused(bool value) = 0;
virtual TorrentContentRemoveOption torrentContentRemoveOption() const = 0;
virtual void setTorrentContentRemoveOption(TorrentContentRemoveOption option) = 0;
virtual bool isRestored() const = 0;
@@ -453,7 +456,7 @@ namespace BitTorrent
virtual bool isKnownTorrent(const InfoHash &infoHash) const = 0;
virtual bool addTorrent(const TorrentDescriptor &torrentDescr, const AddTorrentParams &params = {}) = 0;
virtual bool deleteTorrent(const TorrentID &id, DeleteOption deleteOption = DeleteOption::DeleteTorrent) = 0;
virtual bool removeTorrent(const TorrentID &id, TorrentRemoveOption deleteOption = TorrentRemoveOption::KeepContent) = 0;
virtual bool downloadMetadata(const TorrentDescriptor &torrentDescr) = 0;
virtual bool cancelDownloadMetadata(const TorrentID &id) = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -75,6 +75,7 @@ namespace BitTorrent
class InfoHash;
class ResumeDataStorage;
class Torrent;
class TorrentContentRemover;
class TorrentDescriptor;
class TorrentImpl;
class Tracker;
@@ -402,7 +403,7 @@ namespace BitTorrent
void setExcludedFileNamesEnabled(bool enabled) override;
QStringList excludedFileNames() const override;
void setExcludedFileNames(const QStringList &excludedFileNames) override;
bool isFilenameExcluded(const QString &fileName) const override;
void applyFilenameFilter(const PathList &files, QList<BitTorrent::DownloadPriority> &priorities) override;
QStringList bannedIPs() const override;
void setBannedIPs(const QStringList &newList) override;
ResumeDataStorageType resumeDataStorageType() const override;
@@ -411,6 +412,8 @@ namespace BitTorrent
void setMergeTrackersEnabled(bool enabled) override;
bool isStartPaused() const override;
void setStartPaused(bool value) override;
TorrentContentRemoveOption torrentContentRemoveOption() const override;
void setTorrentContentRemoveOption(TorrentContentRemoveOption option) override;
bool isRestored() const override;
@@ -430,7 +433,7 @@ namespace BitTorrent
bool isKnownTorrent(const InfoHash &infoHash) const override;
bool addTorrent(const TorrentDescriptor &torrentDescr, const AddTorrentParams &params = {}) override;
bool deleteTorrent(const TorrentID &id, DeleteOption deleteOption = DeleteTorrent) override;
bool removeTorrent(const TorrentID &id, TorrentRemoveOption deleteOption = TorrentRemoveOption::KeepContent) override;
bool downloadMetadata(const TorrentDescriptor &torrentDescr) override;
bool cancelDownloadMetadata(const TorrentID &id) override;
@@ -487,11 +490,11 @@ namespace BitTorrent
void configureDeferred();
void readAlerts();
void enqueueRefresh();
void processShareLimits();
void generateResumeData();
void handleIPFilterParsed(int ruleCount);
void handleIPFilterError();
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage);
private:
struct ResumeSessionContext;
@@ -507,8 +510,9 @@ namespace BitTorrent
struct RemovingTorrentData
{
QString name;
Path pathToRemove;
DeleteOption deleteOption {};
Path contentStoragePath;
PathList fileNames;
TorrentRemoveOption removeOption {};
};
explicit SessionImpl(QObject *parent = nullptr);
@@ -536,6 +540,7 @@ namespace BitTorrent
void enableIPFilter();
void disableIPFilter();
void processTrackerStatuses();
void processTorrentShareLimits(TorrentImpl *torrent);
void populateExcludedFileNamesRegExpList();
void prepareStartup();
void handleLoadedResumeData(ResumeSessionContext *context);
@@ -599,13 +604,7 @@ namespace BitTorrent
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
// BitTorrent
lt::session *m_nativeSession = nullptr;
NativeSessionExtension *m_nativeSessionExtension = nullptr;
bool m_deferredConfigureScheduled = false;
bool m_IPFilteringConfigured = false;
mutable bool m_listenInterfaceConfigured = false;
void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {});
CachedSettingValue<QString> m_DHTBootstrapNodes;
CachedSettingValue<bool> m_isDHTEnabled;
@@ -731,8 +730,16 @@ namespace BitTorrent
CachedSettingValue<int> m_I2POutboundQuantity;
CachedSettingValue<int> m_I2PInboundLength;
CachedSettingValue<int> m_I2POutboundLength;
CachedSettingValue<TorrentContentRemoveOption> m_torrentContentRemoveOption;
SettingValue<bool> m_startPaused;
lt::session *m_nativeSession = nullptr;
NativeSessionExtension *m_nativeSessionExtension = nullptr;
bool m_deferredConfigureScheduled = false;
bool m_IPFilteringConfigured = false;
mutable bool m_listenInterfaceConfigured = false;
bool m_isRestored = false;
bool m_isPaused = isStartPaused();
@@ -766,6 +773,7 @@ namespace BitTorrent
QThreadPool *m_asyncWorker = nullptr;
ResumeDataStorage *m_resumeDataStorage = nullptr;
FileSearcher *m_fileSearcher = nullptr;
TorrentContentRemover *m_torrentContentRemover = nullptr;
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
@@ -809,6 +817,8 @@ namespace BitTorrent
QTimer *m_wakeupCheckTimer = nullptr;
QDateTime m_wakeupCheckTimestamp;
QList<TorrentImpl *> m_pendingFinishedTorrents;
friend void Session::initInstance();
friend void Session::freeInstance();
friend Session *Session::instance();

View File

@@ -228,6 +228,7 @@ namespace BitTorrent
virtual void setShareLimitAction(ShareLimitAction action) = 0;
virtual PathList filePaths() const = 0;
virtual PathList actualFilePaths() const = 0;
virtual TorrentInfo info() const = 0;
virtual bool isFinished() const = 0;

View File

@@ -0,0 +1,50 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <QMetaEnum>
namespace BitTorrent
{
// 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
inline namespace TorrentContentRemoveOptionNS
{
Q_NAMESPACE
enum class TorrentContentRemoveOption
{
Delete,
MoveToTrash
};
Q_ENUM_NS(TorrentContentRemoveOption)
}
}

View File

@@ -0,0 +1,61 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "torrentcontentremover.h"
#include "base/utils/fs.h"
void BitTorrent::TorrentContentRemover::performJob(const QString &torrentName, const Path &basePath
, const PathList &fileNames, const TorrentContentRemoveOption option)
{
QString errorMessage;
if (!fileNames.isEmpty())
{
const auto removeFileFn = [&option](const Path &filePath)
{
return ((option == TorrentContentRemoveOption::MoveToTrash)
? Utils::Fs::moveFileToTrash : Utils::Fs::removeFile)(filePath);
};
for (const Path &fileName : fileNames)
{
if (const auto result = removeFileFn(basePath / fileName)
; !result && errorMessage.isEmpty())
{
errorMessage = result.error();
}
}
const Path rootPath = Path::findRootFolder(fileNames);
if (!rootPath.isEmpty())
Utils::Fs::smartRemoveEmptyFolderTree(basePath / rootPath);
}
emit jobFinished(torrentName, errorMessage);
}

View File

@@ -0,0 +1,53 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <QObject>
#include "base/path.h"
#include "torrentcontentremoveoption.h"
namespace BitTorrent
{
class TorrentContentRemover final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(TorrentContentRemover)
public:
using QObject::QObject;
public slots:
void performJob(const QString &torrentName, const Path &basePath
, const PathList &fileNames, TorrentContentRemoveOption option);
signals:
void jobFinished(const QString &torrentName, const QString &errorMessage);
};
}

View File

@@ -77,6 +77,10 @@
#include "base/utils/os.h"
#endif // Q_OS_MACOS || Q_OS_WIN
#ifndef QBT_USES_LIBTORRENT2
#include "customstorage.h"
#endif
using namespace BitTorrent;
namespace
@@ -456,6 +460,8 @@ Path TorrentImpl::savePath() const
void TorrentImpl::setSavePath(const Path &path)
{
Q_ASSERT(!isAutoTMMEnabled());
if (isAutoTMMEnabled()) [[unlikely]]
return;
const Path basePath = m_session->useCategoryPathsInManualMode()
? m_session->categorySavePath(category()) : m_session->savePath();
@@ -483,6 +489,8 @@ Path TorrentImpl::downloadPath() const
void TorrentImpl::setDownloadPath(const Path &path)
{
Q_ASSERT(!isAutoTMMEnabled());
if (isAutoTMMEnabled()) [[unlikely]]
return;
const Path basePath = m_session->useCategoryPathsInManualMode()
? m_session->categoryDownloadPath(category()) : m_session->downloadPath();
@@ -982,6 +990,21 @@ PathList TorrentImpl::filePaths() const
return m_filePaths;
}
PathList TorrentImpl::actualFilePaths() const
{
if (!hasMetadata())
return {};
PathList paths;
paths.reserve(filesCount());
const lt::file_storage files = nativeTorrentInfo()->files();
for (const lt::file_index_t &nativeIndex : asConst(m_torrentInfo.nativeIndexes()))
paths.emplaceBack(files.file_path(nativeIndex));
return paths;
}
QVector<DownloadPriority> TorrentImpl::filePriorities() const
{
return m_filePriorities;
@@ -1447,11 +1470,13 @@ QBitArray TorrentImpl::pieces() const
QBitArray TorrentImpl::downloadingPieces() const
{
QBitArray result(piecesCount());
if (!hasMetadata())
return {};
std::vector<lt::partial_piece_info> queue;
m_nativeHandle.get_download_queue(queue);
QBitArray result {piecesCount()};
for (const lt::partial_piece_info &info : queue)
result.setBit(LT::toUnderlyingType(info.piece_index));
@@ -1791,12 +1816,13 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
const Path filePath = actualFilePath.removedExtension(QB_EXT);
m_filePaths.append(filePath);
lt::download_priority_t &nativePriority = p.file_priorities[LT::toUnderlyingType(nativeIndex)];
if ((nativePriority != lt::dont_download) && m_session->isFilenameExcluded(filePath.filename()))
nativePriority = lt::dont_download;
const auto priority = LT::fromNative(nativePriority);
m_filePriorities.append(priority);
m_filePriorities.append(LT::fromNative(p.file_priorities[LT::toUnderlyingType(nativeIndex)]));
}
m_session->applyFilenameFilter(fileNames, m_filePriorities);
for (int i = 0; i < m_filePriorities.size(); ++i)
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(m_filePriorities[i]);
p.save_path = savePath.toString().toStdString();
p.ti = metadata;
@@ -1859,6 +1885,9 @@ void TorrentImpl::reload()
auto *const extensionData = new ExtensionData;
p.userdata = LTClientData(extensionData);
#ifndef QBT_USES_LIBTORRENT2
p.storage = customStorageConstructor;
#endif
m_nativeHandle = m_nativeSession->add_torrent(p);
m_nativeStatus = extensionData->status;
@@ -1933,8 +1962,17 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageContext cont
{
if (!hasMetadata())
{
m_savePath = newPath;
m_session->handleTorrentSavePathChanged(this);
if (context == MoveStorageContext::ChangeSavePath)
{
m_savePath = newPath;
m_session->handleTorrentSavePathChanged(this);
}
else if (context == MoveStorageContext::ChangeDownloadPath)
{
m_downloadPath = newPath;
m_session->handleTorrentSavePathChanged(this);
}
return;
}

View File

@@ -153,6 +153,7 @@ namespace BitTorrent
Path actualFilePath(int index) const override;
qlonglong fileSize(int index) const override;
PathList filePaths() const override;
PathList actualFilePaths() const override;
QVector<DownloadPriority> filePriorities() const override;
TorrentInfo info() const override;

View File

@@ -44,6 +44,7 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
, m_requestHandler(requestHandler)
{
m_socket->setParent(this);
connect(m_socket, &QAbstractSocket::disconnected, this, &Connection::closed);
// reserve common size for requests, don't use the max allowed size which is too big for
// memory constrained platforms
@@ -62,11 +63,6 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
});
}
Connection::~Connection()
{
m_socket->close();
}
void Connection::read()
{
// reuse existing buffer and avoid unnecessary memory allocation/relocation
@@ -182,11 +178,6 @@ bool Connection::hasExpired(const qint64 timeout) const
&& m_idleTimer.hasExpired(timeout);
}
bool Connection::isClosed() const
{
return (m_socket->state() == QAbstractSocket::UnconnectedState);
}
bool Connection::acceptsGzipEncoding(QString codings)
{
// [rfc7231] 5.3.4. Accept-Encoding

View File

@@ -47,10 +47,11 @@ namespace Http
public:
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = nullptr);
~Connection();
bool hasExpired(qint64 timeout) const;
bool isClosed() const;
signals:
void closed();
private:
static bool acceptsGzipEncoding(QString codings);

View File

@@ -32,7 +32,10 @@
#include <algorithm>
#include <chrono>
#include <memory>
#include <new>
#include <QtLogging>
#include <QNetworkProxy>
#include <QSslCipher>
#include <QSslConfiguration>
@@ -40,7 +43,6 @@
#include <QStringList>
#include <QTimer>
#include "base/algorithm.h"
#include "base/global.h"
#include "base/utils/net.h"
#include "base/utils/sslkey.h"
@@ -113,32 +115,38 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
void Server::incomingConnection(const qintptr socketDescriptor)
{
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
QTcpSocket *serverSocket = nullptr;
if (m_https)
serverSocket = new QSslSocket(this);
else
serverSocket = new QTcpSocket(this);
std::unique_ptr<QTcpSocket> serverSocket = m_https ? std::make_unique<QSslSocket>(this) : std::make_unique<QTcpSocket>(this);
if (!serverSocket->setSocketDescriptor(socketDescriptor))
return;
if (m_connections.size() >= CONNECTIONS_LIMIT)
{
delete serverSocket;
qWarning("Too many connections. Exceeded CONNECTIONS_LIMIT (%d). Connection closed.", CONNECTIONS_LIMIT);
return;
}
if (m_https)
try
{
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
if (m_https)
{
auto *sslSocket = static_cast<QSslSocket *>(serverSocket.get());
sslSocket->setProtocol(QSsl::SecureProtocols);
sslSocket->setPrivateKey(m_key);
sslSocket->setLocalCertificateChain(m_certificates);
sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
sslSocket->startServerEncryption();
}
auto *c = new Connection(serverSocket, m_requestHandler, this);
m_connections.insert(c);
connect(serverSocket, &QAbstractSocket::disconnected, this, [c, this]() { removeConnection(c); });
auto *connection = new Connection(serverSocket.release(), m_requestHandler, this);
m_connections.insert(connection);
connect(connection, &Connection::closed, this, [this, connection] { removeConnection(connection); });
}
catch (const std::bad_alloc &exception)
{
// drop the connection instead of throwing exception and crash
qWarning("Failed to allocate memory for HTTP connection. Connection closed.");
return;
}
}
void Server::removeConnection(Connection *connection)

View File

@@ -43,7 +43,6 @@ class QSslSocket;
#else
class QTcpSocket;
#endif
class QTextCodec;
namespace Net
{

View File

@@ -134,17 +134,17 @@ void Preferences::setCustomUIThemePath(const Path &path)
setValue(u"Preferences/General/CustomUIThemePath"_s, path);
}
bool Preferences::deleteTorrentFilesAsDefault() const
bool Preferences::removeTorrentContent() const
{
return value(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, false);
}
void Preferences::setDeleteTorrentFilesAsDefault(const bool del)
void Preferences::setRemoveTorrentContent(const bool remove)
{
if (del == deleteTorrentFilesAsDefault())
if (remove == removeTorrentContent())
return;
setValue(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, del);
setValue(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, remove);
}
bool Preferences::confirmOnExit() const

View File

@@ -105,8 +105,8 @@ public:
void setUseCustomUITheme(bool use);
Path customUIThemePath() const;
void setCustomUIThemePath(const Path &path);
bool deleteTorrentFilesAsDefault() const;
void setDeleteTorrentFilesAsDefault(bool del);
bool removeTorrentContent() const;
void setRemoveTorrentContent(bool remove);
bool confirmOnExit() const;
void setConfirmOnExit(bool confirm);
bool speedInTitleBar() const;

View File

@@ -52,19 +52,21 @@ const TorrentFilter TorrentFilter::ErroredTorrent(TorrentFilter::Errored);
using BitTorrent::Torrent;
TorrentFilter::TorrentFilter(const Type type, const std::optional<TorrentIDSet> &idSet
, const std::optional<QString> &category, const std::optional<Tag> &tag)
, const std::optional<QString> &category, const std::optional<Tag> &tag, const std::optional<bool> isPrivate)
: m_type {type}
, m_category {category}
, m_tag {tag}
, m_idSet {idSet}
, m_private {isPrivate}
{
}
TorrentFilter::TorrentFilter(const QString &filter, const std::optional<TorrentIDSet> &idSet
, const std::optional<QString> &category, const std::optional<Tag> &tag)
, const std::optional<QString> &category, const std::optional<Tag> &tag, const std::optional<bool> isPrivate)
: m_category {category}
, m_tag {tag}
, m_idSet {idSet}
, m_private {isPrivate}
{
setTypeByName(filter);
}
@@ -147,11 +149,22 @@ bool TorrentFilter::setTag(const std::optional<Tag> &tag)
return false;
}
bool TorrentFilter::setPrivate(const std::optional<bool> isPrivate)
{
if (m_private != isPrivate)
{
m_private = isPrivate;
return true;
}
return false;
}
bool TorrentFilter::match(const Torrent *const torrent) const
{
if (!torrent) return false;
return (matchState(torrent) && matchHash(torrent) && matchCategory(torrent) && matchTag(torrent));
return (matchState(torrent) && matchHash(torrent) && matchCategory(torrent) && matchTag(torrent) && matchPrivate(torrent));
}
bool TorrentFilter::matchState(const BitTorrent::Torrent *const torrent) const
@@ -224,3 +237,11 @@ bool TorrentFilter::matchTag(const BitTorrent::Torrent *const torrent) const
return torrent->hasTag(*m_tag);
}
bool TorrentFilter::matchPrivate(const BitTorrent::Torrent *const torrent) const
{
if (!m_private)
return true;
return m_private == torrent->isPrivate();
}

View File

@@ -87,16 +87,24 @@ public:
TorrentFilter() = default;
// category & tags: pass empty string for uncategorized / untagged torrents.
TorrentFilter(Type type, const std::optional<TorrentIDSet> &idSet = AnyID
, const std::optional<QString> &category = AnyCategory, const std::optional<Tag> &tag = AnyTag);
TorrentFilter(const QString &filter, const std::optional<TorrentIDSet> &idSet = AnyID
, const std::optional<QString> &category = AnyCategory, const std::optional<Tag> &tags = AnyTag);
TorrentFilter(Type type
, const std::optional<TorrentIDSet> &idSet = AnyID
, const std::optional<QString> &category = AnyCategory
, const std::optional<Tag> &tag = AnyTag
, std::optional<bool> isPrivate = {});
TorrentFilter(const QString &filter
, const std::optional<TorrentIDSet> &idSet = AnyID
, const std::optional<QString> &category = AnyCategory
, const std::optional<Tag> &tags = AnyTag
, std::optional<bool> isPrivate = {});
bool setType(Type type);
bool setTypeByName(const QString &filter);
bool setTorrentIDSet(const std::optional<TorrentIDSet> &idSet);
bool setCategory(const std::optional<QString> &category);
bool setTag(const std::optional<Tag> &tag);
bool setPrivate(std::optional<bool> isPrivate);
bool match(const BitTorrent::Torrent *torrent) const;
@@ -105,9 +113,11 @@ private:
bool matchHash(const BitTorrent::Torrent *torrent) const;
bool matchCategory(const BitTorrent::Torrent *torrent) const;
bool matchTag(const BitTorrent::Torrent *torrent) const;
bool matchPrivate(const BitTorrent::Torrent *torrent) const;
Type m_type {All};
std::optional<QString> m_category;
std::optional<Tag> m_tag;
std::optional<TorrentIDSet> m_idSet;
std::optional<bool> m_private;
};

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2022-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -29,8 +29,6 @@
#include "fs.h"
#include <cerrno>
#include <cstring>
#include <filesystem>
#if defined(Q_OS_WIN)
@@ -52,6 +50,7 @@
#include <unistd.h>
#endif
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QDir>
@@ -311,20 +310,42 @@ bool Utils::Fs::renameFile(const Path &from, const Path &to)
*
* This function will try to fix the file permissions before removing it.
*/
bool Utils::Fs::removeFile(const Path &path)
nonstd::expected<void, QString> Utils::Fs::removeFile(const Path &path)
{
if (QFile::remove(path.data()))
return true;
QFile file {path.data()};
if (file.remove())
return {};
if (!file.exists())
return true;
return {};
// Make sure we have read/write permissions
file.setPermissions(file.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
return file.remove();
if (file.remove())
return {};
return nonstd::make_unexpected(file.errorString());
}
nonstd::expected<void, QString> Utils::Fs::moveFileToTrash(const Path &path)
{
QFile file {path.data()};
if (file.moveToTrash())
return {};
if (!file.exists())
return {};
// Make sure we have read/write permissions
file.setPermissions(file.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
if (file.moveToTrash())
return {};
const QString errorMessage = file.errorString();
return nonstd::make_unexpected(!errorMessage.isEmpty() ? errorMessage : QCoreApplication::translate("fs", "Unknown error"));
}
bool Utils::Fs::isReadable(const Path &path)
{
return QFileInfo(path.data()).isReadable();

Some files were not shown because too many files have changed in this diff Show More