diff --git a/Lantean.QBTMud.Test/Lantean.QBTMud.Test.csproj b/Lantean.QBTMud.Test/Lantean.QBTMud.Test.csproj index d7c594f..b21e876 100644 --- a/Lantean.QBTMud.Test/Lantean.QBTMud.Test.csproj +++ b/Lantean.QBTMud.Test/Lantean.QBTMud.Test.csproj @@ -4,24 +4,20 @@ net9.0 enable enable - + true false true - - - - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Lantean.QBTMud.Test/UnitTest1.cs b/Lantean.QBTMud.Test/UnitTest1.cs index 29d5ef2..06de721 100644 --- a/Lantean.QBTMud.Test/UnitTest1.cs +++ b/Lantean.QBTMud.Test/UnitTest1.cs @@ -21,7 +21,7 @@ namespace Lantean.QBTMud.Test Test2(a => a.Name); } - private void Test2(Expression> expr) + private void Test2(Expression> expr) { var body = expr.Body; } @@ -38,7 +38,7 @@ namespace Lantean.QBTMud.Test var l = Expression.Lambda>(convertExpression, expression); - Expression> expr2 = a => a.Name; + Expression> expr2 = a => a.Name; var x = l.Compile(); var res = (long)x(new TestClass { Name = "Name", Value = 12 }); @@ -58,9 +58,9 @@ namespace Lantean.QBTMud.Test public class TestClass { - public string Name { get; set; } + public string? Name { get; set; } - public string Description { get; set; } + public string? Description { get; set; } public long Value { get; set; } } diff --git a/Lantean.QBTMud/Components/TorrentActions.razor.cs b/Lantean.QBTMud/Components/TorrentActions.razor.cs index 8556340..e0d24a5 100644 --- a/Lantean.QBTMud/Components/TorrentActions.razor.cs +++ b/Lantean.QBTMud/Components/TorrentActions.razor.cs @@ -12,10 +12,7 @@ namespace Lantean.QBTMud.Components { public partial class TorrentActions : IAsyncDisposable { - private const int _defaultVersion = 5; - private bool _disposedValue; - private int? _version; private List? _actions; @@ -74,30 +71,7 @@ namespace Lantean.QBTMud.Components protected bool OverlayVisible { get; set; } - protected int MajorVersion - { - get - { - if (_version is not null) - { - return _version.Value; - } - - if (string.IsNullOrEmpty(Version)) - { - return _defaultVersion; - } - - if (!System.Version.TryParse(Version.Replace("v", ""), out var version)) - { - return _defaultVersion; - } - - _version = version.Major; - - return _version.Value; - } - } + protected int MajorVersion => VersionHelper.GetMajorVersion(Version); protected override void OnInitialized() { diff --git a/Lantean.QBTMud/Helpers/DisplayHelpers.cs b/Lantean.QBTMud/Helpers/DisplayHelpers.cs index f2c6fa8..c9c68fd 100644 --- a/Lantean.QBTMud/Helpers/DisplayHelpers.cs +++ b/Lantean.QBTMud/Helpers/DisplayHelpers.cs @@ -129,7 +129,7 @@ namespace Lantean.QBTMud.Helpers return ""; } - return Size(size); + return Size(size, prefix, suffix); } /// diff --git a/Lantean.QBTMud/Helpers/VersionHelper.cs b/Lantean.QBTMud/Helpers/VersionHelper.cs new file mode 100644 index 0000000..e4a1730 --- /dev/null +++ b/Lantean.QBTMud/Helpers/VersionHelper.cs @@ -0,0 +1,34 @@ + +namespace Lantean.QBTMud.Helpers +{ + internal static class VersionHelper + { + private static int? _version; + + private const int _defaultVersion = 5; + + public static int DefaultVersion => _defaultVersion; + + public static int GetMajorVersion(string? version) + { + if (_version is not null) + { + return _version.Value; + } + + if (string.IsNullOrEmpty(version)) + { + return _defaultVersion; + } + + if (!Version.TryParse(version?.Replace("v", ""), out var theVersion)) + { + return _defaultVersion; + } + + _version = theVersion.Major; + + return _version.Value; + } + } +} diff --git a/Lantean.QBTMud/Lantean.QBTMud.csproj b/Lantean.QBTMud/Lantean.QBTMud.csproj index 31e46f3..4ea246c 100644 --- a/Lantean.QBTMud/Lantean.QBTMud.csproj +++ b/Lantean.QBTMud/Lantean.QBTMud.csproj @@ -4,21 +4,19 @@ net9.0 enable enable - true - false - 12 + true + false + 12 - - - - - - - - - + + + + + + + diff --git a/Lantean.QBTMud/Layout/LoggedInLayout.razor.cs b/Lantean.QBTMud/Layout/LoggedInLayout.razor.cs index cad0081..b340abe 100644 --- a/Lantean.QBTMud/Layout/LoggedInLayout.razor.cs +++ b/Lantean.QBTMud/Layout/LoggedInLayout.razor.cs @@ -83,7 +83,7 @@ namespace Lantean.QBTMud.Layout Preferences = await ApiClient.GetApplicationPreferences(); Version = await ApiClient.GetApplicationVersion(); var data = await ApiClient.GetMainData(_requestId); - MainData = DataManager.CreateMainData(data); + MainData = DataManager.CreateMainData(data, Version); _requestId = data.ResponseId; _refreshInterval = MainData.ServerState.RefreshInterval; @@ -128,7 +128,7 @@ namespace Lantean.QBTMud.Layout if (MainData is null || data.FullUpdate) { - MainData = DataManager.CreateMainData(data); + MainData = DataManager.CreateMainData(data, Version); } else { diff --git a/Lantean.QBTMud/Layout/MainLayout.razor.cs b/Lantean.QBTMud/Layout/MainLayout.razor.cs index a080a43..70e17fd 100644 --- a/Lantean.QBTMud/Layout/MainLayout.razor.cs +++ b/Lantean.QBTMud/Layout/MainLayout.razor.cs @@ -13,9 +13,6 @@ namespace Lantean.QBTMud.Layout private bool _disposedValue; - [Inject] - protected NavigationManager NavigationManager { get; set; } = default!; - [Inject] private IBrowserViewportService BrowserViewportService { get; set; } = default!; @@ -78,13 +75,13 @@ namespace Lantean.QBTMud.Layout { IsDarkMode = isDarkMode.Value; } - await MudThemeProvider.WatchSystemPreference(OnSystemPreferenceChanged); + await MudThemeProvider.WatchSystemDarkModeAsync(OnSystemDarkModeChanged); await BrowserViewportService.SubscribeAsync(this, fireImmediately: true); await InvokeAsync(StateHasChanged); } } - protected Task OnSystemPreferenceChanged(bool value) + protected Task OnSystemDarkModeChanged(bool value) { IsDarkMode = value; return Task.CompletedTask; diff --git a/Lantean.QBTMud/Models/MainData.cs b/Lantean.QBTMud/Models/MainData.cs index 444b1da..91e11af 100644 --- a/Lantean.QBTMud/Models/MainData.cs +++ b/Lantean.QBTMud/Models/MainData.cs @@ -11,7 +11,8 @@ Dictionary> tagState, Dictionary> categoriesState, Dictionary> statusState, - Dictionary> trackersState) + Dictionary> trackersState, + int majorVersion) { Torrents = torrents.ToDictionary(); Tags = tags.ToHashSet(); @@ -22,6 +23,7 @@ CategoriesState = categoriesState; StatusState = statusState; TrackersState = trackersState; + MajorVersion = majorVersion; } public Dictionary Torrents { get; } @@ -36,5 +38,6 @@ public Dictionary> TrackersState { get; } public string? SelectedTorrentHash { get; set; } public bool LostConnection { get; set; } + public int MajorVersion { get; } } } \ No newline at end of file diff --git a/Lantean.QBTMud/Models/Status.cs b/Lantean.QBTMud/Models/Status.cs index 55e66e4..9be29be 100644 --- a/Lantean.QBTMud/Models/Status.cs +++ b/Lantean.QBTMud/Models/Status.cs @@ -8,6 +8,7 @@ Completed, Resumed, Paused, + Stopped, Active, Inactive, Stalled, @@ -15,6 +16,6 @@ StalledDownloading, Checking, Errored, - Stopped + } } \ No newline at end of file diff --git a/Lantean.QBTMud/Services/DataManager.cs b/Lantean.QBTMud/Services/DataManager.cs index 0401e22..7a5e8aa 100644 --- a/Lantean.QBTMud/Services/DataManager.cs +++ b/Lantean.QBTMud/Services/DataManager.cs @@ -5,7 +5,7 @@ namespace Lantean.QBTMud.Services { public class DataManager : IDataManager { - private static readonly Status[] _statuses = Enum.GetValues(); + private static Status[]? _statusArray = null; public PeerList CreatePeerList(QBitTorrentClient.Models.TorrentPeers torrentPeers) { @@ -25,8 +25,9 @@ namespace Lantean.QBTMud.Services return peerList; } - public MainData CreateMainData(QBitTorrentClient.Models.MainData mainData) + public MainData CreateMainData(QBitTorrentClient.Models.MainData mainData, string version) { + var majorVersion = VersionHelper.GetMajorVersion(version); var torrents = new Dictionary(mainData.Torrents?.Count ?? 0); if (mainData.Torrents is not null) { @@ -87,8 +88,9 @@ namespace Lantean.QBTMud.Services categoriesState.Add(category, torrents.Values.Where(t => FilterHelper.FilterCategory(t, category, serverState.UseSubcategories)).ToHashesHashSet()); } - var statusState = new Dictionary>(_statuses.Length + 2); - foreach (var status in _statuses) + var statuses = GetStatuses(majorVersion).ToArray(); + var statusState = new Dictionary>(statuses.Length + 2); + foreach (var status in statuses) { statusState.Add(status.ToString(), torrents.Values.Where(t => FilterHelper.FilterStatus(t, status)).ToHashesHashSet()); } @@ -101,7 +103,7 @@ namespace Lantean.QBTMud.Services trackersState.Add(tracker, torrents.Values.Where(t => FilterHelper.FilterTracker(t, tracker)).ToHashesHashSet()); } - var torrentList = new MainData(torrents, tags, categories, trackers, serverState, tagState, categoriesState, statusState, trackersState); + var torrentList = new MainData(torrents, tags, categories, trackers, serverState, tagState, categoriesState, statusState, trackersState, majorVersion); return torrentList; } @@ -206,7 +208,7 @@ namespace Lantean.QBTMud.Services { foreach (var (url, hashes) in mainData.Trackers) { - if (!torrentList.Trackers.TryGetValue(url, out var existingHashes)) + if (!torrentList.Trackers.TryGetValue(url, out _)) { torrentList.Trackers.Add(url, hashes); } @@ -225,7 +227,7 @@ namespace Lantean.QBTMud.Services { var newTorrent = CreateTorrent(hash, torrent); torrentList.Torrents.Add(hash, newTorrent); - AddTorrentToStates(torrentList, hash); + AddTorrentToStates(torrentList, hash, torrentList.MajorVersion); } else { @@ -241,7 +243,7 @@ namespace Lantean.QBTMud.Services } } - private static void AddTorrentToStates(MainData torrentList, string hash) + private static void AddTorrentToStates(MainData torrentList, string hash, int version) { var torrent = torrentList.Torrents[hash]; @@ -271,7 +273,7 @@ namespace Lantean.QBTMud.Services value.AddIfTrue(hash, FilterHelper.FilterCategory(torrent, category, torrentList.ServerState.UseSubcategories)); } - foreach (var status in _statuses) + foreach (var status in GetStatuses(version)) { torrentList.StatusState[status.ToString()].AddIfTrue(hash, FilterHelper.FilterStatus(torrent, status)); } @@ -289,6 +291,25 @@ namespace Lantean.QBTMud.Services } } + private static Status[] GetStatuses(int version) + { + if (_statusArray is not null) + { + return _statusArray; + } + + if (version == 5) + { + _statusArray = Enum.GetValues().Where(s => s != Status.Paused).ToArray(); + } + else + { + _statusArray = Enum.GetValues().Where(s => s != Status.Stopped).ToArray(); + } + + return _statusArray; + } + private static void UpdateTorrentStates(MainData torrentList, string hash) { var torrent = torrentList.Torrents[hash]; @@ -317,7 +338,7 @@ namespace Lantean.QBTMud.Services value.AddIfTrueOrRemove(hash, FilterHelper.FilterCategory(torrent, category, torrentList.ServerState.UseSubcategories)); } - foreach (var status in _statuses) + foreach (var status in GetStatuses(torrentList.MajorVersion)) { torrentList.StatusState[status.ToString()].AddIfTrueOrRemove(hash, FilterHelper.FilterStatus(torrent, status)); } @@ -361,7 +382,7 @@ namespace Lantean.QBTMud.Services categoryState.RemoveIfTrue(hash, FilterHelper.FilterCategory(torrent, category, torrentList.ServerState.UseSubcategories)); } - foreach (var status in _statuses) + foreach (var status in GetStatuses(torrentList.MajorVersion)) { if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusState)) { diff --git a/Lantean.QBTMud/Services/IDataManager.cs b/Lantean.QBTMud/Services/IDataManager.cs index a54d0ca..0463ec6 100644 --- a/Lantean.QBTMud/Services/IDataManager.cs +++ b/Lantean.QBTMud/Services/IDataManager.cs @@ -4,7 +4,7 @@ namespace Lantean.QBTMud.Services { public interface IDataManager { - MainData CreateMainData(QBitTorrentClient.Models.MainData mainData); + MainData CreateMainData(QBitTorrentClient.Models.MainData mainData, string version); Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent); diff --git a/Lantean.QBTMud/wwwroot/index.html b/Lantean.QBTMud/wwwroot/index.html index b52d595..148a5a1 100644 --- a/Lantean.QBTMud/wwwroot/index.html +++ b/Lantean.QBTMud/wwwroot/index.html @@ -9,12 +9,12 @@ - - + + - - - + + + @@ -31,10 +31,10 @@ Reload 🗙 - - - - + + + + diff --git a/Lantean.QBitTorrentClient/Lantean.QBitTorrentClient.csproj b/Lantean.QBitTorrentClient/Lantean.QBitTorrentClient.csproj index 696ae40..45cfe6a 100644 --- a/Lantean.QBitTorrentClient/Lantean.QBitTorrentClient.csproj +++ b/Lantean.QBitTorrentClient/Lantean.QBitTorrentClient.csproj @@ -4,7 +4,7 @@ net9.0 enable enable - true + true diff --git a/readme.md b/readme.md index 5519e0e..ef09366 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,11 @@ qbtmud replicates all core features of the qBittorrent WebUI, including: - **Bandwidth Scheduler** – Schedule bandwidth limits. - **WebUI Access** – Remotely manage torrents through the WebUI. +![image](https://github.com/user-attachments/assets/c4e383fd-bff0-4367-b6de-79e19a632f11) +![image](https://github.com/user-attachments/assets/4ff56ed6-cc11-42cd-a070-23f086fd8821) +![image](https://github.com/user-attachments/assets/e321c5a2-ccf1-4205-828d-7ed7adade7dd) + + For a detailed explanation of these features, refer to the [qBittorrent Options Guide](https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent). ---