From 8796cc0f2445ba6832b84a9889749daec3c10bba Mon Sep 17 00:00:00 2001 From: ahjephson <16685186+ahjephson@users.noreply.github.com> Date: Sat, 18 Oct 2025 15:37:04 +0100 Subject: [PATCH 1/2] Fix #9 and bug related to invalid TimeSpan in duration --- Lantean.QBTMud/Helpers/DisplayHelpers.cs | 23 ++++---- Lantean.QBTMud/Helpers/FilterHelper.cs | 34 ++++++------ Lantean.QBTMud/Services/DataManager.cs | 67 +++++++++++++++++++++--- 3 files changed, 88 insertions(+), 36 deletions(-) diff --git a/Lantean.QBTMud/Helpers/DisplayHelpers.cs b/Lantean.QBTMud/Helpers/DisplayHelpers.cs index c9c68fd..19d1753 100644 --- a/Lantean.QBTMud/Helpers/DisplayHelpers.cs +++ b/Lantean.QBTMud/Helpers/DisplayHelpers.cs @@ -19,28 +19,28 @@ namespace Lantean.QBTMud.Helpers { 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 >= (long)TimeSpan.MaxValue.TotalSeconds || value == InfiniteEtaSentinelSeconds) { return "∞"; } - if (seconds < 60) + if (value <= 0) { return "< 1m"; } - TimeSpan time; - try + var time = TimeSpan.FromSeconds(value); + if (time.TotalMinutes < 1) { - time = TimeSpan.FromSeconds(seconds.Value); - } - catch - { - return "∞"; + return "< 1m"; } + var sb = new StringBuilder(); if (prefix is not null) { @@ -83,6 +83,7 @@ namespace Lantean.QBTMud.Helpers return sb.ToString(); } + /// /// Formats a file size in bytes into an appropriate unit based on the size. /// @@ -418,4 +419,4 @@ namespace Lantean.QBTMud.Helpers }; } } -} \ No newline at end of file +} diff --git a/Lantean.QBTMud/Helpers/FilterHelper.cs b/Lantean.QBTMud/Helpers/FilterHelper.cs index 4c9f5e2..c5ab51e 100644 --- a/Lantean.QBTMud/Helpers/FilterHelper.cs +++ b/Lantean.QBTMud/Helpers/FilterHelper.cs @@ -119,34 +119,34 @@ namespace Lantean.QBTMud.Helpers switch (category) { case CATEGORY_ALL: - break; + return true; case CATEGORY_UNCATEGORIZED: if (!string.IsNullOrEmpty(torrent.Category)) { return false; } - break; + return true; default: + if (string.IsNullOrEmpty(torrent.Category)) + { + return false; + } + if (!useSubcategories) { - if (torrent.Category != category) - { - return false; - } - else - { - if (!torrent.Category.StartsWith(category)) - { - return false; - } - } + return string.Equals(torrent.Category, category, StringComparison.Ordinal); } - 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) @@ -284,4 +284,4 @@ namespace Lantean.QBTMud.Helpers }; } } -} \ No newline at end of file +} diff --git a/Lantean.QBTMud/Services/DataManager.cs b/Lantean.QBTMud/Services/DataManager.cs index 7a5e8aa..3552331 100644 --- a/Lantean.QBTMud/Services/DataManager.cs +++ b/Lantean.QBTMud/Services/DataManager.cs @@ -39,12 +39,19 @@ namespace Lantean.QBTMud.Services } } - var tags = new List(mainData.Tags?.Count ?? 0); + var tags = new List(); if (mainData.Tags is not null) { + var seenTags = new HashSet(StringComparer.Ordinal); 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) { - torrentList.Tags.Remove(tag); - torrentList.TagState.Remove(tag); + var normalizedTag = NormalizeTag(tag); + if (string.IsNullOrEmpty(normalizedTag)) + { + continue; + } + + torrentList.Tags.Remove(normalizedTag); + torrentList.TagState.Remove(normalizedTag); } } @@ -200,7 +213,19 @@ namespace Lantean.QBTMud.Services { foreach (var tag in mainData.Tags) { - torrentList.Tags.Add(tag); + var normalizedTag = NormalizeTag(tag); + if (string.IsNullOrEmpty(normalizedTag)) + { + continue; + } + + torrentList.Tags.Add(normalizedTag); + if (!torrentList.TagState.ContainsKey(normalizedTag)) + { + torrentList.TagState[normalizedTag] = torrentList.Torrents.Values + .Where(t => FilterHelper.FilterTag(t, normalizedTag)) + .ToHashesHashSet(); + } } } @@ -508,6 +533,12 @@ namespace Lantean.QBTMud.Services public Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent) { + var normalizedTags = torrent.Tags? + .Select(NormalizeTag) + .Where(static tag => !string.IsNullOrEmpty(tag)) + .ToList() + ?? new List(); + return new Torrent( hash, torrent.AddedOn.GetValueOrDefault(), @@ -548,7 +579,7 @@ namespace Lantean.QBTMud.Services torrent.Size.GetValueOrDefault(), torrent.State!, torrent.SuperSeeding.GetValueOrDefault(), - torrent.Tags!, + normalizedTags, torrent.TimeActive.GetValueOrDefault(), torrent.TotalSize.GetValueOrDefault(), torrent.Tracker!, @@ -561,6 +592,19 @@ namespace Lantean.QBTMud.Services 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) { existingCategory.SavePath = category.SavePath ?? existingCategory.SavePath; @@ -609,7 +653,14 @@ namespace Lantean.QBTMud.Services if (torrent.Tags is not null) { 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.TotalSize = torrent.TotalSize ?? existingTorrent.TotalSize; @@ -1175,4 +1226,4 @@ namespace Lantean.QBTMud.Services return new RssList(feeds, articles); } } -} \ No newline at end of file +} From 7370d73c595dbc88b669a131e0c46b18f36c21d2 Mon Sep 17 00:00:00 2001 From: ahjephson <16685186+ahjephson@users.noreply.github.com> Date: Sat, 18 Oct 2025 16:01:53 +0100 Subject: [PATCH 2/2] Fix minor display issues --- .../Components/Options/DownloadsOptions.razor | 2 +- Lantean.QBTMud/Helpers/DisplayHelpers.cs | 4 ++-- Lantean.QBTMud/Helpers/FilterHelper.cs | 4 ++-- Lantean.QBTMud/Services/DataManager.cs | 13 ++++++------- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Lantean.QBTMud/Components/Options/DownloadsOptions.razor b/Lantean.QBTMud/Components/Options/DownloadsOptions.razor index 52ff348..044d18c 100644 --- a/Lantean.QBTMud/Components/Options/DownloadsOptions.razor +++ b/Lantean.QBTMud/Components/Options/DownloadsOptions.razor @@ -62,7 +62,7 @@ - + Manual Automatic diff --git a/Lantean.QBTMud/Helpers/DisplayHelpers.cs b/Lantean.QBTMud/Helpers/DisplayHelpers.cs index 19d1753..dae2f82 100644 --- a/Lantean.QBTMud/Helpers/DisplayHelpers.cs +++ b/Lantean.QBTMud/Helpers/DisplayHelpers.cs @@ -25,7 +25,7 @@ namespace Lantean.QBTMud.Helpers const long InfiniteEtaSentinelSeconds = 8_640_000; // ~100 days, used by qBittorrent for "infinite" ETA. var value = seconds.Value; - if (value >= long.MaxValue || value >= (long)TimeSpan.MaxValue.TotalSeconds || value == InfiniteEtaSentinelSeconds) + if (value >= long.MaxValue || value >= TimeSpan.MaxValue.TotalSeconds || value == InfiniteEtaSentinelSeconds) { return "∞"; } @@ -419,4 +419,4 @@ namespace Lantean.QBTMud.Helpers }; } } -} +} \ No newline at end of file diff --git a/Lantean.QBTMud/Helpers/FilterHelper.cs b/Lantean.QBTMud/Helpers/FilterHelper.cs index c5ab51e..993d92e 100644 --- a/Lantean.QBTMud/Helpers/FilterHelper.cs +++ b/Lantean.QBTMud/Helpers/FilterHelper.cs @@ -207,7 +207,7 @@ namespace Lantean.QBTMud.Helpers break; case Status.Paused: - if (!state.Contains("paused") || !state.Contains("stopped")) + if (!state.Contains("paused") && !state.Contains("stopped")) { return false; } @@ -284,4 +284,4 @@ namespace Lantean.QBTMud.Helpers }; } } -} +} \ No newline at end of file diff --git a/Lantean.QBTMud/Services/DataManager.cs b/Lantean.QBTMud/Services/DataManager.cs index 3552331..5c3a9ee 100644 --- a/Lantean.QBTMud/Services/DataManager.cs +++ b/Lantean.QBTMud/Services/DataManager.cs @@ -220,12 +220,11 @@ namespace Lantean.QBTMud.Services } torrentList.Tags.Add(normalizedTag); - if (!torrentList.TagState.ContainsKey(normalizedTag)) - { - torrentList.TagState[normalizedTag] = torrentList.Torrents.Values - .Where(t => FilterHelper.FilterTag(t, normalizedTag)) - .ToHashesHashSet(); - } + var matchingHashes = torrentList.Torrents + .Where(pair => FilterHelper.FilterTag(pair.Value, normalizedTag)) + .Select(pair => pair.Key) + .ToHashSet(); + torrentList.TagState[normalizedTag] = matchingHashes; } } @@ -1226,4 +1225,4 @@ namespace Lantean.QBTMud.Services return new RssList(feeds, articles); } } -} +} \ No newline at end of file