mirror of
				https://github.com/lantean-code/qbtmud.git
				synced 2025-10-31 12:03:42 +00:00 
			
		
		
		
	Compare commits
	
		
			19 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 4f9129fd46 | ||
|  | 9a9d2c2ee2 | ||
|  | 736bc46745 | ||
|  | 23ae19c4c7 | ||
|  | 603470eb30 | ||
|  | 27c2406340 | ||
|  | 4578dcc11f | ||
|  | 3215fa3936 | ||
|  | 78e62f31d0 | ||
|  | e23842fcb0 | ||
|  | 411c7f87cc | ||
|  | 4098f8f5a9 | ||
|  | 12f81c5978 | ||
|  | 717738d720 | ||
|  | 885c34c8cf | ||
|  | ef3c68a6aa | ||
|  | a29e64fc1b | ||
|  | e55955c75e | ||
|  | c54f73a517 | 
| @@ -4,24 +4,20 @@ | |||||||
|     <TargetFramework>net9.0</TargetFramework> |     <TargetFramework>net9.0</TargetFramework> | ||||||
|     <ImplicitUsings>enable</ImplicitUsings> |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
|  |     <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||||
|     <IsPackable>false</IsPackable> |     <IsPackable>false</IsPackable> | ||||||
|     <IsTestProject>true</IsTestProject> |     <IsTestProject>true</IsTestProject> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
| 	<PackageReference Include="FluentAssertions" Version="7.1.0" AllowedVersions="[5.0.0,7.*.*)" /> |     <PackageReference Include="AwesomeAssertions" Version="9.0.0" /> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" /> |     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" /> | ||||||
|     <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" /> |  | ||||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" /> |  | ||||||
|     <PackageReference Include="MudBlazor" Version="8.2.0" /> |  | ||||||
|     <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" /> |     <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" /> | ||||||
|     <PackageReference Include="xunit" Version="2.9.3" /> |     <PackageReference Include="xunit" Version="2.9.3" /> | ||||||
|     <PackageReference Include="xunit.runner.visualstudio" Version="3.0.1"> |     <PackageReference Include="xunit.runner.visualstudio" Version="3.1.0"> | ||||||
|       <PrivateAssets>all</PrivateAssets> |       <PrivateAssets>all</PrivateAssets> | ||||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||||
|     </PackageReference> |     </PackageReference> | ||||||
| 	  <PackageReference Include="System.Net.Http" Version="4.3.4" /> |  | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ namespace Lantean.QBTMud.Test | |||||||
|             Test2(a => a.Name); |             Test2(a => a.Name); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void Test2(Expression<Func<TestClass, object>> expr) |         private void Test2(Expression<Func<TestClass, object?>> expr) | ||||||
|         { |         { | ||||||
|             var body = expr.Body; |             var body = expr.Body; | ||||||
|         } |         } | ||||||
| @@ -38,7 +38,7 @@ namespace Lantean.QBTMud.Test | |||||||
|  |  | ||||||
|             var l = Expression.Lambda<Func<TestClass, object>>(convertExpression, expression); |             var l = Expression.Lambda<Func<TestClass, object>>(convertExpression, expression); | ||||||
|  |  | ||||||
|             Expression<Func<TestClass, object>> expr2 = a => a.Name; |             Expression<Func<TestClass, object?>> expr2 = a => a.Name; | ||||||
|  |  | ||||||
|             var x = l.Compile(); |             var x = l.Compile(); | ||||||
|             var res = (long)x(new TestClass { Name = "Name", Value = 12 }); |             var res = (long)x(new TestClass { Name = "Name", Value = 12 }); | ||||||
| @@ -58,9 +58,9 @@ namespace Lantean.QBTMud.Test | |||||||
|  |  | ||||||
|     public class TestClass |     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; } |         public long Value { get; set; } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|     <MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem> |     <MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem> | ||||||
| </ContextMenu> | </ContextMenu> | ||||||
|  |  | ||||||
|  | <div style="overflow-x: auto; white-space: nowrap; width: 100%;"> | ||||||
| <MudToolBar Gutters="false" Dense="true"> | <MudToolBar Gutters="false" Dense="true"> | ||||||
|     <MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" /> |     <MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" /> | ||||||
|     <MudDivider Vertical="true" /> |     <MudDivider Vertical="true" /> | ||||||
| @@ -22,6 +23,7 @@ | |||||||
|     <MudSpacer /> |     <MudSpacer /> | ||||||
|     <MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField> |     <MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField> | ||||||
| </MudToolBar> | </MudToolBar> | ||||||
|  | </div> | ||||||
|  |  | ||||||
| <DynamicTable | <DynamicTable | ||||||
|     @ref="Table" |     @ref="Table" | ||||||
|   | |||||||
| @@ -12,10 +12,7 @@ namespace Lantean.QBTMud.Components | |||||||
| { | { | ||||||
|     public partial class TorrentActions : IAsyncDisposable |     public partial class TorrentActions : IAsyncDisposable | ||||||
|     { |     { | ||||||
|         private const int _defaultVersion = 5; |  | ||||||
|  |  | ||||||
|         private bool _disposedValue; |         private bool _disposedValue; | ||||||
|         private int? _version; |  | ||||||
|  |  | ||||||
|         private List<UIAction>? _actions; |         private List<UIAction>? _actions; | ||||||
|  |  | ||||||
| @@ -74,30 +71,7 @@ namespace Lantean.QBTMud.Components | |||||||
|  |  | ||||||
|         protected bool OverlayVisible { get; set; } |         protected bool OverlayVisible { get; set; } | ||||||
|  |  | ||||||
|         protected int MajorVersion |         protected int MajorVersion => VersionHelper.GetMajorVersion(Version); | ||||||
|         { |  | ||||||
|             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 override void OnInitialized() |         protected override void OnInitialized() | ||||||
|         { |         { | ||||||
| @@ -441,7 +415,7 @@ namespace Lantean.QBTMud.Components | |||||||
|                     thereAreFirstLastPiecePrio = true; |                     thereAreFirstLastPiecePrio = true; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (torrent.Progress > 0.999999) // not downloaded |                 if (torrent.Progress < 0.999999) // not downloaded | ||||||
|                 { |                 { | ||||||
|                     allAreDownloaded = false; |                     allAreDownloaded = false; | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ namespace Lantean.QBTMud.Helpers | |||||||
|             { |             { | ||||||
|                 time = TimeSpan.FromSeconds(seconds.Value); |                 time = TimeSpan.FromSeconds(seconds.Value); | ||||||
|             } |             } | ||||||
|             catch (OverflowException) |             catch | ||||||
|             { |             { | ||||||
|                 return "∞"; |                 return "∞"; | ||||||
|             } |             } | ||||||
| @@ -129,7 +129,7 @@ namespace Lantean.QBTMud.Helpers | |||||||
|                 return ""; |                 return ""; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return Size(size); |             return Size(size, prefix, suffix); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								Lantean.QBTMud/Helpers/VersionHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Lantean.QBTMud/Helpers/VersionHelper.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -4,21 +4,19 @@ | |||||||
|     <TargetFramework>net9.0</TargetFramework> |     <TargetFramework>net9.0</TargetFramework> | ||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
|     <ImplicitUsings>enable</ImplicitUsings> |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
| 	<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | 	  <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||||
| 	<CompressionEnabled>false</CompressionEnabled> | 	  <CompressionEnabled>false</CompressionEnabled> | ||||||
| 	<LangVersion>12</LangVersion> | 	  <LangVersion>12</LangVersion> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
| 	<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" /> | 	  <PackageReference Include="Blazored.LocalStorage" Version="4.5.0" /> | ||||||
| 	<PackageReference Include="ByteSize" Version="2.1.2" /> | 	  <PackageReference Include="ByteSize" Version="2.1.2" /> | ||||||
| 	<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" /> | 	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" /> | ||||||
| 	<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.1" PrivateAssets="all" /> | 	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" PrivateAssets="all" /> | ||||||
| 	<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" /> | 	  <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" /> | ||||||
| 	<PackageReference Include="MudBlazor" Version="8.2.0" /> | 	  <PackageReference Include="MudBlazor" Version="8.7.0" /> | ||||||
| 	<PackageReference Include="MudBlazor.ThemeManager" Version="3.0.0" /> | 	  <PackageReference Include="MudBlazor.ThemeManager" Version="3.0.0" /> | ||||||
|     <!-- added to fix vuln in dependency --> |  | ||||||
| 	<PackageReference Include="System.Text.Json" Version="9.0.1" /> |  | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|   | |||||||
| @@ -36,23 +36,23 @@ | |||||||
|                 </CascadingValue> |                 </CascadingValue> | ||||||
|             </CascadingValue> |             </CascadingValue> | ||||||
|         </CascadingValue> |         </CascadingValue> | ||||||
|         <MudAppBar Bottom="true" Fixed="true" Elevation="0" Dense="true" Style="background-color: var(--mud-palette-dark-lighten);"> |         <MudAppBar Bottom="true" Fixed="true" Elevation="0" Dense="true" Style="background-color: var(--mud-palette-dark-lighten); z-index: 900"> | ||||||
|             @if (MainData?.LostConnection == true) |             @if (MainData?.LostConnection == true) | ||||||
|             { |             { | ||||||
|                 <MudText Class="mx-2 mb-1" Color="Color.Error">qBittorrent client is not reachable</MudText> |                 <MudText Class="mx-2 mb-1 d-none d-sm-flex" Color="Color.Error">qBittorrent client is not reachable</MudText> | ||||||
|             } |             } | ||||||
|             <MudSpacer /> |             <MudSpacer /> | ||||||
|             <MudText Class="mx-2 mb-1">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText> |             <MudText Class="mx-2 mb-1 d-none d-sm-flex">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText> | ||||||
|             <MudDivider Vertical="true" /> |             <MudDivider Vertical="true" Class="d-none d-sm-flex" /> | ||||||
|             <MudText Class="mx-2 mb-1">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText> |             <MudText Class="mx-2 mb-1 d-none d-sm-flex">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText> | ||||||
|             <MudDivider Vertical="true" /> |             <MudDivider Vertical="true" Class="d-none d-sm-flex" /> | ||||||
|             @{ |             @{ | ||||||
|                 var (icon, colour) = GetConnectionIcon(MainData?.ServerState.ConnectionStatus); |                 var (icon, colour) = GetConnectionIcon(MainData?.ServerState.ConnectionStatus); | ||||||
|             } |             } | ||||||
|             <MudIcon Class="mx-1 mb-1" Icon="@icon" Color="@colour" Title="MainData?.ServerState.ConnectionStatus" /> |             <MudIcon Class="mx-1 mb-1" Icon="@icon" Color="@colour" Title="MainData?.ServerState.ConnectionStatus" /> | ||||||
|             <MudDivider Vertical="true" /> |             <MudDivider Vertical="true" Class="" /> | ||||||
|             <MudIcon Class="mx-1 mb-1" Icon="@Icons.Material.Outlined.Speed" Color="@((MainData?.ServerState.UseAltSpeedLimits ?? false) ? Color.Error : Color.Success)" /> |             <MudIcon Class="mx-1 mb-1" Icon="@Icons.Material.Outlined.Speed" Color="@((MainData?.ServerState.UseAltSpeedLimits ?? false) ? Color.Error : Color.Success)" /> | ||||||
|             <MudDivider Vertical="true" /> |             <MudDivider Vertical="true" Class="" /> | ||||||
|             <MudIcon Class="ml-1 mb-1" Icon="@Icons.Material.Filled.KeyboardDoubleArrowDown" Color="Color.Success" /> |             <MudIcon Class="ml-1 mb-1" Icon="@Icons.Material.Filled.KeyboardDoubleArrowDown" Color="Color.Success" /> | ||||||
|             <MudText Class="mr-1 mb-1"> |             <MudText Class="mr-1 mb-1"> | ||||||
|                 @DisplayHelpers.Size(MainData?.ServerState.DownloadInfoSpeed, null, "/s") |                 @DisplayHelpers.Size(MainData?.ServerState.DownloadInfoSpeed, null, "/s") | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ namespace Lantean.QBTMud.Layout | |||||||
|             Preferences = await ApiClient.GetApplicationPreferences(); |             Preferences = await ApiClient.GetApplicationPreferences(); | ||||||
|             Version = await ApiClient.GetApplicationVersion(); |             Version = await ApiClient.GetApplicationVersion(); | ||||||
|             var data = await ApiClient.GetMainData(_requestId); |             var data = await ApiClient.GetMainData(_requestId); | ||||||
|             MainData = DataManager.CreateMainData(data); |             MainData = DataManager.CreateMainData(data, Version); | ||||||
|  |  | ||||||
|             _requestId = data.ResponseId; |             _requestId = data.ResponseId; | ||||||
|             _refreshInterval = MainData.ServerState.RefreshInterval; |             _refreshInterval = MainData.ServerState.RefreshInterval; | ||||||
| @@ -128,7 +128,7 @@ namespace Lantean.QBTMud.Layout | |||||||
|  |  | ||||||
|                         if (MainData is null || data.FullUpdate) |                         if (MainData is null || data.FullUpdate) | ||||||
|                         { |                         { | ||||||
|                             MainData = DataManager.CreateMainData(data); |                             MainData = DataManager.CreateMainData(data, Version); | ||||||
|                         } |                         } | ||||||
|                         else |                         else | ||||||
|                         { |                         { | ||||||
|   | |||||||
| @@ -23,16 +23,15 @@ | |||||||
|                 <MudSwitch T="bool" Label="Dark Mode" LabelPlacement="Placement.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" /> |                 <MudSwitch T="bool" Label="Dark Mode" LabelPlacement="Placement.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" /> | ||||||
|                 <Menu @ref="Menu" /> |                 <Menu @ref="Menu" /> | ||||||
|             </MudAppBar> |             </MudAppBar> | ||||||
|             @if (IsDebug) |             <MudDrawer @bind-Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right"> | ||||||
|             { |                 <ErrorDisplay ErrorBoundary="ErrorBoundary" /> | ||||||
|                 <MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right"> |             </MudDrawer> | ||||||
|                     <ErrorDisplay ErrorBoundary="ErrorBoundary" /> |  | ||||||
|                 </MudDrawer> |  | ||||||
|             } |  | ||||||
|             <CascadingValue Value="Theme"> |             <CascadingValue Value="Theme"> | ||||||
|                 <CascadingValue Value="IsDarkMode" Name="IsDarkMode"> |                 <CascadingValue Value="IsDarkMode" Name="IsDarkMode"> | ||||||
|                     <CascadingValue Value="Menu"> |                     <CascadingValue Value="Menu"> | ||||||
|                         @Body |                         <CascadingValue Value="DrawerOpen" Name="DrawerOpen"> | ||||||
|  |                             @Body | ||||||
|  |                         </CascadingValue> | ||||||
|                     </CascadingValue> |                     </CascadingValue> | ||||||
|                 </CascadingValue> |                 </CascadingValue> | ||||||
|             </CascadingValue> |             </CascadingValue> | ||||||
|   | |||||||
| @@ -13,9 +13,6 @@ namespace Lantean.QBTMud.Layout | |||||||
|  |  | ||||||
|         private bool _disposedValue; |         private bool _disposedValue; | ||||||
|  |  | ||||||
|         [Inject] |  | ||||||
|         protected NavigationManager NavigationManager { get; set; } = default!; |  | ||||||
|  |  | ||||||
|         [Inject] |         [Inject] | ||||||
|         private IBrowserViewportService BrowserViewportService { get; set; } = default!; |         private IBrowserViewportService BrowserViewportService { get; set; } = default!; | ||||||
|  |  | ||||||
| @@ -44,12 +41,6 @@ namespace Lantean.QBTMud.Layout | |||||||
|  |  | ||||||
|         protected MudTheme Theme { get; set; } |         protected MudTheme Theme { get; set; } | ||||||
|  |  | ||||||
| #if DEBUG |  | ||||||
|         private bool IsDebug { get; } = true; |  | ||||||
| #else |  | ||||||
|         private bool IsDebug { get; } = false; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|         public MainLayout() |         public MainLayout() | ||||||
|         { |         { | ||||||
|             Theme = new MudTheme(); |             Theme = new MudTheme(); | ||||||
| @@ -84,21 +75,21 @@ namespace Lantean.QBTMud.Layout | |||||||
|                 { |                 { | ||||||
|                     IsDarkMode = isDarkMode.Value; |                     IsDarkMode = isDarkMode.Value; | ||||||
|                 } |                 } | ||||||
|                 await MudThemeProvider.WatchSystemPreference(OnSystemPreferenceChanged); |                 await MudThemeProvider.WatchSystemDarkModeAsync(OnSystemDarkModeChanged); | ||||||
|                 await BrowserViewportService.SubscribeAsync(this, fireImmediately: true); |                 await BrowserViewportService.SubscribeAsync(this, fireImmediately: true); | ||||||
|                 await InvokeAsync(StateHasChanged); |                 await InvokeAsync(StateHasChanged); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected Task OnSystemPreferenceChanged(bool value) |         protected Task OnSystemDarkModeChanged(bool value) | ||||||
|         { |         { | ||||||
|             IsDarkMode = value; |             IsDarkMode = value; | ||||||
|             return Task.CompletedTask; |             return Task.CompletedTask; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs) |         public async Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs) | ||||||
|         { |         { | ||||||
|             if (browserViewportEventArgs.Breakpoint == Breakpoint.Sm && DrawerOpen) |             if (browserViewportEventArgs.Breakpoint <= Breakpoint.Sm) | ||||||
|             { |             { | ||||||
|                 DrawerOpen = false; |                 DrawerOpen = false; | ||||||
|             } |             } | ||||||
| @@ -107,7 +98,17 @@ namespace Lantean.QBTMud.Layout | |||||||
|                 DrawerOpen = true; |                 DrawerOpen = true; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return Task.CompletedTask; |             if (ErrorBoundary?.Errors.Count > 0) | ||||||
|  |             { | ||||||
|  |                 ErrorDrawerOpen = true; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 await Task.Delay(250); | ||||||
|  |                 ErrorDrawerOpen = false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             await InvokeAsync(StateHasChanged); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected void ToggleErrorDrawer() |         protected void ToggleErrorDrawer() | ||||||
|   | |||||||
| @@ -11,7 +11,8 @@ | |||||||
|             Dictionary<string, HashSet<string>> tagState, |             Dictionary<string, HashSet<string>> tagState, | ||||||
|             Dictionary<string, HashSet<string>> categoriesState, |             Dictionary<string, HashSet<string>> categoriesState, | ||||||
|             Dictionary<string, HashSet<string>> statusState, |             Dictionary<string, HashSet<string>> statusState, | ||||||
|             Dictionary<string, HashSet<string>> trackersState) |             Dictionary<string, HashSet<string>> trackersState, | ||||||
|  |             int majorVersion) | ||||||
|         { |         { | ||||||
|             Torrents = torrents.ToDictionary(); |             Torrents = torrents.ToDictionary(); | ||||||
|             Tags = tags.ToHashSet(); |             Tags = tags.ToHashSet(); | ||||||
| @@ -22,6 +23,7 @@ | |||||||
|             CategoriesState = categoriesState; |             CategoriesState = categoriesState; | ||||||
|             StatusState = statusState; |             StatusState = statusState; | ||||||
|             TrackersState = trackersState; |             TrackersState = trackersState; | ||||||
|  |             MajorVersion = majorVersion; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public Dictionary<string, Torrent> Torrents { get; } |         public Dictionary<string, Torrent> Torrents { get; } | ||||||
| @@ -36,5 +38,6 @@ | |||||||
|         public Dictionary<string, HashSet<string>> TrackersState { get; } |         public Dictionary<string, HashSet<string>> TrackersState { get; } | ||||||
|         public string? SelectedTorrentHash { get; set; } |         public string? SelectedTorrentHash { get; set; } | ||||||
|         public bool LostConnection { get; set; } |         public bool LostConnection { get; set; } | ||||||
|  |         public int MajorVersion { get; } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -8,6 +8,7 @@ | |||||||
|         Completed, |         Completed, | ||||||
|         Resumed, |         Resumed, | ||||||
|         Paused, |         Paused, | ||||||
|  |         Stopped, | ||||||
|         Active, |         Active, | ||||||
|         Inactive, |         Inactive, | ||||||
|         Stalled, |         Stalled, | ||||||
| @@ -15,6 +16,6 @@ | |||||||
|         StalledDownloading, |         StalledDownloading, | ||||||
|         Checking, |         Checking, | ||||||
|         Errored, |         Errored, | ||||||
|         Stopped |          | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -12,89 +12,96 @@ | |||||||
|  |  | ||||||
| <MudTabs Elevation="2" ApplyEffectsToContainer="true"> | <MudTabs Elevation="2" ApplyEffectsToContainer="true"> | ||||||
|     <MudTabPanel Text="About"> |     <MudTabPanel Text="About"> | ||||||
|         <div class="d-flex gap-4"> |         <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3"> | ||||||
|             <MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6" Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop" Height="162" Width="94" /> |             <MudGrid Class="mt-0 mb-4"> | ||||||
|             <MudGrid Class="mx-0 mt-0 mb-3"> |                 <MudItem xs="12" sm="3" md="2" lg="2" xl="1" Class="d-flex justify-center"> | ||||||
|                 <MudItem xs="12"> |                     <MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6" | ||||||
|                     <div class="d-flex gap-3"> |                               Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop" | ||||||
|                         <MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None" Alt="QBT" Height="32" Width="32"  /><MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText> |                               Height="162" Width="94" /> | ||||||
|  |                 </MudItem> | ||||||
|  |  | ||||||
|  |                 <MudItem xs="12" sm="9" md="10" lg="10" xl="11"> | ||||||
|  |                     <div class="d-flex flex-column gap-2"> | ||||||
|  |                         <div class="d-flex gap-3 align-items-center"> | ||||||
|  |                             <MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None" | ||||||
|  |                                       Alt="QBT" Height="32" Width="32" /> | ||||||
|  |                             <MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText> | ||||||
|  |                         </div> | ||||||
|  |  | ||||||
|  |                         <MudText Typo="Typo.body1"> | ||||||
|  |                             An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar. | ||||||
|  |                         </MudText> | ||||||
|  |  | ||||||
|  |                         <MudText Typo="Typo.body1">Copyright © 2006-2024 The qBittorrent project</MudText> | ||||||
|  |  | ||||||
|  |                         <div class="d-flex flex-wrap"> | ||||||
|  |                             <MudText Typo="Typo.body1" Class="fw-bold">Home Page: </MudText> | ||||||
|  |                             <MudLink Href="https://www.qbittorrent.org" Target="_blank" Class="ms-2"> | ||||||
|  |                                 qbittorrent.org | ||||||
|  |                             </MudLink> | ||||||
|  |                         </div> | ||||||
|  |  | ||||||
|  |                         <div class="d-flex flex-wrap"> | ||||||
|  |                             <MudText Typo="Typo.body1" Class="fw-bold">Bug Tracker: </MudText> | ||||||
|  |                             <MudLink Href="https://bugs.qbittorrent.org" Target="_blank" Class="ms-2"> | ||||||
|  |                                 bugs.qbittorrent.org | ||||||
|  |                             </MudLink> | ||||||
|  |                         </div> | ||||||
|  |  | ||||||
|  |                         <div class="d-flex flex-wrap"> | ||||||
|  |                             <MudText Typo="Typo.body1" Class="fw-bold">Forum: </MudText> | ||||||
|  |                             <MudLink Href="https://forum.qbittorrent.org" Target="_blank" Class="ms-2"> | ||||||
|  |                                 forum.qbittorrent.org | ||||||
|  |                             </MudLink> | ||||||
|  |                         </div> | ||||||
|                     </div> |                     </div> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|  |  | ||||||
|                 <MudItem xs="12"> |  | ||||||
|                     <MudText Typo="Typo.body1">An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.</MudText> |  | ||||||
|                 </MudItem> |  | ||||||
|  |  | ||||||
|                 <MudItem xs="12"> |  | ||||||
|                     <MudText Typo="Typo.body1">Copyright © 2006-2024 The qBittorrent project</MudText> |  | ||||||
|                 </MudItem> |  | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |  | ||||||
|                     <MudText Typo="Typo.body1">Home Page</MudText> |  | ||||||
|                 </MudItem> |  | ||||||
|                 <MudItem xs="10"> |  | ||||||
|                     <MudLink Href="https://www.qbittorrent.org" Target="https://www.qbittorrent.org">https://www.qbittorrent.org</MudLink> |  | ||||||
|                 </MudItem> |  | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |  | ||||||
|                     <MudText Typo="Typo.body1">Bug Tracker</MudText> |  | ||||||
|                 </MudItem> |  | ||||||
|                 <MudItem xs="10"> |  | ||||||
|                     <MudLink Href="https://bugs.qbittorrent.org" Target="https://bugs.qbittorrent.org">https://bugs.qbittorrent.org</MudLink> |  | ||||||
|                 </MudItem> |  | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |  | ||||||
|                     <MudText Typo="Typo.body1">Forum</MudText> |  | ||||||
|                 </MudItem> |  | ||||||
|                 <MudItem xs="10"> |  | ||||||
|                     <MudLink Href="https://forum.qbittorrent.org" Target="https://forum.qbittorrent.org">https://forum.qbittorrent.org</MudLink> |  | ||||||
|                 </MudItem> |  | ||||||
|             </MudGrid> |             </MudGrid> | ||||||
|         </div> |         </MudContainer> | ||||||
|     </MudTabPanel> |     </MudTabPanel> | ||||||
|     <MudTabPanel Text="Authors"> |     <MudTabPanel Text="Authors"> | ||||||
|         <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3"> |         <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3"> | ||||||
|             <MudText Typo="Typo.body1" Class="py-1">Current maintainer</MudText> |             <MudText Typo="Typo.h5" Class="py-1">Current maintainer</MudText> | ||||||
|  |  | ||||||
|             <MudGrid Class="mt-0 mb-4"> |             <MudGrid Class="mt-0 mb-4"> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudText Typo="Typo.body1">Name</MudText> |                     <MudText Typo="Typo.h6">Name</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="12" md="10"> | ||||||
|                     <MudText Typo="Typo.body1">Sledgehammer999</MudText> |                     <MudText Typo="Typo.body1">Sledgehammer999</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudText Typo="Typo.body1">Nationality</MudText> |                     <MudText Typo="Typo.h6">Nationality</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="12" md="10"> | ||||||
|                     <MudText Typo="Typo.body1">Greece</MudText> |                     <MudText Typo="Typo.body1">Greece</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudText Typo="Typo.body1">E-mail</MudText> |                     <MudText Typo="Typo.h6">E-mail</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="12" md="10"> | ||||||
|                     <MudLink Href="mailto:sledgehammer999@qbittorrent.org">sledgehammer999@qbittorrent.org</MudLink> |                     <MudLink Href="mailto:sledgehammer999@qbittorrent.org">sledgehammer999@qbittorrent.org</MudLink> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|             </MudGrid> |             </MudGrid> | ||||||
|  |  | ||||||
|             <MudText Typo="Typo.body1" Class="py-1">Original author</MudText> |             <MudText Typo="Typo.h5" Class="py-1">Original author</MudText> | ||||||
|             <MudGrid Class="mt-0 mb-4"> |             <MudGrid Class="mt-0 mb-4"> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudText Typo="Typo.body1">Name</MudText> |                     <MudText Typo="Typo.h6">Name</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="12" md="10"> | ||||||
|                     <MudText Typo="Typo.body1">Christophe Dumez</MudText> |                     <MudText Typo="Typo.body1">Christophe Dumez</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudText Typo="Typo.body1">Nationality</MudText> |                     <MudText Typo="Typo.h6">Nationality</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="12" md="10"> | ||||||
|                     <MudText Typo="Typo.body1">France</MudText> |                     <MudText Typo="Typo.body1">France</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudText Typo="Typo.body1">E-mail</MudText> |                     <MudText Typo="Typo.h6">E-mail</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="12" md="10"> | ||||||
|                     <MudLink Href="mailto:chris@qbittorrent.org">chris@qbittorrent.org</MudLink> |                     <MudLink Href="mailto:chris@qbittorrent.org">chris@qbittorrent.org</MudLink> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|             </MudGrid> |             </MudGrid> | ||||||
| @@ -118,7 +125,7 @@ | |||||||
|                 (the list might not be up to date) |                 (the list might not be up to date) | ||||||
|             </MudText> |             </MudText> | ||||||
|             <MudList T="string" ReadOnly> |             <MudList T="string" ReadOnly> | ||||||
|                 <MudListItem Icon="@Icons.Material.Filled.Circle" IconColor="Color.Info"><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</MudListItem> |                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</MudListItem> | ||||||
|                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Armenian:</u> Hrant Ohanyan (hrantohanyan@mail.am)</MudListItem> |                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Armenian:</u> Hrant Ohanyan (hrantohanyan@mail.am)</MudListItem> | ||||||
|                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Basque:</u> Xabier Aramendi (azpidatziak@gmail.com)</MudListItem> |                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Basque:</u> Xabier Aramendi (azpidatziak@gmail.com)</MudListItem> | ||||||
|                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Belarusian:</u> Mihas Varantsou (meequz@gmail.com)</MudListItem> |                 <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Belarusian:</u> Mihas Varantsou (meequz@gmail.com)</MudListItem> | ||||||
| @@ -1058,38 +1065,38 @@ | |||||||
|             <MudText Typo="Typo.body1" Class="py-1">qBittorrent was built with the following libraries:</MudText> |             <MudText Typo="Typo.body1" Class="py-1">qBittorrent was built with the following libraries:</MudText> | ||||||
|  |  | ||||||
|             <MudGrid Class="mt-1 mb-4"> |             <MudGrid Class="mt-1 mb-4"> | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="3"> | ||||||
|                     <MudText Typo="Typo.body1">Qt</MudText> |                     <MudText Typo="Typo.body1">Qt</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="9"> | ||||||
|                     <MudText Typo="Typo.body1">@QtVersion</MudText> |                     <MudText Typo="Typo.body1">@QtVersion</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="3"> | ||||||
|                     <MudText Typo="Typo.body1">Libtorrent</MudText> |                     <MudText Typo="Typo.body1">Libtorrent</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="9"> | ||||||
|                     <MudText Typo="Typo.body1">@LibtorrentVersion</MudText> |                     <MudText Typo="Typo.body1">@LibtorrentVersion</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="3"> | ||||||
|                     <MudText Typo="Typo.body1">Boost</MudText> |                     <MudText Typo="Typo.body1">Boost</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="9"> | ||||||
|                     <MudText Typo="Typo.body1">@BoostVersion</MudText> |                     <MudText Typo="Typo.body1">@BoostVersion</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="3"> | ||||||
|                     <MudText Typo="Typo.body1">OpenSSL</MudText> |                     <MudText Typo="Typo.body1">OpenSSL</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="9"> | ||||||
|                     <MudText Typo="Typo.body1">@OpensslVersion</MudText> |                     <MudText Typo="Typo.body1">@OpensslVersion</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|  |  | ||||||
|                 <MudItem xs="2"> |                 <MudItem xs="3"> | ||||||
|                     <MudText Typo="Typo.body1">zlib</MudText> |                     <MudText Typo="Typo.body1">zlib</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="10"> |                 <MudItem xs="9"> | ||||||
|                     <MudText Typo="Typo.body1">@ZlibVersion</MudText> |                     <MudText Typo="Typo.body1">@ZlibVersion</MudText> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|             </MudGrid> |             </MudGrid> | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| @page "/details/{hash}" | @page "/details/{hash}" | ||||||
| @layout DetailsLayout | @layout DetailsLayout | ||||||
|  |  | ||||||
|  | <div style="overflow-x: auto; white-space: nowrap; width: 100%;"> | ||||||
| <MudToolBar Gutters="false" Dense="true"> | <MudToolBar Gutters="false" Dense="true"> | ||||||
|     @if (!DrawerOpen) |     @if (!DrawerOpen) | ||||||
|     { |     { | ||||||
| @@ -14,6 +15,7 @@ | |||||||
|     <MudDivider Vertical="true" /> |     <MudDivider Vertical="true" /> | ||||||
|     <MudText Class="pl-5 no-wrap">@Name</MudText> |     <MudText Class="pl-5 no-wrap">@Name</MudText> | ||||||
| </MudToolBar> | </MudToolBar> | ||||||
|  | </div> | ||||||
|  |  | ||||||
| @if (ShowTabs) | @if (ShowTabs) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|                 <MudItem xs="12" md="4"> |                 <MudItem xs="12" md="4"> | ||||||
|                     <MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" Variant="Variant.Outlined" /> |                     <MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" Variant="Variant.Outlined" /> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2" md="3"> |                 <MudItem xs="12" md="3"> | ||||||
|                     <MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" Variant="Variant.Outlined"> |                     <MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" Variant="Variant.Outlined"> | ||||||
|                         @foreach (var (value, name) in Categories) |                         @foreach (var (value, name) in Categories) | ||||||
|                         { |                         { | ||||||
| @@ -30,17 +30,21 @@ | |||||||
|                         } |                         } | ||||||
|                     </MudSelect> |                     </MudSelect> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2" md="3"> |                 <MudItem xs="12" md="3"> | ||||||
|                     <MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" Variant="Variant.Outlined"> |                     <MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" Variant="Variant.Outlined"> | ||||||
|                         <MudSelectItem Value="@("all")">All</MudSelectItem> |                         <MudSelectItem Value="@("all")">All</MudSelectItem> | ||||||
|                         <MudDivider /> |                         @if (Plugins.Count > 0) | ||||||
|  |                         { | ||||||
|  |                             <MudDivider /> | ||||||
|  |  | ||||||
|  |                         } | ||||||
|                         @foreach (var (value, name) in Plugins) |                         @foreach (var (value, name) in Plugins) | ||||||
|                         { |                         { | ||||||
|                             <MudSelectItem Value="value">@name</MudSelectItem> |                             <MudSelectItem Value="value">@name</MudSelectItem> | ||||||
|                         } |                         } | ||||||
|                     </MudSelect> |                     </MudSelect> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|                 <MudItem xs="2" md="2"> |                 <MudItem xs="12" md="2"> | ||||||
|                     <MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">@(_searchId is null ? "Search" : "Stop")</MudButton> |                     <MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">@(_searchId is null ? "Search" : "Stop")</MudButton> | ||||||
|                 </MudItem> |                 </MudItem> | ||||||
|              |              | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
|     <TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" /> |     <TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" /> | ||||||
| </ContextMenu> | </ContextMenu> | ||||||
|  |  | ||||||
|  | <div style="overflow-x: auto; white-space: nowrap; width: 100%;"> | ||||||
| <MudToolBar Gutters="false" Dense="true"> | <MudToolBar Gutters="false" Dense="true"> | ||||||
|     <MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" title="Add torrent link" /> |     <MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" title="Add torrent link" /> | ||||||
|     <MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" title="Add torrent file" /> |     <MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" title="Add torrent file" /> | ||||||
| @@ -18,6 +19,7 @@ | |||||||
|     <MudSpacer /> |     <MudSpacer /> | ||||||
|     <MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField> |     <MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField> | ||||||
| </MudToolBar> | </MudToolBar> | ||||||
|  | </div> | ||||||
|  |  | ||||||
| <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0"> | <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0"> | ||||||
|     <DynamicTable |     <DynamicTable | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ namespace Lantean.QBTMud.Services | |||||||
| { | { | ||||||
|     public class DataManager : IDataManager |     public class DataManager : IDataManager | ||||||
|     { |     { | ||||||
|         private static readonly Status[] _statuses = Enum.GetValues<Status>(); |         private static Status[]? _statusArray = null; | ||||||
|  |  | ||||||
|         public PeerList CreatePeerList(QBitTorrentClient.Models.TorrentPeers torrentPeers) |         public PeerList CreatePeerList(QBitTorrentClient.Models.TorrentPeers torrentPeers) | ||||||
|         { |         { | ||||||
| @@ -25,8 +25,9 @@ namespace Lantean.QBTMud.Services | |||||||
|             return peerList; |             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<string, Torrent>(mainData.Torrents?.Count ?? 0); |             var torrents = new Dictionary<string, Torrent>(mainData.Torrents?.Count ?? 0); | ||||||
|             if (mainData.Torrents is not null) |             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()); |                 categoriesState.Add(category, torrents.Values.Where(t => FilterHelper.FilterCategory(t, category, serverState.UseSubcategories)).ToHashesHashSet()); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             var statusState = new Dictionary<string, HashSet<string>>(_statuses.Length + 2); |             var statuses = GetStatuses(majorVersion).ToArray(); | ||||||
|             foreach (var status in _statuses) |             var statusState = new Dictionary<string, HashSet<string>>(statuses.Length + 2); | ||||||
|  |             foreach (var status in statuses) | ||||||
|             { |             { | ||||||
|                 statusState.Add(status.ToString(), torrents.Values.Where(t => FilterHelper.FilterStatus(t, status)).ToHashesHashSet()); |                 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()); |                 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; |             return torrentList; | ||||||
|         } |         } | ||||||
| @@ -206,7 +208,7 @@ namespace Lantean.QBTMud.Services | |||||||
|             { |             { | ||||||
|                 foreach (var (url, hashes) in mainData.Trackers) |                 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); |                         torrentList.Trackers.Add(url, hashes); | ||||||
|                     } |                     } | ||||||
| @@ -225,7 +227,7 @@ namespace Lantean.QBTMud.Services | |||||||
|                     { |                     { | ||||||
|                         var newTorrent = CreateTorrent(hash, torrent); |                         var newTorrent = CreateTorrent(hash, torrent); | ||||||
|                         torrentList.Torrents.Add(hash, newTorrent); |                         torrentList.Torrents.Add(hash, newTorrent); | ||||||
|                         AddTorrentToStates(torrentList, hash); |                         AddTorrentToStates(torrentList, hash, torrentList.MajorVersion); | ||||||
|                     } |                     } | ||||||
|                     else |                     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]; |             var torrent = torrentList.Torrents[hash]; | ||||||
|  |  | ||||||
| @@ -271,7 +273,7 @@ namespace Lantean.QBTMud.Services | |||||||
|                 value.AddIfTrue(hash, FilterHelper.FilterCategory(torrent, category, torrentList.ServerState.UseSubcategories)); |                 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)); |                 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<Status>().Where(s => s != Status.Paused).ToArray(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 _statusArray = Enum.GetValues<Status>().Where(s => s != Status.Stopped).ToArray(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return _statusArray; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private static void UpdateTorrentStates(MainData torrentList, string hash) |         private static void UpdateTorrentStates(MainData torrentList, string hash) | ||||||
|         { |         { | ||||||
|             var torrent = torrentList.Torrents[hash]; |             var torrent = torrentList.Torrents[hash]; | ||||||
| @@ -317,7 +338,7 @@ namespace Lantean.QBTMud.Services | |||||||
|                 value.AddIfTrueOrRemove(hash, FilterHelper.FilterCategory(torrent, category, torrentList.ServerState.UseSubcategories)); |                 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)); |                 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)); |                 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)) |                 if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusState)) | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ namespace Lantean.QBTMud.Services | |||||||
| { | { | ||||||
|     public interface IDataManager |     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); |         Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -155,7 +155,7 @@ code { | |||||||
| } | } | ||||||
|  |  | ||||||
| .torrent-list .mud-table-container { | .torrent-list .mud-table-container { | ||||||
|     height: calc(100vh - 149px); |     height: calc(100vh - 160px); | ||||||
| } | } | ||||||
|  |  | ||||||
| .file-list .mud-table-container { | .file-list .mud-table-container { | ||||||
| @@ -251,4 +251,8 @@ td .folder-button { | |||||||
|     width: 25px; |     width: 25px; | ||||||
|     max-width: 25px; |     max-width: 25px; | ||||||
|     padding: 0 8px !important; |     padding: 0 8px !important; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .mud-popover .mud-divider:last-child { | ||||||
|  |     display: none; | ||||||
| } | } | ||||||
| @@ -9,12 +9,12 @@ | |||||||
|     <link rel="preconnect" href="https://fonts.googleapis.com"> |     <link rel="preconnect" href="https://fonts.googleapis.com"> | ||||||
|     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||||||
|     <link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap" rel="stylesheet"> |     <link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap" rel="stylesheet"> | ||||||
|     <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" /> |     <link href="./_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" /> | ||||||
|     <link rel="stylesheet" href="css/app.css" /> |     <link rel="stylesheet" href="./css/app.css" /> | ||||||
|     <link rel="icon" type="image/png" href="images/qbittorrent32.png" /> |     <link rel="icon" type="image/png" href="images/qbittorrent32.png" /> | ||||||
|     <link rel="icon" href="images/qbittorrent-tray.svg"> |     <link rel="icon" href="./images/qbittorrent-tray.svg"> | ||||||
|     <link rel="mask-icon" href="images/qbittorrent-tray.svg" color="#000000"> |     <link rel="mask-icon" href="./images/qbittorrent-tray.svg" color="#000000"> | ||||||
|     <link rel="apple-touch-icon" href="images/qbittorrent32.png"> |     <link rel="apple-touch-icon" href="./images/qbittorrent32.png"> | ||||||
| </head> | </head> | ||||||
|  |  | ||||||
| <body> | <body> | ||||||
| @@ -31,10 +31,10 @@ | |||||||
|         <a href="" class="reload">Reload</a> |         <a href="" class="reload">Reload</a> | ||||||
|         <a class="dismiss">🗙</a> |         <a class="dismiss">🗙</a> | ||||||
|     </div> |     </div> | ||||||
|     <script src="_framework/blazor.webassembly.js"></script> |     <script src="./_framework/blazor.webassembly.js"></script> | ||||||
|     <script src="_content/MudBlazor/MudBlazor.min.js"></script> |     <script src="./_content/MudBlazor/MudBlazor.min.js"></script> | ||||||
|     <script src="js/piecesbar.js"></script> |     <script src="./js/piecesbar.js"></script> | ||||||
|     <script src="js/interop.js"></script> |     <script src="./js/interop.js"></script> | ||||||
| </body> | </body> | ||||||
|  |  | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ | |||||||
|     <TargetFramework>net9.0</TargetFramework> |     <TargetFramework>net9.0</TargetFramework> | ||||||
|     <ImplicitUsings>enable</ImplicitUsings> |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
| 	<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | 	  <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|   | |||||||
							
								
								
									
										92
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								readme.md
									
									
									
									
									
								
							| @@ -1,14 +1,82 @@ | |||||||
| # qbt-mud | # qbtmud | ||||||
|  |  | ||||||
| ## To-Do | qbtmud is a drop-in replacement for qBittorrent's default WebUI, implementing all of its functionality with a modern and user-friendly interface. | ||||||
|  |  | ||||||
| - Rename multiple files dialog | ## Features | ||||||
| - ~~RSS feeds and dialogs~~ |  | ||||||
| - ~~About~~ | qbtmud replicates all core features of the qBittorrent WebUI, including: | ||||||
| - ~~Context menu for files list/trackers list/peers list~~ |  | ||||||
| - ~~Tag management page~~ | - **Torrent Management** – Add, remove, and control torrents. | ||||||
| - ~~Category management page~~ | - **Tracker Control** – View and manage trackers. | ||||||
| - ~~Update all tables to use DynamicTable~~ | - **Peer Management** – Monitor and manage peers connected to torrents. | ||||||
|   - ~~Log~~ | - **File Prioritization** – Select and prioritize specific files within a torrent. | ||||||
|   - ~~Blocks~~ | - **Speed Limits** – Set global and per-torrent speed limits. | ||||||
|   - ~~Search~~ | - **RSS Integration** – Subscribe to RSS feeds for automated torrent downloads. | ||||||
|  | - **Search Functionality** – Integrated torrent search. | ||||||
|  | - **Sequential Downloading** – Download files in order for media streaming. | ||||||
|  | - **Super Seeding Mode** – Efficiently distribute torrents as an initial seeder. | ||||||
|  | - **IP Filtering** – Improve security by filtering specific IP addresses. | ||||||
|  | - **IPv6 Support** – Full support for IPv6 networks. | ||||||
|  | - **Bandwidth Scheduler** – Schedule bandwidth limits. | ||||||
|  | - **WebUI Access** – Remotely manage torrents through the WebUI. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | For a detailed explanation of these features, refer to the [qBittorrent Options Guide](https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent). | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  |  | ||||||
|  | To install qbtmud without building from source: | ||||||
|  |  | ||||||
|  | ### 1. Download the Latest Release | ||||||
|  | - Go to the [qbtmud Releases](https://github.com/lantean-code/qbtmud/releases) page. | ||||||
|  | - Download the latest release archive for your operating system. | ||||||
|  |  | ||||||
|  | ### 2. Extract the Archive | ||||||
|  | - Extract the contents of the downloaded archive to a directory of your choice. | ||||||
|  |  | ||||||
|  | ### 3. Configure qBittorrent to Use qbtmud | ||||||
|  | - Open qBittorrent and navigate to `Tools` > `Options` > `Web UI`. | ||||||
|  | - Enable the option **"Use alternative WebUI"**. | ||||||
|  | - Set the **"Root Folder"** to the directory where you extracted qbtmud. | ||||||
|  | - Click **OK** to save the settings. | ||||||
|  |  | ||||||
|  | ### 4. Access qbtmud | ||||||
|  | - Open your web browser and go to `http://localhost:8080` (or the port configured in qBittorrent). | ||||||
|  |  | ||||||
|  | For more detailed instructions, refer to the [Alternate WebUI Usage Guide](https://github.com/qbittorrent/qBittorrent/wiki/Alternate-WebUI-usage). | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Building from Source | ||||||
|  |  | ||||||
|  | To build qbtmud from source, you need to have the **.NET 9.0 SDK** installed on your system. | ||||||
|  |  | ||||||
|  | ### 1. Clone the Repository | ||||||
|  | ```sh | ||||||
|  | git clone https://github.com/lantean-code/qbtmud.git | ||||||
|  | cd qbtmud | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### 2. Restore Dependencies | ||||||
|  | ```sh | ||||||
|  | dotnet restore | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### 3. Build the Application | ||||||
|  | ```sh | ||||||
|  | dotnet build --configuration Release | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### 4. Configure qBittorrent to Use qbtmud | ||||||
|  | Follow the same steps as in the **Installation** section to set qbtmud as your WebUI. | ||||||
|  |  | ||||||
|  | ### 5. Run qbtmud | ||||||
|  | Navigate to the directory containing the built files and run the application using the appropriate command for your OS. | ||||||
|  |  | ||||||
|  | By following these steps, you can set up qbtmud to manage your qBittorrent server with an improved web interface, offering better functionality and usability. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user