mirror of
				https://github.com/lantean-code/qbtmud.git
				synced 2025-11-03 21:43:19 +00:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4f9129fd46 | ||
| 
						 | 
					9a9d2c2ee2 | ||
| 
						 | 
					736bc46745 | ||
| 
						 | 
					23ae19c4c7 | ||
| 
						 | 
					603470eb30 | ||
| 
						 | 
					27c2406340 | ||
| 
						 | 
					4578dcc11f | ||
| 
						 | 
					3215fa3936 | ||
| 
						 | 
					78e62f31d0 | ||
| 
						 | 
					e23842fcb0 | ||
| 
						 | 
					411c7f87cc | ||
| 
						 | 
					4098f8f5a9 | ||
| 
						 | 
					12f81c5978 | ||
| 
						 | 
					717738d720 | ||
| 
						 | 
					885c34c8cf | ||
| 
						 | 
					ef3c68a6aa | ||
| 
						 | 
					a29e64fc1b | ||
| 
						 | 
					e55955c75e | ||
| 
						 | 
					aa80396862 | ||
| 
						 | 
					30ced3293c | ||
| 
						 | 
					c54f73a517 | 
							
								
								
									
										2
									
								
								.github/workflows/dotnet.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/dotnet.yml
									
									
									
									
										vendored
									
									
								
							@@ -26,7 +26,7 @@ jobs:
 | 
			
		||||
      - name: Install GitVersion
 | 
			
		||||
        uses: gittools/actions/gitversion/setup@v3.0.0
 | 
			
		||||
        with:
 | 
			
		||||
          versionSpec: '6.x'
 | 
			
		||||
          versionSpec: '6.0.0'
 | 
			
		||||
 | 
			
		||||
      - name: Determine Version
 | 
			
		||||
        id: gitversion
 | 
			
		||||
 
 | 
			
		||||
@@ -4,24 +4,20 @@
 | 
			
		||||
    <TargetFramework>net9.0</TargetFramework>
 | 
			
		||||
    <ImplicitUsings>enable</ImplicitUsings>
 | 
			
		||||
    <Nullable>enable</Nullable>
 | 
			
		||||
 | 
			
		||||
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
			
		||||
    <IsPackable>false</IsPackable>
 | 
			
		||||
    <IsTestProject>true</IsTestProject>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
	<PackageReference Include="FluentAssertions" Version="7.1.0" AllowedVersions="[5.0.0,7.*.*)" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
 | 
			
		||||
    <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="AwesomeAssertions" Version="9.0.0" />
 | 
			
		||||
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
 | 
			
		||||
    <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
 | 
			
		||||
    <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>
 | 
			
		||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
    </PackageReference>
 | 
			
		||||
	  <PackageReference Include="System.Net.Http" Version="4.3.4" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ namespace Lantean.QBTMud.Test
 | 
			
		||||
            Test2(a => a.Name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Test2(Expression<Func<TestClass, object>> expr)
 | 
			
		||||
        private void Test2(Expression<Func<TestClass, object?>> expr)
 | 
			
		||||
        {
 | 
			
		||||
            var body = expr.Body;
 | 
			
		||||
        }
 | 
			
		||||
@@ -38,7 +38,7 @@ namespace Lantean.QBTMud.Test
 | 
			
		||||
 | 
			
		||||
            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 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; }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
    <MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem>
 | 
			
		||||
</ContextMenu>
 | 
			
		||||
 | 
			
		||||
<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
 | 
			
		||||
<MudToolBar Gutters="false" Dense="true">
 | 
			
		||||
    <MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" />
 | 
			
		||||
    <MudDivider Vertical="true" />
 | 
			
		||||
@@ -22,6 +23,7 @@
 | 
			
		||||
    <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>
 | 
			
		||||
</MudToolBar>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<DynamicTable
 | 
			
		||||
    @ref="Table"
 | 
			
		||||
 
 | 
			
		||||
@@ -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<UIAction>? _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()
 | 
			
		||||
        {
 | 
			
		||||
@@ -441,7 +415,7 @@ namespace Lantean.QBTMud.Components
 | 
			
		||||
                    thereAreFirstLastPiecePrio = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (torrent.Progress > 0.999999) // not downloaded
 | 
			
		||||
                if (torrent.Progress < 0.999999) // not downloaded
 | 
			
		||||
                {
 | 
			
		||||
                    allAreDownloaded = false;
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ namespace Lantean.QBTMud.Helpers
 | 
			
		||||
            {
 | 
			
		||||
                time = TimeSpan.FromSeconds(seconds.Value);
 | 
			
		||||
            }
 | 
			
		||||
            catch (OverflowException)
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return "∞";
 | 
			
		||||
            }
 | 
			
		||||
@@ -129,7 +129,7 @@ namespace Lantean.QBTMud.Helpers
 | 
			
		||||
                return "";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Size(size);
 | 
			
		||||
            return Size(size, prefix, suffix);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,13 +12,11 @@
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
	  <PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
 | 
			
		||||
	  <PackageReference Include="ByteSize" Version="2.1.2" />
 | 
			
		||||
	<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
 | 
			
		||||
	<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.1" PrivateAssets="all" />
 | 
			
		||||
	<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
 | 
			
		||||
	<PackageReference Include="MudBlazor" Version="8.2.0" />
 | 
			
		||||
	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
 | 
			
		||||
	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" PrivateAssets="all" />
 | 
			
		||||
	  <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
 | 
			
		||||
	  <PackageReference Include="MudBlazor" Version="8.7.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>
 | 
			
		||||
 
 | 
			
		||||
@@ -36,23 +36,23 @@
 | 
			
		||||
                </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)
 | 
			
		||||
            {
 | 
			
		||||
                <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 />
 | 
			
		||||
            <MudText Class="mx-2 mb-1">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText>
 | 
			
		||||
            <MudDivider Vertical="true" />
 | 
			
		||||
            <MudText Class="mx-2 mb-1">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText>
 | 
			
		||||
            <MudDivider Vertical="true" />
 | 
			
		||||
            <MudText Class="mx-2 mb-1 d-none d-sm-flex">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText>
 | 
			
		||||
            <MudDivider Vertical="true" Class="d-none d-sm-flex" />
 | 
			
		||||
            <MudText Class="mx-2 mb-1 d-none d-sm-flex">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText>
 | 
			
		||||
            <MudDivider Vertical="true" Class="d-none d-sm-flex" />
 | 
			
		||||
            @{
 | 
			
		||||
                var (icon, colour) = GetConnectionIcon(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)" />
 | 
			
		||||
            <MudDivider Vertical="true" />
 | 
			
		||||
            <MudDivider Vertical="true" Class="" />
 | 
			
		||||
            <MudIcon Class="ml-1 mb-1" Icon="@Icons.Material.Filled.KeyboardDoubleArrowDown" Color="Color.Success" />
 | 
			
		||||
            <MudText Class="mr-1 mb-1">
 | 
			
		||||
                @DisplayHelpers.Size(MainData?.ServerState.DownloadInfoSpeed, null, "/s")
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
                        {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,19 +23,18 @@
 | 
			
		||||
                <MudSwitch T="bool" Label="Dark Mode" LabelPlacement="Placement.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" />
 | 
			
		||||
                <Menu @ref="Menu" />
 | 
			
		||||
            </MudAppBar>
 | 
			
		||||
            @if (IsDebug)
 | 
			
		||||
            {
 | 
			
		||||
                <MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
 | 
			
		||||
            <MudDrawer @bind-Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
 | 
			
		||||
                <ErrorDisplay ErrorBoundary="ErrorBoundary" />
 | 
			
		||||
            </MudDrawer>
 | 
			
		||||
            }
 | 
			
		||||
            <CascadingValue Value="Theme">
 | 
			
		||||
                <CascadingValue Value="IsDarkMode" Name="IsDarkMode">
 | 
			
		||||
                    <CascadingValue Value="Menu">
 | 
			
		||||
                        <CascadingValue Value="DrawerOpen" Name="DrawerOpen">
 | 
			
		||||
                            @Body
 | 
			
		||||
                        </CascadingValue>
 | 
			
		||||
                    </CascadingValue>
 | 
			
		||||
                </CascadingValue>
 | 
			
		||||
            </CascadingValue>
 | 
			
		||||
        </MudLayout>
 | 
			
		||||
    </EnhancedErrorBoundary>
 | 
			
		||||
</CascadingValue>
 | 
			
		||||
@@ -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!;
 | 
			
		||||
 | 
			
		||||
@@ -44,12 +41,6 @@ namespace Lantean.QBTMud.Layout
 | 
			
		||||
 | 
			
		||||
        protected MudTheme Theme { get; set; }
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
        private bool IsDebug { get; } = true;
 | 
			
		||||
#else
 | 
			
		||||
        private bool IsDebug { get; } = false;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        public MainLayout()
 | 
			
		||||
        {
 | 
			
		||||
            Theme = new MudTheme();
 | 
			
		||||
@@ -84,21 +75,21 @@ 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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
 | 
			
		||||
        public async Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
 | 
			
		||||
        {
 | 
			
		||||
            if (browserViewportEventArgs.Breakpoint == Breakpoint.Sm && DrawerOpen)
 | 
			
		||||
            if (browserViewportEventArgs.Breakpoint <= Breakpoint.Sm)
 | 
			
		||||
            {
 | 
			
		||||
                DrawerOpen = false;
 | 
			
		||||
            }
 | 
			
		||||
@@ -107,7 +98,17 @@ namespace Lantean.QBTMud.Layout
 | 
			
		||||
                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()
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,8 @@
 | 
			
		||||
            Dictionary<string, HashSet<string>> tagState,
 | 
			
		||||
            Dictionary<string, HashSet<string>> categoriesState,
 | 
			
		||||
            Dictionary<string, HashSet<string>> statusState,
 | 
			
		||||
            Dictionary<string, HashSet<string>> trackersState)
 | 
			
		||||
            Dictionary<string, HashSet<string>> trackersState,
 | 
			
		||||
            int majorVersion)
 | 
			
		||||
        {
 | 
			
		||||
            Torrents = torrents.ToDictionary();
 | 
			
		||||
            Tags = tags.ToHashSet();
 | 
			
		||||
@@ -22,6 +23,7 @@
 | 
			
		||||
            CategoriesState = categoriesState;
 | 
			
		||||
            StatusState = statusState;
 | 
			
		||||
            TrackersState = trackersState;
 | 
			
		||||
            MajorVersion = majorVersion;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Dictionary<string, Torrent> Torrents { get; }
 | 
			
		||||
@@ -36,5 +38,6 @@
 | 
			
		||||
        public Dictionary<string, HashSet<string>> TrackersState { get; }
 | 
			
		||||
        public string? SelectedTorrentHash { get; set; }
 | 
			
		||||
        public bool LostConnection { get; set; }
 | 
			
		||||
        public int MajorVersion { get; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
        Completed,
 | 
			
		||||
        Resumed,
 | 
			
		||||
        Paused,
 | 
			
		||||
        Stopped,
 | 
			
		||||
        Active,
 | 
			
		||||
        Inactive,
 | 
			
		||||
        Stalled,
 | 
			
		||||
@@ -15,6 +16,6 @@
 | 
			
		||||
        StalledDownloading,
 | 
			
		||||
        Checking,
 | 
			
		||||
        Errored,
 | 
			
		||||
        Stopped
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,89 +12,96 @@
 | 
			
		||||
 | 
			
		||||
<MudTabs Elevation="2" ApplyEffectsToContainer="true">
 | 
			
		||||
    <MudTabPanel Text="About">
 | 
			
		||||
        <div class="d-flex gap-4">
 | 
			
		||||
            <MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6" Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop" Height="162" Width="94" />
 | 
			
		||||
            <MudGrid Class="mx-0 mt-0 mb-3">
 | 
			
		||||
                <MudItem xs="12">
 | 
			
		||||
                    <div class="d-flex gap-3">
 | 
			
		||||
                        <MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None" Alt="QBT" Height="32" Width="32"  /><MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText>
 | 
			
		||||
        <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3">
 | 
			
		||||
            <MudGrid Class="mt-0 mb-4">
 | 
			
		||||
                <MudItem xs="12" sm="3" md="2" lg="2" xl="1" Class="d-flex justify-center">
 | 
			
		||||
                    <MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6"
 | 
			
		||||
                              Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop"
 | 
			
		||||
                              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>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
 | 
			
		||||
                <MudItem xs="12">
 | 
			
		||||
                    <MudText Typo="Typo.body1">An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                        <MudText Typo="Typo.body1">
 | 
			
		||||
                            An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.
 | 
			
		||||
                        </MudText>
 | 
			
		||||
 | 
			
		||||
                <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>
 | 
			
		||||
                        <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>
 | 
			
		||||
 | 
			
		||||
                <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>
 | 
			
		||||
                        <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>
 | 
			
		||||
 | 
			
		||||
                <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>
 | 
			
		||||
                        <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>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
            </MudGrid>
 | 
			
		||||
        </div>
 | 
			
		||||
        </MudContainer>
 | 
			
		||||
    </MudTabPanel>
 | 
			
		||||
    <MudTabPanel Text="Authors">
 | 
			
		||||
        <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">
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Name</MudText>
 | 
			
		||||
                <MudItem xs="12" md="2">
 | 
			
		||||
                    <MudText Typo="Typo.h6">Name</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="12" md="10">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Sledgehammer999</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Nationality</MudText>
 | 
			
		||||
                <MudItem xs="12" md="2">
 | 
			
		||||
                    <MudText Typo="Typo.h6">Nationality</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="12" md="10">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Greece</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                    <MudText Typo="Typo.body1">E-mail</MudText>
 | 
			
		||||
                <MudItem xs="12" md="2">
 | 
			
		||||
                    <MudText Typo="Typo.h6">E-mail</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="12" md="10">
 | 
			
		||||
                    <MudLink Href="mailto:sledgehammer999@qbittorrent.org">sledgehammer999@qbittorrent.org</MudLink>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
            </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">
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Name</MudText>
 | 
			
		||||
                <MudItem xs="12" md="2">
 | 
			
		||||
                    <MudText Typo="Typo.h6">Name</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="12" md="10">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Christophe Dumez</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Nationality</MudText>
 | 
			
		||||
                <MudItem xs="12" md="2">
 | 
			
		||||
                    <MudText Typo="Typo.h6">Nationality</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="12" md="10">
 | 
			
		||||
                    <MudText Typo="Typo.body1">France</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                    <MudText Typo="Typo.body1">E-mail</MudText>
 | 
			
		||||
                <MudItem xs="12" md="2">
 | 
			
		||||
                    <MudText Typo="Typo.h6">E-mail</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="12" md="10">
 | 
			
		||||
                    <MudLink Href="mailto:chris@qbittorrent.org">chris@qbittorrent.org</MudLink>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
            </MudGrid>
 | 
			
		||||
@@ -118,7 +125,7 @@
 | 
			
		||||
                (the list might not be up to date)
 | 
			
		||||
            </MudText>
 | 
			
		||||
            <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>Basque:</u> Xabier Aramendi (azpidatziak@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>
 | 
			
		||||
 | 
			
		||||
            <MudGrid Class="mt-1 mb-4">
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                <MudItem xs="3">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Qt</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="9">
 | 
			
		||||
                    <MudText Typo="Typo.body1">@QtVersion</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                <MudItem xs="3">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Libtorrent</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="9">
 | 
			
		||||
                    <MudText Typo="Typo.body1">@LibtorrentVersion</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                <MudItem xs="3">
 | 
			
		||||
                    <MudText Typo="Typo.body1">Boost</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="9">
 | 
			
		||||
                    <MudText Typo="Typo.body1">@BoostVersion</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                <MudItem xs="3">
 | 
			
		||||
                    <MudText Typo="Typo.body1">OpenSSL</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="9">
 | 
			
		||||
                    <MudText Typo="Typo.body1">@OpensslVersion</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
 | 
			
		||||
                <MudItem xs="2">
 | 
			
		||||
                <MudItem xs="3">
 | 
			
		||||
                    <MudText Typo="Typo.body1">zlib</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="10">
 | 
			
		||||
                <MudItem xs="9">
 | 
			
		||||
                    <MudText Typo="Typo.body1">@ZlibVersion</MudText>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
            </MudGrid>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
@page "/details/{hash}"
 | 
			
		||||
@layout DetailsLayout
 | 
			
		||||
 | 
			
		||||
<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
 | 
			
		||||
<MudToolBar Gutters="false" Dense="true">
 | 
			
		||||
    @if (!DrawerOpen)
 | 
			
		||||
    {
 | 
			
		||||
@@ -14,6 +15,7 @@
 | 
			
		||||
    <MudDivider Vertical="true" />
 | 
			
		||||
    <MudText Class="pl-5 no-wrap">@Name</MudText>
 | 
			
		||||
</MudToolBar>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@if (ShowTabs)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
                <MudItem xs="12" md="4">
 | 
			
		||||
                    <MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" Variant="Variant.Outlined" />
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="2" md="3">
 | 
			
		||||
                <MudItem xs="12" md="3">
 | 
			
		||||
                    <MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" Variant="Variant.Outlined">
 | 
			
		||||
                        @foreach (var (value, name) in Categories)
 | 
			
		||||
                        {
 | 
			
		||||
@@ -30,17 +30,21 @@
 | 
			
		||||
                        }
 | 
			
		||||
                    </MudSelect>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
                <MudItem xs="2" md="3">
 | 
			
		||||
                <MudItem xs="12" md="3">
 | 
			
		||||
                    <MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" Variant="Variant.Outlined">
 | 
			
		||||
                        <MudSelectItem Value="@("all")">All</MudSelectItem>
 | 
			
		||||
                        @if (Plugins.Count > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            <MudDivider />
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        @foreach (var (value, name) in Plugins)
 | 
			
		||||
                        {
 | 
			
		||||
                            <MudSelectItem Value="value">@name</MudSelectItem>
 | 
			
		||||
                        }
 | 
			
		||||
                    </MudSelect>
 | 
			
		||||
                </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>
 | 
			
		||||
                </MudItem>
 | 
			
		||||
            
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
    <TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" />
 | 
			
		||||
</ContextMenu>
 | 
			
		||||
 | 
			
		||||
<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
 | 
			
		||||
<MudToolBar Gutters="false" Dense="true">
 | 
			
		||||
    <MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" title="Add torrent link" />
 | 
			
		||||
    <MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" title="Add torrent file" />
 | 
			
		||||
@@ -18,6 +19,7 @@
 | 
			
		||||
    <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>
 | 
			
		||||
</MudToolBar>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0">
 | 
			
		||||
    <DynamicTable
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace Lantean.QBTMud.Services
 | 
			
		||||
{
 | 
			
		||||
    public class DataManager : IDataManager
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly Status[] _statuses = Enum.GetValues<Status>();
 | 
			
		||||
        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<string, Torrent>(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<string, HashSet<string>>(_statuses.Length + 2);
 | 
			
		||||
            foreach (var status in _statuses)
 | 
			
		||||
            var statuses = GetStatuses(majorVersion).ToArray();
 | 
			
		||||
            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());
 | 
			
		||||
            }
 | 
			
		||||
@@ -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<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)
 | 
			
		||||
        {
 | 
			
		||||
            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))
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,7 @@ code {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.torrent-list .mud-table-container {
 | 
			
		||||
    height: calc(100vh - 149px);
 | 
			
		||||
    height: calc(100vh - 160px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.file-list .mud-table-container {
 | 
			
		||||
@@ -252,3 +252,7 @@ td .folder-button {
 | 
			
		||||
    max-width: 25px;
 | 
			
		||||
    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.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="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
 | 
			
		||||
    <link rel="stylesheet" href="css/app.css" />
 | 
			
		||||
    <link href="./_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
 | 
			
		||||
    <link rel="stylesheet" href="./css/app.css" />
 | 
			
		||||
    <link rel="icon" type="image/png" href="images/qbittorrent32.png" />
 | 
			
		||||
    <link rel="icon" href="images/qbittorrent-tray.svg">
 | 
			
		||||
    <link rel="mask-icon" href="images/qbittorrent-tray.svg" color="#000000">
 | 
			
		||||
    <link rel="apple-touch-icon" href="images/qbittorrent32.png">
 | 
			
		||||
    <link rel="icon" href="./images/qbittorrent-tray.svg">
 | 
			
		||||
    <link rel="mask-icon" href="./images/qbittorrent-tray.svg" color="#000000">
 | 
			
		||||
    <link rel="apple-touch-icon" href="./images/qbittorrent32.png">
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
@@ -31,10 +31,10 @@
 | 
			
		||||
        <a href="" class="reload">Reload</a>
 | 
			
		||||
        <a class="dismiss">🗙</a>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script src="_framework/blazor.webassembly.js"></script>
 | 
			
		||||
    <script src="_content/MudBlazor/MudBlazor.min.js"></script>
 | 
			
		||||
    <script src="js/piecesbar.js"></script>
 | 
			
		||||
    <script src="js/interop.js"></script>
 | 
			
		||||
    <script src="./_framework/blazor.webassembly.js"></script>
 | 
			
		||||
    <script src="./_content/MudBlazor/MudBlazor.min.js"></script>
 | 
			
		||||
    <script src="./js/piecesbar.js"></script>
 | 
			
		||||
    <script src="./js/interop.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
- ~~RSS feeds and dialogs~~
 | 
			
		||||
- ~~About~~
 | 
			
		||||
- ~~Context menu for files list/trackers list/peers list~~
 | 
			
		||||
- ~~Tag management page~~
 | 
			
		||||
- ~~Category management page~~
 | 
			
		||||
- ~~Update all tables to use DynamicTable~~
 | 
			
		||||
  - ~~Log~~
 | 
			
		||||
  - ~~Blocks~~
 | 
			
		||||
  - ~~Search~~
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
qbtmud replicates all core features of the qBittorrent WebUI, including:
 | 
			
		||||
 | 
			
		||||
- **Torrent Management** – Add, remove, and control torrents.
 | 
			
		||||
- **Tracker Control** – View and manage trackers.
 | 
			
		||||
- **Peer Management** – Monitor and manage peers connected to torrents.
 | 
			
		||||
- **File Prioritization** – Select and prioritize specific files within a torrent.
 | 
			
		||||
- **Speed Limits** – Set global and per-torrent speed limits.
 | 
			
		||||
- **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