mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-03-02 22:57:32 -05:00
Inheriting Torrent Settings #8121
Labels
No labels
Accessibility
AppImage
Bounty
Build system
CI
Can't reproduce
Code cleanup
Confirmed bug
Confirmed bug
Core
Crash
Data loss
Discussion
Docker
Documentation
Duplicate
Feature
Feature request
Feature request
Feature request
Filters
Flatpak
GUI
Has workaround
I2P
Invalid
Libtorrent
Look and feel
Meta
NSIS
Network
Not an issue
OS: *BSD
OS: Linux
OS: Windows
OS: macOS
PPA
Performance
Project management
Proxy/VPN
Qt bugs
Qt6 compat
RSS
Search engine
Security
Temp folder
Themes
Translations
Triggers
Waiting diagnosis
Waiting info
Waiting upstream
Waiting web implementation
Watched folders
WebAPI
WebUI
autoCloseOldIssue
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/qBittorrent#8121
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @glassez on GitHub (Nov 29, 2018).
Originally assigned to: @glassez on GitHub.
Let me to introduce "Inheriting Torrent Settings" feature.
It should be a generalized replacement for some existing mechanisms (e.g. Automatic Torrent Management) and bring a convenient way to implement all similar capabilities.
From a user perspective, it is a set of torrent settings (like Save path, Ratio limit, etc.) that have some default values and can be overridden at different levels (e.g. at Category level or Torrent level).

From a developer perspective, it is a set of torrent settings that is bound to each "manageable item" (Application, Category, Torrent).
Each setting can be either set or not set.
The following resolution algorithm is used to obtain the effective value of some setting:
Example

Consider the following example of one of the settings (Save path):
A value for "Save path" setting of "Torrent A" is not set so it inherits the value from its parent item (Category "Misc"). Category "Misc" also hasn't explicit value of "Save path" and it inherits the value of its parent item (Application default "Save path" setting). So the effective value of "Torrent A" "Save path" setting is "C:\Users\John\Downloads".
"Torrent B" has explicit value of "Save path" setting so the effective value is its value resolved against the effective value of appropriate parent item setting. Since it's absolute path it is used as is.
The effective values for "Save path" setting of all other torrents we get in the same way (e.g. "Torrent E" has "D:\Videos\Movies").
Inheritance of Setting works in two ways:
Each setting is mapped to a policy that determines how this setting will be created. There is also a default policy that allows you to not explicitly set all policies.

When a user (or some automation tool) creates Category or adds Torrent it pass "Torrent Settings" that are pre-processed by the Session using Policies. If appropriate Policy is "Bypass" the setting is passed as is. If Policy is "Resolve" the setting is resolved first and passed having explicit value. If an appropriate Policy for some Setting is not set the Default Policy is used.
I want to pre-discuss this feature here and get the main approval before proceeding with its implementation. So waiting for your comments (I'm more interested in the comments of team members, but users can also speak out if someone has something really valuable).
@sledgehammer999, @qbittorrent/demigods, @qbittorrent/frequent-contributors, ping.
@sledgehammer999 commented on GitHub (Nov 29, 2018):
@glassez commented on GitHub (Nov 29, 2018):
You misunderstood me. They're just handle the Torrent Settings but don't use themselves. See my example carefully.
Tomorrow I will add an example of their work.
@glassez commented on GitHub (Nov 30, 2018):
I've added the description of Creation Policies.
@glassez commented on GitHub (Nov 30, 2018):
I intend to implement this feature in the application core initially with minimal UI changes. But in the future it will require significant rework and unification.
@Seeker2 commented on GitHub (Dec 9, 2018):
Is this what's necessary to implement separate max connection limits for downloading and seeding torrents? Found here:
[Wishlist] Alternate connections per torrent while seeding - Upload Slots per SEEDING torrent limit!
https://github.com/qbittorrent/qBittorrent/issues/2193
...Which would explain why "Milestone: qBitTorrent v3.2.1" has long-since been passed.
@rrrevin commented on GitHub (Apr 22, 2019):
Any update on this?
@glassez commented on GitHub (Apr 23, 2019):
Unfortunately, I do not have enough time to make any significant contributions to the project.
@Pentaphon commented on GitHub (Nov 9, 2020):
We should probably take this off the 4.3.1 milestone because there is no way this is going to make that version.
@radry commented on GitHub (Feb 22, 2021):
How about putting this again on the next milestone? At least 4.4.0?
@damomato commented on GitHub (Mar 5, 2022):
Any updates on this? I would like to set different seeding limits for different categories
@tayl commented on GitHub (Jan 3, 2023):
+1 for this, setting limits by category would be useful. Stealing someone else's comment from elsewhere as it reflects my own need:
@matthieu-vergne commented on GitHub (May 23, 2023):
May I suggest an architecture for the implementation? Please be aware that I am a Java developer. So I cannot help much on C++ code, but I hope that at design level it can be of some use.
Basic idea
Basically, your default settings would be a
SettingsValuesinstance, and every additional layer below it would be aSettingsCombinertaking the instance of the previous layer asfallbackSettings+ anotherSettingsValuestelling what are the values set specifically at this new layer. I call the first one "fallback" because I have first in mind that you want to "fall back" on this instance if the "current settings" does not define a value. The code in the bottom right note implements such a fallback strategy. The key point is to call the fallback only if needed.If the user changes the save path of the default settings, it calls
savePath(newPath)on theSettingsValuesused for the defaults. If the user changes the save path of a category, it retrieves theSettingsCombinerat the category level and calls itscurrentSettings.savePath(newPath)to change the settings at the category level.Examples of traversing when we retrieve the save path:
torrentD.savePath(), look at itscurrentSettings.savePath(), defined so returns it.torrentC.savePath(), look at itscurrentSettings.savePath(), not defined so fall back tomusic.savePath(), look at itscurrentSettings.savePath(), defined so returns it.torrentA.savePath(), look at itscurrentSettings.savePath(), not defined so fall back tomisc.savePath(), look at itscurrentSettings.savePath(), not defined so fall back todefaultSettings.savePath(), immediately returns its value because it is aSettingsValues, not aSettingsCombiner.Settings resolution
Rather than simply falling back on the previous layer, you speak about resolving it against its parent. In this case, the

SettingsCombineruses a more complex implementation, which I abstract in a function:The function is a bit convoluted, but this is because we don't want to retrieve both paths before to check whether or not it is worth it. Otherwise you would systematically retrieve the settings through all the hierarchy even if you use only one of them.
To avoid that, the "calls" are abstracted through the 3rd argument
Settings::savePath, which is java code for a method reference. An object that you can apply on aSettingsinstance to call itssavePath()method (and obtain its returned value). I don't know what would be the equivalent in C++.The pseudo-code for this function would be the following:
When applied to torrent E case:
Settings::savePathontorrentE.currentSettingsto havecurrentPathn°1currentPathn°1 and applySettings::savePathon fallbackSettings::savePathonmovies.currentSettingsto havecurrentPathn°2currentPathn°2 and applySettings::savePathon fallbackSettings::savePathonvideos.currentSettingsto havecurrentPathn°3currentPathn°3 without going furthercurrentPathn°2 against the result of the fallbackThe notions of absoluteness/relativeness are rather trivial for paths, but they have to be interpreted in a generic way to be applicable to other types of data. For instance, what would it mean to have a "relative speed" of download to be resolved on the parent settings? Would it be a factor? But then it would not be a "speed" since a factor has no unit. You can always "fall back" on the parent settings, but the resolution must be considered case by case.
Snapshot
Finally, the notions of "real" and "create-time" inheritance. As far as I understand, you want to remove the capacity for the resulting settings to change based on some changes on some arbitrary layers. Which is what we usually call a snapshot:

Here I add a function in the interface, but assuming we can implement it at this level (default implementation in Java). If it is not the case, you can have this function somewhere else out of this architecture. It would take the source
Settingsin argument and return the new one after filling it.@shanew1694 commented on GitHub (Jan 16, 2024):
Has development come to an end for this feature? It would be a very nice feature to have. I think it is the only major feature missing from qbittorrent
@kpupp commented on GitHub (Jul 23, 2024):
Definitely the only one that keeps me coming back to Deluge. Having seen it been waffled and hemmed and hawed and hmm'd and closed and reopened and reimagined and waffled over for years (in #590 and #5222), it'd be great to finally include it... Especially with 5.0 lurking around the corner it seems.
@AdamT20054 commented on GitHub (Jul 26, 2024):
Completely agree.
It's a borderline basic feature.For those who use both public and private trackers, we would much prefer different limits for both as ratios/seedtimes might exceed that of another.
@JackyHe398 commented on GitHub (Dec 5, 2025):
It has been 7 year since the issue opened(actually more than 8 if counting in the similar). May i know is this idea still under developed?
@kpupp commented on GitHub (Dec 5, 2025):
I had wondered about this again the other day too. Not to spam the comment
chain and maintainers but I dearly hope it is. One of the few features
keeping qbit from being on par with (and far exceeding imo) its peers.
On Fri, Dec 5, 2025, 5:39 PM Jacky He @.***> wrote:
@matthieu-vergne commented on GitHub (Dec 5, 2025):
Still hoping for it as well. Hopefully my suggestions above can help. I still didn't convert to C++, so still can't help much beside design.
@glassez commented on GitHub (Dec 6, 2025):
In fact, design has never been a sticking point here. It's just that initially there was a desire to revamp "everything at once", but it turned out to be beyond my strength (I just don't have that much time for this project, besides, most of it is spent on participating in current issues and reviews of other PRs).
But due to the demand for such a feature, I decided to return to this topic, adding a piece at a time.
@glassez commented on GitHub (Dec 6, 2025):
See #23577.
@matthieu-vergne commented on GitHub (Dec 6, 2025):
Revamp "everything at once" is never a good idea in practice. Even in huge professional environments, such projects are bound to fail if not strictly planned and organised, and they never fully go the "all at once" route. If time is scarce or uncertainty is high (like wondering what would really fit the bill) agile practices (go one piece at a time) is far more practical. So I can only support that approach. Having a final target is good to drive the effort, but always work it one step after the other.
Hopefully other skilled C++ devs will join you in that effort.