Add ShareRatio to include ShareLimitAction

This commit is contained in:
ahjephson
2025-10-22 12:42:30 +01:00
parent e4ea79a8ed
commit e64a13c7c9
7 changed files with 141 additions and 53 deletions

View File

@@ -1,4 +1,5 @@
@inherits SubmittableDialog @inherits SubmittableDialog
@using Lantean.QBitTorrentClient.Models
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
@@ -34,10 +35,19 @@
<MudItem xs="9"> <MudItem xs="9">
<MudNumericField T="int" Value="InactiveMinutes" ValueChanged="InactiveMinutesChanged" Disabled="@(!(CustomEnabled && InactiveMinutesEnabled))" Min="1" Max="1024000" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="minutes" /> <MudNumericField T="int" Value="InactiveMinutes" ValueChanged="InactiveMinutesChanged" Disabled="@(!(CustomEnabled && InactiveMinutesEnabled))" Min="1" Max="1024000" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="minutes" />
</MudItem> </MudItem>
<MudItem xs="12">
<MudSelect T="ShareLimitAction" Label="Action when limit is reached" Value="SelectedShareLimitAction" ValueChanged="ShareLimitActionChanged" Disabled="@(!CustomEnabled)" Variant="Variant.Outlined">
<MudSelectItem Value="ShareLimitAction.Default">Default</MudSelectItem>
<MudSelectItem Value="ShareLimitAction.Stop">Stop torrent</MudSelectItem>
<MudSelectItem Value="ShareLimitAction.Remove">Remove torrent</MudSelectItem>
<MudSelectItem Value="ShareLimitAction.RemoveWithContent">Remove torrent and data</MudSelectItem>
<MudSelectItem Value="ShareLimitAction.EnableSuperSeeding">Enable super seeding</MudSelectItem>
</MudSelect>
</MudItem>
</MudGrid> </MudGrid>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton> <MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton> <MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton>
</DialogActions> </DialogActions>
</MudDialog> </MudDialog>

View File

@@ -1,4 +1,7 @@
using Lantean.QBitTorrentClient; using System;
using System.Collections.Generic;
using Lantean.QBitTorrentClient;
using Lantean.QBitTorrentClient.Models;
using Lantean.QBTMud.Models; using Lantean.QBTMud.Models;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using MudBlazor; using MudBlazor;
@@ -16,6 +19,9 @@ namespace Lantean.QBTMud.Components.Dialogs
[Parameter] [Parameter]
public ShareRatioMax? Value { get; set; } public ShareRatioMax? Value { get; set; }
[Parameter]
public ShareRatioMax? CurrentValue { get; set; }
[Parameter] [Parameter]
public bool Disabled { get; set; } public bool Disabled { get; set; }
@@ -33,6 +39,8 @@ namespace Lantean.QBTMud.Components.Dialogs
protected int InactiveMinutes { get; set; } protected int InactiveMinutes { get; set; }
protected ShareLimitAction SelectedShareLimitAction { get; set; } = ShareLimitAction.Default;
protected bool CustomEnabled => ShareRatioType == 0; protected bool CustomEnabled => ShareRatioType == 0;
protected void RatioEnabledChanged(bool value) protected void RatioEnabledChanged(bool value)
@@ -65,40 +73,75 @@ namespace Lantean.QBTMud.Components.Dialogs
InactiveMinutes = value; InactiveMinutes = value;
} }
protected void ShareLimitActionChanged(ShareLimitAction value)
{
SelectedShareLimitAction = value;
}
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
if (Value is null || Value.RatioLimit == Limits.GlobalLimit && Value.SeedingTimeLimit == Limits.GlobalLimit && Value.InactiveSeedingTimeLimit == Limits.GlobalLimit) RatioEnabled = false;
TotalMinutesEnabled = false;
InactiveMinutesEnabled = false;
var baseline = Value ?? CurrentValue;
SelectedShareLimitAction = baseline?.ShareLimitAction ?? ShareLimitAction.Default;
if (baseline is null || baseline.RatioLimit == Limits.GlobalLimit && baseline.SeedingTimeLimit == Limits.GlobalLimit && baseline.InactiveSeedingTimeLimit == Limits.GlobalLimit)
{ {
ShareRatioType = Limits.GlobalLimit; ShareRatioType = Limits.GlobalLimit;
return;
} }
else if (Value.MaxRatio == Limits.NoLimit && Value.MaxSeedingTime == Limits.NoLimit && Value.MaxInactiveSeedingTime == Limits.NoLimit)
if (baseline.MaxRatio == Limits.NoLimit && baseline.MaxSeedingTime == Limits.NoLimit && baseline.MaxInactiveSeedingTime == Limits.NoLimit)
{ {
ShareRatioType = Limits.NoLimit; ShareRatioType = Limits.NoLimit;
return;
}
ShareRatioType = 0;
if (baseline.RatioLimit >= 0)
{
RatioEnabled = true;
Ratio = baseline.RatioLimit;
} }
else else
{ {
ShareRatioType = 0; Ratio = 0;
if (Value.RatioLimit >= 0) }
{
RatioEnabled = true; if (baseline.SeedingTimeLimit >= 0)
Ratio = Value.RatioLimit; {
} TotalMinutesEnabled = true;
if (Value.SeedingTimeLimit >= 0) TotalMinutes = (int)baseline.SeedingTimeLimit;
{ }
TotalMinutesEnabled = true; else
TotalMinutes = (int)Value.SeedingTimeLimit; {
} TotalMinutes = 0;
if (Value.InactiveSeedingTimeLimit >= 0) }
{
InactiveMinutesEnabled = true; if (baseline.InactiveSeedingTimeLimit >= 0)
InactiveMinutes = (int)Value.InactiveSeedingTimeLimit; {
} InactiveMinutesEnabled = true;
InactiveMinutes = (int)baseline.InactiveSeedingTimeLimit;
}
else
{
InactiveMinutes = 0;
} }
} }
protected void ShareRatioTypeChanged(int value) protected void ShareRatioTypeChanged(int value)
{ {
ShareRatioType = value; ShareRatioType = value;
if (!CustomEnabled)
{
RatioEnabled = false;
TotalMinutesEnabled = false;
InactiveMinutesEnabled = false;
SelectedShareLimitAction = ShareLimitAction.Default;
}
} }
protected void Cancel() protected void Cancel()
@@ -112,16 +155,19 @@ namespace Lantean.QBTMud.Components.Dialogs
if (ShareRatioType == Limits.GlobalLimit) if (ShareRatioType == Limits.GlobalLimit)
{ {
result.RatioLimit = result.SeedingTimeLimit = result.InactiveSeedingTimeLimit = Limits.GlobalLimit; result.RatioLimit = result.SeedingTimeLimit = result.InactiveSeedingTimeLimit = Limits.GlobalLimit;
result.ShareLimitAction = ShareLimitAction.Default;
} }
else if (ShareRatioType == Limits.NoLimit) else if (ShareRatioType == Limits.NoLimit)
{ {
result.RatioLimit = result.SeedingTimeLimit = result.InactiveSeedingTimeLimit = Limits.NoLimit; result.RatioLimit = result.SeedingTimeLimit = result.InactiveSeedingTimeLimit = Limits.NoLimit;
result.ShareLimitAction = ShareLimitAction.Default;
} }
else else
{ {
result.RatioLimit = RatioEnabled ? Ratio : Limits.NoLimit; result.RatioLimit = RatioEnabled ? Ratio : Limits.NoLimit;
result.SeedingTimeLimit = TotalMinutesEnabled ? TotalMinutes : Limits.NoLimit; result.SeedingTimeLimit = TotalMinutesEnabled ? TotalMinutes : Limits.NoLimit;
result.InactiveSeedingTimeLimit = InactiveMinutesEnabled ? InactiveMinutes : Limits.NoLimit; result.InactiveSeedingTimeLimit = InactiveMinutesEnabled ? InactiveMinutes : Limits.NoLimit;
result.ShareLimitAction = SelectedShareLimitAction;
} }
MudDialog.Close(DialogResult.Ok(result)); MudDialog.Close(DialogResult.Ok(result));
} }
@@ -133,4 +179,4 @@ namespace Lantean.QBTMud.Components.Dialogs
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
} }

View File

@@ -1,9 +1,10 @@
using Lantean.QBitTorrentClient; using System.Linq;
using Lantean.QBitTorrentClient;
using ShareLimitAction = Lantean.QBitTorrentClient.Models.ShareLimitAction;
using Lantean.QBTMud.Components.Dialogs; using Lantean.QBTMud.Components.Dialogs;
using Lantean.QBTMud.Filter; using Lantean.QBTMud.Filter;
using Lantean.QBTMud.Models; using Lantean.QBTMud.Models;
using MudBlazor; using MudBlazor;
using System.Linq;
namespace Lantean.QBTMud.Helpers namespace Lantean.QBTMud.Helpers
{ {
@@ -241,21 +242,30 @@ namespace Lantean.QBTMud.Helpers
public static async Task InvokeShareRatioDialog(this IDialogService dialogService, IApiClient apiClient, IEnumerable<Torrent> torrents) public static async Task InvokeShareRatioDialog(this IDialogService dialogService, IApiClient apiClient, IEnumerable<Torrent> torrents)
{ {
var torrentShareRatios = torrents.Select(t => new ShareRatioMax var torrentList = torrents.ToList();
if (torrentList.Count == 0)
{
return;
}
var shareRatioValues = torrentList.Select(t => new ShareRatioMax
{ {
InactiveSeedingTimeLimit = t.InactiveSeedingTimeLimit, InactiveSeedingTimeLimit = t.InactiveSeedingTimeLimit,
MaxInactiveSeedingTime = t.InactiveSeedingTimeLimit, MaxInactiveSeedingTime = t.MaxInactiveSeedingTime,
MaxRatio = t.MaxRatio, MaxRatio = t.MaxRatio,
MaxSeedingTime = t.MaxSeedingTime, MaxSeedingTime = t.MaxSeedingTime,
RatioLimit = t.RatioLimit, RatioLimit = t.RatioLimit,
SeedingTimeLimit = t.SeedingTimeLimit, SeedingTimeLimit = t.SeedingTimeLimit,
}); ShareLimitAction = t.ShareLimitAction,
}).ToList();
var torrentsHaveSameShareRatio = torrentShareRatios.Distinct().Count() == 1; var referenceValue = shareRatioValues.First();
var torrentsHaveSameShareRatio = shareRatioValues.Distinct().Count() == 1;
var parameters = new DialogParameters var parameters = new DialogParameters
{ {
{ nameof(ShareRatioDialog.Value), torrentsHaveSameShareRatio ? torrentShareRatios.FirstOrDefault() : null }, { nameof(ShareRatioDialog.Value), torrentsHaveSameShareRatio ? referenceValue : null },
{ nameof(ShareRatioDialog.CurrentValue), referenceValue },
}; };
var result = await dialogService.ShowAsync<ShareRatioDialog>("Share ratio", parameters, FormDialogOptions); var result = await dialogService.ShowAsync<ShareRatioDialog>("Share ratio", parameters, FormDialogOptions);
@@ -267,7 +277,7 @@ namespace Lantean.QBTMud.Helpers
var shareRatio = (ShareRatio)dialogResult.Data; var shareRatio = (ShareRatio)dialogResult.Data;
await apiClient.SetTorrentShareLimit(shareRatio.RatioLimit, shareRatio.SeedingTimeLimit, shareRatio.InactiveSeedingTimeLimit, hashes: torrents.Select(t => t.Hash).ToArray()); await apiClient.SetTorrentShareLimit(shareRatio.RatioLimit, shareRatio.SeedingTimeLimit, shareRatio.InactiveSeedingTimeLimit, shareRatio.ShareLimitAction ?? ShareLimitAction.Default, hashes: torrentList.Select(t => t.Hash).ToArray());
} }
public static async Task InvokeStringFieldDialog(this IDialogService dialogService, string title, string label, string? value, Func<string, Task> onSuccess) public static async Task InvokeStringFieldDialog(this IDialogService dialogService, string title, string label, string? value, Func<string, Task> onSuccess)
@@ -461,3 +471,5 @@ namespace Lantean.QBTMud.Helpers
} }
} }
} }

View File

@@ -1,10 +1,13 @@
namespace Lantean.QBTMud.Models using Lantean.QBitTorrentClient.Models;
namespace Lantean.QBTMud.Models
{ {
public record ShareRatio public record ShareRatio
{ {
public float RatioLimit { get; set; } public float RatioLimit { get; set; }
public float SeedingTimeLimit { get; set; } public float SeedingTimeLimit { get; set; }
public float InactiveSeedingTimeLimit { get; set; } public float InactiveSeedingTimeLimit { get; set; }
public ShareLimitAction? ShareLimitAction { get; set; }
} }
public record ShareRatioMax : ShareRatio public record ShareRatioMax : ShareRatio
@@ -13,4 +16,4 @@
public float MaxSeedingTime { get; set; } public float MaxSeedingTime { get; set; }
public float MaxInactiveSeedingTime { get; set; } public float MaxInactiveSeedingTime { get; set; }
} }
} }

View File

@@ -1,4 +1,9 @@
namespace Lantean.QBTMud.Models using System;
using System.Collections.Generic;
using System.Linq;
using Lantean.QBitTorrentClient.Models;
namespace Lantean.QBTMud.Models
{ {
public class Torrent public class Torrent
{ {
@@ -57,6 +62,7 @@
string downloadPath, string downloadPath,
string rootPath, string rootPath,
bool isPrivate, bool isPrivate,
ShareLimitAction shareLimitAction,
string comment) string comment)
{ {
Hash = hash; Hash = hash;
@@ -113,25 +119,27 @@
DownloadPath = downloadPath; DownloadPath = downloadPath;
RootPath = rootPath; RootPath = rootPath;
IsPrivate = isPrivate; IsPrivate = isPrivate;
ShareLimitAction = shareLimitAction;
Comment = comment; Comment = comment;
} }
protected Torrent() protected Torrent()
{ {
Hash = ""; Hash = string.Empty;
Category = ""; Category = string.Empty;
ContentPath = ""; ContentPath = string.Empty;
InfoHashV1 = ""; InfoHashV1 = string.Empty;
InfoHashV2 = ""; InfoHashV2 = string.Empty;
MagnetUri = ""; MagnetUri = string.Empty;
Name = ""; Name = string.Empty;
SavePath = ""; SavePath = string.Empty;
DownloadPath = ""; DownloadPath = string.Empty;
RootPath = ""; RootPath = string.Empty;
State = ""; State = string.Empty;
Tags = []; Tags = new List<string>();
Tracker = ""; Tracker = string.Empty;
Comment = ""; ShareLimitAction = ShareLimitAction.Default;
Comment = string.Empty;
} }
public string Hash { get; } public string Hash { get; }
@@ -242,6 +250,8 @@
public bool IsPrivate { get; set; } public bool IsPrivate { get; set; }
public ShareLimitAction ShareLimitAction { get; set; }
public string Comment { get; set; } public string Comment { get; set; }
public override bool Equals(object? obj) public override bool Equals(object? obj)

View File

@@ -1,5 +1,6 @@
using Lantean.QBTMud.Helpers; using Lantean.QBTMud.Helpers;
using Lantean.QBTMud.Models; using Lantean.QBTMud.Models;
using ShareLimitAction = Lantean.QBitTorrentClient.Models.ShareLimitAction;
namespace Lantean.QBTMud.Services namespace Lantean.QBTMud.Services
{ {
@@ -705,6 +706,7 @@ namespace Lantean.QBTMud.Services
torrent.DownloadPath ?? string.Empty, torrent.DownloadPath ?? string.Empty,
torrent.RootPath ?? string.Empty, torrent.RootPath ?? string.Empty,
torrent.IsPrivate.GetValueOrDefault(), torrent.IsPrivate.GetValueOrDefault(),
torrent.ShareLimitAction ?? ShareLimitAction.Default,
torrent.Comment ?? string.Empty); torrent.Comment ?? string.Empty);
} }
@@ -1354,6 +1356,11 @@ namespace Lantean.QBTMud.Services
existingTorrent.IsPrivate = torrent.IsPrivate.Value; existingTorrent.IsPrivate = torrent.IsPrivate.Value;
dataChanged = true; dataChanged = true;
} }
if (torrent.ShareLimitAction.HasValue && existingTorrent.ShareLimitAction != torrent.ShareLimitAction.Value)
{
existingTorrent.ShareLimitAction = torrent.ShareLimitAction.Value;
dataChanged = true;
}
if (torrent.Comment is not null && !string.Equals(existingTorrent.Comment, torrent.Comment, StringComparison.Ordinal)) if (torrent.Comment is not null && !string.Equals(existingTorrent.Comment, torrent.Comment, StringComparison.Ordinal))
{ {

View File

@@ -10,16 +10,16 @@
- **Tracker grouping & removal**: When grouping trackers by host in `FiltersNav`, retain original URL entries so removal can target the right string. Replace the placeholder "Remove tracker" action with a real implementation and disable it for synthetic buckets. - **Tracker grouping & removal**: When grouping trackers by host in `FiltersNav`, retain original URL entries so removal can target the right string. Replace the placeholder "Remove tracker" action with a real implementation and disable it for synthetic buckets.
## ~~Torrent Data Model & Columns~~ ## ~~Torrent Data Model & Columns~~
~~- **Model sync**: Bring `Lantean.QBTMud.Models.Torrent` into parity with v5 (`Popularity`, `DownloadPath`, `RootPath`, `InfoHashV1/2`, `IsPrivate`, share-limit action fields, tracker flags, etc.) and map them in `DataManager.CreateTorrent`.~~ - ~~**Model sync**: Bring `Lantean.QBTMud.Models.Torrent` into parity with v5 (`Popularity`, `DownloadPath`, `RootPath`, `InfoHashV1/2`, `IsPrivate`, share-limit action fields, tracker flags, etc.) and map them in `DataManager.CreateTorrent`.~~
~~- **Column set alignment**: Match the v5 table defaults—add missing columns (Popularity, Reannounce in, Info hashes, Download path, Private, etc.), fix "Ratio Limit" to display `RatioLimit`, and ensure column ordering/enabled state mirrors `DynamicTable.TorrentsTable`.~~ - ~~**Column set alignment**: Match the v5 table defaults—add missing columns (Popularity, Reannounce in, Info hashes, Download path, Private, etc.), fix "Ratio Limit" to display `RatioLimit`, and ensure column ordering/enabled state mirrors `DynamicTable.TorrentsTable`.~~
~~- **Helper updates**: Extend `DisplayHelpers` to format the new fields (popularity, private flag, info hashes, error state icons).~~ - ~~**Helper updates**: Extend `DisplayHelpers` to format the new fields (popularity, private flag, info hashes, error state icons).~~
## Actions & Dialogs ## Actions & Dialogs
- ~~**Copy submenu**: Add "Copy comment" and "Copy content path" to the copy submenu in `TorrentActions`, keeping clipboard behaviour identical to v5.~~ - ~~**Copy submenu**: Add "Copy comment" and "Copy content path" to the copy submenu in `TorrentActions`, keeping clipboard behaviour identical to v5.~~
- **Share ratio dialog**: Update `ShareRatioDialog`, `ShareRatio/ShareRatioMax`, and `DialogHelper.InvokeShareRatioDialog` to surface `ShareLimitAction`, fix the `MaxInactiveSeedingTime` mapping, and call `SetTorrentShareLimit` with the action. - ~~**Share ratio dialog**: Update `ShareRatioDialog`, `ShareRatio/ShareRatioMax`, and `DialogHelper.InvokeShareRatioDialog` to surface `ShareLimitAction`, fix the `MaxInactiveSeedingTime` mapping, and call `SetTorrentShareLimit` with the action.~~
## Add-Torrent Flow ## Add-Torrent Flow
- Mirror the v5 add-torrent pane: add controls for incomplete save path, tags, auto-start, queue position, share-limit action, etc., in `AddTorrentOptions.razor`, and wire the new fields into the submission object. - Mirror the v5 add-torrent pane: add controls for incomplete save path, tags, auto-start, queue position, share-limit action, etc., in `AddTorrentOptions.razor`, and wire the new fields into the submission object.
## ~~Preferences & Local Settings~~ ## ~~Preferences & Local Settings~~
~~- Introduce new v5 toggles such as "Display full tracker URL" in `AdvancedOptions`, persist them via the preferences service, and respect the setting in the tracker column rendering.~~ - ~~Introduce new v5 toggles such as "Display full tracker URL" in `AdvancedOptions`, persist them via the preferences service, and respect the setting in the tracker column rendering.~~