qbittorrent / qBittorrent

qBittorrent BitTorrent client
https://www.qbittorrent.org
Other
28.67k stars 4.01k forks source link

Inheriting Torrent Settings #9939

Open glassez opened 6 years ago

glassez commented 6 years ago

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). settings

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:

  1. If setting is not set the effective value of parent "manageable item" is used (the setting inherits the value).
  2. If setting is explicitly set its value is resolved relative to the effective value of the parent "manageable item" and then used. By default the resolution simply boils down to returning the value itself. But some particular settings (e.g. Save path) can actually make relative resolution.

Example Consider the following example of one of the settings (Save path): inheriting

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:

  1. Real inheritance. When "manageable item" is created some settings are passed as is. When value of one of these settings is changed by the user the "manageable item" calls its children to adjust the appropriate setting.
  2. Create-time inheritance. When "manageable item" is created some settings are resolved against parent item settings so the explicit values are really stored and such settings aren't affected by parent settings changes.

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. 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 6 years ago
  1. kudos for the included charts
  2. I agree with the abstract concept
  3. I understand how the inheritable settings can work with Torrents. But you seem to mention other areas where they could be used (Application, Category). Do you have abstract examples on how the settings will work for the other areas?
  4. About policies: Is some example possible to see how they relate to settings?
glassez commented 6 years ago

But you seem to mention other areas where they could be used (Application, Category).

You misunderstood me. They're just handle the Torrent Settings but don't use themselves. See my example carefully.

About policies: Is some example possible to see how they relate to settings?

Tomorrow I will add an example of their work.

glassez commented 6 years ago

Tomorrow I will add an example of their work.

I've added the description of Creation Policies.

glassez commented 6 years ago

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 5 years ago

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 5 years ago

Any update on this?

glassez commented 5 years ago

Any update on this?

Unfortunately, I do not have enough time to make any significant contributions to the project.

Pentaphon commented 4 years ago

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 3 years ago

How about putting this again on the next milestone? At least 4.4.0?

damomato commented 2 years ago

Any updates on this? I would like to set different seeding limits for different categories

tayl commented 1 year ago

+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 1 year ago

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 SettingsValues instance, and every additional layer below it would be a SettingsCombiner taking the instance of the previous layer as fallbackSettings + another SettingsValues telling 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 the SettingsValues used for the defaults. If the user changes the save path of a category, it retrieves the SettingsCombiner at the category level and calls its currentSettings.savePath(newPath) to change the settings at the category level.

Examples of traversing when we retrieve the save path:

Settings resolution

Rather than simply falling back on the previous layer, you speak about resolving it against its parent. In this case, the SettingsCombiner uses 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 a Settings instance to call its savePath() 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:

Path currentPath = apply Settings::savePath on currentSettings
if (currentPath is undefined) {
  // Not needed, just return fallback settings
  return apply Settings::savePath on fallbackSettings
} else if (currentPath is relative) {
  // Needed but incomplete, resolve against fallback
  Path parentPath = apply Settings::savePath on fallbackSettings
  Path resultPath = resolve currentPath against parentPath
  return resultPath
} else {
  // Needed and completely override fallback, so no need to go further
  return currentPath
}

When applied to torrent E case:

  1. apply Settings::savePath on torrentE.currentSettings to have currentPath n°1
  2. undefined, so discard currentPath n°1 and apply Settings::savePath on fallback
    1. apply Settings::savePath on movies.currentSettings to have currentPath n°2
    2. defined but relative, so keep currentPath n°2 and apply Settings::savePath on fallback
      1. apply Settings::savePath on videos.currentSettings to have currentPath n°3
      2. defined and absolute so return the currentPath n°3 without going further
    3. resolve currentPath n°2 against the result of the fallback
    4. returns the resolution
  3. returns the result of the fallback

The 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 Settings in argument and return the new one after filling it.

shanew1694 commented 10 months ago

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 4 months ago

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

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 4 months ago

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

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.

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.