1
mirror of https://github.com/qbittorrent/qBittorrent synced 2025-10-12 03:12:18 +02:00

Compare commits

..

65 Commits

Author SHA1 Message Date
sledgehammer999
4b794a9930 Bump to 5.0.5 2025-04-13 10:55:32 +03:00
sledgehammer999
96b27a313e Update Changelog 2025-04-13 10:45:01 +03:00
Daniel Nylander
c39849bd3b NSIS: Update Swedish translation
PR #22046.
2025-04-13 10:43:51 +03:00
sledgehammer999
daf7af0d7b Sync translations from Transifex and run lupdate 2025-04-13 10:37:24 +03:00
Vladimir Golovnev
a6809efbbb Backport changes to v5.0.x branch
PR #22336.
2025-04-02 11:16:56 +03:00
Vladimir Golovnev
ddd8fbd34e Add option to enable previous Add new torrent dialog behavior
Some people are still unhappy with "standalone window mode" of "Add new torrent dialog" so just provide them with an option to use old "modal dialog mode" in all the current qBittorrent branches.

PR #22492 (based on original PR #19874).
2025-03-31 09:19:26 +03:00
Vladimir Golovnev
13fcd3d635 Add missing includes
PR #22362.
2025-03-05 09:07:18 +03:00
Vladimir Golovnev
3c6ff0097f Don't miss to declare some of the color IDs
PR #22330.
Closes #22326.
2025-02-25 18:57:00 +03:00
Vladimir Golovnev
03dc089148 Improve command line parameters serialization
PR #22319.
Closes #22306.
2025-02-25 09:11:59 +03:00
sledgehammer999
100ee5dbe0 Bump to 5.0.4 2025-02-18 16:13:57 +02:00
sledgehammer999
310a9d8e1a Update Changelog 2025-02-18 16:11:44 +02:00
Vladimir Golovnev
677cabcbdf GHA CI: fix AppImage building
PR #22286.
2025-02-16 17:46:38 +03:00
sledgehammer999
b86079974c Bump copyright year 2025-02-16 16:39:55 +02:00
sledgehammer999
ca6a89e238 Sync translations from Transifex and run lupdate 2025-02-16 16:27:34 +02:00
Chocobo1
0132b17af6 GHA CI: fix AppImage building
Upstream now defaults to static runtime and the previous URL is invalid now.
Upstream commits:
* c28054bab6
* ce5291e259

Also fuse2 is not needed now as stated on:
https://github.com/AppImage/type2-runtime?tab=readme-ov-file#type2-runtime-

PR #22286.
2025-02-16 11:02:36 +03:00
Vladimir Golovnev
505c1e1c0a Backport changes to v5.0.x branch
PR #22207.
2025-02-14 13:56:49 +03:00
Vladimir Golovnev
ecde201ec5 WebAPI: Don't trim string parameters
PR #22266.
Closes #19485.
Closes #22254.
2025-02-12 09:34:37 +03:00
skomerko
730bf957a4 WebUI: Don't keep references to context menu targets
PR https://github.com/qbittorrent/qBittorrent/pull/22234.
2025-02-11 20:26:43 +03:00
Hugo Carvalho
069cd029eb NSIS: Update Portuguese translation
PR #21632.
2025-02-11 12:19:56 +03:00
Vladimir Golovnev
375e6800e9 Remove stopped torrent from "error" tracker filter
PR #22219.
2025-01-31 15:25:23 +03:00
Vladimir Golovnev
09fb92466a Handle Qt style options uniformly
PR #22133.
Closes #22061.
2025-01-28 21:18:19 +03:00
thalieht
69321f0e94 Hide zero and infinity values in peer list only when that setting is set to Always
PR #22205.
Closes #21998.
2025-01-27 09:47:54 +03:00
thalieht
f39e066672 Fix torrent content checkbox state under certain conditions
PR #22190.
Closes #22189.
2025-01-26 17:08:19 +03:00
Chocobo1
6a5ea93c92 Avoid memory leak on macOS
Only Mark-of-the-Web and Power Management are affected.

PR #22176.
2025-01-19 16:35:45 +08:00
Chocobo1
35dce07c63 Fix cannot remove trackers via WebAPI
The backport commit c3c7f28bad was insufficient.

Closes 22039.
PR #22071.
2024-12-29 14:40:17 +08:00
sledgehammer999
0188e11dd7 Bump to 5.0.3 2024-12-16 01:51:32 +02:00
sledgehammer999
1dc348539b Update Changelog 2024-12-16 01:49:38 +02:00
sledgehammer999
241a0e91bf Sync translations from Transifex and run lupdate 2024-12-16 01:49:09 +02:00
Vladimir Golovnev
68f7295500 Avoid race condition when update tracker entries
PR #21995.
2024-12-15 17:50:19 +03:00
Vladimir Golovnev
53adb7bfa8 Backport changes to v5.0.x branch
PR #21898.
2024-12-09 07:04:06 +03:00
Giacomo411
6128f6eecc NSIS: Update Italian translation
PR #21920.
2024-12-08 12:18:07 +02:00
sledgehammer999
d156a44f8d WebUI: Fix reloading page after login
Manual backport of PR  #21832
Original author: Evgenii Ryshkov
See commit: 1e851b3637
2024-12-08 12:15:15 +02:00
Thomas Piccirello
c3c7f28bad WebUI: Fix removing tracker URL with '|' character
Closes #19074.
PR #21346.
2024-12-07 23:47:49 +02:00
Chocobo1
9ac14cdf9f Don't follow symlink when creating torrents on Windows
Now on Windows, it won't follow/include .lnk files when creating torrents.
Note that libtorrent will throw errors if we force adding .lnk files.

Non-Windows OS will still follow symlinks.

Closes #13286.
PR #21944.
2024-12-07 21:19:46 +03:00
Vladimir Golovnev
b899ea8c40 Use cached current time when parse RSS feed
PR #21959.
2024-12-07 11:12:31 +03:00
Vladimir Golovnev
0d7c367332 Avoid redundant requests of announce entries from libtorrent
PR #21949.
2024-12-06 20:00:27 +03:00
wavygecko
22826499d5 Don't add duplicate episodes to previously matched
PR #21917.
2024-11-28 15:12:17 +03:00
Vladimir Golovnev
dbfd830b56 Avoid repeatedly creating the same QDateTime values
PR #21904.
2024-11-26 15:11:07 +03:00
Vladimir Golovnev
ad3348b95f Fix incorrect SQL column definition
PR #21874.
2024-11-23 11:25:40 +03:00
Bartu Özen
44b08fcb74 WebAPI: Fix incorrect key in torrent creator
PR #21879.
2024-11-23 11:22:04 +03:00
Vladimir Golovnev
71b752baf3 Discard obsolete "state update" events after torrent is reloaded
PR #21873.
Closes #21827.
2024-11-23 11:21:17 +03:00
sledgehammer999
15b6091261 Bump to 5.0.2 2024-11-17 23:25:16 +02:00
sledgehammer999
abe457389d Update Changelog 2024-11-17 23:22:51 +02:00
sledgehammer999
abce4cd1bc Sync translations from Transifex and run lupdate 2024-11-17 23:22:09 +02:00
3gf8jv4dv
2bfb336905 NSIS: Update Traditional Chinese translation
PR #21694.

---------

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2024-11-17 22:45:24 +02:00
3gf8jv4dv
2dee65fa52 NSIS: Update Simplified Chinese translation
PR #21693.

---------

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2024-11-17 22:45:23 +02:00
Ikko Eltociear Ashimine
423b3ed9bf NSIS: update luxembourgish
PR #21456.
2024-11-17 22:45:18 +02:00
Vladimir Golovnev
3454f064f0 Backport changes in v5.0.x branch
PR #21698.
2024-11-17 10:50:54 +03:00
Chocobo1
ac9ca4f452 Don't apply Mark-of-the-Web on existing files
`TorrentImpl::isDownloading()` was excessively broad which included unexpected events for the
case here. So use the underlying state directly.

Closes #21788.
PR #21836.
2024-11-16 16:04:51 +03:00
Chocobo1
09899a7d0d Avoid reapplying Mark-of-the-Web when it already exists
Also use scope guards to handle resources.

Related #21788.
PR #21806.
2024-11-14 10:14:52 +03:00
Hanabishi
9ab3c573dc WebUI: fix color scheme for iframes
A backport of #21750 as a follow up to #21748.

Original PR #21750.
PR #21810.
2024-11-11 19:03:40 +08:00
Vladimir Golovnev
993eb25323 Preserve initial torrent progress while checking resume data
PR #21784.
2024-11-10 12:51:17 +03:00
sledgehammer999
1e27e6504e Merge pull request #21748 from sledgehammer999/backport_webui_color_switcher
WebUI: Add color scheme switcher (v5_0_x)

Bacport of #21613
2024-11-09 12:15:47 +02:00
Vladimir Golovnev
330dce6aa2 Correctly handle "torrent finished" events
PR #21786.
Closes #21699.
2024-11-08 11:47:37 +03:00
Vladimir Golovnev
39b965af48 Check real palette darkness to detect "dark theme"
`QStyleHints::colorScheme()` returns chosen color scheme even if current style doesn't support it and uses different palette.

PR #21771.
2024-11-08 11:47:26 +03:00
Vladimir Golovnev
5e105b0348 Optimize checking for outdated tracker endpoints
PR #21768.
2024-11-07 09:42:53 +03:00
Vladimir Golovnev
f2b2a2b034 Optimize converting TCP endpoints to strings
There may be quite a few endpoint names (one for each available network card), and they usually remain unchanged throughout the session, while previously producing such names was performed every time they were accessed. Now they are retrieved from the cache.

PR #21770.
2024-11-07 09:42:43 +03:00
Vladimir Golovnev
10499dffe9 Optimize conversion of time points from libtorrent to Qt clocks
Obtain current date time of Qt and libtorrent clocks only once
for processing entire current libtorrent alerts bunch.

PR #21764.
2024-11-05 16:46:28 +03:00
Vladimir Golovnev
eea01b94a3 Reset tracker entries when pause the session
PR #21738.
2024-11-04 16:28:04 +03:00
Vladimir Golovnev
374951f6f2 Handle Qt style names in a case insensitive way
PR #21720.
Closes #21716.
2024-11-03 10:06:30 +03:00
sledgehammer999
6d6f9bc619 Reorder code to match UI 2024-11-02 17:52:13 +02:00
sledgehammer999
84ee620fdc Webui: Add color scheme switcher
Closes #21600
2024-11-02 17:32:55 +02:00
Vladimir Golovnev
6079b25419 Fix .torrent file could not be deleted when torrent is canceled
PR #21735.
Closes #21723.
2024-11-02 16:42:11 +03:00
Vladimir Golovnev
fe24bc825b Remove trackers from previous category when moved to new one
PR #21717.
Closes #21637.
2024-11-02 16:42:07 +03:00
Vladimir Golovnev
565c6d843a Correctly delete the moved search tab
PR #21687.
Closes #21675.
2024-10-28 09:45:23 +03:00
196 changed files with 31512 additions and 29483 deletions

View File

@@ -134,16 +134,15 @@ jobs:
- name: Install AppImage
run: |
sudo apt install libfuse2
curl \
-L \
-Z \
-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/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-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage
chmod +x \
linuxdeploy-static-x86_64.AppImage \
linuxdeploy-plugin-qt-static-x86_64.AppImage \
linuxdeploy-x86_64.AppImage \
linuxdeploy-plugin-qt-x86_64.AppImage \
linuxdeploy-plugin-appimage-x86_64.AppImage
- name: Prepare files for AppImage
@@ -156,12 +155,12 @@ jobs:
- name: Package AppImage
run: |
./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --plugin qt
./linuxdeploy-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-static-x86_64.AppImage --appdir qbittorrent --output appimage
./linuxdeploy-x86_64.AppImage --appdir qbittorrent --output appimage
- name: Upload build artifacts
uses: actions/upload-artifact@v4

View File

@@ -1,3 +1,46 @@
Sun Apr 13th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.5
- FEATURE: Add an advanced setting for setting the "Add New Torrent" dialog as modal (glassez)
- BUGFIX: Improve command line parameters serialization (glassez)
- BUGFIX: Declare missing color IDs for theming (glassez)
- WINDOWS: NSIS: Update Swedish translation (Daniel Nylander)
Tue Feb 18th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.4
- BUGFIX: Fix cannot remove trackers via WebAPI (Chocobo1)
- BUGFIX: Fix torrent content checkbox state under certain conditions (thalieht)
- BUGFIX: Hide zero and infinity values in peer list only when that setting is set to `Always` (thalieht)
- BUGFIX: Remove stopped torrent from "error" tracker filter (glassez)
- WEBUI: Fix memory leak in context menus (skomerko)
- WEBAPI: Don't trim string parameters (glassez)
- WINDOWS: Handle Qt style options uniformly (glassez)
- WINDOWS: NSIS: Update Portuguese translation (Hugo Carvalho)
- MACOS: Avoid memory leak (Chocobo1)
Mon Dec 16th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.3
- BUGFIX: Discard obsolete "state update" events after torrent is reloaded (glassez)
- BUGFIX: Fix incorrect SQL column definition (glassez)
- BUGFIX: Avoid redundant requests of announce entries from libtorrent (glassez)
- WEBUI: Fix removing tracker URL with '|' character (Thomas Piccirello)
- WEBUI: Fix reloading page after login (Evgenii Ryshkov)
- WEBAPI: Fix incorrect key in torrent creator (Bartu Özen)
- RSS: Don't add duplicate episodes to previously matched (wavygecko)
- RSS: Use cached current time when parsing RSS feed (glassez)
- WINDOWS: Don't follow symlink when creating torrents on Windows (Chocobo1)
- WINDOWS: NSIS: Update Italian translation (Giacomo411)
Sun Nov 17th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.2
- BUGFIX: Remove trackers from previous category when moved to new one (glassez)
- BUGFIX: Fix `.torrent` file could not be deleted when torrent is canceled (glassez)
- BUGFIX: Reset tracker entries when pausing the session (glassez)
- BUGFIX: Check real palette darkness to detect "dark theme" (glassez)
- BUGFIX: Correctly handle "torrent finished" events (glassez)
- BUGFIX: Preserve initial torrent progress while checking resume data (glassez)
- BUGFIX: Avoid reapplying Mark-of-the-Web when it already exists (Chocobo1)
- BUGFIX: Don't apply Mark-of-the-Web on existing files (Chocobo1)
- WEBUI: Add color scheme switcher (sledgehammer999)
- SEARCH: Correctly delete the moved search tab (glassez)
- WINDOWS: Correctly save and restore Qt style setting (glassez)
- WINDOWS: NSIS: update Luxembourgish, Simplified Chinese and Traditional Chinese translations (Ikko Eltociear Ashimine, 3gf8jv4dv)
Mon Oct 28th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.1
- FEATURE: Add "Simple pread/pwrite" disk IO type (Hanabishi)
- BUGFIX: Don't ignore SSL errors (sledgehammer999)

4
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.0.1</string>
<string>5.0.5</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -67,7 +67,7 @@
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2006-2024 The qBittorrent project</string>
<string>Copyright © 2006-2025 The qBittorrent project</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>

View File

@@ -105,7 +105,7 @@ GenericName[ka]=BitTorrent კლიენტი
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
Name[ka]=qBittorrent
GenericName[ko]=BitTorrent 클라이언트
Comment[ko]=BitTorrent를 통 파일 다운로드 및 공유
Comment[ko]=BitTorrent를 통 파일 다운로드 및 공유
Name[ko]=qBittorrent
GenericName[lt]=BitTorrent klientas
Comment[lt]=Atsisiųskite bei dalinkitės failais BitTorrent tinkle
@@ -161,7 +161,7 @@ Name[ta]=qBittorrent
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్స్ దిగుమతి చేసుకోండి , పంచుకోండి
Name[te]=qBittorrent
GenericName[th]=โปรแกรมบิทอร์เรนต์
GenericName[th]=ไคลเอนต์บิทอร์เรนต์
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่านบิตทอร์เรนต์
Name[th]=qBittorrent
GenericName[tr]=BitTorrent istemcisi

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.1" date="2024-10-28"/>
<release version="5.0.5" date="2025-04-13"/>
</releases>
</component>

View File

@@ -14,7 +14,7 @@
; 4.5.1.3 -> good
; 4.5.1.3.2 -> bad
; 4.5.0beta -> bad
!define /ifndef QBT_VERSION "5.0.1"
!define /ifndef QBT_VERSION "5.0.5"
; Option that controls the installer's window name
; If set, its value will be used like this:
@@ -86,7 +86,7 @@ OutFile "qbittorrent_${QBT_INSTALLER_FILENAME}_setup.exe"
;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2024 The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2025 The qBittorrent project"
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${QBT_VERSION}"

View File

@@ -29,7 +29,7 @@ LangString launch_qbt ${LANG_ITALIAN} "Esegui qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_ITALIAN} "Questo installer funziona solo con versioni di Windows a 64bit."
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_ITALIAN} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_ITALIAN} "Questo installer richiede almeno Windows 10 (1809) / Windows Server 2019."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_ITALIAN} "Disinstalla qBittorrent"

View File

@@ -15,7 +15,7 @@ LangString inst_magnet ${LANG_LUXEMBOURGISH} "Magnet-Linken mat qBittorrent opma
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_LUXEMBOURGISH} "Reegel an der Windows Firewall dobäisetzen"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_LUXEMBOURGISH} "D'Windows path lenght (Padlängtbeschränkung) desaktivéieren (260 Zeechen MAX_PATH Beschränkung, erfuerdert min. Windows 10 1607)"
LangString inst_pathlimit ${LANG_LUXEMBOURGISH} "D'Windows path length (Padlängtbeschränkung) desaktivéieren (260 Zeechen MAX_PATH Beschränkung, erfuerdert min. Windows 10 1607)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_LUXEMBOURGISH} "Reegel an der Windows Firewall dobäisetzen"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."

View File

@@ -7,7 +7,7 @@ LangString inst_desktop ${LANG_PORTUGUESE} "Criar atalho no ambiente de trabalho
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_PORTUGUESE} "Criar atalho no menu Iniciar"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_PORTUGUESE} "Iniciar o qBittorrent na inicialização do Windows"
LangString inst_startup ${LANG_PORTUGUESE} "Iniciar o qBittorrent no arranque do Windows"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_PORTUGUESE} "Abrir ficheiros .torrent com o qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
@@ -29,7 +29,7 @@ LangString launch_qbt ${LANG_PORTUGUESE} "Iniciar qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_PORTUGUESE} "Este instalador funciona apenas em versões Windows de 64 bits."
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_PORTUGUESE} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_PORTUGUESE} "Este instalador requer, pelo menos, o Windows 10 (1809) / Windows Server 2019."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_PORTUGUESE} "Desinstalar qBittorrent"

View File

@@ -23,13 +23,13 @@ LangString inst_warning ${LANG_SIMPCHINESE} "qBittorrent 正在运行。 安装
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_SIMPCHINESE} "当前版本会被卸载。 用户设置和种子会被完整保留。"
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_SIMPCHINESE} "卸载以前的版本。"
LangString inst_unist ${LANG_SIMPCHINESE} "正在卸载以前的版本。"
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SIMPCHINESE} "启动 qBittorrent。"
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SIMPCHINESE} "此安装程序仅支持 64 位 Windows 系统。"
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_SIMPCHINESE} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_SIMPCHINESE} "此安装程序仅支持 Windows 10 (1809) / Windows Server 2019 或更新的系统。"
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_SIMPCHINESE} "卸载 qBittorrent"

View File

@@ -7,21 +7,21 @@ LangString inst_desktop ${LANG_SWEDISH} "Skapa skrivbordsgenväg"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_SWEDISH} "Skapa startmenygenväg"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows start"
LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows-uppstart"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_SWEDISH} "Öppna .torrent-filer med qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_SWEDISH} "Öppna magnetlänkar med qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggregel"
LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggsregel"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_SWEDISH} "Inaktivera gränsen för Windows-sökvägslängd (260 tecken MAX_PATH-begränsning, kräver Windows 10 1607 eller senare)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggregel"
LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggsregel"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du installerar."
LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Stäng programmet innan du installerar."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_SWEDISH} "Nuvarande version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta."
LangString inst_uninstall_question ${LANG_SWEDISH} "Aktuell version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
@@ -53,7 +53,7 @@ LangString remove_firewallinfo ${LANG_SWEDISH} "Tar bort Windows-brandväggsrege
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_SWEDISH} "Ta bort torrenter och cachade data"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du avinstallerar."
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Stäng programmet innan du avinstallerar."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_SWEDISH} "Tar inte bort .torrent-association. Den är associerad med:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"

View File

@@ -29,7 +29,7 @@ LangString launch_qbt ${LANG_TRADCHINESE} "啟動 qBittorrent"
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_TRADCHINESE} "此安裝程式僅支援 64 位元版本的 Windows。"
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_TRADCHINESE} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_TRADCHINESE} "此安裝程式僅支援 Windows 10 (1809) / Windows Server 2019 以上的系統。"
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_TRADCHINESE} "移除 qBittorrent"

View File

@@ -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
*
* This program is free software; you can redistribute it and/or
@@ -124,6 +124,28 @@ namespace
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
#endif
const QString PARAM_ADDSTOPPED = u"@addStopped"_s;
const QString PARAM_CATEGORY = u"@category"_s;
const QString PARAM_FIRSTLASTPIECEPRIORITY = u"@firstLastPiecePriority"_s;
const QString PARAM_SAVEPATH = u"@savePath"_s;
const QString PARAM_SEQUENTIAL = u"@sequential"_s;
const QString PARAM_SKIPCHECKING = u"@skipChecking"_s;
const QString PARAM_SKIPDIALOG = u"@skipDialog"_s;
QString bindParamValue(const QStringView paramName, const QStringView paramValue)
{
return paramName + u'=' + paramValue;
}
std::pair<QStringView, QStringView> parseParam(const QStringView param)
{
const qsizetype sepIndex = param.indexOf(u'=');
if (sepIndex >= 0)
return {param.first(sepIndex), param.sliced(sepIndex + 1)};
return {param, {}};
}
QString serializeParams(const QBtCommandLineParameters &params)
{
QStringList result;
@@ -138,85 +160,86 @@ namespace
const BitTorrent::AddTorrentParams &addTorrentParams = params.addTorrentParams;
if (!addTorrentParams.savePath.isEmpty())
result.append(u"@savePath=" + addTorrentParams.savePath.data());
result.append(bindParamValue(PARAM_SAVEPATH, addTorrentParams.savePath.data()));
if (addTorrentParams.addStopped.has_value())
result.append(*addTorrentParams.addStopped ? u"@addStopped=1"_s : u"@addStopped=0"_s);
result.append(bindParamValue(PARAM_ADDSTOPPED, (*addTorrentParams.addStopped ? u"1" : u"0")));
if (addTorrentParams.skipChecking)
result.append(u"@skipChecking"_s);
result.append(PARAM_SKIPCHECKING);
if (!addTorrentParams.category.isEmpty())
result.append(u"@category=" + addTorrentParams.category);
result.append(bindParamValue(PARAM_CATEGORY, addTorrentParams.category));
if (addTorrentParams.sequential)
result.append(u"@sequential"_s);
result.append(PARAM_SEQUENTIAL);
if (addTorrentParams.firstLastPiecePriority)
result.append(u"@firstLastPiecePriority"_s);
result.append(PARAM_FIRSTLASTPIECEPRIORITY);
if (params.skipDialog.has_value())
result.append(*params.skipDialog ? u"@skipDialog=1"_s : u"@skipDialog=0"_s);
result.append(bindParamValue(PARAM_SKIPDIALOG, (*params.skipDialog ? u"1" : u"0")));
result += params.torrentSources;
return result.join(PARAMS_SEPARATOR);
}
QBtCommandLineParameters parseParams(const QString &str)
QBtCommandLineParameters parseParams(const QStringView str)
{
QBtCommandLineParameters parsedParams;
BitTorrent::AddTorrentParams &addTorrentParams = parsedParams.addTorrentParams;
for (QString param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts)))
for (QStringView param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts)))
{
param = param.trimmed();
const auto [paramName, paramValue] = parseParam(param);
// Process strings indicating options specified by the user.
if (param.startsWith(u"@savePath="))
if (paramName == PARAM_SAVEPATH)
{
addTorrentParams.savePath = Path(param.mid(10));
addTorrentParams.savePath = Path(paramValue.toString());
continue;
}
if (param.startsWith(u"@addStopped="))
if (paramName == PARAM_ADDSTOPPED)
{
addTorrentParams.addStopped = (QStringView(param).mid(11).toInt() != 0);
addTorrentParams.addStopped = (paramValue.toInt() != 0);
continue;
}
if (param == u"@skipChecking")
if (paramName == PARAM_SKIPCHECKING)
{
addTorrentParams.skipChecking = true;
continue;
}
if (param.startsWith(u"@category="))
if (paramName == PARAM_CATEGORY)
{
addTorrentParams.category = param.mid(10);
addTorrentParams.category = paramValue.toString();
continue;
}
if (param == u"@sequential")
if (paramName == PARAM_SEQUENTIAL)
{
addTorrentParams.sequential = true;
continue;
}
if (param == u"@firstLastPiecePriority")
if (paramName == PARAM_FIRSTLASTPIECEPRIORITY)
{
addTorrentParams.firstLastPiecePriority = true;
continue;
}
if (param.startsWith(u"@skipDialog="))
if (paramName == PARAM_SKIPDIALOG)
{
parsedParams.skipDialog = (QStringView(param).mid(12).toInt() != 0);
parsedParams.skipDialog = (paramValue.toInt() != 0);
continue;
}
parsedParams.torrentSources.append(param);
parsedParams.torrentSources.append(param.toString());
}
return parsedParams;

View File

@@ -195,11 +195,9 @@ void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_p
m_guardedTorrentFiles.emplace(source, std::move(torrentFileGuard));
}
void AddTorrentManager::releaseTorrentFileGuard(const QString &source)
std::shared_ptr<TorrentFileGuard> AddTorrentManager::releaseTorrentFileGuard(const QString &source)
{
auto torrentFileGuard = m_guardedTorrentFiles.take(source);
if (torrentFileGuard)
torrentFileGuard->setAutoRemove(false);
return m_guardedTorrentFiles.take(source);
}
bool AddTorrentManager::processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr

View File

@@ -74,7 +74,7 @@ protected:
void handleAddTorrentFailed(const QString &source, const QString &reason);
void handleDuplicateTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, BitTorrent::Torrent *existingTorrent);
void setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard);
void releaseTorrentFileGuard(const QString &source);
std::shared_ptr<TorrentFileGuard> releaseTorrentFileGuard(const QString &source);
private:
void onDownloadFinished(const Net::DownloadResult &result);

View File

@@ -67,7 +67,7 @@ namespace
{
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s;
const int DB_VERSION = 7;
const int DB_VERSION = 8;
const QString DB_TABLE_META = u"meta"_s;
const QString DB_TABLE_TORRENTS = u"torrents"_s;
@@ -628,7 +628,31 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
}
if (fromVersion <= 6)
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, "TEXTNOT NULL DEFAULT `Default`");
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, "TEXT NOT NULL DEFAULT `Default`");
if (fromVersion == 7)
{
const QString TEMP_COLUMN_NAME = DB_COLUMN_SHARE_LIMIT_ACTION.name + u"_temp";
auto queryStr = u"ALTER TABLE %1 ADD %2 %3"_s
.arg(quoted(DB_TABLE_TORRENTS), TEMP_COLUMN_NAME, u"TEXT NOT NULL DEFAULT `Default`");
if (!query.exec(queryStr))
throw RuntimeError(query.lastError().text());
queryStr = u"UPDATE %1 SET %2 = %3"_s
.arg(quoted(DB_TABLE_TORRENTS), quoted(TEMP_COLUMN_NAME), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name));
if (!query.exec(queryStr))
throw RuntimeError(query.lastError().text());
queryStr = u"ALTER TABLE %1 DROP %2"_s.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name));
if (!query.exec(queryStr))
throw RuntimeError(query.lastError().text());
queryStr = u"ALTER TABLE %1 RENAME %2 TO %3"_s
.arg(quoted(DB_TABLE_TORRENTS), quoted(TEMP_COLUMN_NAME), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name));
if (!query.exec(queryStr))
throw RuntimeError(query.lastError().text());
}
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
if (!query.prepare(updateMetaVersionQuery))

View File

@@ -66,6 +66,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QMutexLocker>
#include <QNetworkAddressEntry>
#include <QNetworkInterface>
#include <QRegularExpression>
@@ -1606,7 +1607,7 @@ void SessionImpl::endStartup(ResumeSessionContext *context)
reannounceToAllTrackers();
}
m_wakeupCheckTimestamp = QDateTime::currentDateTime();
m_wakeupCheckTimestamp = now;
});
m_wakeupCheckTimestamp = QDateTime::currentDateTime();
m_wakeupCheckTimer->start(30s);
@@ -4065,14 +4066,29 @@ bool SessionImpl::isPaused() const
void SessionImpl::pause()
{
if (!m_isPaused)
{
if (isRestored())
m_nativeSession->pause();
if (m_isPaused)
return;
m_isPaused = true;
emit paused();
if (isRestored())
{
m_nativeSession->pause();
for (TorrentImpl *torrent : asConst(m_torrents))
{
torrent->resetTrackerEntryStatuses();
const QList<TrackerEntryStatus> trackers = torrent->trackers();
QHash<QString, TrackerEntryStatus> updatedTrackers;
updatedTrackers.reserve(trackers.size());
for (const TrackerEntryStatus &status : trackers)
updatedTrackers.emplace(status.url, status);
emit trackerEntryStatusesUpdated(torrent, updatedTrackers);
}
}
m_isPaused = true;
emit paused();
}
void SessionImpl::resume()
@@ -5215,9 +5231,6 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
if (torrent)
{
torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob);
// The torrent may become "finished" at the end of the move if it was moved
// from the "incomplete" location after downloading finished.
processPendingFinishedTorrents();
}
else if (!torrentHasOutstandingJob)
{
@@ -5481,6 +5494,11 @@ void SessionImpl::setTorrentContentLayout(const TorrentContentLayout value)
// Read alerts sent by libtorrent session
void SessionImpl::readAlerts()
{
// cache current datetime of Qt and libtorrent clocks in order
// to optimize conversion of time points from lt to Qt clocks
m_ltNow = lt::clock_type::now();
m_qNow = QDateTime::currentDateTime();
const std::vector<lt::alert *> alerts = getPendingAlerts();
Q_ASSERT(m_loadedTorrents.isEmpty());
@@ -5507,7 +5525,8 @@ void SessionImpl::readAlerts()
}
}
processTrackerStatuses();
// Some torrents may become "finished" after different alerts handling.
processPendingFinishedTorrents();
}
void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert)
@@ -6146,8 +6165,6 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
if (!updatedTorrents.isEmpty())
emit torrentsUpdated(updatedTorrents);
processPendingFinishedTorrents();
if (m_needSaveTorrentsQueue)
saveTorrentsQueue();
@@ -6185,7 +6202,12 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert)
if (!torrent)
return;
[[maybe_unused]] const QMutexLocker updatedTrackerStatusesLocker {&m_updatedTrackerStatusesMutex};
const auto prevSize = m_updatedTrackerStatuses.size();
QMap<int, int> &updateInfo = m_updatedTrackerStatuses[torrent->nativeHandle()][std::string(alert->tracker_url())][alert->local_endpoint];
if (prevSize < m_updatedTrackerStatuses.size())
updateTrackerEntryStatuses(torrent->nativeHandle());
if (alert->type() == lt::tracker_reply_alert::alert_type)
{
@@ -6247,17 +6269,6 @@ void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *a
}
#endif
void SessionImpl::processTrackerStatuses()
{
if (m_updatedTrackerStatuses.isEmpty())
return;
for (auto it = m_updatedTrackerStatuses.cbegin(); it != m_updatedTrackerStatuses.cend(); ++it)
updateTrackerEntryStatuses(it.key(), it.value());
m_updatedTrackerStatuses.clear();
}
void SessionImpl::saveStatistics() const
{
if (!m_isStatisticsDirty)
@@ -6282,15 +6293,19 @@ void SessionImpl::loadStatistics()
m_previouslyUploaded = value[u"AlltimeUL"_s].toLongLong();
}
void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers)
void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle)
{
invokeAsync([this, torrentHandle = std::move(torrentHandle), updatedTrackers = std::move(updatedTrackers)]() mutable
invokeAsync([this, torrentHandle = std::move(torrentHandle)]() mutable
{
try
{
std::vector<lt::announce_entry> nativeTrackers = torrentHandle.trackers();
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)
, updatedTrackers = std::move(updatedTrackers)]
QMutexLocker updatedTrackerStatusesLocker {&m_updatedTrackerStatusesMutex};
QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers = m_updatedTrackerStatuses.take(torrentHandle);
updatedTrackerStatusesLocker.unlock();
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers), updatedTrackers = std::move(updatedTrackers)]
{
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
if (!torrent || torrent->isStopped())
@@ -6344,3 +6359,9 @@ void SessionImpl::handleRemovedTorrent(const TorrentID &torrentID, const QString
m_removingTorrents.erase(removingTorrentDataIter);
}
QDateTime SessionImpl::fromLTTimePoint32(const lt::time_point32 &timePoint) const
{
const auto secsSinceNow = lt::duration_cast<lt::seconds>(timePoint - m_ltNow + lt::milliseconds(500)).count();
return m_qNow.addSecs(secsSinceNow);
}

View File

@@ -41,6 +41,7 @@
#include <QElapsedTimer>
#include <QHash>
#include <QMap>
#include <QMutex>
#include <QPointer>
#include <QSet>
#include <QVector>
@@ -475,6 +476,8 @@ namespace BitTorrent
void addMappedPorts(const QSet<quint16> &ports);
void removeMappedPorts(const QSet<quint16> &ports);
QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint) const;
template <typename Func>
void invoke(Func &&func)
{
@@ -539,7 +542,6 @@ namespace BitTorrent
void populateAdditionalTrackers();
void enableIPFilter();
void disableIPFilter();
void processTrackerStatuses();
void processTorrentShareLimits(TorrentImpl *torrent);
void populateExcludedFileNamesRegExpList();
void prepareStartup();
@@ -603,7 +605,7 @@ namespace BitTorrent
void saveStatistics() const;
void loadStatistics();
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle);
void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {});
@@ -792,6 +794,7 @@ namespace BitTorrent
// This field holds amounts of peers reported by trackers in their responses to announces
// (torrent.tracker_name.tracker_local_endpoint.protocol_version.num_peers)
QHash<lt::torrent_handle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>>> m_updatedTrackerStatuses;
QMutex m_updatedTrackerStatusesMutex;
// I/O errored torrents
QSet<TorrentID> m_recentErroredTorrents;
@@ -820,6 +823,9 @@ namespace BitTorrent
QList<TorrentImpl *> m_pendingFinishedTorrents;
QDateTime m_qNow;
lt::clock_type::time_point m_ltNow;
friend void Session::initInstance();
friend void Session::freeInstance();
friend Session *Session::instance();

View File

@@ -215,7 +215,15 @@ namespace BitTorrent
virtual int piecesCount() const = 0;
virtual int piecesHave() const = 0;
virtual qreal progress() const = 0;
virtual QDateTime addedTime() const = 0;
virtual QDateTime completedTime() const = 0;
virtual QDateTime lastSeenComplete() const = 0;
virtual qlonglong activeTime() const = 0;
virtual qlonglong finishedTime() const = 0;
virtual qlonglong timeSinceUpload() const = 0;
virtual qlonglong timeSinceDownload() const = 0;
virtual qlonglong timeSinceActivity() const = 0;
// Share limits
virtual qreal ratioLimit() const = 0;
@@ -254,8 +262,6 @@ namespace BitTorrent
virtual QString error() const = 0;
virtual qlonglong totalDownload() const = 0;
virtual qlonglong totalUpload() const = 0;
virtual qlonglong activeTime() const = 0;
virtual qlonglong finishedTime() const = 0;
virtual qlonglong eta() const = 0;
virtual int seedsCount() const = 0;
virtual int peersCount() const = 0;
@@ -263,11 +269,6 @@ namespace BitTorrent
virtual int totalSeedsCount() const = 0;
virtual int totalPeersCount() const = 0;
virtual int totalLeechersCount() const = 0;
virtual QDateTime lastSeenComplete() const = 0;
virtual QDateTime completedTime() const = 0;
virtual qlonglong timeSinceUpload() const = 0;
virtual qlonglong timeSinceDownload() const = 0;
virtual qlonglong timeSinceActivity() const = 0;
virtual int downloadLimit() const = 0;
virtual int uploadLimit() const = 0;
virtual bool superSeeding() const = 0;

View File

@@ -36,6 +36,7 @@
#include <libtorrent/file_storage.hpp>
#include <libtorrent/torrent_info.hpp>
#include <QtSystemDetection>
#include <QDirIterator>
#include <QFileInfo>
#include <QHash>
@@ -123,7 +124,14 @@ void TorrentCreator::run()
// need to sort the file names by natural sort order
QStringList dirs = {m_params.sourcePath.data()};
QDirIterator dirIter {m_params.sourcePath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
#ifdef Q_OS_WIN
// libtorrent couldn't handle .lnk files on Windows
// Also, Windows users do not expect torrent creator to traverse into .lnk files so skip over them
const QDir::Filters dirFilters {QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks};
#else
const QDir::Filters dirFilters {QDir::AllDirs | QDir::NoDotAndDotDot};
#endif
QDirIterator dirIter {m_params.sourcePath.data(), dirFilters, QDirIterator::Subdirectories};
while (dirIter.hasNext())
{
const QString filePath = dirIter.next();
@@ -138,7 +146,12 @@ void TorrentCreator::run()
{
QStringList tmpNames; // natural sort files within each dir
QDirIterator fileIter {dir, QDir::Files};
#ifdef Q_OS_WIN
const QDir::Filters fileFilters {QDir::Files | QDir::NoSymLinks};
#else
const QDir::Filters fileFilters {QDir::Files};
#endif
QDirIterator fileIter {dir, fileFilters};
while (fileIter.hasNext())
{
const QFileInfo fileInfo = fileIter.nextFileInfo();

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -35,9 +35,7 @@
#include <libtorrent/write_resume_data.hpp>
#include <QByteArray>
#include <QDateTime>
#include <QRegularExpression>
#include <QString>
#include <QUrl>
#include "base/global.h"
@@ -147,7 +145,13 @@ BitTorrent::TorrentDescriptor::TorrentDescriptor(lt::add_torrent_params ltAddTor
: m_ltAddTorrentParams {std::move(ltAddTorrentParams)}
{
if (m_ltAddTorrentParams.ti && m_ltAddTorrentParams.ti->is_valid())
{
m_info.emplace(*m_ltAddTorrentParams.ti);
if (m_ltAddTorrentParams.ti->creation_date() > 0)
m_creationDate = QDateTime::fromSecsSinceEpoch(m_ltAddTorrentParams.ti->creation_date());
m_creator = QString::fromStdString(m_ltAddTorrentParams.ti->creator());
m_comment = QString::fromStdString(m_ltAddTorrentParams.ti->comment());
}
}
BitTorrent::InfoHash BitTorrent::TorrentDescriptor::infoHash() const
@@ -166,18 +170,17 @@ QString BitTorrent::TorrentDescriptor::name() const
QDateTime BitTorrent::TorrentDescriptor::creationDate() const
{
return ((m_ltAddTorrentParams.ti->creation_date() != 0)
? QDateTime::fromSecsSinceEpoch(m_ltAddTorrentParams.ti->creation_date()) : QDateTime());
return m_creationDate;
}
QString BitTorrent::TorrentDescriptor::creator() const
{
return QString::fromStdString(m_ltAddTorrentParams.ti->creator());
return m_creator;
}
QString BitTorrent::TorrentDescriptor::comment() const
{
return QString::fromStdString(m_ltAddTorrentParams.ti->comment());
return m_comment;
}
const std::optional<BitTorrent::TorrentInfo> &BitTorrent::TorrentDescriptor::info() const

View File

@@ -33,7 +33,9 @@
#include <libtorrent/add_torrent_params.hpp>
#include <QtContainerFwd>
#include <QDateTime>
#include <QMetaType>
#include <QString>
#include "base/3rdparty/expected.hpp"
#include "base/path.h"
@@ -41,8 +43,6 @@
#include "torrentinfo.h"
class QByteArray;
class QDateTime;
class QString;
class QUrl;
namespace BitTorrent
@@ -78,6 +78,9 @@ namespace BitTorrent
lt::add_torrent_params m_ltAddTorrentParams;
std::optional<TorrentInfo> m_info;
QDateTime m_creationDate;
QString m_creator;
QString m_comment;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -138,7 +138,15 @@ namespace BitTorrent
int piecesCount() const override;
int piecesHave() const override;
qreal progress() const override;
QDateTime addedTime() const override;
QDateTime completedTime() const override;
QDateTime lastSeenComplete() const override;
qlonglong activeTime() const override;
qlonglong finishedTime() const override;
qlonglong timeSinceUpload() const override;
qlonglong timeSinceDownload() const override;
qlonglong timeSinceActivity() const override;
qreal ratioLimit() const override;
void setRatioLimit(qreal limit) override;
@@ -181,8 +189,6 @@ namespace BitTorrent
QString error() const override;
qlonglong totalDownload() const override;
qlonglong totalUpload() const override;
qlonglong activeTime() const override;
qlonglong finishedTime() const override;
qlonglong eta() const override;
QVector<qreal> filesProgress() const override;
int seedsCount() const override;
@@ -191,11 +197,6 @@ namespace BitTorrent
int totalSeedsCount() const override;
int totalPeersCount() const override;
int totalLeechersCount() const override;
QDateTime lastSeenComplete() const override;
QDateTime completedTime() const override;
qlonglong timeSinceUpload() const override;
qlonglong timeSinceDownload() const override;
qlonglong timeSinceActivity() const override;
int downloadLimit() const override;
int uploadLimit() const override;
bool superSeeding() const override;
@@ -342,6 +343,14 @@ namespace BitTorrent
InfoHash m_infoHash;
QDateTime m_creationDate;
QString m_creator;
QString m_comment;
QDateTime m_addedTime;
QDateTime m_completedTime;
QDateTime m_lastSeenComplete;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete
QQueue<EventTrigger> m_moveFinishedTriggers;

View File

@@ -29,7 +29,10 @@
#pragma once
#include <QByteArray>
#include <QHash>
#include <QHostAddress>
#include <QMap>
#include <QString>
#include <QVector>

View File

@@ -2000,6 +2000,19 @@ void Preferences::setAddNewTorrentDialogSavePathHistoryLength(const int value)
setValue(u"AddNewTorrentDialog/SavePathHistoryLength"_s, clampedValue);
}
bool Preferences::isAddNewTorrentDialogAttached() const
{
return value(u"AddNewTorrentDialog/Attached"_s, false);
}
void Preferences::setAddNewTorrentDialogAttached(const bool attached)
{
if (attached == isAddNewTorrentDialogAttached())
return;
setValue(u"AddNewTorrentDialog/Attached"_s, attached);
}
void Preferences::apply()
{
if (SettingsStorage::instance()->save())

View File

@@ -423,6 +423,8 @@ public:
void setAddNewTorrentDialogTopLevel(bool value);
int addNewTorrentDialogSavePathHistoryLength() const;
void setAddNewTorrentDialogSavePathHistoryLength(int value);
bool isAddNewTorrentDialogAttached() const;
void setAddNewTorrentDialogAttached(bool attached);
public slots:
void setStatusFilterState(bool checked);

View File

@@ -396,6 +396,8 @@ bool AutoDownloadRule::matchesSmartEpisodeFilter(const QString &articleTitle) co
m_dataPtr->lastComputedEpisodes.append(episodeStr + u"-REPACK");
m_dataPtr->lastComputedEpisodes.append(episodeStr + u"-PROPER");
}
return true;
}
m_dataPtr->lastComputedEpisodes.append(episodeStr);

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-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,11 +29,8 @@
#include "rss_parser.h"
#include <QDateTime>
#include <QDebug>
#include <QGlobalStatic>
#include <QHash>
#include <QMetaObject>
#include <QRegularExpression>
#include <QStringList>
#include <QVariant>
@@ -359,7 +356,7 @@ namespace
};
// Ported to Qt from KDElibs4
QDateTime parseDate(const QString &string)
QDateTime parseDate(const QString &string, const QDateTime &fallbackDate)
{
const char16_t shortDay[][4] =
{
@@ -382,7 +379,7 @@ namespace
const QString str = string.trimmed();
if (str.isEmpty())
return QDateTime::currentDateTime();
return fallbackDate;
int nyear = 6; // indexes within string to values
int nmonth = 4;
@@ -402,14 +399,14 @@ namespace
const bool h1 = (parts[3] == u"-");
const bool h2 = (parts[5] == u"-");
if (h1 != h2)
return QDateTime::currentDateTime();
return fallbackDate;
}
else
{
// Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY"
rx = QRegularExpression {u"^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"_s};
if (str.indexOf(rx, 0, &rxMatch) != 0)
return QDateTime::currentDateTime();
return fallbackDate;
nyear = 7;
nmonth = 2;
@@ -427,14 +424,14 @@ namespace
const int hour = parts[nhour].toInt(&ok[2]);
const int minute = parts[nmin].toInt(&ok[3]);
if (!ok[0] || !ok[1] || !ok[2] || !ok[3])
return QDateTime::currentDateTime();
return fallbackDate;
int second = 0;
if (!parts[nsec].isEmpty())
{
second = parts[nsec].toInt(&ok[0]);
if (!ok[0])
return QDateTime::currentDateTime();
return fallbackDate;
}
const bool leapSecond = (second == 60);
@@ -518,21 +515,21 @@ namespace
const QDate qDate(year, month + 1, day); // convert date, and check for out-of-range
if (!qDate.isValid())
return QDateTime::currentDateTime();
return fallbackDate;
const QTime qTime(hour, minute, second);
QDateTime result(qDate, qTime, Qt::UTC);
if (offset)
result = result.addSecs(-offset);
if (!result.isValid())
return QDateTime::currentDateTime(); // invalid date/time
return fallbackDate; // invalid date/time
if (leapSecond)
{
// Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC.
// Convert the time to UTC and check that it is 00:00:00.
if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours)
return QDateTime::currentDateTime(); // the time isn't the last second of the day
return fallbackDate; // the time isn't the last second of the day
}
return result;
@@ -550,6 +547,7 @@ RSS::Private::Parser::Parser(const QString &lastBuildDate)
void RSS::Private::Parser::parse(const QByteArray &feedData)
{
QXmlStreamReader xml {feedData};
m_fallbackDate = QDateTime::currentDateTime();
XmlStreamEntityResolver resolver;
xml.setEntityResolver(&resolver);
bool foundChannel = false;
@@ -641,7 +639,7 @@ void RSS::Private::Parser::parseRssArticle(QXmlStreamReader &xml)
}
else if (name == u"pubDate")
{
article[Article::KeyDate] = parseDate(xml.readElementText().trimmed());
article[Article::KeyDate] = parseDate(xml.readElementText().trimmed(), m_fallbackDate);
}
else if (name == u"author")
{
@@ -755,7 +753,7 @@ void RSS::Private::Parser::parseAtomArticle(QXmlStreamReader &xml)
{
// ATOM uses standard compliant date, don't do fancy stuff
const QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate);
article[Article::KeyDate] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime());
article[Article::KeyDate] = (articleDate.isValid() ? articleDate : m_fallbackDate);
}
else if (name == u"author")
{

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-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,6 +29,7 @@
#pragma once
#include <QDateTime>
#include <QList>
#include <QObject>
#include <QSet>
@@ -66,6 +67,7 @@ namespace RSS::Private
void parseAtomChannel(QXmlStreamReader &xml);
void addArticle(QVariantHash article);
QDateTime m_fallbackDate;
QString m_baseUrl;
ParsingResult m_result;
QSet<QString> m_articleIDs;

View File

@@ -31,6 +31,8 @@
#include "os.h"
#ifdef Q_OS_WIN
#include <algorithm>
#include <windows.h>
#include <powrprof.h>
#include <shlobj.h>
@@ -42,6 +44,8 @@
#include <CoreServices/CoreServices.h>
#endif // Q_OS_MACOS
#include <QScopeGuard>
#ifdef QBT_USES_DBUS
#include <QDBusInterface>
#endif // QBT_USES_DBUS
@@ -283,34 +287,53 @@ bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
// https://searchfox.org/mozilla-central/rev/ffdc4971dc18e1141cb2a90c2b0b776365650270/xpcom/io/CocoaFileUtils.mm#230
// https://github.com/transmission/transmission/blob/f62f7427edb1fd5c430e0ef6956bbaa4f03ae597/macosx/Torrent.mm#L1945-L1955
const CFStringRef fileString = file.toString().toCFString();
[[maybe_unused]] const auto fileStringGuard = qScopeGuard([&fileString] { ::CFRelease(fileString); });
const CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, fileString, kCFURLPOSIXPathStyle, false);
[[maybe_unused]] const auto fileURLGuard = qScopeGuard([&fileURL] { ::CFRelease(fileURL); });
if (CFDictionaryRef currentProperties = nullptr;
::CFURLCopyResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey, &currentProperties, NULL)
&& currentProperties)
{
[[maybe_unused]] const auto currentPropertiesGuard = qScopeGuard([&currentProperties] { ::CFRelease(currentProperties); });
if (CFStringRef quarantineType = nullptr;
::CFDictionaryGetValueIfPresent(currentProperties, kLSQuarantineTypeKey, reinterpret_cast<const void **>(&quarantineType))
&& quarantineType)
{
if (::CFStringCompare(quarantineType, kLSQuarantineTypeOtherDownload, 0) == kCFCompareEqualTo)
return true;
}
}
CFMutableDictionaryRef properties = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0
, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (properties == NULL)
if (!properties)
return false;
[[maybe_unused]] const auto propertiesGuard = qScopeGuard([&properties] { ::CFRelease(properties); });
::CFDictionarySetValue(properties, kLSQuarantineTypeKey, kLSQuarantineTypeOtherDownload);
if (!url.isEmpty())
::CFDictionarySetValue(properties, kLSQuarantineDataURLKey, url.toCFString());
const CFStringRef fileString = file.toString().toCFString();
const CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, fileString, kCFURLPOSIXPathStyle, false);
{
const CFStringRef urlCFString = url.toCFString();
[[maybe_unused]] const auto urlStringGuard = qScopeGuard([&urlCFString] { ::CFRelease(urlCFString); });
::CFDictionarySetValue(properties, kLSQuarantineDataURLKey, urlCFString);
}
const Boolean success = ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey
, properties, NULL);
::CFRelease(fileURL);
::CFRelease(fileString);
::CFRelease(properties);
return success;
#elif defined(Q_OS_WIN)
const QString zoneIDStream = file.toString() + u":Zone.Identifier";
HANDLE handle = ::CreateFileW(zoneIDStream.toStdWString().c_str(), GENERIC_WRITE
HANDLE handle = ::CreateFileW(zoneIDStream.toStdWString().c_str(), (GENERIC_READ | GENERIC_WRITE)
, (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE)
, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
[[maybe_unused]] const auto handleGuard = qScopeGuard([&handle] { ::CloseHandle(handle); });
// 5.6.1 Zone.Identifier Stream Name
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
@@ -318,10 +341,27 @@ bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
const QByteArray zoneID = QByteArrayLiteral("[ZoneTransfer]\r\nZoneId=3\r\n")
+ u"HostUrl=%1\r\n"_s.arg(hostURL).toUtf8();
if (LARGE_INTEGER streamSize = {0};
::GetFileSizeEx(handle, &streamSize) && (streamSize.QuadPart > 0))
{
const DWORD expectedReadSize = std::min<LONGLONG>(streamSize.QuadPart, 1024);
QByteArray buf {expectedReadSize, '\0'};
if (DWORD actualReadSize = 0;
::ReadFile(handle, buf.data(), expectedReadSize, &actualReadSize, nullptr) && (actualReadSize == expectedReadSize))
{
if (buf.startsWith("[ZoneTransfer]\r\n") && buf.contains("\r\nZoneId=3\r\n") && buf.contains("\r\nHostUrl="))
return true;
}
}
if (!::SetFilePointerEx(handle, {0}, nullptr, FILE_BEGIN))
return false;
if (!::SetEndOfFile(handle))
return false;
DWORD written = 0;
const BOOL writeResult = ::WriteFile(handle, zoneID.constData(), zoneID.size(), &written, nullptr);
::CloseHandle(handle);
return writeResult && (written == zoneID.size());
#endif
}

View File

@@ -30,7 +30,7 @@
#define QBT_VERSION_MAJOR 5
#define QBT_VERSION_MINOR 0
#define QBT_VERSION_BUGFIX 1
#define QBT_VERSION_BUGFIX 5
#define QBT_VERSION_BUILD 0
#define QBT_VERSION_STATUS "" // Should be empty for stable releases!

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