1
mirror of https://github.com/qbittorrent/qBittorrent synced 2025-10-16 20:32:23 +02:00

Compare commits

..

136 Commits

Author SHA1 Message Date
sledgehammer999
202ff8a099 Bump to 5.1.2 2025-07-02 09:13:03 +03:00
sledgehammer999
c0585441fb Update Changelog 2025-07-02 09:10:05 +03:00
sledgehammer999
a8b6cbceb0 Sync translations from Transifex and run lupdate 2025-07-02 09:09:21 +03:00
Vladimir Golovnev (Glassez)
6ad073e0bc Show warning message box on opening inappropriate URL 2025-07-02 08:48:27 +03:00
Vladimir Golovnev (Glassez)
ad68813fe8 Prevent opening local files if web page is expected 2025-07-02 08:48:27 +03:00
Vladimir Golovnev
22df0b45c5 Backport changes to v5.1.x branch
PR #22905.
2025-07-01 17:18:53 +03:00
sledgehammer999
bb34444ddc Store version numbers in the appropriate type 2025-07-01 13:07:55 +03:00
sledgehammer999
dd5c934103 Add fallback to update mechanism
This brings a fallback version check to the update mechanism,
which should be as stable as it can be.
It will allow migrating to another primary mechanism without
having to have updated the older primary mechanism too.
2025-07-01 13:07:24 +03:00
Ryu481
3fca180e98 Make qBittorrent quit on MacOS with main window closed
Fixes the reported bug that you couldn't quit qBittorrent when the main window was closed on MacOS.

Closes #22849.
PR #22931.
2025-06-29 21:37:30 +03:00
Chocobo1
9b29d37d21 WebAPI: Trim leading whitespaces on Run External Program fields
Hacked qbt instances may contain malicious script placed in Run External Program and the script
will attempt to hide itself by adding a lot whitespaces at the start of the command string.
Users may mistake the field of being empty but is actually not.
So trim the leading whitespaces to easily expose the malicious script.

Note that GUI already trim the fields and only WebAPI doesn't trim them. This patch will unify
the behavior.
Related: https://github.com/qbittorrent/docker-qbittorrent-nox/issues/71#issuecomment-2993567440

PR #22939.
2025-06-29 21:37:09 +03:00
Vladimir Golovnev
206d5abf84 Don't expose palette colors in UI theme editor
PR #22923.
Fixes regression introduced by #22330.
2025-06-27 15:54:51 +03:00
Vladimir Golovnev
101f35dcf2 WebUI: Fix incorrectly backported changes
PR #22910.
2025-06-26 08:48:40 +03:00
Vladimir Golovnev
13282d94ef Don't ignore QFile::open() result
PR #22889.
Closes #22888.
2025-06-23 12:15:34 +03:00
Vladimir Golovnev
1daa42e4fe Find CorePrivate package with Qt >= 6.10
PR #22890.
Closes #22887.
2025-06-23 12:15:12 +03:00
sledgehammer999
ea9f3800ce Bump to 5.1.1 2025-06-23 00:40:55 +03:00
sledgehammer999
af14584772 Update Changelog 2025-06-23 00:37:53 +03:00
sledgehammer999
7d51524251 Sync translations from Transifex and run lupdate 2025-06-22 23:25:18 +03:00
Vladimir Golovnev
7a9aac79f9 Backport changes to v5.1.x branch
PR #22591.
2025-06-20 19:16:30 +03:00
Vladimir Golovnev
085ae0d1c4 Don't limit the size of read "resume data"
PR #22825.
2025-06-08 18:39:58 +03:00
tehcneko
f748a682ca WebUI: Fix path autofill in set location and new category
Attach `PathAutofill` after `DOMContentLoaded`.
Also removed pathAutofill.js in newfolder.html since it's meant for new RSS folder name.

PR #22773.
2025-05-29 21:37:07 +03:00
Chocobo1
df987cc954 NSIS: revise license page
The GPL doesn't need to be agreed with and therefore remove 'I accept agreement' checkbox and
adjust related UI elements.

Closes #22660.
PR #22775.
2025-05-27 11:22:40 +03:00
bolshoytoster
535fc42747 WebUI: Fix memory leak
See #22734, there is a memory leak in the MooTools .destroy(), this replaces all uses of that with the browser native .remove().

This also overrides the MooTools Document.id function, which is used by $(id). The original function always allocates an ID to elements it selects, the override doesn't, and is also a little more efficient.

Closes #22734.
PR  #22754.

---------

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2025-05-26 15:56:37 +03:00
Vladimir Golovnev
1da31bc2e1 RSS: Mark matched article as "read" if refers to duplicate torrent
PR #22477.
2025-05-26 15:47:09 +03:00
Vladimir Golovnev
9515ca59f2 Improve add torrent error handling
PR #22468.
2025-05-26 15:46:17 +03:00
Chocobo1
eaf9017aa4 WebUI: fix wrong replacement sequence
Only IPv6 addresses may have a 'zone index' and therefore it should be replaced last for the
result to be correct.

PR #22724.
2025-05-18 12:41:19 +03:00
Chocobo1
f51ad39ad9 Add fallback for random number generator
`getrandom()` is available since Linux 3.17 (2014/10/05) yet there are older devices that don't
meet this requirement.

Closes #22691.
PR #22723.
2025-05-18 12:40:49 +03:00
Atk
9133b16431 WebUI: Make multi-rename search & replace fields use a monospace font
Before the release of 5.1.0 I believe these two fields both used monospace fonts and also had an increased font size, maybe 14px (I couldn't find in blame where/how this regressed). Ideally, I'd like to bring this back, as it makes elaborate regexes easier to grok. This PR currently just tells the browser to use its monospace font, but I could also add a similar in-line style for font size. I just don't know if this is the best way to solve this problem; if there's a better place for this sort of styling to happen let me know

PR #22719.
2025-05-18 12:40:24 +03:00
KanishkaHalder1771
909a3eb44e Update help message for Windows systems
For windows environment the `--help` output will show :
```
set QBT_NO_SPLASH=1
C:\Program Files\qBittorrent\qbittorrent.exe
```
instead of
```
QBT_NO_SPLASH=1 C:\Program Files\qBittorrent\qbittorrent.exe
```

Fixes #22662.
PR #22695.
2025-05-18 12:39:53 +03:00
Chocobo1
778aa64c54 WebUI: add versioning to local preferences
And provide migration path for changing preferences.

Fixes #22639.
PR #22677.
2025-05-14 10:41:17 +03:00
Vladimir Golovnev
7049f80a01 Fix compilation with Qt 6.6.0
PR #22678.
2025-05-12 12:23:21 +03:00
dezza
87b90b7fd7 WebUI: Remove unselectable from General tab
Making General-tab text `unselectable` is not an improvement.

It begs to add a new `Copy -> Save path` feature, because using `Set location` to copy save path (*which requires a request*) is not faster than simply copying it from the `General` tab by double-left clicking and pressing `CTRL+C`.

I don't see a reason why its necessary to software-restrict people from copying details from the `General`-tab - there are several reasons why you would - incl. the above mentioned usecase for quickly copying save-path, but other than that its counterproductive to limit people from copying the details displayed.

PR #22663.
2025-05-10 11:58:58 +03:00
Vladimir Golovnev
b3690494ab Fix ratio handling
PR #22638.
2025-05-01 21:17:07 +03:00
Vladimir Golovnev
f4e6b515c2 Remove dubious seeding time max value
PR #22624.
2025-05-01 21:16:17 +03:00
Isak05
a721540e6c Fix preview not opening on Wayland
Deferring the opening of the preview slightly gives the preview select
dialog time to close and for focus to shift back to the main window.

PR #22608.
Closes #22607.

---------

Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
2025-05-01 21:09:45 +03:00
Vladimir Golovnev
3fd05d001f Fix appearance of search history length spinbox
PR #22605.
2025-05-01 21:09:44 +03:00
Vladimir Golovnev
f04b114b64 Don't interpret wildcard pattern as filepath globbing
PR #22590.
Closes #22583.
2025-05-01 21:09:44 +03:00
sledgehammer999
da87be2b12 Bump to 5.1.0 2025-04-27 11:53:39 +03:00
sledgehammer999
891265b390 Update Changelog 2025-04-27 11:53:25 +03:00
sledgehammer999
f46e44d3ed Sync translations from Transifex and run lupdate 2025-04-27 11:52:43 +03:00
sledgehammer999
a4094a440d Bump copyright year 2025-04-20 23:26:52 +03:00
sledgehammer999
46c3da21e1 Sync translations from Transifex and run lupdate 2025-04-20 23:23:23 +03:00
Vladimir Golovnev
2f06ea2587 Backport changes to v5.1.x branch
PR #22490.
2025-04-17 20:54:03 +03:00
Vladimir Golovnev
cfbf6b73ff Prevent crash due to corrupted resume data
PR #22569.
Closes #22540.
2025-04-17 11:17:19 +03:00
Vladimir Golovnev
c687a7d0d3 Fix the torrent relocates files when switching to "manual" mode
PR #22564.
Closes #22283.
Closes #22546.
2025-04-16 10:24:34 +03:00
Vladimir Golovnev
009cc71f9b Explicitly reject opened Add torrent dialogs when exiting app
PR #22535.
Closes #19933.
Supercedes #22533.
2025-04-14 09:53:07 +03:00
Chocobo1
de1cf208ce WebUI: avoid saving invalid size
Don't save the wrong size when the tab is collapsed.
Reported in: https://github.com/qbittorrent/qBittorrent/pull/21215/files#r1966052959

PR #22537.
2025-04-12 15:13:47 +03:00
skomerko
5f49472fa4 WebUI: Set status filter to 'All' if selected filter is no longer visible
Fixup for #21145

To reproduce:
1. Select status filter with 0 torrents
2. Enable 'Auto hide zero status filters' and save settings. Hidden filter is still selected:

PR #22487.
2025-04-12 07:12:23 +03:00
skomerko
2076302170 WebUI: Show 'Edit tracker URL...' only when one tracker is selected
We can only edit one URL through the dialog, so there's no point in showing this context option when more than one tracker is selected in trackers table.

PR #22311.
2025-04-12 07:11:41 +03:00
skomerko
2a33e187eb WebUI: Update sort icon after changing column order
This PR fixes a bug where the sort icon did not update correctly after reordering columns.

Steps to reproduce:
1. Sort a column
2. Move it to a different position
3. The sort icon remains in its original location

PR #22299.
2025-04-12 07:10:59 +03:00
FredBill1
00149e03c0 Migrate socks.py from SocksiPy to PySocks 1.7.1
Migrate `socks.py` from SocksiPy 1.01 to [PySocks 1.7.1](c2fa43cbe1/socks.py), allowing python 3+ compatibility, [details](https://github.com/qbittorrent/qBittorrent/issues/16447#issuecomment-2776894026).

The content of the `socks.py` is entirely copied from the [PySocks repository](c2fa43cbe1/socks.py), the only modification is the license header at the top of the file and trimming trail whitespaces.

Closes #16447.
PR #22507.
2025-04-09 12:57:05 +03:00
Chocobo1
57d529c17a WebUI: fix preferences not applied in magnet handler
Thanks for the diagnosis in this [post](https://github.com/qbittorrent/qBittorrent/issues/22495#issue-2958553624).

Closes #21486.
Closes #22495.
PR #22504.
2025-04-05 08:58:38 +03:00
Vladimir Golovnev
d492fcf29a 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:03 +03:00
Chocobo1
d0caa35b39 WebUI: fix Tag counter counting wrong
Related: 73e9116d21 (r2014898781)

PR #22480.
2025-03-29 16:02:47 +03:00
Vladimir Golovnev
ec7a00af92 Restore ability to use server-side translation by custom WebUI
PR #20968.
2025-03-28 09:08:56 +03:00
Vladimir Golovnev
76a3aba7e0 Backport changes to v5.1.x branch
PR #22268.
2025-03-16 10:39:31 +03:00
Chocobo1
7003ac3f4d WebUI v5.1 fixes
PR #22282.
2025-03-15 14:52:48 +03:00
skomerko
964be0fa1c WebUI: Maintain row highlight after rearranging table columns
This PR fixes a bug where row highlight effect would be lost after reordering columns.

PR #22339.
2025-03-15 12:47:11 +03:00
skomerko
c1defceccf WebUI: Fix bug where the 'Tracker editing' dialog displays incorrect data
In Trackers table, moving the 'URL' column from its default (2) position caused the 'Tracker editing' dialog to display incorrect data.
Steps to reproduce:
1. Move 'URL' column in Trackers table to any position from default
2. Choose tracker URL and click 'Edit tracker URL'

PR #22338.
2025-03-15 12:46:17 +03:00
Vladimir Golovnev
260394623d Add missing includes
PR #22362.
2025-03-05 09:07:47 +03:00
Vladimir Golovnev
478c2d5b12 Don't miss to declare some of the color IDs
PR #22330.
Closes #22326.
2025-02-25 18:57:22 +03:00
Vladimir Golovnev
49cfbd9a49 Improve command line parameters serialization
PR #22319.
Closes #22306.
2025-02-25 09:12:26 +03:00
Luke Memet
d028f46fab Fix shift-click selection on macOS
PR #22284.
Closes #16818.
2025-02-19 13:53:49 +03:00
Daniel Nylander
57b24a200e NSIS: Update Swedish translation
PR #22046.
2025-02-19 13:49:25 +03:00
Chocobo1
269dfe87e0 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 08:22:26 +03:00
Vladimir Golovnev
6a1c465d85 WebAPI: Don't trim string parameters
PR #22266.
Closes #19485.
Closes #22254.
2025-02-12 09:35:29 +03:00
sledgehammer999
bc7d5c1f8f Bump to 5.1.0rc1 2025-02-11 02:01:34 +02:00
sledgehammer999
8aabef423c Create new resources for this branch for Transifex 2025-02-11 01:59:07 +02:00
sledgehammer999
bd564a99a3 Sync translations from Transifex and run lupdate 2025-02-11 01:56:49 +02:00
Chocobo1
b052ad0923 WebUI: inline redundant function
This also fix share ratio dialog which had been broken in recent cleanup.

PR #22252.
2025-02-09 16:03:01 +08:00
Chocobo1
c65a68251e WebUI: use native function when converting to numbers
PR #22246.
2025-02-08 17:58:48 +08:00
skomerko
93925042dd WebUI: Fix memory leak in context menus
This PR fixes a memory leak in context menus. Previously, for some reason, each menu retained references to its target elements without utilizing them further. Since the targets property was accessible/reachable from the root (window object), these references persisted even after the elements were removed from the DOM, preventing them from being garbage collected.
It's easily reproducible - just add a decent amount of torrents, switch between categories multiple times, then capture heap/detached elements snapshot in the Memory tab (Chrome dev tools). The number of detached elements will continue to increase after each category switch and they won't be cleaned up.
[More context](https://github.com/qbittorrent/qBittorrent/pull/22220/files#r1941137796)

PR #22234.
2025-02-08 17:51:21 +08:00
Chocobo1
e55b59d9ca Use switch statement
PR #22247.
2025-02-08 17:39:10 +08:00
xavier2k6
f8469d02f7 GHA CI: Bump FORTIFY_SOURCE hardening flag to 3
PR #22248.
2025-02-08 17:00:10 +08:00
Chocobo1
dc10b88cec GHA CI: explicitly set C++20 mode for libtorrent
Bump Boost version. Boost::asio 1.76 has a bug that prevents compiling in C++20 mode.

PR #22245.
2025-02-08 16:30:36 +08:00
Maxime Thiebaut
4406a3f173 Add announce_port support
The `announce_port` setting permits to overwrite the port passed along to trackers as the `&port=` parameter. If left as the default, the listening port is used. This setting is only meant for very special cases where a seed's listening port differs from the effectively exposed port (e.g., through external NAT-PMP). See https://github.com/arvidn/libtorrent/pull/7771 for an example use-case.

This PR adds the relevant setting alongside the existing `announce_ip` setting.

PR #21692.
2025-02-08 16:12:50 +08:00
skomerko
9c2e698514 WebUI: Replace getElements & getChildren
This PR further reduces Mootools usage.
PR #22220.
2025-02-04 17:08:18 +08:00
xavier2k6
463700b76d GHA CI: Bump libtorrent versions
PR #22217.
2025-02-03 09:07:35 +03:00
Vladimir Golovnev
86387fbe49 Return first tracker as fallback for "current tracker"
PR #22224.
Closes #20415.
2025-02-01 14:58:12 +03:00
Vladimir Golovnev
a018cfa56c Remove stopped torrent from "error" tracker filter
PR #22219.
2025-01-31 06:23:04 +03:00
Vladimir Golovnev
b76054beba Store search history
PR #22208.
2025-01-30 08:59:10 +03:00
Chocobo1
f8536162f2 Use compact format for JSON files
It saves a bit of space on disk and deters novice users from tampering them.

PR #22211.
2025-01-29 15:31:06 +08:00
skomerko
af65ddd012 WebUI: Allow to move state icon to name column in torrents table
PR #22118.
2025-01-28 14:46:09 +08:00
Chocobo1
fe9dc131bc WebUI: don't wrap regex literal in regex constructor
PR #22206.
2025-01-28 00:33:04 +08:00
thalieht
bb4a668ddd Hide zero and infinity values in peer list only when that setting is set to Always
PR #22205.
Closes #21998.
2025-01-27 09:40:44 +03:00
Vladimir Golovnev
3978137534 Store opened search tabs
PR #22163.
Closes #167.
2025-01-26 17:12:50 +03:00
Chocobo1
3ef4d0d798 GHA CI: add checking for GHA workflows
PR #22200.

---------

Co-authored-by: userdocs <16525024+userdocs@users.noreply.github.com>
2025-01-26 03:44:59 +08:00
Chocobo1
e2341f5217 Merge pull request #22199 from Chocobo1/webui_eslint
WebUI: improve lint checks
2025-01-26 03:34:34 +08:00
xavier2k6
abd3cd54bc GHA CI: Bump numerous hook revs
PR #22193.
2025-01-26 03:26:27 +08:00
Chocobo1
dc8ac38494 WebUI: revise lint rules for css
Some rules are already covered by other tools, so remove them.
2025-01-24 23:58:13 +08:00
Chocobo1
e3eacf2bf7 WebUI: migrate stylelint config file format
From https://stylelint.io/user-guide/configure :
>Stylelint currently supports other configuration locations and formats, but we may remove these
>in the future:
>...
>.stylelintrc.json file
2025-01-24 22:26:36 +08:00
Chocobo1
5098519d46 WebUI: enable cache for lint checks
Also, WebUI gets its own .gitignore file.
2025-01-24 22:26:35 +08:00
ze0s
82c36aea89 WebAPI: add new method setTags to upsert tags on torrents
This is another optimization for torrent management on large scale instances with the goal to minimize the amount of required API calls. Ref #22128.

This new function and endpoint torrents/setTags does an upsert to replace the torrent tags and handles the removal and add internally, instead of doing multiple calls to add and remove tags on torrents from the client.

PR #22156.

---------

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
2025-01-24 22:24:35 +08:00
thalieht
05787d94ec Fix torrent content checkbox state under certain conditions
PR #22190.
Closes #22189.
2025-01-24 12:11:19 +03:00
Chocobo1
f8c48349a1 WebUI: use native function for selecting elements
PR #22179.
2025-01-20 23:36:11 +08:00
Chocobo1
1ee84033ec WebUI: use template literals instead of string concatenation
PR #22177.
2025-01-18 20:51:47 +08:00
Chocobo1
f2eecf8a4e Avoid memory leak on macOS
Only Mark-of-the-Web and Power Management are affected.

PR #22176.
2025-01-18 20:30:14 +08:00
ze0s
76e1040232 WebAPI: optionally include trackers list in torrent info response
This PR adds an optional parameter includeTrackers to the Torrent info endpoint /torrents/info to include the trackers list of each torrent in the response under the key trackers.

PR #22128.
2025-01-17 17:10:25 +08:00
Chocobo1
4686d6709e GHA CI: show installed version
Before this change, it wasn't clear which exact version were installed.

PR #22155.
2025-01-13 17:24:08 +08:00
Chocobo1
2cc7ec90a8 WebUI: add percentage sign for hsl components
This is a workaround to avoid confusing the stylelint checker (and probably some other checker
internally). They cannot accept the fact that the component can be `<number>` but instead
insist it should be `<percentage>`
Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl#formal_syntax

Also, MDN recommended to use `hsl()` instead of `hsla()`.
From https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl :
> Note: The hsla() functional notation is an alias for hsl(). They are
> exactly equivalent. It is recommended to use hsl().

PR #22154.
2025-01-13 17:13:20 +08:00
Vladimir Golovnev
99adb16090 Allow to reuse existing search term
PR #22148.
2025-01-13 10:21:40 +03:00
Chocobo1
c622d50814 WebUI: use native API for accessing query string
PR #22141.
2025-01-12 21:36:59 +08:00
Chocobo1
11991e62f5 WebUI: fix error when opening the same dialog twice
PR #22139.
2025-01-12 21:18:41 +08:00
Chocobo1
82d90e599c Preserve allocated buffer capacity
PR #22138.
2025-01-12 21:01:39 +08:00
xavier2k6
45b7947cd0 GHA CI: Update Boost URL
The agreement between Boost & JFrog came to an end in December 2024.
Reference: https://github.com/boostorg/boost/issues/924

PR #22125.
2025-01-11 21:33:51 +08:00
Chocobo1
2e21cf76de WebUI: temporarily pin dependency
The plugin was causing problems in new versions so pin it with an older
version and look into it later.
Upstream issue: https://github.com/JamieMason/eslint-plugin-prefer-arrow-functions/issues/50

PR #22140.
2025-01-11 16:28:56 +08:00
Vladimir Golovnev
76151110e5 Handle Qt style options uniformly
PR #22133.
Closes #22061.
2025-01-11 10:17:38 +03:00
Vladimir Golovnev
5875d8bff3 Allow multiple simultaneous searches
PR #22127.
2025-01-11 09:53:40 +03:00
Hanabishi
68ecb13d14 Change URL seed error message
Current URL seed error message assumes that only possible error is DNS lookup failure, which is not true.
So replace it with a more generic message. Real reason is provided by the 'Error:' part.

PR #22119.
2025-01-10 08:31:28 +03:00
Vladimir Golovnev
f9f4b60b83 Allow to refresh existing search
PR #22122.
Closes #17184.
2025-01-08 17:03:32 +03:00
Thomas Piccirello
4fc36b9e99 Support fetching tracker list from URL
Trackers specified at the URL will be added to newly added public torrents.

This feature is adapted from qBittorrent-Enhanced-Edition to allow for automatically adding trackers retrieved from a URL. @ngosang's trackerlist repo is a good example, however I've opted not to include a default URL.

Partially addresses #14535.
PR #21828.
2025-01-08 14:51:09 +08:00
Chocobo1
4f3d77963f Add parameter to control whether to unescape HTML entities
Some plugin needed the raw data for further processing.
Related: #22074.

PR #22106.
2025-01-06 19:05:57 +08:00
Chocobo1
d911928c59 WebUI: Remove unnecessary hashing
Now the containers support using string as key so the intermediate hashing/mapping to number
isn't needed now.

PR #22103.
2025-01-06 18:53:18 +08:00
Chocobo1
22e156e0af Simplify captured variable type
By capturing the specific data instead of torrent handle the lambda can become non-mutable.

PR #22102.
2025-01-06 18:22:43 +08:00
skomerko
6fe02895a8 WebUI: Remove redundant event listener
This PR removes broken event listener attached to RSS Rules table. 

PR #22083.
2024-12-31 21:43:39 +08:00
skomerko
395dbaa5c6 WebUI: Replace getElement with querySelector
All `getElement` instances (Mootools) were changed to `querySelector`.

PR #22082.
2024-12-31 21:31:46 +08:00
Chocobo1
efe06f133d Revise DHT bootstrap node list
Discussion:
https://github.com/qbittorrent/qBittorrent/pull/21296#issuecomment-2562341328
https://github.com/qbittorrent/qBittorrent/pull/21296#issuecomment-2561373010

PR #22081.
2024-12-31 13:34:08 +08:00
Chocobo1
9c0475ebfa WebUI: migrate to fetch API
This is the final part of it.

PR #22072.
2024-12-29 15:44:28 +08:00
Chocobo1
e740a42366 Merge pull request #22070 from Chocobo1/py_html_decode
Improve Search engine
2024-12-29 14:39:11 +08:00
Chocobo1
cc31a90931 Provide SSL context field
The allows the caller to provide proper SSL parameters and avoid dirty monkey patching to
suppress SSL errors.
2024-12-27 03:53:46 +08:00
Chocobo1
90e457a671 Use built-in method for decoding HTML entities 2024-12-27 03:52:50 +08:00
Chocobo1
7487cd7e6d WebUI: disallow unnecessary quotes in property name
Those two forms are the same and from now on we enforce to one style.

PR #22051.
2024-12-24 22:25:18 +08:00
skomerko
bbc3c2832f WebUI: Use closest() to get parent element
All `getParent()` instances (Mootools) were changed to use `closest()` method:
https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

PR #22048.
2024-12-23 23:07:17 +08:00
Chocobo1
879c6bf9ff Simplify conversion to string
PR #22036.
2024-12-23 22:59:01 +08:00
Chocobo1
f2097dc4b5 Avoid redundant copy
PR #22035.
2024-12-23 22:43:23 +08:00
Hugo Carvalho
166feb5bdf NSIS: Update Portuguese translation
PR #21632.
2024-12-23 22:37:46 +08:00
Chocobo1
a841fe9320 WebUI: migrate to fetch API
And away from mootools.

PR #22037.
2024-12-22 17:51:19 +08:00
Patrik Elfström
9709672b34 WebUI: Change filter inputs to type search
Changing input type from text to search for all search and filter inputs
to enable user to easily clear input.

This feature is yet to be implemented in Firefox.
See tracking bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1654288

Also fix search icon horizontal positioning and minor input box paddings.

Closes #15481.
PR #22033.
2024-12-22 17:19:03 +08:00
xavier2k6
e2db0bc866 GHA CI: Update pandoc to latest
* Bumped `pandoc` -> 3.6

PR #22021.
2024-12-20 18:27:05 +08:00
xavier2k6
fee45e4ba6 GHA CI: Bump some pre-commit hook revs
* Bumped `pre-commit-hooks` -> v5.0.0
* Bumped `typos` -> v1.28.4

PR #22020.
2024-12-20 18:16:43 +08:00
Zentino
257d928ab3 Resolve relative URLs within RSS article description
PR #21943.


---------

Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
2024-12-20 10:19:34 +03:00
Chocobo1
34c8849f22 Fix tab order in RSS widget
Related: https://github.com/qbittorrent/qBittorrent/pull/21996#issuecomment-2543127251

PR #21999.
2024-12-18 02:29:51 +08:00
Chocobo1
1c82eb3dff Merge pull request #21996 from Chocobo1/check_ui
Sort grid items properly
2024-12-18 02:19:34 +08:00
Chocobo1
7886ca65f9 Make tab key switch focus
These fields do not expect tab characters.
2024-12-16 01:30:47 +08:00
Chocobo1
85c4ddf616 Make links accessible by keyboard 2024-12-16 01:30:47 +08:00
Chocobo1
0a36171999 Sort grid items properly
Supersedes #21856.
2024-12-16 01:30:47 +08:00
Chocobo1
c887a6f7d8 GHA CI: add checks for grid items order
Now all items under `QGridLayout` are required to be sorted. This allow
us to omit tabstop order. The tabstop order will follow the layout order.

The script can be invoked to fix wrong grid items order in .ui files:
```console
python check_grid_items_order.py file.ui
```
2024-12-14 17:08:18 +08:00
303 changed files with 199635 additions and 139344 deletions

View File

@@ -12,11 +12,15 @@ jobs:
ci:
name: Check
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install tools
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: "*"
@@ -32,7 +36,7 @@ jobs:
curl \
-L \
-o "${{ runner.temp }}/pandoc.tar.gz" \
"https://github.com/jgm/pandoc/releases/download/3.4/pandoc-3.4-linux-amd64.tar.gz"
"https://github.com/jgm/pandoc/releases/download/3.6/pandoc-3.6-linux-amd64.tar.gz"
tar -xf "${{ runner.temp }}/pandoc.tar.gz" -C "${{ github.workspace }}/.."
mv "${{ github.workspace }}/.."/pandoc-* "${{ env.pandoc_path }}"
# run pandoc
@@ -42,3 +46,26 @@ jobs:
done
# check diff, ignore "Automatically generated by ..." part
git diff -I '\.\\".*' --exit-code
- name: Check GitHub Actions workflow
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pip install zizmor
IGNORE_RULEID='(.ruleId != "template-injection")
and (.ruleId != "unpinned-uses")'
IGNORE_ID='(.id != "template-injection")
and (.id != "unpinned-uses")'
zizmor \
--format sarif \
--pedantic \
./ \
| jq "(.runs[].results |= map(select($IGNORE_RULEID)))
| (.runs[].tool.driver.rules |= map(select($IGNORE_ID)))" \
> "${{ runner.temp }}/zizmor_results.sarif"
- name: Upload zizmor results
uses: github/codeql-action/upload-sarif@v3
with:
category: zizmor
sarif_file: "${{ runner.temp }}/zizmor_results.sarif"

View File

@@ -2,8 +2,7 @@ name: CI - macOS
on: [pull_request, push]
permissions:
actions: write
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -13,11 +12,13 @@ jobs:
ci:
name: Build
runs-on: macos-latest
permissions:
actions: write
strategy:
fail-fast: false
matrix:
libt_version: ["2.0.10", "1.2.19"]
libt_version: ["2.0.11", "1.2.20"]
qbt_gui: ["GUI=ON", "GUI=OFF"]
qt_version: ["6.7.0"]
@@ -28,6 +29,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install dependencies
uses: Wandalen/wretry.action@v3
@@ -57,7 +60,7 @@ jobs:
BOOST_MINOR_VERSION: "86"
BOOST_PATCH_VERSION: "0"
run: |
boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
set +e
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
@@ -90,7 +93,7 @@ jobs:
-G "Ninja" \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBOOST_ROOT="${{ env.boost_path }}" \
-Ddeprecated-functions=OFF

View File

@@ -16,6 +16,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup python (auxiliary scripts)
uses: actions/setup-python@v5
@@ -34,7 +36,7 @@ jobs:
- name: Lint code (auxiliary scripts)
run: |
pyflakes $PY_FILES
bandit --skip B314,B405 $PY_FILES
bandit --skip B101,B314,B405 $PY_FILES
- name: Format code (auxiliary scripts)
run: |
@@ -61,7 +63,7 @@ jobs:
echo $PY_FILES
echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV"
- name: Check typings (search engine)
- name: Check typings (search engine)
run: |
MYPYPATH="src/searchengine/nova3" \
mypy \

View File

@@ -2,9 +2,7 @@ name: CI - Ubuntu
on: [pull_request, push]
permissions:
actions: write
security-events: write
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -14,22 +12,27 @@ jobs:
ci:
name: Build
runs-on: ubuntu-latest
permissions:
actions: write
security-events: write
strategy:
fail-fast: false
matrix:
libt_version: ["2.0.10", "1.2.19"]
libt_version: ["2.0.11", "1.2.20"]
qbt_gui: ["GUI=ON", "GUI=OFF"]
qt_version: ["6.5.2"]
env:
boost_path: "${{ github.workspace }}/../boost"
harden_flags: "-D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS"
harden_flags: "-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS"
libtorrent_path: "${{ github.workspace }}/../libtorrent"
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install dependencies
run: |
@@ -49,10 +52,10 @@ jobs:
- name: Install boost
env:
BOOST_MAJOR_VERSION: "1"
BOOST_MINOR_VERSION: "76"
BOOST_MINOR_VERSION: "77"
BOOST_PATCH_VERSION: "0"
run: |
boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
set +e
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
@@ -85,6 +88,7 @@ jobs:
-G "Ninja" \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBOOST_ROOT="${{ env.boost_path }}" \
-Ddeprecated-functions=OFF
@@ -134,16 +138,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 +159,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

@@ -2,8 +2,7 @@ name: CI - WebUI
on: [pull_request, push]
permissions:
security-events: write
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -13,6 +12,8 @@ jobs:
ci:
name: Check
runs-on: ubuntu-latest
permissions:
security-events: write
defaults:
run:
@@ -21,6 +22,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup nodejs
uses: actions/setup-node@v4
@@ -28,7 +31,10 @@ jobs:
node-version: 'lts/*'
- name: Install tools
run: npm install
run: |
npm install
npm ls
npm ls --all
- name: Lint code
run: npm run lint

View File

@@ -2,8 +2,7 @@ name: CI - Windows
on: [pull_request, push]
permissions:
actions: write
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -13,11 +12,13 @@ jobs:
ci:
name: Build
runs-on: windows-latest
permissions:
actions: write
strategy:
fail-fast: false
matrix:
libt_version: ["2.0.10", "1.2.19"]
libt_version: ["2.0.11", "1.2.20"]
env:
boost_path: "${{ github.workspace }}/../boost"
@@ -27,6 +28,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup devcmd
uses: ilammy/msvc-dev-cmd@v1
@@ -81,7 +84,7 @@ jobs:
BOOST_MINOR_VERSION: "86"
BOOST_PATCH_VERSION: "0"
run: |
$boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
$boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
$boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
tar -xf "${{ runner.temp }}/boost.tar.gz" -C "${{ github.workspace }}/.."
@@ -115,6 +118,7 @@ jobs:
-B build `
-G "Ninja" `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DCMAKE_CXX_STANDARD=20 `
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
-DCMAKE_INSTALL_PREFIX="${{ env.libtorrent_path }}/install" `
-DCMAKE_TOOLCHAIN_FILE="${{ env.vcpkg_path }}/scripts/buildsystems/vcpkg.cmake" `

View File

@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
libt_version: ["2.0.10"]
libt_version: ["2.0.11"]
qbt_gui: ["GUI=ON"]
qt_version: ["6.5.2"]
@@ -26,6 +26,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install dependencies
run: |
@@ -40,7 +42,7 @@ jobs:
BOOST_MINOR_VERSION: "86"
BOOST_PATCH_VERSION: "0"
run: |
boost_url="https://boostorg.jfrog.io/artifactory/main/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
boost_url="https://archives.boost.io/release/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/source/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
boost_url2="https://sourceforge.net/projects/boost/files/boost/${{ env.BOOST_MAJOR_VERSION }}.${{ env.BOOST_MINOR_VERSION }}.${{ env.BOOST_PATCH_VERSION }}/boost_${{ env.BOOST_MAJOR_VERSION }}_${{ env.BOOST_MINOR_VERSION }}_${{ env.BOOST_PATCH_VERSION }}.tar.gz"
set +e
curl -L -o "${{ runner.temp }}/boost.tar.gz" "$boost_url"
@@ -71,6 +73,7 @@ jobs:
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_STANDARD=20 \
-DBOOST_ROOT="${{ env.boost_path }}" \
-Ddeprecated-functions=OFF
cmake --build build

View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
# A pre-commit hook for checking items order in grid layouts
# Copyright (C) 2024 Mike Tzou (Chocobo1)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# In addition, as a special exception, the copyright holders give permission to
# link this program with the OpenSSL project's "OpenSSL" library (or with
# modified versions of it that use the same license as the "OpenSSL" library),
# and distribute the linked executables. You must obey the GNU General Public
# License in all respects for all of the code used other than "OpenSSL". If you
# modify file(s), you may extend this exception to your version of the file(s),
# but you are not obligated to do so. If you do not wish to do so, delete this
# exception statement from your version.
from collections.abc import Callable, Sequence
from typing import Optional
import argparse
import re
import xml.etree.ElementTree as ElementTree
import sys
def traversePostOrder(root: ElementTree.Element, visitFunc: Callable[[ElementTree.Element], None]) -> None:
stack = [(root, False)]
while len(stack) > 0:
(element, visit) = stack.pop()
if visit:
visitFunc(element)
else:
stack.append((element, True))
stack.extend((child, False) for child in reversed(element))
def modifyElement(element: ElementTree.Element) -> None:
def getSortKey(e: ElementTree.Element) -> tuple[int, int]:
if e.tag == 'item':
return (int(e.attrib['row']), int(e.attrib['column']))
return (-1, -1) # don't care
if element.tag == 'layout' and element.attrib['class'] == 'QGridLayout' and len(element) > 0:
element[:] = sorted(element, key=getSortKey)
# workaround_2a: ElementTree will unescape `&quot;` and we need to escape it back
if element.tag == 'string' and element.text is not None:
element.text = element.text.replace('"', '&quot;')
def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv)
for filename in args.filenames:
with open(filename, 'r+') as f:
orig = f.read()
root = ElementTree.fromstring(orig)
traversePostOrder(root, modifyElement)
ElementTree.indent(root, ' ')
# workaround_1: cannot use `xml_declaration=True` since it uses single quotes instead of Qt preferred double quotes
ret = f'<?xml version="1.0" encoding="UTF-8"?>\n{ElementTree.tostring(root, 'unicode')}\n'
# workaround_2b: ElementTree will turn `&quot;` into `&amp;quot;`, so revert it back
ret = ret.replace('&amp;quot;', '&quot;')
# workaround_3: Qt prefers no whitespaces in self-closing tags
ret = re.sub('<(.+) +/>', r'<\1/>', ret)
if ret != orig:
print(f'Tip: run this script to apply the fix: `python {__file__} {filename}`', file=sys.stderr)
f.seek(0)
f.write(ret)
f.truncate()
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -26,9 +26,11 @@
# but you are not obligated to do so. If you do not wish to do so, delete this
# exception statement from your version.
from typing import Optional, Sequence
from collections.abc import Sequence
from typing import Optional
import argparse
import re
import sys
def main(argv: Optional[Sequence[str]] = None) -> int:
@@ -67,4 +69,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
if __name__ == '__main__':
exit(main())
sys.exit(main())

View File

@@ -4,12 +4,13 @@ on:
schedule:
- cron: '0 0 * * *'
permissions:
pull-requests: write
permissions: {}
jobs:
stale:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Mark and close stale PRs
uses: actions/stale@v9

4
.gitignore vendored
View File

@@ -41,7 +41,3 @@ src/icons/skin/build-icons/icons/*.png
# CMake build directory
build/
# Web UI tools
node_modules
package-lock.json

View File

@@ -1,6 +1,12 @@
repos:
- repo: local
hooks:
- id: check-grid-order
name: Check items order in grid layouts
entry: .github/workflows/helper/pre-commit/check_grid_items_order.py
language: script
files: \.ui$
- id: check-translation-tag
name: Check newline characters in <translation> tag
entry: .github/workflows/helper/pre-commit/check_translation_tag.py
@@ -13,7 +19,7 @@ repos:
- ts
- repo: https://github.com/pre-commit/pre-commit-hooks.git
rev: v4.6.0
rev: v5.0.0
hooks:
- id: check-json
name: Check JSON files
@@ -63,7 +69,7 @@ repos:
- ts
- repo: https://github.com/codespell-project/codespell.git
rev: v2.3.0
rev: v2.4.0
hooks:
- id: codespell
name: Check spelling (codespell)
@@ -82,7 +88,7 @@ repos:
- ts
- repo: https://github.com/crate-ci/typos.git
rev: v1.25.0
rev: v1.29.4
hooks:
- id: typos
name: Check spelling (typos)

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_v51x]
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_v51x]
file_filter = src/webui/www/translations/webui_<lang>.ts
source_file = src/webui/www/translations/webui_en.ts
source_lang = en

119
Changelog
View File

@@ -1,4 +1,121 @@
Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.0
Wed Jul 02nd 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.2
- BUGFIX: Don't expose palette colors in UI theme editor since they are not customizable (glassez)
- BUGFIX: Add fallback to update mechanism (sledgehammer999)
- WEBUI: Fix incorrectly backported changes (glassez)
- WEBAPI: Trim leading whitespaces on Run External Program fields (Chocobo1)
- RSS/SEARCH: Prevent opening local files if web page is expected (glassez)
- MACOS: Make qBittorrent quit on MacOS with main window closed (Ryu481)
Mon Jun 23rd 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.1
- BUGFIX: Don't interpret wildcard pattern as filepath globbing (glassez)
- BUGFIX: Fix appearance of search history length spinbox (glassez)
- BUGFIX: Remove dubious seeding time max value (glassez)
- BUGFIX: Fix ratio handling (glassez)
- BUGFIX: Fix compilation with Qt 6.6.0 (glassez)
- WEBUI: Make General tab text selectable by default (dezza)
- WEBUI: Add versioning to local preferences (Chocobo1)
- WEBUI: Make multi-rename search & replace fields use a monospace font (Atk)
- WEBUI: Fix wrong replacement sequence in IPv6 string (Chocobo1)
- WEBUI: Fix memory leak (bolshoytoster)
- WEBUI: Fix path autofill in set location and new category (tehcneko)
- RSS: Mark matched article as "read" if it refers to a duplicate torrent (glassez)
- WINDOWS: Update command line help message (KanishkaHalder1771)
- WINDOWS: NSIS: Don't require agreement on the license page (Chocobo1)
- LINUX: Fix preview not opening on Wayland (Isak05)
- LINUX: Add fallback for random number generator (Chocobo1)
Sun Apr 27th 2025 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.1.0
- FEATURE: Enable customizing the save statistics time interval (Burnerelu)
- FEATURE: Add drag support to torrent content widget (Chocobo1)
- FEATURE: Display External IP Address in status bar (Thomas Piccirello)
- FEATURE: Use modern functions to get random numbers under Linux/Windows (security related) (Chocobo1)
- FEATURE: Add eXact Length parameter when creating magnet URI (antanilol)
- FEATURE: Support fetching tracker list from URL (Thomas Piccirello)
- FEATURE: Add `announce_port` support (Maxime Thiebaut)
- BUGFIX: Enable adaptive step size for upload and download limits (Harald Nordgren)
- BUGFIX: Add URL link for reverse proxy setup examples (Chocobo1)
- BUGFIX: Allow drop action only on transfer list (Chocobo1)
- BUGFIX: Fix the tab order in dialogs (thalieht)
- BUGFIX: Fix filesize sorting in preview dialog (DoubleSpicy)
- BUGFIX: Improve the speed icons in the status bar (Mahdi Hosseinzadeh)
- BUGFIX: Update link to news (tinyboxvk)
- BUGFIX: Fix tab stop order in various dialogs and UI elements (Chocobo1)
- BUGFIX: Make links accessible by keyboard (Chocobo1)
- BUGFIX: Make tab key switch focus (Chocobo1)
- BUGFIX: Revise DHT bootstrap node list (stalkerok, Chocobo1)
- BUGFIX: Return first tracker as fallback for "current tracker" (glassez)
- BUGFIX: Prevent crash when exiting app with `Add torrent` dialogs opened (glassez)
- BUGFIX: Fix torrent relocating files when switching to "manual" mode (glassez)
- BUGFIX: Prevent crash due to corrupted resume data (glassez)
- WEBUI: Improvements that should help with assistive technologies (Chocobo1)
- WEBUI: Internal refactoring to migrate away from MooTools and towards native browser APIs (Chocobo1, skomerko)
- WEBUI: Implement path autocompletion (Paweł Kotiuk)
- WEBUI: Implement double-click behavior controls (Hanabishi)
- WEBUI: Add ability to toggle alternating row colors in tables (skomerko)
- WEBUI: Improve visibility of unread RSS articles (skomerko)
- WEBUI: Remove deleted torrents even if they are currently filtered out (Carmelo Scandaliato)
- WEBUI: Highlight torrent category in context menu (skomerko)
- WEBUI: Implement 'Auto hide zero status filters' (skomerko)
- WEBUI: Allow to filter torrent list by save path (skomerko)
- WEBUI: Handle regex syntax error for torrent filtering (HamletDuFromage)
- WEBUI: Add missing icons (skomerko)
- WEBUI: Add link to 'List of alternative WebUI' wiki page in Options (Chocobo1)
- WEBUI: Improve properties panel, torrent deletion dialog, filter list, subcategories, torrent deletion, statistics window (skomerko)
- WEBUI: Allow to display only hostname in the Tracker column (skomerko)
- WEBUI: Show country/region name next to its flag (skomerko)
- WEBUI: Improve hash copy actions in context menu (skomerko)
- WEBUI: Support removing tracker from all torrents in WebUI/WebAPI (Thomas Piccirello)
- WEBUI: Display DHT information in the Status bar only when DHT is enabled (skomerko)
- WEBUI: Add 'Confirm torrent recheck' option (skomerko)
- WEBUI: Support managing web seeds (Thomas Piccirello)
- WEBUI: Add colors to log table rows (skomerko)
- WEBUI: Prevent text selection within tabs, menu items (skomerko)
- WEBUI: Use correct text and background colors in RSS details view (skomerko)
- WEBUI: Reduce padding in torrents table (skomerko)
- WEBUI: Add WebAPI/WebUI for managing cookies (Thomas Piccirello)
- WEBUI: Support updating RSS feed URL (Thomas Piccirello)
- WEBUI: Add 'Engine' column to Search table (skomerko)
- WEBUI: Add confirm dialog for Auto TMM (skomerko)
- WEBUI: Add context menu to search tabs (skomerko)
- WEBUI: Show file filter when Content tab selected on load (Thomas Piccirello)
- WEBUI: DHT, PeX and LSD rows are now always on top in Trackers table (skomerko)
- WEBUI: Clear properties panel when torrent no longer selected (skomerko)
- WEBUI: Support auto resizing table columns (Thomas Piccirello)
- WEBUI: Fix displaying RSS panel on load (Thomas Piccirello)
- WEBUI: Add tooltip to regex filter button (Patrik Elfström)
- WEBUI: Hide context menu when clicking on a table row (Patrik Elfström)
- WEBUI: Display torrent progress percentage in General tab (skomerko)
- WEBUI: Use thin scrollbars (skomerko)
- WEBUI: Show 'Rename...' context menu item only when one torrent is selected (skomerko)
- WEBUI: Display error when download fails (Thomas Piccirello)
- WEBUI: Add colors to 'Status' column in Trackers table (skomerko)
- WEBUI: Add missing icon to 'Queue' context menu item (skomerko)
- WEBUI: Change filter inputs to type search (Patrik Elfström)
- WEBUI: Allow to move state icon to name column in torrents table (skomerko)
- WEBUI: Fix bug where the 'Tracker editing' dialog displays incorrect data (skomerko)
- WEBUI: Maintain row highlight after rearranging table columns (skomerko)
- WEBUI: Fix preferences not applied in magnet handler (Chocobo1)
- WEBUI: Update sort icon after changing column order (skomerko)
- WEBUI: Show 'Edit tracker URL...' only when one tracker is selected (skomerko)
- WEBUI: Set status filter to 'All' if selected filter is no longer visible (skomerko)
- WEBAPI: Don't reannounce when removing tracker via WebAPI (Thomas Piccirello)
- WEBAPI: Add WebAPI for managing torrent webseeds (Thomas Piccirello)
- WEBAPI: Add `forced` parameter to `torrents/add` (Chris B)
- WEBAPI: Optionally include trackers list in torrent info response (ze0s)
- WEBAPI: Add new method `setTags` to upsert tags on torrents (ze0s)
- RSS: Resolve relative URLs within RSS article description (Zentino)
- SEARCH: Provide SSL context field (Chocobo1)
- SEARCH: Allow to refresh existing search (glassez)
- SEARCH: Allow multiple simultaneous searches (glassez)
- SEARCH: Store opened search tabs (glassez)
- SEARCH: Store search history (glassez)
- SEARCH: Migrate socks.py from SocksiPy to PySocks 1.7.1 (FredBill1)
- SEARCH: Bump Python version minimum requirement (Chocobo1)
- WINDOWS: Opt into Windows SegmentHeap (Andarwinux)
- WINDOWS: Allow to choose color scheme on Windows (glassez)
- WINDOWS: Verify hash of Python installer (Chocobo1)
- LINUX: Add support for Thunar file manager (algebnaly)
- MACOS: Fix shift-click selection on macOS (Luke Memet)
Mon Oct 28th 2024 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v5.0.1
- FEATURE: Add "Simple pread/pwrite" disk IO type (Hanabishi)

View File

@@ -47,6 +47,9 @@ find_package(Boost ${minBoostVersion} REQUIRED)
find_package(OpenSSL ${minOpenSSLVersion} REQUIRED)
find_package(ZLIB ${minZlibVersion} REQUIRED)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
if (Qt6_FOUND AND (Qt6_VERSION VERSION_GREATER_EQUAL 6.10))
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS CorePrivate)
endif()
if (DBUS)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus)
set_package_properties(Qt6DBus PROPERTIES

4
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.1.0</string>
<string>5.1.2</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>

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.1.0~beta1" date="2024-12-16"/>
<release version="5.1.2" date="2025-07-02"/>
</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.1.0"
!define /ifndef QBT_VERSION "5.1.2"
; 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}"
@@ -111,7 +111,8 @@ RequestExecutionLevel user
!define MUI_HEADERIMAGE
!define MUI_COMPONENTSPAGE_NODESC
;!define MUI_ICON "qbittorrent.ico"
!define MUI_LICENSEPAGE_CHECKBOX
!define MUI_LICENSEPAGE_BUTTON $(^NextBtn)
!define MUI_LICENSEPAGE_TEXT_BOTTOM "$_CLICK"
!define MUI_LANGDLL_ALLLANGUAGES
;--------------------------------

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

@@ -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

@@ -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;
@@ -579,7 +602,7 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
const QString logMsg = tr("Running external program. Torrent: \"%1\". Command: `%2`");
const QString logMsgError = tr("Failed to run external program. Torrent: \"%1\". Command: `%2`");
// The processing sequenece is different for Windows and other OS, this is intentional
// The processing sequence is different for Windows and other OS, this is intentional
#if defined(Q_OS_WIN)
const QString program = replaceVariables(programTemplate);
const std::wstring programWStr = program.toStdWString();
@@ -897,10 +920,10 @@ int Application::exec()
m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name()));
});
connect(m_addTorrentManager, &AddTorrentManager::addTorrentFailed, this
, [this](const QString &source, const QString &reason)
, [this](const QString &source, const BitTorrent::AddTorrentError &reason)
{
m_desktopIntegration->showNotification(tr("Add torrent failed")
, tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason));
, tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason.message));
});
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);

View File

@@ -491,6 +491,12 @@ QString makeUsage(const QString &prgName)
{
const QString indentation {USAGE_INDENTATION, u' '};
#if defined(Q_OS_WIN)
const QString noSplashCommand = u"set QBT_NO_SPLASH=1 && " + prgName;
#else
const QString noSplashCommand = u"QBT_NO_SPLASH=1 " + prgName;
#endif
const QString text = QCoreApplication::translate("CMD Options", "Usage:") + u'\n'
+ indentation + prgName + u' ' + QCoreApplication::translate("CMD Options", "[options] [(<filename> | <url>)...]") + u'\n'
@@ -542,7 +548,7 @@ QString makeUsage(const QString &prgName)
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
"'TRUE'. For example, to disable the splash screen: "), 0) + u'\n'
+ u"QBT_NO_SPLASH=1 " + prgName + u'\n'
+ noSplashCommand + u'\n'
+ wrapText(QCoreApplication::translate("CMD Options", "Command line parameters take precedence over environment variables"), 0) + u'\n';
return text;

View File

@@ -6,6 +6,7 @@ add_library(qbt_base STATIC
applicationcomponent.h
asyncfilestorage.h
bittorrent/abstractfilestorage.h
bittorrent/addtorrenterror.h
bittorrent/addtorrentparams.h
bittorrent/announcetimepoint.h
bittorrent/bandwidthscheduler.h

View File

@@ -140,7 +140,7 @@ void AddTorrentManager::onSessionTorrentAdded(BitTorrent::Torrent *torrent)
}
}
void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason)
void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason)
{
if (const QString source = m_sourcesByInfoHash.take(infoHash); !source.isEmpty())
{
@@ -154,7 +154,7 @@ void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &in
void AddTorrentManager::handleAddTorrentFailed(const QString &source, const QString &reason)
{
LogMsg(tr("Failed to add torrent. Source: \"%1\". Reason: \"%2\"").arg(source, reason), Log::WARNING);
emit addTorrentFailed(source, reason);
emit addTorrentFailed(source, {BitTorrent::AddTorrentError::Other, reason});
}
void AddTorrentManager::handleDuplicateTorrent(const QString &source
@@ -187,7 +187,7 @@ void AddTorrentManager::handleDuplicateTorrent(const QString &source
LogMsg(tr("Detected an attempt to add a duplicate torrent. Source: %1. Existing torrent: %2. Result: %3")
.arg(source, existingTorrent->name(), message));
emit addTorrentFailed(source, message);
emit addTorrentFailed(source, {BitTorrent::AddTorrentError::DuplicateTorrent, message});
}
void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard)

View File

@@ -35,6 +35,7 @@
#include <QObject>
#include "base/applicationcomponent.h"
#include "base/bittorrent/addtorrenterror.h"
#include "base/bittorrent/addtorrentparams.h"
#include "base/torrentfileguard.h"
@@ -66,7 +67,7 @@ public:
signals:
void torrentAdded(const QString &source, BitTorrent::Torrent *torrent);
void addTorrentFailed(const QString &source, const QString &reason);
void addTorrentFailed(const QString &source, const BitTorrent::AddTorrentError &reason);
protected:
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
@@ -79,7 +80,7 @@ protected:
private:
void onDownloadFinished(const Net::DownloadResult &result);
void onSessionTorrentAdded(BitTorrent::Torrent *torrent);
void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason);
void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason);
bool processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
, const BitTorrent::AddTorrentParams &addTorrentParams);

View File

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

View File

@@ -147,7 +147,7 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::load(cons
const Path torrentFilePath = path() / Path(idString + u".torrent");
const qint64 torrentSizeLimit = Preferences::instance()->getTorrentFileSizeLimit();
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, torrentSizeLimit);
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, -1);
if (!resumeDataReadResult)
return nonstd::make_unexpected(resumeDataReadResult.error().message);
@@ -290,6 +290,8 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
p = lt::read_resume_data(resumeDataRoot, ec);
if (ec)
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
if (!metadata.isEmpty())
{
@@ -320,6 +322,8 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
p.save_path = Profile::instance()->fromPortablePath(
Path(fromLTString(p.save_path))).toString().toStdString();
if (p.save_path.empty())
return nonstd::make_unexpected(tr("Corrupted resume data: %1").arg(tr("save_path is invalid")));
torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed)

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2021-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2021-2025 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -217,80 +217,6 @@ namespace
{
return u"%1 %2"_s.arg(quoted(column.name), definition);
}
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
{
LoadTorrentParams resumeData;
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
if (!tagsData.isEmpty())
{
const QStringList tagList = tagsData.split(u',');
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
}
resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt();
resumeData.shareLimitAction = Utils::String::toEnum<ShareLimitAction>(
query.value(DB_COLUMN_SHARE_LIMIT_ACTION.name).toString(), ShareLimitAction::Default);
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
resumeData.stopCondition = Utils::String::toEnum(
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
resumeData.sslParameters =
{
.certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()),
.privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()),
.dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray()
};
resumeData.savePath = Profile::instance()->fromPortablePath(
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
if (!resumeData.useAutoTMM)
{
resumeData.downloadPath = Profile::instance()->fromPortablePath(
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
}
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
const auto *pref = Preferences::instance();
const int bdecodeDepthLimit = pref->getBdecodeDepthLimit();
const int bdecodeTokenLimit = pref->getBdecodeTokenLimit();
lt::error_code ec;
const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
p = lt::read_resume_data(resumeDataRoot, ec);
if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray()
; !bencodedMetadata.isEmpty())
{
const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
p.ti = std::make_shared<lt::torrent_info>(torentInfoRoot, ec);
}
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
.toString().toStdString();
if (p.flags & lt::torrent_flags::stop_when_ready)
{
p.flags &= ~lt::torrent_flags::stop_when_ready;
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
}
return resumeData;
}
}
namespace BitTorrent
@@ -688,6 +614,90 @@ void BitTorrent::DBResumeDataStorage::enableWALMode() const
throw RuntimeError(tr("WAL mode is probably unsupported due to filesystem limitations."));
}
LoadResumeDataResult DBResumeDataStorage::parseQueryResultRow(const QSqlQuery &query) const
{
LoadTorrentParams resumeData;
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
if (!tagsData.isEmpty())
{
const QStringList tagList = tagsData.split(u',');
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
}
resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt();
resumeData.shareLimitAction = Utils::String::toEnum<ShareLimitAction>(
query.value(DB_COLUMN_SHARE_LIMIT_ACTION.name).toString(), ShareLimitAction::Default);
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
resumeData.stopCondition = Utils::String::toEnum(
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
resumeData.sslParameters =
{
.certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()),
.privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()),
.dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray()
};
resumeData.savePath = Profile::instance()->fromPortablePath(
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
if (!resumeData.useAutoTMM)
{
resumeData.downloadPath = Profile::instance()->fromPortablePath(
Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
}
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
const auto *pref = Preferences::instance();
const int bdecodeDepthLimit = pref->getBdecodeDepthLimit();
const int bdecodeTokenLimit = pref->getBdecodeTokenLimit();
lt::error_code ec;
const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
if (ec)
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
p = lt::read_resume_data(resumeDataRoot, ec);
if (ec)
return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message())));
if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray()
; !bencodedMetadata.isEmpty())
{
const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec
, nullptr, bdecodeDepthLimit, bdecodeTokenLimit);
if (ec)
return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message())));
p.ti = std::make_shared<lt::torrent_info>(torentInfoRoot, ec);
if (ec)
return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message())));
}
p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path)))
.toString().toStdString();
if (p.save_path.empty())
return nonstd::make_unexpected(tr("Corrupted resume data: %1").arg(tr("save_path is invalid")));
if (p.flags & lt::torrent_flags::stop_when_ready)
{
p.flags &= ~lt::torrent_flags::stop_when_ready;
resumeData.stopCondition = Torrent::StopCondition::FilesChecked;
}
return resumeData;
}
BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, QReadWriteLock &dbLock, QObject *parent)
: QThread(parent)
, m_path {dbPath}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2021-2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2021-2025 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,9 +31,10 @@
#include <QReadWriteLock>
#include "base/pathfwd.h"
#include "base/utils/thread.h"
#include "resumedatastorage.h"
class QSqlQuery;
namespace BitTorrent
{
class DBResumeDataStorage final : public ResumeDataStorage
@@ -58,6 +59,7 @@ namespace BitTorrent
void createDB() const;
void updateDB(int fromVersion) const;
void enableWALMode() const;
LoadResumeDataResult parseQueryResultRow(const QSqlQuery &query) const;
class Worker;
Worker *m_asyncWorker = nullptr;

View File

@@ -34,6 +34,7 @@
#include "base/pathfwd.h"
#include "base/tagset.h"
#include "addtorrenterror.h"
#include "addtorrentparams.h"
#include "categoryoptions.h"
#include "sharelimitaction.h"
@@ -237,6 +238,12 @@ namespace BitTorrent
virtual Path finishedTorrentExportDirectory() const = 0;
virtual void setFinishedTorrentExportDirectory(const Path &path) = 0;
virtual bool isAddTrackersFromURLEnabled() const = 0;
virtual void setAddTrackersFromURLEnabled(bool enabled) = 0;
virtual QString additionalTrackersURL() const = 0;
virtual void setAdditionalTrackersURL(const QString &url) = 0;
virtual QString additionalTrackersFromURL() const = 0;
virtual int globalDownloadSpeedLimit() const = 0;
virtual void setGlobalDownloadSpeedLimit(int limit) = 0;
virtual int globalUploadSpeedLimit() const = 0;
@@ -386,6 +393,8 @@ namespace BitTorrent
virtual void setIncludeOverheadInLimits(bool include) = 0;
virtual QString announceIP() const = 0;
virtual void setAnnounceIP(const QString &ip) = 0;
virtual int announcePort() const = 0;
virtual void setAnnouncePort(int port) = 0;
virtual int maxConcurrentHTTPAnnounces() const = 0;
virtual void setMaxConcurrentHTTPAnnounces(int value) = 0;
virtual bool isReannounceWhenAddressChangedEnabled() const = 0;
@@ -473,7 +482,7 @@ namespace BitTorrent
signals:
void startupProgressUpdated(int progress);
void addTorrentFailed(const InfoHash &infoHash, const QString &reason);
void addTorrentFailed(const InfoHash &infoHash, const AddTorrentError &reason);
void allTorrentsFinished();
void categoryAdded(const QString &categoryName);
void categoryRemoved(const QString &categoryName);

File diff suppressed because it is too large Load Diff

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