mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-10-23 04:52:22 +00:00
Merge branch 'develop' into codex/find-and-fix-a-bug
This commit is contained in:
@@ -92,7 +92,9 @@
|
|||||||
<FieldSwitch Label="When ratio reaches" Value="MaxRatioEnabled" ValueChanged="MaxRatioEnabledChanged" />
|
<FieldSwitch Label="When ratio reaches" Value="MaxRatioEnabled" ValueChanged="MaxRatioEnabledChanged" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
<MudItem xs="9">
|
<MudItem xs="9">
|
||||||
<MudNumericField T="int" Label="" Value="MaxRatio" ValueChanged="MaxRatioChanged" Disabled="@(!MaxRatioEnabled)" Min="0" Max="9998" Variant="Variant.Outlined" Validation="MaxRatioValidation" />
|
<MudNumericField T="float" Label="" Value="MaxRatio" ValueChanged="MaxRatioChanged"
|
||||||
|
Disabled="@(!MaxRatioEnabled)" Min="0" Max="9998" Variant="Variant.Outlined"
|
||||||
|
Validation="MaxRatioValidation" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
<MudItem xs="3">
|
<MudItem xs="3">
|
||||||
<FieldSwitch Label="When total seeding time reaches" Value="MaxSeedingTimeEnabled" ValueChanged="MaxSeedingTimeEnabledChanged" />
|
<FieldSwitch Label="When total seeding time reaches" Value="MaxSeedingTimeEnabled" ValueChanged="MaxSeedingTimeEnabledChanged" />
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
protected int SlowTorrentUlRateThreshold { get; private set; }
|
protected int SlowTorrentUlRateThreshold { get; private set; }
|
||||||
protected int SlowTorrentInactiveTimer { get; private set; }
|
protected int SlowTorrentInactiveTimer { get; private set; }
|
||||||
protected bool MaxRatioEnabled { get; private set; }
|
protected bool MaxRatioEnabled { get; private set; }
|
||||||
protected int MaxRatio { get; private set; }
|
protected float MaxRatio { get; private set; }
|
||||||
protected bool MaxSeedingTimeEnabled { get; private set; }
|
protected bool MaxSeedingTimeEnabled { get; private set; }
|
||||||
protected int MaxSeedingTime { get; private set; }
|
protected int MaxSeedingTime { get; private set; }
|
||||||
protected int MaxRatioAct { get; private set; }
|
protected int MaxRatioAct { get; private set; }
|
||||||
@@ -275,7 +275,7 @@
|
|||||||
await PreferencesChanged.InvokeAsync(UpdatePreferences);
|
await PreferencesChanged.InvokeAsync(UpdatePreferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task MaxRatioChanged(int value)
|
protected async Task MaxRatioChanged(float value)
|
||||||
{
|
{
|
||||||
MaxRatio = value;
|
MaxRatio = value;
|
||||||
UpdatePreferences.MaxRatio = value;
|
UpdatePreferences.MaxRatio = value;
|
||||||
|
@@ -62,7 +62,7 @@
|
|||||||
<MudCardContent Class="pt-0">
|
<MudCardContent Class="pt-0">
|
||||||
<MudGrid>
|
<MudGrid>
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSelect T="bool" Label="Default Torrent Management Mode" Value="AutoTmmEnabled" ValueChanged="AutoDeleteModeChanged" Variant="Variant.Outlined">
|
<MudSelect T="bool" Label="Default Torrent Management Mode" Value="AutoTmmEnabled" ValueChanged="AutoTmmEnabledChanged" Variant="Variant.Outlined">
|
||||||
<MudSelectItem Value="false">Manual</MudSelectItem>
|
<MudSelectItem Value="false">Manual</MudSelectItem>
|
||||||
<MudSelectItem Value="true">Automatic</MudSelectItem>
|
<MudSelectItem Value="true">Automatic</MudSelectItem>
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
@@ -19,28 +19,28 @@ namespace Lantean.QBTMud.Helpers
|
|||||||
{
|
{
|
||||||
if (seconds is null)
|
if (seconds is null)
|
||||||
{
|
{
|
||||||
return "";
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seconds == 8640000)
|
const long InfiniteEtaSentinelSeconds = 8_640_000; // ~100 days, used by qBittorrent for "infinite" ETA.
|
||||||
|
var value = seconds.Value;
|
||||||
|
|
||||||
|
if (value >= long.MaxValue || value >= TimeSpan.MaxValue.TotalSeconds || value == InfiniteEtaSentinelSeconds)
|
||||||
{
|
{
|
||||||
return "∞";
|
return "∞";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seconds < 60)
|
if (value <= 0)
|
||||||
{
|
{
|
||||||
return "< 1m";
|
return "< 1m";
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeSpan time;
|
var time = TimeSpan.FromSeconds(value);
|
||||||
try
|
if (time.TotalMinutes < 1)
|
||||||
{
|
{
|
||||||
time = TimeSpan.FromSeconds(seconds.Value);
|
return "< 1m";
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return "∞";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
if (prefix is not null)
|
if (prefix is not null)
|
||||||
{
|
{
|
||||||
@@ -83,6 +83,7 @@ namespace Lantean.QBTMud.Helpers
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Formats a file size in bytes into an appropriate unit based on the size.
|
/// Formats a file size in bytes into an appropriate unit based on the size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -119,34 +119,34 @@ namespace Lantean.QBTMud.Helpers
|
|||||||
switch (category)
|
switch (category)
|
||||||
{
|
{
|
||||||
case CATEGORY_ALL:
|
case CATEGORY_ALL:
|
||||||
break;
|
return true;
|
||||||
|
|
||||||
case CATEGORY_UNCATEGORIZED:
|
case CATEGORY_UNCATEGORIZED:
|
||||||
if (!string.IsNullOrEmpty(torrent.Category))
|
if (!string.IsNullOrEmpty(torrent.Category))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
|
if (string.IsNullOrEmpty(torrent.Category))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!useSubcategories)
|
if (!useSubcategories)
|
||||||
{
|
{
|
||||||
if (torrent.Category != category)
|
return string.Equals(torrent.Category, category, StringComparison.Ordinal);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!torrent.Category.StartsWith(category))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
if (string.Equals(torrent.Category, category, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = string.Concat(category, "/");
|
||||||
|
return torrent.Category.StartsWith(prefix, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool FilterTag(Torrent torrent, string tag)
|
public static bool FilterTag(Torrent torrent, string tag)
|
||||||
@@ -207,7 +207,7 @@ namespace Lantean.QBTMud.Helpers
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Status.Paused:
|
case Status.Paused:
|
||||||
if (!state.Contains("paused") || !state.Contains("stopped"))
|
if (!state.Contains("paused") && !state.Contains("stopped"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -39,12 +39,19 @@ namespace Lantean.QBTMud.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags = new List<string>(mainData.Tags?.Count ?? 0);
|
var tags = new List<string>();
|
||||||
if (mainData.Tags is not null)
|
if (mainData.Tags is not null)
|
||||||
{
|
{
|
||||||
|
var seenTags = new HashSet<string>(StringComparer.Ordinal);
|
||||||
foreach (var tag in mainData.Tags)
|
foreach (var tag in mainData.Tags)
|
||||||
{
|
{
|
||||||
tags.Add(tag);
|
var normalizedTag = NormalizeTag(tag);
|
||||||
|
if (string.IsNullOrEmpty(normalizedTag) || !seenTags.Add(normalizedTag))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.Add(normalizedTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +164,14 @@ namespace Lantean.QBTMud.Services
|
|||||||
{
|
{
|
||||||
foreach (var tag in mainData.TagsRemoved)
|
foreach (var tag in mainData.TagsRemoved)
|
||||||
{
|
{
|
||||||
torrentList.Tags.Remove(tag);
|
var normalizedTag = NormalizeTag(tag);
|
||||||
torrentList.TagState.Remove(tag);
|
if (string.IsNullOrEmpty(normalizedTag))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
torrentList.Tags.Remove(normalizedTag);
|
||||||
|
torrentList.TagState.Remove(normalizedTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +213,18 @@ namespace Lantean.QBTMud.Services
|
|||||||
{
|
{
|
||||||
foreach (var tag in mainData.Tags)
|
foreach (var tag in mainData.Tags)
|
||||||
{
|
{
|
||||||
torrentList.Tags.Add(tag);
|
var normalizedTag = NormalizeTag(tag);
|
||||||
|
if (string.IsNullOrEmpty(normalizedTag))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
torrentList.Tags.Add(normalizedTag);
|
||||||
|
var matchingHashes = torrentList.Torrents
|
||||||
|
.Where(pair => FilterHelper.FilterTag(pair.Value, normalizedTag))
|
||||||
|
.Select(pair => pair.Key)
|
||||||
|
.ToHashSet();
|
||||||
|
torrentList.TagState[normalizedTag] = matchingHashes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,6 +532,12 @@ namespace Lantean.QBTMud.Services
|
|||||||
|
|
||||||
public Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent)
|
public Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent)
|
||||||
{
|
{
|
||||||
|
var normalizedTags = torrent.Tags?
|
||||||
|
.Select(NormalizeTag)
|
||||||
|
.Where(static tag => !string.IsNullOrEmpty(tag))
|
||||||
|
.ToList()
|
||||||
|
?? new List<string>();
|
||||||
|
|
||||||
return new Torrent(
|
return new Torrent(
|
||||||
hash,
|
hash,
|
||||||
torrent.AddedOn.GetValueOrDefault(),
|
torrent.AddedOn.GetValueOrDefault(),
|
||||||
@@ -548,7 +578,7 @@ namespace Lantean.QBTMud.Services
|
|||||||
torrent.Size.GetValueOrDefault(),
|
torrent.Size.GetValueOrDefault(),
|
||||||
torrent.State!,
|
torrent.State!,
|
||||||
torrent.SuperSeeding.GetValueOrDefault(),
|
torrent.SuperSeeding.GetValueOrDefault(),
|
||||||
torrent.Tags!,
|
normalizedTags,
|
||||||
torrent.TimeActive.GetValueOrDefault(),
|
torrent.TimeActive.GetValueOrDefault(),
|
||||||
torrent.TotalSize.GetValueOrDefault(),
|
torrent.TotalSize.GetValueOrDefault(),
|
||||||
torrent.Tracker!,
|
torrent.Tracker!,
|
||||||
@@ -561,6 +591,19 @@ namespace Lantean.QBTMud.Services
|
|||||||
torrent.MaxInactiveSeedingTime.GetValueOrDefault());
|
torrent.MaxInactiveSeedingTime.GetValueOrDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeTag(string? tag)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(tag))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var separatorIndex = tag.IndexOf('\t');
|
||||||
|
var normalized = (separatorIndex >= 0) ? tag[..separatorIndex] : tag;
|
||||||
|
|
||||||
|
return normalized.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
private static void UpdateCategory(Category existingCategory, QBitTorrentClient.Models.Category category)
|
private static void UpdateCategory(Category existingCategory, QBitTorrentClient.Models.Category category)
|
||||||
{
|
{
|
||||||
existingCategory.SavePath = category.SavePath ?? existingCategory.SavePath;
|
existingCategory.SavePath = category.SavePath ?? existingCategory.SavePath;
|
||||||
@@ -609,7 +652,14 @@ namespace Lantean.QBTMud.Services
|
|||||||
if (torrent.Tags is not null)
|
if (torrent.Tags is not null)
|
||||||
{
|
{
|
||||||
existingTorrent.Tags.Clear();
|
existingTorrent.Tags.Clear();
|
||||||
existingTorrent.Tags.AddRange(torrent.Tags);
|
foreach (var tag in torrent.Tags)
|
||||||
|
{
|
||||||
|
var normalizedTag = NormalizeTag(tag);
|
||||||
|
if (!string.IsNullOrEmpty(normalizedTag))
|
||||||
|
{
|
||||||
|
existingTorrent.Tags.Add(normalizedTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
existingTorrent.TimeActive = torrent.TimeActive ?? existingTorrent.TimeActive;
|
existingTorrent.TimeActive = torrent.TimeActive ?? existingTorrent.TimeActive;
|
||||||
existingTorrent.TotalSize = torrent.TotalSize ?? existingTorrent.TotalSize;
|
existingTorrent.TotalSize = torrent.TotalSize ?? existingTorrent.TotalSize;
|
||||||
|
@@ -112,7 +112,7 @@ namespace Lantean.QBitTorrentClient.Models
|
|||||||
int maxConnecPerTorrent,
|
int maxConnecPerTorrent,
|
||||||
int maxInactiveSeedingTime,
|
int maxInactiveSeedingTime,
|
||||||
bool maxInactiveSeedingTimeEnabled,
|
bool maxInactiveSeedingTimeEnabled,
|
||||||
int maxRatio,
|
float maxRatio,
|
||||||
int maxRatioAct,
|
int maxRatioAct,
|
||||||
bool maxRatioEnabled,
|
bool maxRatioEnabled,
|
||||||
int maxSeedingTime,
|
int maxSeedingTime,
|
||||||
@@ -745,7 +745,7 @@ namespace Lantean.QBitTorrentClient.Models
|
|||||||
public bool MaxInactiveSeedingTimeEnabled { get; }
|
public bool MaxInactiveSeedingTimeEnabled { get; }
|
||||||
|
|
||||||
[JsonPropertyName("max_ratio")]
|
[JsonPropertyName("max_ratio")]
|
||||||
public int MaxRatio { get; }
|
public float MaxRatio { get; }
|
||||||
|
|
||||||
[JsonPropertyName("max_ratio_act")]
|
[JsonPropertyName("max_ratio_act")]
|
||||||
public int MaxRatioAct { get; }
|
public int MaxRatioAct { get; }
|
||||||
|
@@ -323,7 +323,7 @@ namespace Lantean.QBitTorrentClient.Models
|
|||||||
public bool? MaxInactiveSeedingTimeEnabled { get; set; }
|
public bool? MaxInactiveSeedingTimeEnabled { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("max_ratio")]
|
[JsonPropertyName("max_ratio")]
|
||||||
public int? MaxRatio { get; set; }
|
public float? MaxRatio { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("max_ratio_act")]
|
[JsonPropertyName("max_ratio_act")]
|
||||||
public int? MaxRatioAct { get; set; }
|
public int? MaxRatioAct { get; set; }
|
||||||
|
@@ -68,11 +68,13 @@ cd qbtmud
|
|||||||
dotnet restore
|
dotnet restore
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Build the Application
|
### 3. Build and Publish the Application
|
||||||
```sh
|
```sh
|
||||||
dotnet build --configuration Release
|
dotnet publish --configuration Release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will output the Web UI files to `Lantean.QBTMud\bin\Release\net9.0\publish\wwwroot`.
|
||||||
|
|
||||||
### 4. Configure qBittorrent to Use qbtmud
|
### 4. Configure qBittorrent to Use qbtmud
|
||||||
Follow the same steps as in the **Installation** section to set qbtmud as your WebUI.
|
Follow the same steps as in the **Installation** section to set qbtmud as your WebUI.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user