mirror of
				https://github.com/lantean-code/qbtmud.git
				synced 2025-11-04 05:53:22 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			d8535fa262
			...
			feature/ap
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0db0ad4374 | ||
| 
						 | 
					c390d83e4d | ||
| 
						 | 
					8dd29c238d | ||
| 
						 | 
					fca17edfd1 | 
@@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 | 
				
			|||||||
		readme.md = readme.md
 | 
							readme.md = readme.md
 | 
				
			||||||
	EndProjectSection
 | 
						EndProjectSection
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lantean.QBitTorrentClient.Test", "Lantean.QBitTorrentClient.Test\Lantean.QBitTorrentClient.Test.csproj", "{796E865C-7AA6-4BD9-B12F-394801199A75}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
Global
 | 
					Global
 | 
				
			||||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
						GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
				
			||||||
		Debug|Any CPU = Debug|Any CPU
 | 
							Debug|Any CPU = Debug|Any CPU
 | 
				
			||||||
@@ -33,6 +35,10 @@ Global
 | 
				
			|||||||
		{83BC76CC-D51B-42AF-A6EE-FA400C300098}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
							{83BC76CC-D51B-42AF-A6EE-FA400C300098}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
		{83BC76CC-D51B-42AF-A6EE-FA400C300098}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
							{83BC76CC-D51B-42AF-A6EE-FA400C300098}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
		{83BC76CC-D51B-42AF-A6EE-FA400C300098}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
							{83BC76CC-D51B-42AF-A6EE-FA400C300098}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{796E865C-7AA6-4BD9-B12F-394801199A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{796E865C-7AA6-4BD9-B12F-394801199A75}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{796E865C-7AA6-4BD9-B12F-394801199A75}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{796E865C-7AA6-4BD9-B12F-394801199A75}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
	EndGlobalSection
 | 
						EndGlobalSection
 | 
				
			||||||
	GlobalSection(SolutionProperties) = preSolution
 | 
						GlobalSection(SolutionProperties) = preSolution
 | 
				
			||||||
		HideSolutionNode = FALSE
 | 
							HideSolutionNode = FALSE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,7 +53,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            TorrentManagementMode = preferences.AutoTmmEnabled;
 | 
					            TorrentManagementMode = preferences.AutoTmmEnabled;
 | 
				
			||||||
            SavePath = preferences.SavePath;
 | 
					            SavePath = preferences.SavePath;
 | 
				
			||||||
            StartTorrent = !preferences.StartPausedEnabled;
 | 
					            StartTorrent = !preferences.AddStoppedEnabled;
 | 
				
			||||||
            AddToTopOfQueue = preferences.AddToTopOfQueue;
 | 
					            AddToTopOfQueue = preferences.AddToTopOfQueue;
 | 
				
			||||||
            StopCondition = preferences.TorrentStopCondition;
 | 
					            StopCondition = preferences.TorrentStopCondition;
 | 
				
			||||||
            ContentLayout = preferences.TorrentContentLayout;
 | 
					            ContentLayout = preferences.TorrentContentLayout;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,7 +53,7 @@
 | 
				
			|||||||
                        <MudNumericField T="int" Label="Ignore Subsequent Matches for (0 to Disable)" Value="IgnoreDays" ValueChanged="IgnoreDaysChanged" Disabled="@(SelectedRuleName is null)" Variant="Variant.Outlined" />
 | 
					                        <MudNumericField T="int" Label="Ignore Subsequent Matches for (0 to Disable)" Value="IgnoreDays" ValueChanged="IgnoreDaysChanged" Disabled="@(SelectedRuleName is null)" Variant="Variant.Outlined" />
 | 
				
			||||||
                    </MudItem>
 | 
					                    </MudItem>
 | 
				
			||||||
                    <MudItem xs="12">
 | 
					                    <MudItem xs="12">
 | 
				
			||||||
                        <MudSelect T="string" Label="Add paused" Value="AddPaused" ValueChanged="AddPausedChanged" Disabled="@(SelectedRuleName is null)" Variant="Variant.Outlined">
 | 
					                        <MudSelect T="string" Label="Add stopped" Value="AddStopped" ValueChanged="AddStoppedChanged" Disabled="@(SelectedRuleName is null)" Variant="Variant.Outlined">
 | 
				
			||||||
                            <MudSelectItem Value="@("default")">Use global settings</MudSelectItem>
 | 
					                            <MudSelectItem Value="@("default")">Use global settings</MudSelectItem>
 | 
				
			||||||
                            <MudSelectItem Value="@("always")">Always</MudSelectItem>
 | 
					                            <MudSelectItem Value="@("always")">Always</MudSelectItem>
 | 
				
			||||||
                            <MudSelectItem Value="@("never")">Never</MudSelectItem>
 | 
					                            <MudSelectItem Value="@("never")">Never</MudSelectItem>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,11 +114,11 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
            SelectedRule.IgnoreDays = value;
 | 
					            SelectedRule.IgnoreDays = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected string? AddPaused { get; set; }
 | 
					        protected string? AddStopped { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected void AddPausedChanged(string value)
 | 
					        protected void AddStoppedChanged(string value)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            AddPaused = value;
 | 
					            AddStopped = value;
 | 
				
			||||||
            switch (value)
 | 
					            switch (value)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case "default":
 | 
					                case "default":
 | 
				
			||||||
@@ -273,15 +273,15 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
            switch (SelectedRule.TorrentParams.Stopped)
 | 
					            switch (SelectedRule.TorrentParams.Stopped)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case null:
 | 
					                case null:
 | 
				
			||||||
                    AddPaused = "default";
 | 
					                    AddStopped = "default";
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case true:
 | 
					                case true:
 | 
				
			||||||
                    AddPaused = "always";
 | 
					                    AddStopped = "always";
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case false:
 | 
					                case false:
 | 
				
			||||||
                    AddPaused = "never";
 | 
					                    AddStopped = "never";
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,8 +65,8 @@
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return __builder =>
 | 
					        return __builder =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            <MudMenuItem Icon="@Icons.Material.Filled.PlayArrow" IconColor="Color.Success" OnClick="@(e => ResumeTorrents(type))">Resume torrents</MudMenuItem>
 | 
					            <MudMenuItem Icon="@Icons.Material.Filled.PlayArrow" IconColor="Color.Success" OnClick="@(e => StartTorrents(type))">Start torrents</MudMenuItem>
 | 
				
			||||||
            <MudMenuItem Icon="@Icons.Material.Filled.Pause" IconColor="Color.Warning" OnClick="@(e => PauseTorrents(type))">Pause torrents</MudMenuItem>
 | 
					            <MudMenuItem Icon="@Icons.Material.Filled.Stop" IconColor="Color.Warning" OnClick="@(e => StopTorrents(type))">Stop torrents</MudMenuItem>
 | 
				
			||||||
            <MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error" OnClick="@(e => RemoveTorrents(type))">Remove torrents</MudMenuItem>
 | 
					            <MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error" OnClick="@(e => RemoveTorrents(type))">Remove torrents</MudMenuItem>
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ using Lantean.QBTMud.Models;
 | 
				
			|||||||
using Microsoft.AspNetCore.Components;
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
using Microsoft.AspNetCore.Components.Web;
 | 
					using Microsoft.AspNetCore.Components.Web;
 | 
				
			||||||
using MudBlazor;
 | 
					using MudBlazor;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMud.Components
 | 
					namespace Lantean.QBTMud.Components
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -352,18 +353,18 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task ResumeTorrents(string type)
 | 
					        protected async Task StartTorrents(string type)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var torrents = GetAffectedTorrentHashes(type);
 | 
					            var torrents = GetAffectedTorrentHashes(type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ApiClient.ResumeTorrents(torrents);
 | 
					            await ApiClient.StartTorrents(hashes: torrents.ToArray());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task PauseTorrents(string type)
 | 
					        protected async Task StopTorrents(string type)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var torrents = GetAffectedTorrentHashes(type);
 | 
					            var torrents = GetAffectedTorrentHashes(type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ApiClient.PauseTorrents(torrents);
 | 
					            await ApiClient.StopTorrents(hashes: torrents.ToArray());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task RemoveTorrents(string type)
 | 
					        protected async Task RemoveTorrents(string type)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@
 | 
				
			|||||||
                <FieldSwitch Label="Add to top of queue" Value="AddToTopOfQueue" ValueChanged="AddToTopOfQueueChanged" />
 | 
					                <FieldSwitch Label="Add to top of queue" Value="AddToTopOfQueue" ValueChanged="AddToTopOfQueueChanged" />
 | 
				
			||||||
            </MudItem>
 | 
					            </MudItem>
 | 
				
			||||||
            <MudItem xs="12">
 | 
					            <MudItem xs="12">
 | 
				
			||||||
                <FieldSwitch Label="Do not start the download automatically" Value="StartPausedEnabled" ValueChanged="StartPausedEnabledChanged" />
 | 
					                <FieldSwitch Label="Do not start the download automatically" Value="AddStoppedEnabled" ValueChanged="AddStoppedEnabledChanged" />
 | 
				
			||||||
            </MudItem>
 | 
					            </MudItem>
 | 
				
			||||||
            <MudItem xs="12">
 | 
					            <MudItem xs="12">
 | 
				
			||||||
                <MudSelect T="string" Label="Torrent stop condition" Value="TorrentStopCondition" ValueChanged="TorrentStopConditionChanged" Variant="Variant.Outlined">
 | 
					                <MudSelect T="string" Label="Torrent stop condition" Value="TorrentStopCondition" ValueChanged="TorrentStopConditionChanged" Variant="Variant.Outlined">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ namespace Lantean.QBTMud.Components.Options
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        protected string? TorrentContentLayout { get; set; }
 | 
					        protected string? TorrentContentLayout { get; set; }
 | 
				
			||||||
        protected bool AddToTopOfQueue { get; set; }
 | 
					        protected bool AddToTopOfQueue { get; set; }
 | 
				
			||||||
        protected bool StartPausedEnabled { get; set; }
 | 
					        protected bool AddStoppedEnabled { get; set; }
 | 
				
			||||||
        protected string? TorrentStopCondition { get; set; }
 | 
					        protected string? TorrentStopCondition { get; set; }
 | 
				
			||||||
        protected bool AutoDeleteMode { get; set; }
 | 
					        protected bool AutoDeleteMode { get; set; }
 | 
				
			||||||
        protected bool PreallocateAll { get; set; }
 | 
					        protected bool PreallocateAll { get; set; }
 | 
				
			||||||
@@ -51,7 +51,7 @@ namespace Lantean.QBTMud.Components.Options
 | 
				
			|||||||
            // when adding a torrent
 | 
					            // when adding a torrent
 | 
				
			||||||
            TorrentContentLayout = Preferences.TorrentContentLayout;
 | 
					            TorrentContentLayout = Preferences.TorrentContentLayout;
 | 
				
			||||||
            AddToTopOfQueue = Preferences.AddToTopOfQueue;
 | 
					            AddToTopOfQueue = Preferences.AddToTopOfQueue;
 | 
				
			||||||
            StartPausedEnabled = Preferences.StartPausedEnabled;
 | 
					            AddStoppedEnabled = Preferences.AddStoppedEnabled;
 | 
				
			||||||
            TorrentStopCondition = Preferences.TorrentStopCondition;
 | 
					            TorrentStopCondition = Preferences.TorrentStopCondition;
 | 
				
			||||||
            AutoDeleteMode = Preferences.AutoDeleteMode == 1;
 | 
					            AutoDeleteMode = Preferences.AutoDeleteMode == 1;
 | 
				
			||||||
            PreallocateAll = Preferences.PreallocateAll;
 | 
					            PreallocateAll = Preferences.PreallocateAll;
 | 
				
			||||||
@@ -116,10 +116,10 @@ namespace Lantean.QBTMud.Components.Options
 | 
				
			|||||||
            await PreferencesChanged.InvokeAsync(UpdatePreferences);
 | 
					            await PreferencesChanged.InvokeAsync(UpdatePreferences);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task StartPausedEnabledChanged(bool value)
 | 
					        protected async Task AddStoppedEnabledChanged(bool value)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            StartPausedEnabled = value;
 | 
					            AddStoppedEnabled = value;
 | 
				
			||||||
            UpdatePreferences.StartPausedEnabled = value;
 | 
					            UpdatePreferences.AddStoppedEnabled = value;
 | 
				
			||||||
            await PreferencesChanged.InvokeAsync(UpdatePreferences);
 | 
					            await PreferencesChanged.InvokeAsync(UpdatePreferences);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ using Lantean.QBTMud.Services;
 | 
				
			|||||||
using Microsoft.AspNetCore.Components;
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
using Microsoft.JSInterop;
 | 
					using Microsoft.JSInterop;
 | 
				
			||||||
using MudBlazor;
 | 
					using MudBlazor;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMud.Components
 | 
					namespace Lantean.QBTMud.Components
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -37,9 +38,6 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
        [Inject]
 | 
					        [Inject]
 | 
				
			||||||
        protected IKeyboardService KeyboardService { get; set; } = default!;
 | 
					        protected IKeyboardService KeyboardService { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter(Name = "Version")]
 | 
					 | 
				
			||||||
        public string? Version { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        [EditorRequired]
 | 
					        [EditorRequired]
 | 
				
			||||||
        public IEnumerable<string> Hashes { get; set; } = default!;
 | 
					        public IEnumerable<string> Hashes { get; set; } = default!;
 | 
				
			||||||
@@ -71,14 +69,12 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected bool OverlayVisible { get; set; }
 | 
					        protected bool OverlayVisible { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected int MajorVersion => VersionHelper.GetMajorVersion(Version);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected override void OnInitialized()
 | 
					        protected override void OnInitialized()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _actions =
 | 
					            _actions =
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                new("start", "Start", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Resume)),
 | 
					                new("start", "Start", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Start)),
 | 
				
			||||||
                new("pause", "Pause", MajorVersion < 5 ? Icons.Material.Filled.Pause : Icons.Material.Filled.Stop, Color.Warning, CreateCallback(Pause)),
 | 
					                new("stop", "Stop", Icons.Material.Filled.Stop, Color.Warning, CreateCallback(Stop)),
 | 
				
			||||||
                new("forceStart", "Force start", Icons.Material.Filled.Forward, Color.Warning, CreateCallback(ForceStart)),
 | 
					                new("forceStart", "Force start", Icons.Material.Filled.Forward, Color.Warning, CreateCallback(ForceStart)),
 | 
				
			||||||
                new("delete", "Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove), separatorBefore: true),
 | 
					                new("delete", "Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove), separatorBefore: true),
 | 
				
			||||||
                new("setLocation", "Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation), separatorBefore: true),
 | 
					                new("setLocation", "Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation), separatorBefore: true),
 | 
				
			||||||
@@ -146,33 +142,17 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
            OverlayVisible = value;
 | 
					            OverlayVisible = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task Pause()
 | 
					        protected async Task Stop()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (MajorVersion < 5)
 | 
					            await ApiClient.StopTorrents(hashes: Hashes.ToArray());
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ApiClient.PauseTorrents(Hashes);
 | 
					 | 
				
			||||||
                Snackbar.Add("Torrent paused.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ApiClient.StopTorrents(Hashes);
 | 
					 | 
				
			||||||
            Snackbar.Add("Torrent stopped.");
 | 
					            Snackbar.Add("Torrent stopped.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task Resume()
 | 
					        protected async Task Start()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (MajorVersion < 5)
 | 
					            await ApiClient.StartTorrents(hashes: Hashes.ToArray());
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ApiClient.ResumeTorrents(Hashes);
 | 
					 | 
				
			||||||
                Snackbar.Add("Torrent resumed.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ApiClient.StartTorrents(Hashes);
 | 
					 | 
				
			||||||
            Snackbar.Add("Torrent started.");
 | 
					            Snackbar.Add("Torrent started.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task ForceStart()
 | 
					        protected async Task ForceStart()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -385,8 +365,8 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
            var allAreFirstLastPiecePrio = true;
 | 
					            var allAreFirstLastPiecePrio = true;
 | 
				
			||||||
            var thereAreFirstLastPiecePrio = false;
 | 
					            var thereAreFirstLastPiecePrio = false;
 | 
				
			||||||
            var allAreDownloaded = true;
 | 
					            var allAreDownloaded = true;
 | 
				
			||||||
            var allArePaused = true;
 | 
					            var allAreStopped = true;
 | 
				
			||||||
            var thereArePaused = false;
 | 
					            var thereAreStopped = false;
 | 
				
			||||||
            var allAreForceStart = true;
 | 
					            var allAreForceStart = true;
 | 
				
			||||||
            var thereAreForceStart = false;
 | 
					            var thereAreForceStart = false;
 | 
				
			||||||
            var allAreSuperSeeding = true;
 | 
					            var allAreSuperSeeding = true;
 | 
				
			||||||
@@ -424,27 +404,13 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
                    allAreSuperSeeding = false;
 | 
					                    allAreSuperSeeding = false;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (MajorVersion < 5)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (torrent.State != "pausedUP" && torrent.State != "pausedDL")
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        allArePaused = false;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        thereArePaused = true;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                if (torrent.State != "stoppedUP" && torrent.State != "stoppedDL")
 | 
					                if (torrent.State != "stoppedUP" && torrent.State != "stoppedDL")
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                        allArePaused = false;
 | 
					                    allAreStopped = false;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                        thereArePaused = true;
 | 
					                    thereAreStopped = true;
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!torrent.ForceStart)
 | 
					                if (!torrent.ForceStart)
 | 
				
			||||||
@@ -532,7 +498,7 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
                actionStates["superSeeding"] = ActionState.Hidden;
 | 
					                actionStates["superSeeding"] = ActionState.Hidden;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (allArePaused)
 | 
					            if (allAreStopped)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                actionStates["pause"] = ActionState.Hidden;
 | 
					                actionStates["pause"] = ActionState.Hidden;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -540,13 +506,11 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                actionStates["forceStart"] = ActionState.Hidden;
 | 
					                actionStates["forceStart"] = ActionState.Hidden;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (!thereArePaused && !thereAreForceStart)
 | 
					            else if (!thereAreStopped && !thereAreForceStart)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                actionStates["start"] = ActionState.Hidden;
 | 
					                actionStates["start"] = ActionState.Hidden;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (MajorVersion >= 5)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (actionStates.TryGetValue("start", out ActionState? startActionState))
 | 
					                if (actionStates.TryGetValue("start", out ActionState? startActionState))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    startActionState.TextOverride = "Start";
 | 
					                    startActionState.TextOverride = "Start";
 | 
				
			||||||
@@ -564,7 +528,6 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    actionStates["pause"] = new ActionState { TextOverride = "Stop" };
 | 
					                    actionStates["pause"] = new ActionState { TextOverride = "Stop" };
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!allAreAutoTmm && thereAreAutoTmm)
 | 
					            if (!allAreAutoTmm && thereAreAutoTmm)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,7 +171,7 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ApiClient.AddTrackersToTorrent(Hash, trackers);
 | 
					            await ApiClient.AddTrackersToTorrent(trackers, hashes: new[] { Hash });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected Task EditTrackerToolbar()
 | 
					        protected Task EditTrackerToolbar()
 | 
				
			||||||
@@ -211,7 +211,7 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ApiClient.RemoveTrackers(Hash, [tracker.Url]);
 | 
					            await ApiClient.RemoveTrackers([tracker.Url], hashes: new[] { Hash });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected Task CopyTrackerUrlToolbar()
 | 
					        protected Task CopyTrackerUrlToolbar()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ using Lantean.QBTMud.Components.Dialogs;
 | 
				
			|||||||
using Lantean.QBTMud.Filter;
 | 
					using Lantean.QBTMud.Filter;
 | 
				
			||||||
using Lantean.QBTMud.Models;
 | 
					using Lantean.QBTMud.Models;
 | 
				
			||||||
using MudBlazor;
 | 
					using MudBlazor;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMud.Helpers
 | 
					namespace Lantean.QBTMud.Helpers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -56,7 +57,7 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
            var addTorrentParams = CreateAddTorrentParams(options);
 | 
					            var addTorrentParams = CreateAddTorrentParams(options);
 | 
				
			||||||
            addTorrentParams.Torrents = files;
 | 
					            addTorrentParams.Torrents = files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await apiClient.AddTorrent(addTorrentParams);
 | 
					            _ = await apiClient.AddTorrent(addTorrentParams);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var stream in streams)
 | 
					            foreach (var stream in streams)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -74,15 +75,10 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                addTorrentParams.ContentLayout = Enum.Parse<QBitTorrentClient.Models.TorrentContentLayout>(options.ContentLayout);
 | 
					                addTorrentParams.ContentLayout = Enum.Parse<QBitTorrentClient.Models.TorrentContentLayout>(options.ContentLayout);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (string.IsNullOrEmpty(options.Cookie))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                addTorrentParams.Cookie = options.Cookie;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            addTorrentParams.DownloadLimit = options.DownloadLimit;
 | 
					            addTorrentParams.DownloadLimit = options.DownloadLimit;
 | 
				
			||||||
            addTorrentParams.DownloadPath = options.DownloadPath;
 | 
					            addTorrentParams.DownloadPath = options.DownloadPath;
 | 
				
			||||||
            addTorrentParams.FirstLastPiecePriority = options.DownloadFirstAndLastPiecesFirst;
 | 
					            addTorrentParams.FirstLastPiecePriority = options.DownloadFirstAndLastPiecesFirst;
 | 
				
			||||||
            addTorrentParams.InactiveSeedingTimeLimit = options.InactiveSeedingTimeLimit;
 | 
					            addTorrentParams.InactiveSeedingTimeLimit = options.InactiveSeedingTimeLimit;
 | 
				
			||||||
            addTorrentParams.Paused = !options.StartTorrent;
 | 
					 | 
				
			||||||
            addTorrentParams.RatioLimit = options.RatioLimit;
 | 
					            addTorrentParams.RatioLimit = options.RatioLimit;
 | 
				
			||||||
            addTorrentParams.RenameTorrent = options.RenameTorrent;
 | 
					            addTorrentParams.RenameTorrent = options.RenameTorrent;
 | 
				
			||||||
            addTorrentParams.SavePath = options.SavePath;
 | 
					            addTorrentParams.SavePath = options.SavePath;
 | 
				
			||||||
@@ -123,7 +119,7 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
            var addTorrentParams = CreateAddTorrentParams(options);
 | 
					            var addTorrentParams = CreateAddTorrentParams(options);
 | 
				
			||||||
            addTorrentParams.Urls = options.Urls;
 | 
					            addTorrentParams.Urls = options.Urls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await apiClient.AddTorrent(addTorrentParams);
 | 
					            _ = await apiClient.AddTorrent(addTorrentParams);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task<bool> InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, params string[] hashes)
 | 
					        public static async Task<bool> InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, params string[] hashes)
 | 
				
			||||||
@@ -243,7 +239,7 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var shareRatio = (ShareRatio)dialogResult.Data;
 | 
					            var shareRatio = (ShareRatio)dialogResult.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await apiClient.SetTorrentShareLimit(shareRatio.RatioLimit, shareRatio.SeedingTimeLimit, shareRatio.InactiveSeedingTimeLimit, null, torrents.Select(t => t.Hash).ToArray());
 | 
					            await apiClient.SetTorrentShareLimit(shareRatio.RatioLimit, shareRatio.SeedingTimeLimit, shareRatio.InactiveSeedingTimeLimit, hashes: torrents.Select(t => t.Hash).ToArray());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task InvokeStringFieldDialog(this IDialogService dialogService, string title, string label, string? value, Func<string, Task> onSuccess)
 | 
					        public static async Task InvokeStringFieldDialog(this IDialogService dialogService, string title, string label, string? value, Func<string, Task> onSuccess)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -404,8 +404,6 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
                Status.Downloading => (Icons.Material.Filled.Downloading, Color.Success),
 | 
					                Status.Downloading => (Icons.Material.Filled.Downloading, Color.Success),
 | 
				
			||||||
                Status.Seeding => (Icons.Material.Filled.Upload, Color.Info),
 | 
					                Status.Seeding => (Icons.Material.Filled.Upload, Color.Info),
 | 
				
			||||||
                Status.Completed => (Icons.Material.Filled.Check, Color.Default),
 | 
					                Status.Completed => (Icons.Material.Filled.Check, Color.Default),
 | 
				
			||||||
                Status.Resumed => (Icons.Material.Filled.PlayArrow, Color.Success),
 | 
					 | 
				
			||||||
                Status.Paused => (Icons.Material.Filled.Pause, Color.Default),
 | 
					 | 
				
			||||||
                Status.Stopped => (Icons.Material.Filled.Stop, Color.Default),
 | 
					                Status.Stopped => (Icons.Material.Filled.Stop, Color.Default),
 | 
				
			||||||
                Status.Active => (Icons.Material.Filled.Sort, Color.Success),
 | 
					                Status.Active => (Icons.Material.Filled.Sort, Color.Success),
 | 
				
			||||||
                Status.Inactive => (Icons.Material.Filled.Sort, Color.Error),
 | 
					                Status.Inactive => (Icons.Material.Filled.Sort, Color.Error),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -200,15 +200,8 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case Status.Resumed:
 | 
					                case Status.Stopped:
 | 
				
			||||||
                    if (!state.Contains("resumed"))
 | 
					                    if (state != "stoppedDL" && state != "stoppedUP")
 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        return false;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case Status.Paused:
 | 
					 | 
				
			||||||
                    if (!state.Contains("paused") && !state.Contains("stopped"))
 | 
					 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return false;
 | 
					                        return false;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,33 +0,0 @@
 | 
				
			|||||||
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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -97,7 +97,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, Version);
 | 
					            MainData = DataManager.CreateMainData(data);
 | 
				
			||||||
            MarkTorrentsDirty();
 | 
					            MarkTorrentsDirty();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _requestId = data.ResponseId;
 | 
					            _requestId = data.ResponseId;
 | 
				
			||||||
@@ -145,7 +145,7 @@ namespace Lantean.QBTMud.Layout
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                        if (MainData is null || data.FullUpdate)
 | 
					                        if (MainData is null || data.FullUpdate)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            MainData = DataManager.CreateMainData(data, Version);
 | 
					                            MainData = DataManager.CreateMainData(data);
 | 
				
			||||||
                            MarkTorrentsDirty();
 | 
					                            MarkTorrentsDirty();
 | 
				
			||||||
                            shouldRender = true;
 | 
					                            shouldRender = true;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,7 @@
 | 
				
			|||||||
            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();
 | 
				
			||||||
@@ -23,7 +22,6 @@
 | 
				
			|||||||
            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; }
 | 
				
			||||||
@@ -38,6 +36,5 @@
 | 
				
			|||||||
        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; }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,6 @@
 | 
				
			|||||||
        Downloading,
 | 
					        Downloading,
 | 
				
			||||||
        Seeding,
 | 
					        Seeding,
 | 
				
			||||||
        Completed,
 | 
					        Completed,
 | 
				
			||||||
        Resumed,
 | 
					 | 
				
			||||||
        Paused,
 | 
					        Paused,
 | 
				
			||||||
        Stopped,
 | 
					        Stopped,
 | 
				
			||||||
        Active,
 | 
					        Active,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,9 +25,8 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
            return peerList;
 | 
					            return peerList;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MainData CreateMainData(QBitTorrentClient.Models.MainData mainData, string version)
 | 
					        public MainData CreateMainData(QBitTorrentClient.Models.MainData mainData)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            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)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -95,7 +94,7 @@ 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 statuses = GetStatuses(majorVersion).ToArray();
 | 
					            var statuses = GetStatuses().ToArray();
 | 
				
			||||||
            var statusState = new Dictionary<string, HashSet<string>>(statuses.Length + 2);
 | 
					            var statusState = new Dictionary<string, HashSet<string>>(statuses.Length + 2);
 | 
				
			||||||
            foreach (var status in statuses)
 | 
					            foreach (var status in statuses)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -110,7 +109,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, majorVersion);
 | 
					            var torrentList = new MainData(torrents, tags, categories, trackers, serverState, tagState, categoriesState, statusState, trackersState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return torrentList;
 | 
					            return torrentList;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -284,7 +283,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, torrentList.MajorVersion);
 | 
					                        AddTorrentToStates(torrentList, hash);
 | 
				
			||||||
                        dataChanged = true;
 | 
					                        dataChanged = true;
 | 
				
			||||||
                        filterChanged = true;
 | 
					                        filterChanged = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -316,7 +315,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
            return dataChanged;
 | 
					            return dataChanged;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static void AddTorrentToStates(MainData torrentList, string hash, int version)
 | 
					        private static void AddTorrentToStates(MainData torrentList, string hash)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!torrentList.Torrents.TryGetValue(hash, out var torrent))
 | 
					            if (!torrentList.Torrents.TryGetValue(hash, out var torrent))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -329,7 +328,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
            torrentList.CategoriesState[FilterHelper.CATEGORY_ALL].Add(hash);
 | 
					            torrentList.CategoriesState[FilterHelper.CATEGORY_ALL].Add(hash);
 | 
				
			||||||
            UpdateCategoryState(torrentList, torrent, hash, previousCategory: null);
 | 
					            UpdateCategoryState(torrentList, torrent, hash, previousCategory: null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var status in GetStatuses(version))
 | 
					            foreach (var status in GetStatuses())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusSet))
 | 
					                if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusSet))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -346,21 +345,16 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
            UpdateTrackerState(torrentList, torrent, hash, previousTracker: null);
 | 
					            UpdateTrackerState(torrentList, torrent, hash, previousTracker: null);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static Status[] GetStatuses(int version)
 | 
					        private static Status[] GetStatuses()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_statusArray is not null)
 | 
					            if (_statusArray is not null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return _statusArray;
 | 
					                return _statusArray;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (version == 5)
 | 
					            _statusArray = Enum.GetValues<Status>()
 | 
				
			||||||
            {
 | 
					                .Where(s => s != Status.Paused)
 | 
				
			||||||
                _statusArray = Enum.GetValues<Status>().Where(s => s != Status.Paused).ToArray();
 | 
					                .ToArray();
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _statusArray = Enum.GetValues<Status>().Where(s => s != Status.Stopped).ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return _statusArray;
 | 
					            return _statusArray;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -388,7 +382,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
            torrentList.CategoriesState[FilterHelper.CATEGORY_ALL].Remove(hash);
 | 
					            torrentList.CategoriesState[FilterHelper.CATEGORY_ALL].Remove(hash);
 | 
				
			||||||
            UpdateCategoryStateForRemoval(torrentList, hash, snapshot.Category);
 | 
					            UpdateCategoryStateForRemoval(torrentList, hash, snapshot.Category);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var status in GetStatuses(torrentList.MajorVersion))
 | 
					            foreach (var status in GetStatuses())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusState))
 | 
					                if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusState))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -852,7 +846,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private static void UpdateStatusState(MainData torrentList, string hash, string previousState, long previousUploadSpeed, string newState, long newUploadSpeed)
 | 
					        private static void UpdateStatusState(MainData torrentList, string hash, string previousState, long previousUploadSpeed, string newState, long newUploadSpeed)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var status in GetStatuses(torrentList.MajorVersion))
 | 
					            foreach (var status in GetStatuses())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusSet))
 | 
					                if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusSet))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -1738,7 +1732,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
                    SocketReceiveBufferSize = changed.SocketReceiveBufferSize,
 | 
					                    SocketReceiveBufferSize = changed.SocketReceiveBufferSize,
 | 
				
			||||||
                    SocketSendBufferSize = changed.SocketSendBufferSize,
 | 
					                    SocketSendBufferSize = changed.SocketSendBufferSize,
 | 
				
			||||||
                    SsrfMitigation = changed.SsrfMitigation,
 | 
					                    SsrfMitigation = changed.SsrfMitigation,
 | 
				
			||||||
                    StartPausedEnabled = changed.StartPausedEnabled,
 | 
					                    AddStoppedEnabled = changed.AddStoppedEnabled,
 | 
				
			||||||
                    StopTrackerTimeout = changed.StopTrackerTimeout,
 | 
					                    StopTrackerTimeout = changed.StopTrackerTimeout,
 | 
				
			||||||
                    TempPath = changed.TempPath,
 | 
					                    TempPath = changed.TempPath,
 | 
				
			||||||
                    TempPathEnabled = changed.TempPathEnabled,
 | 
					                    TempPathEnabled = changed.TempPathEnabled,
 | 
				
			||||||
@@ -1944,7 +1938,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
                original.SocketReceiveBufferSize = changed.SocketReceiveBufferSize ?? original.SocketReceiveBufferSize;
 | 
					                original.SocketReceiveBufferSize = changed.SocketReceiveBufferSize ?? original.SocketReceiveBufferSize;
 | 
				
			||||||
                original.SocketSendBufferSize = changed.SocketSendBufferSize ?? original.SocketSendBufferSize;
 | 
					                original.SocketSendBufferSize = changed.SocketSendBufferSize ?? original.SocketSendBufferSize;
 | 
				
			||||||
                original.SsrfMitigation = changed.SsrfMitigation ?? original.SsrfMitigation;
 | 
					                original.SsrfMitigation = changed.SsrfMitigation ?? original.SsrfMitigation;
 | 
				
			||||||
                original.StartPausedEnabled = changed.StartPausedEnabled ?? original.StartPausedEnabled;
 | 
					                original.AddStoppedEnabled = changed.AddStoppedEnabled ?? original.AddStoppedEnabled;
 | 
				
			||||||
                original.StopTrackerTimeout = changed.StopTrackerTimeout ?? original.StopTrackerTimeout;
 | 
					                original.StopTrackerTimeout = changed.StopTrackerTimeout ?? original.StopTrackerTimeout;
 | 
				
			||||||
                original.TempPath = changed.TempPath ?? original.TempPath;
 | 
					                original.TempPath = changed.TempPath ?? original.TempPath;
 | 
				
			||||||
                original.TempPathEnabled = changed.TempPathEnabled ?? original.TempPathEnabled;
 | 
					                original.TempPathEnabled = changed.TempPathEnabled ?? original.TempPathEnabled;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ namespace Lantean.QBTMud.Services
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public interface IDataManager
 | 
					    public interface IDataManager
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        MainData CreateMainData(QBitTorrentClient.Models.MainData mainData, string version);
 | 
					        MainData CreateMainData(QBitTorrentClient.Models.MainData mainData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent);
 | 
					        Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <TargetFramework>net9.0</TargetFramework>
 | 
				
			||||||
 | 
					    <ImplicitUsings>enable</ImplicitUsings>
 | 
				
			||||||
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
 | 
					    <IsPackable>false</IsPackable>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <PackageReference Include="coverlet.collector" Version="6.0.2" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
 | 
				
			||||||
 | 
					    <PackageReference Include="xunit" Version="2.9.2" />
 | 
				
			||||||
 | 
					    <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <Using Include="Xunit" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										11
									
								
								Lantean.QBitTorrentClient.Test/UnitTest1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Lantean.QBitTorrentClient.Test/UnitTest1.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					namespace Lantean.QBitTorrentClient.Test
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class UnitTest1
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void Test1()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,9 @@
 | 
				
			|||||||
using Lantean.QBitTorrentClient.Models;
 | 
					using Lantean.QBitTorrentClient.Models;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
using System.Net.Http.Json;
 | 
					using System.Net.Http.Json;
 | 
				
			||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
@@ -105,6 +110,8 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public async Task SetApplicationPreferences(UpdatePreferences preferences)
 | 
					        public async Task SetApplicationPreferences(UpdatePreferences preferences)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            preferences.Validate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var json = JsonSerializer.Serialize(preferences, _options);
 | 
					            var json = JsonSerializer.Serialize(preferences, _options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
@@ -116,6 +123,49 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<IReadOnlyList<ApplicationCookie>> GetApplicationCookies()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var response = await _httpClient.GetAsync("app/cookies");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await GetJsonList<ApplicationCookie>(response.Content);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task SetApplicationCookies(IEnumerable<ApplicationCookie> cookies)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = JsonSerializer.Serialize(cookies, _options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("cookies", json)
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("app/setCookies", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<string> RotateApiKey()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("app/rotateAPIKey", null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var payload = await response.Content.ReadAsStringAsync();
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(payload))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return string.Empty;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var json = JsonSerializer.Deserialize<JsonElement>(payload, _options);
 | 
				
			||||||
 | 
					            if (json.ValueKind == JsonValueKind.Object && json.TryGetProperty("apiKey", out var apiKeyElement))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return apiKeyElement.GetString() ?? string.Empty;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<string> GetDefaultSavePath()
 | 
					        public async Task<string> GetDefaultSavePath()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var response = await _httpClient.GetAsync("app/defaultSavePath");
 | 
					            var response = await _httpClient.GetAsync("app/defaultSavePath");
 | 
				
			||||||
@@ -145,6 +195,43 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        #endregion Application
 | 
					        #endregion Application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Client data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<IReadOnlyDictionary<string, JsonElement>> LoadClientData(IEnumerable<string>? keys = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            HttpResponseMessage response;
 | 
				
			||||||
 | 
					            if (keys is null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                response = await _httpClient.GetAsync("clientdata/load");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var query = new QueryBuilder()
 | 
				
			||||||
 | 
					                    .Add("keys", JsonSerializer.Serialize(keys, _options));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                response = await _httpClient.GetAsync("clientdata/load", query);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await GetJsonDictionary<string, JsonElement>(response.Content);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task StoreClientData(IReadOnlyDictionary<string, JsonElement> data)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = JsonSerializer.Serialize(data, _options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("data", json)
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("clientdata/store", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion Client data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region Log
 | 
					        #region Log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null)
 | 
					        public async Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null)
 | 
				
			||||||
@@ -305,7 +392,7 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        #region Torrent management
 | 
					        #region Torrent management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, bool? isPrivate = null, params string[] hashes)
 | 
					        public async Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, bool? isPrivate = null, bool? includeFiles = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var query = new QueryBuilder();
 | 
					            var query = new QueryBuilder();
 | 
				
			||||||
            if (filter is not null)
 | 
					            if (filter is not null)
 | 
				
			||||||
@@ -344,6 +431,10 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                query.Add("private", isPrivate.Value ? "true" : "false");
 | 
					                query.Add("private", isPrivate.Value ? "true" : "false");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (includeFiles is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                query.Add("includeFiles", includeFiles.Value ? "true" : "false");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = await _httpClient.GetAsync("torrents/info", query);
 | 
					            var response = await _httpClient.GetAsync("torrents/info", query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -379,6 +470,43 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            return await GetJsonList<WebSeed>(response.Content);
 | 
					            return await GetJsonList<WebSeed>(response.Content);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task AddTorrentWebSeeds(string hash, IEnumerable<string> urls)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("hash", hash)
 | 
				
			||||||
 | 
					                .Add("urls", string.Join('|', urls))
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/addWebSeeds", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task EditTorrentWebSeed(string hash, string originalUrl, string newUrl)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("hash", hash)
 | 
				
			||||||
 | 
					                .Add("origUrl", originalUrl)
 | 
				
			||||||
 | 
					                .Add("newUrl", newUrl)
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/editWebSeed", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task RemoveTorrentWebSeeds(string hash, IEnumerable<string> urls)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("hash", hash)
 | 
				
			||||||
 | 
					                .Add("urls", string.Join('|', urls))
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/removeWebSeeds", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes)
 | 
					        public async Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var query = new QueryBuilder();
 | 
					            var query = new QueryBuilder();
 | 
				
			||||||
@@ -411,18 +539,6 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return await GetJsonList<string>(response.Content);
 | 
					            return await GetJsonList<string>(response.Content);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task PauseTorrents(bool? all = null, params string[] hashes)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					 | 
				
			||||||
                .AddAllOrPipeSeparated("hashes", all, hashes)
 | 
					 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var response = await _httpClient.PostAsync("torrents/pause", content);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task StopTorrents(bool? all = null, params string[] hashes)
 | 
					        public async Task StopTorrents(bool? all = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
@@ -433,18 +549,6 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task ResumeTorrents(bool? all = null, params string[] hashes)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					 | 
				
			||||||
                .AddAllOrPipeSeparated("hashes", all, hashes)
 | 
					 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var response = await _httpClient.PostAsync("torrents/resume", content);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task StartTorrents(bool? all = null, params string[] hashes)
 | 
					        public async Task StartTorrents(bool? all = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
@@ -479,10 +583,11 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task ReannounceTorrents(bool? all = null, params string[] hashes)
 | 
					        public async Task ReannounceTorrents(bool? all = null, IEnumerable<string>? trackers = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
                .AddAllOrPipeSeparated("hashes", all, hashes)
 | 
					                .AddAllOrPipeSeparated("hashes", all, hashes)
 | 
				
			||||||
 | 
					                .AddIfNotNullOrEmpty("urls", trackers is null ? null : string.Join('|', trackers))
 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = await _httpClient.PostAsync("torrents/reannounce", content);
 | 
					            var response = await _httpClient.PostAsync("torrents/reannounce", content);
 | 
				
			||||||
@@ -490,13 +595,15 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task AddTorrent(AddTorrentParams addTorrentParams)
 | 
					        public async Task<AddTorrentResult> AddTorrent(AddTorrentParams addTorrentParams)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var content = new MultipartFormDataContent();
 | 
					            var content = new MultipartFormDataContent();
 | 
				
			||||||
            if (addTorrentParams.Urls is not null)
 | 
					
 | 
				
			||||||
 | 
					            if (addTorrentParams.Urls?.Any() == true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("urls", string.Join('\n', addTorrentParams.Urls));
 | 
					                content.AddString("urls", string.Join('\n', addTorrentParams.Urls));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (addTorrentParams.Torrents is not null)
 | 
					            if (addTorrentParams.Torrents is not null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var (name, stream) in addTorrentParams.Torrents)
 | 
					                foreach (var (name, stream) in addTorrentParams.Torrents)
 | 
				
			||||||
@@ -504,6 +611,7 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
                    content.Add(new StreamContent(stream), "torrents", name);
 | 
					                    content.Add(new StreamContent(stream), "torrents", name);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (addTorrentParams.SkipChecking is not null)
 | 
					            if (addTorrentParams.SkipChecking is not null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("skip_checking", addTorrentParams.SkipChecking.Value);
 | 
					                content.AddString("skip_checking", addTorrentParams.SkipChecking.Value);
 | 
				
			||||||
@@ -520,12 +628,10 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("addToTopOfQueue", addTorrentParams.AddToTopOfQueue.Value);
 | 
					                content.AddString("addToTopOfQueue", addTorrentParams.AddToTopOfQueue.Value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // v4
 | 
					            if (addTorrentParams.Forced is not null)
 | 
				
			||||||
            if (addTorrentParams.Paused is not null)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("paused", addTorrentParams.Paused.Value);
 | 
					                content.AddString("forced", addTorrentParams.Forced.Value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // v5
 | 
					 | 
				
			||||||
            if (addTorrentParams.Stopped is not null)
 | 
					            if (addTorrentParams.Stopped is not null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("stopped", addTorrentParams.Stopped.Value);
 | 
					                content.AddString("stopped", addTorrentParams.Stopped.Value);
 | 
				
			||||||
@@ -590,21 +696,61 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("contentLayout", addTorrentParams.ContentLayout.Value);
 | 
					                content.AddString("contentLayout", addTorrentParams.ContentLayout.Value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (addTorrentParams.Downloader is not null)
 | 
				
			||||||
            if (addTorrentParams.Cookie is not null)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                content.AddString("cookie", addTorrentParams.Cookie);
 | 
					                content.AddString("downloader", addTorrentParams.Downloader);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (addTorrentParams.FilePriorities is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var priorities = string.Join(',', addTorrentParams.FilePriorities.Select(priority => ((int)priority).ToString(CultureInfo.InvariantCulture)));
 | 
				
			||||||
 | 
					                content.AddString("filePriorities", priorities);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(addTorrentParams.SslCertificate))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                content.AddString("ssl_certificate", addTorrentParams.SslCertificate!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(addTorrentParams.SslPrivateKey))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                content.AddString("ssl_private_key", addTorrentParams.SslPrivateKey!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(addTorrentParams.SslDhParams))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                content.AddString("ssl_dh_params", addTorrentParams.SslDhParams!);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = await _httpClient.PostAsync("torrents/add", content);
 | 
					            var response = await _httpClient.PostAsync("torrents/add", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            if (response.StatusCode == HttpStatusCode.Conflict)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var conflictMessage = await response.Content.ReadAsStringAsync();
 | 
				
			||||||
 | 
					                if (string.IsNullOrWhiteSpace(conflictMessage))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    conflictMessage = "All torrents failed to add.";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task AddTrackersToTorrent(string hash, IEnumerable<string> urls)
 | 
					                throw new HttpRequestException(conflictMessage, null, response.StatusCode);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var payload = await response.Content.ReadAsStringAsync();
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(payload))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                return new AddTorrentResult(0, 0, 0, Array.Empty<string>());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return JsonSerializer.Deserialize<AddTorrentResult>(payload, _options) ?? new AddTorrentResult(0, 0, 0, Array.Empty<string>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task AddTrackersToTorrent(IEnumerable<string> urls, bool? all = null, params string[] hashes)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (all is not true && (hashes is null || hashes.Length == 0))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentException("Specify at least one torrent hash or set all=true.", nameof(hashes));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
                .Add("hash", hash)
 | 
					                .AddAllOrPipeSeparated("hash", all, hashes ?? Array.Empty<string>())
 | 
				
			||||||
                .Add("urls", string.Join('\n', urls))
 | 
					                .Add("urls", string.Join('\n', urls))
 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -613,23 +759,42 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task EditTracker(string hash, string originalUrl, string newUrl)
 | 
					        public async Task EditTracker(string hash, string url, string? newUrl = null, int? tier = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if ((newUrl is null) && (tier is null))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentException("Must specify at least one of newUrl or tier.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
                .Add("hash", hash)
 | 
					                .Add("hash", hash)
 | 
				
			||||||
                .Add("originalUrl", originalUrl)
 | 
					                .Add("url", url);
 | 
				
			||||||
                .Add("newUrl", newUrl)
 | 
					 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = await _httpClient.PostAsync("torrents/editTracker", content);
 | 
					            if (!string.IsNullOrEmpty(newUrl))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                content.Add("newUrl", newUrl!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (tier is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                content.Add("tier", tier.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var form = content.ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/editTracker", form);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task RemoveTrackers(string hash, IEnumerable<string> urls)
 | 
					        public async Task RemoveTrackers(IEnumerable<string> urls, bool? all = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (all is not true && (hashes is null || hashes.Length == 0))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentException("Specify at least one torrent hash or set all=true.", nameof(hashes));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
                .Add("hash", hash)
 | 
					                .AddAllOrPipeSeparated("hash", all, hashes ?? Array.Empty<string>())
 | 
				
			||||||
                .AddPipeSeparated("urls", urls)
 | 
					                .AddPipeSeparated("urls", urls)
 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -732,13 +897,14 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, float inactiveSeedingTimeLimit, bool? all = null, params string[] hashes)
 | 
					        public async Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, float inactiveSeedingTimeLimit, ShareLimitAction? shareLimitAction = null, bool? all = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
                .AddAllOrPipeSeparated("hashes", all, hashes)
 | 
					                .AddAllOrPipeSeparated("hashes", all, hashes)
 | 
				
			||||||
                .Add("ratioLimit", ratioLimit)
 | 
					                .Add("ratioLimit", ratioLimit)
 | 
				
			||||||
                .Add("seedingTimeLimit", seedingTimeLimit)
 | 
					                .Add("seedingTimeLimit", seedingTimeLimit)
 | 
				
			||||||
                .Add("inactiveSeedingTimeLimit", inactiveSeedingTimeLimit)
 | 
					                .Add("inactiveSeedingTimeLimit", inactiveSeedingTimeLimit)
 | 
				
			||||||
 | 
					                .AddIfNotNullOrEmpty("shareLimitAction", shareLimitAction)
 | 
				
			||||||
                .ToFormUrlEncodedContent();
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = await _httpClient.PostAsync("torrents/setShareLimits", content);
 | 
					            var response = await _httpClient.PostAsync("torrents/setShareLimits", content);
 | 
				
			||||||
@@ -795,6 +961,18 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            await ThrowIfNotSuccessfulStatusCode(response);
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task SetTorrentComment(IEnumerable<string> hashes, string comment)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("hashes", string.Join('|', hashes))
 | 
				
			||||||
 | 
					                .Add("comment", comment)
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/setComment", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task SetTorrentCategory(string category, bool? all = null, params string[] hashes)
 | 
					        public async Task SetTorrentCategory(string category, bool? all = null, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var content = new FormUrlEncodedBuilder()
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
@@ -995,8 +1173,180 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            return Task.FromResult($"{_httpClient.BaseAddress}torrents/export?hash={hash}");
 | 
					            return Task.FromResult($"{_httpClient.BaseAddress}torrents/export?hash={hash}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<TorrentMetadata?> FetchMetadata(string source, string? downloader = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var builder = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("source", source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(downloader))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("downloader", downloader!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/fetchMetadata", builder.ToFormUrlEncodedContent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var payload = await response.Content.ReadAsStringAsync();
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(payload))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return JsonSerializer.Deserialize<TorrentMetadata>(payload, _options);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<IReadOnlyList<TorrentMetadata>> ParseMetadata(IEnumerable<(string FileName, Stream Content)> torrents)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new MultipartFormDataContent();
 | 
				
			||||||
 | 
					            foreach (var (fileName, stream) in torrents)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                content.Add(new StreamContent(stream), "torrents", fileName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/parseMetadata", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await GetJsonList<TorrentMetadata>(response.Content);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<byte[]> SaveMetadata(string source)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("source", source)
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrents/saveMetadata", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await response.Content.ReadAsByteArrayAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion Torrent management
 | 
					        #endregion Torrent management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Torrent creator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<string> AddTorrentCreationTask(TorrentCreationTaskRequest request)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (request is null)
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(request));
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(request.SourcePath))
 | 
				
			||||||
 | 
					                throw new ArgumentException("SourcePath is required.", nameof(request));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var builder = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("sourcePath", request.SourcePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(request.TorrentFilePath))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("torrentFilePath", request.TorrentFilePath!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.PieceSize.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("pieceSize", request.PieceSize.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.Private.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("private", request.Private.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.StartSeeding.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("startSeeding", request.StartSeeding.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(request.Comment))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("comment", request.Comment!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(request.Source))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("source", request.Source!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.Trackers is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("trackers", string.Join('|', request.Trackers));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.UrlSeeds is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("urlSeeds", string.Join('|', request.UrlSeeds));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!string.IsNullOrWhiteSpace(request.Format))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("format", request.Format!);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.OptimizeAlignment.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("optimizeAlignment", request.OptimizeAlignment.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (request.PaddedFileSizeLimit.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Add("paddedFileSizeLimit", request.PaddedFileSizeLimit.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrentcreator/addTask", builder.ToFormUrlEncodedContent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var payload = await response.Content.ReadAsStringAsync();
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(payload))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return string.Empty;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var json = JsonSerializer.Deserialize<JsonElement>(payload, _options);
 | 
				
			||||||
 | 
					            if (json.ValueKind == JsonValueKind.Object && json.TryGetProperty("taskID", out var idElement))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return idElement.GetString() ?? string.Empty;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<IReadOnlyList<TorrentCreationTaskStatus>> GetTorrentCreationTasks(string? taskId = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            HttpResponseMessage response;
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(taskId))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                response = await _httpClient.GetAsync("torrentcreator/status");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var query = new QueryBuilder()
 | 
				
			||||||
 | 
					                    .Add("taskID", taskId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                response = await _httpClient.GetAsync("torrentcreator/status", query);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await GetJsonList<TorrentCreationTaskStatus>(response.Content);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<byte[]> GetTorrentCreationTaskFile(string taskId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var query = new QueryBuilder()
 | 
				
			||||||
 | 
					                .Add("taskID", taskId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.GetAsync("torrentcreator/torrentFile", query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await response.Content.ReadAsByteArrayAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task DeleteTorrentCreationTask(string taskId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var content = new FormUrlEncodedBuilder()
 | 
				
			||||||
 | 
					                .Add("taskID", taskId)
 | 
				
			||||||
 | 
					                .ToFormUrlEncodedContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var response = await _httpClient.PostAsync("torrentcreator/deleteTask", content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await ThrowIfNotSuccessfulStatusCode(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion Torrent creator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region RSS
 | 
					        #region RSS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task AddRssFolder(string path)
 | 
					        public async Task AddRssFolder(string path)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,10 @@
 | 
				
			|||||||
using Lantean.QBitTorrentClient.Models;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Lantean.QBitTorrentClient.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBitTorrentClient
 | 
					namespace Lantean.QBitTorrentClient
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public static class ApiClientExtensions
 | 
					    public static class ApiClientExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static Task PauseTorrent(this IApiClient apiClient, string hash)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return apiClient.PauseTorrents(null, hash);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static Task PauseTorrents(this IApiClient apiClient, IEnumerable<string> hashes)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return apiClient.PauseTorrents(null, hashes.ToArray());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static Task PauseAllTorrents(this IApiClient apiClient)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return apiClient.PauseTorrents(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static Task StopTorrent(this IApiClient apiClient, string hash)
 | 
					        public static Task StopTorrent(this IApiClient apiClient, string hash)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return apiClient.StopTorrents(null, hash);
 | 
					            return apiClient.StopTorrents(null, hash);
 | 
				
			||||||
@@ -34,21 +20,6 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
            return apiClient.StopTorrents(true);
 | 
					            return apiClient.StopTorrents(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Task ResumeTorrent(this IApiClient apiClient, string hash)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return apiClient.ResumeTorrents(null, hash);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static Task ResumeTorrents(this IApiClient apiClient, IEnumerable<string> hashes)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return apiClient.ResumeTorrents(null, hashes.ToArray());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static Task ResumeAllTorrents(this IApiClient apiClient)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return apiClient.ResumeTorrents(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static Task StartTorrent(this IApiClient apiClient, string hash)
 | 
					        public static Task StartTorrent(this IApiClient apiClient, string hash)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return apiClient.StartTorrents(null, hash);
 | 
					            return apiClient.StartTorrents(null, hash);
 | 
				
			||||||
@@ -158,7 +129,7 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public static Task ReannounceTorrent(this IApiClient apiClient, string hash)
 | 
					        public static Task ReannounceTorrent(this IApiClient apiClient, string hash)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return apiClient.ReannounceTorrents(null, hash);
 | 
					            return apiClient.ReannounceTorrents(null, null, hash);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task<IEnumerable<string>> RemoveUnusedCategories(this IApiClient apiClient)
 | 
					        public static async Task<IEnumerable<string>> RemoveUnusedCategories(this IApiClient apiClient)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,8 @@
 | 
				
			|||||||
using Lantean.QBitTorrentClient.Models;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Lantean.QBitTorrentClient.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBitTorrentClient
 | 
					namespace Lantean.QBitTorrentClient
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -28,6 +32,12 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Task SetApplicationPreferences(UpdatePreferences preferences);
 | 
					        Task SetApplicationPreferences(UpdatePreferences preferences);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<IReadOnlyList<ApplicationCookie>> GetApplicationCookies();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task SetApplicationCookies(IEnumerable<ApplicationCookie> cookies);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<string> RotateApiKey();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<string> GetDefaultSavePath();
 | 
					        Task<string> GetDefaultSavePath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<NetworkInterface>> GetNetworkInterfaces();
 | 
					        Task<IReadOnlyList<NetworkInterface>> GetNetworkInterfaces();
 | 
				
			||||||
@@ -36,6 +46,14 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        #endregion Application
 | 
					        #endregion Application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Client data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<IReadOnlyDictionary<string, JsonElement>> LoadClientData(IEnumerable<string>? keys = null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task StoreClientData(IReadOnlyDictionary<string, JsonElement> data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion Client data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region Log
 | 
					        #region Log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null);
 | 
					        Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null);
 | 
				
			||||||
@@ -74,7 +92,7 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        #region Torrent management
 | 
					        #region Torrent management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, bool? isPrivate = null, params string[] hashes);
 | 
					        Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, bool? isPrivate = null, bool? includeFiles = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<TorrentProperties> GetTorrentProperties(string hash);
 | 
					        Task<TorrentProperties> GetTorrentProperties(string hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,16 +100,18 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<WebSeed>> GetTorrentWebSeeds(string hash);
 | 
					        Task<IReadOnlyList<WebSeed>> GetTorrentWebSeeds(string hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task AddTorrentWebSeeds(string hash, IEnumerable<string> urls);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task EditTorrentWebSeed(string hash, string originalUrl, string newUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task RemoveTorrentWebSeeds(string hash, IEnumerable<string> urls);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes);
 | 
					        Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<PieceState>> GetTorrentPieceStates(string hash);
 | 
					        Task<IReadOnlyList<PieceState>> GetTorrentPieceStates(string hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyList<string>> GetTorrentPieceHashes(string hash);
 | 
					        Task<IReadOnlyList<string>> GetTorrentPieceHashes(string hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task PauseTorrents(bool? all = null, params string[] hashes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Task ResumeTorrents(bool? all = null, params string[] hashes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Task StartTorrents(bool? all = null, params string[] hashes);
 | 
					        Task StartTorrents(bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task StopTorrents(bool? all = null, params string[] hashes);
 | 
					        Task StopTorrents(bool? all = null, params string[] hashes);
 | 
				
			||||||
@@ -100,15 +120,15 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Task RecheckTorrents(bool? all = null, params string[] hashes);
 | 
					        Task RecheckTorrents(bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task ReannounceTorrents(bool? all = null, params string[] hashes);
 | 
					        Task ReannounceTorrents(bool? all = null, IEnumerable<string>? trackers = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task AddTorrent(AddTorrentParams addTorrentParams);
 | 
					        Task<AddTorrentResult> AddTorrent(AddTorrentParams addTorrentParams);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task AddTrackersToTorrent(string hash, IEnumerable<string> urls);
 | 
					        Task AddTrackersToTorrent(IEnumerable<string> urls, bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task EditTracker(string hash, string originalUrl, string newUrl);
 | 
					        Task EditTracker(string hash, string url, string? newUrl = null, int? tier = null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task RemoveTrackers(string hash, IEnumerable<string> urls);
 | 
					        Task RemoveTrackers(IEnumerable<string> urls, bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task AddPeers(IEnumerable<string> hashes, IEnumerable<PeerId> peers);
 | 
					        Task AddPeers(IEnumerable<string> hashes, IEnumerable<PeerId> peers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,7 +146,7 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Task SetTorrentDownloadLimit(long limit, bool? all = null, params string[] hashes);
 | 
					        Task SetTorrentDownloadLimit(long limit, bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, float inactiveSeedingTimeLimit, bool? all = null, params string[] hashes);
 | 
					        Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, float inactiveSeedingTimeLimit, ShareLimitAction? shareLimitAction = null, bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyDictionary<string, long>> GetTorrentUploadLimit(bool? all = null, params string[] hashes);
 | 
					        Task<IReadOnlyDictionary<string, long>> GetTorrentUploadLimit(bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -136,6 +156,8 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Task SetTorrentName(string name, string hash);
 | 
					        Task SetTorrentName(string name, string hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task SetTorrentComment(IEnumerable<string> hashes, string comment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task SetTorrentCategory(string category, bool? all = null, params string[] hashes);
 | 
					        Task SetTorrentCategory(string category, bool? all = null, params string[] hashes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task<IReadOnlyDictionary<string, Category>> GetAllCategories();
 | 
					        Task<IReadOnlyDictionary<string, Category>> GetAllCategories();
 | 
				
			||||||
@@ -172,8 +194,26 @@ namespace Lantean.QBitTorrentClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Task<string> GetExportUrl(string hash);
 | 
					        Task<string> GetExportUrl(string hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<TorrentMetadata?> FetchMetadata(string source, string? downloader = null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<IReadOnlyList<TorrentMetadata>> ParseMetadata(IEnumerable<(string FileName, Stream Content)> torrents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<byte[]> SaveMetadata(string source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion Torrent management
 | 
					        #endregion Torrent management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Torrent creator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<string> AddTorrentCreationTask(TorrentCreationTaskRequest request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<IReadOnlyList<TorrentCreationTaskStatus>> GetTorrentCreationTasks(string? taskId = null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task<byte[]> GetTorrentCreationTaskFile(string taskId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Task DeleteTorrentCreationTask(string taskId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion Torrent creator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region RSS
 | 
					        #region RSS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task AddRssFolder(string path);
 | 
					        Task AddRssFolder(string path);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,9 +12,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public bool? AddToTopOfQueue { get; set; }
 | 
					        public bool? AddToTopOfQueue { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // v4
 | 
					        public bool? Forced { get; set; }
 | 
				
			||||||
        public bool? Paused { get; set; }
 | 
					
 | 
				
			||||||
        // v5
 | 
					 | 
				
			||||||
        public bool? Stopped { get; set; }
 | 
					        public bool? Stopped { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string? SavePath { get; set; }
 | 
					        public string? SavePath { get; set; }
 | 
				
			||||||
@@ -47,7 +46,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public TorrentContentLayout? ContentLayout { get; set; }
 | 
					        public TorrentContentLayout? ContentLayout { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string? Cookie { get; set; }
 | 
					        public IEnumerable<Priority>? FilePriorities { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? Downloader { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? SslCertificate { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? SslPrivateKey { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? SslDhParams { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Dictionary<string, Stream>? Torrents { get; set; }
 | 
					        public Dictionary<string, Stream>? Torrents { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								Lantean.QBitTorrentClient/Models/AddTorrentResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								Lantean.QBitTorrentClient/Models/AddTorrentResult.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public record AddTorrentResult
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [JsonConstructor]
 | 
				
			||||||
 | 
					        public AddTorrentResult(int successCount, int failureCount, int pendingCount, IReadOnlyList<string>? addedTorrentIds)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SuccessCount = successCount;
 | 
				
			||||||
 | 
					            FailureCount = failureCount;
 | 
				
			||||||
 | 
					            PendingCount = pendingCount;
 | 
				
			||||||
 | 
					            AddedTorrentIds = addedTorrentIds ?? Array.Empty<string>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("success_count")]
 | 
				
			||||||
 | 
					        public int SuccessCount { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("failure_count")]
 | 
				
			||||||
 | 
					        public int FailureCount { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("pending_count")]
 | 
				
			||||||
 | 
					        public int PendingCount { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("added_torrent_ids")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<string> AddedTorrentIds { get; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										33
									
								
								Lantean.QBitTorrentClient/Models/ApplicationCookie.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Lantean.QBitTorrentClient/Models/ApplicationCookie.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public record ApplicationCookie
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [JsonConstructor]
 | 
				
			||||||
 | 
					        public ApplicationCookie(string name, string? domain, string? path, string? value, long? expirationDate)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Name = name;
 | 
				
			||||||
 | 
					            Domain = domain;
 | 
				
			||||||
 | 
					            Path = path;
 | 
				
			||||||
 | 
					            Value = value;
 | 
				
			||||||
 | 
					            ExpirationDate = expirationDate;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("name")]
 | 
				
			||||||
 | 
					        public string Name { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("domain")]
 | 
				
			||||||
 | 
					        public string? Domain { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("path")]
 | 
				
			||||||
 | 
					        public string? Path { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("value")]
 | 
				
			||||||
 | 
					        public string? Value { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("expirationDate")]
 | 
				
			||||||
 | 
					        public long? ExpirationDate { get; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,6 +15,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            IReadOnlyList<string>? tags,
 | 
					            IReadOnlyList<string>? tags,
 | 
				
			||||||
            IReadOnlyList<string>? tagsRemoved,
 | 
					            IReadOnlyList<string>? tagsRemoved,
 | 
				
			||||||
            IReadOnlyDictionary<string, IReadOnlyList<string>> trackers,
 | 
					            IReadOnlyDictionary<string, IReadOnlyList<string>> trackers,
 | 
				
			||||||
 | 
					            IReadOnlyList<string>? trackersRemoved,
 | 
				
			||||||
            ServerState? serverState)
 | 
					            ServerState? serverState)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ResponseId = responseId;
 | 
					            ResponseId = responseId;
 | 
				
			||||||
@@ -26,6 +27,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            Tags = tags;
 | 
					            Tags = tags;
 | 
				
			||||||
            TagsRemoved = tagsRemoved;
 | 
					            TagsRemoved = tagsRemoved;
 | 
				
			||||||
            Trackers = trackers;
 | 
					            Trackers = trackers;
 | 
				
			||||||
 | 
					            TrackersRemoved = trackersRemoved;
 | 
				
			||||||
            ServerState = serverState;
 | 
					            ServerState = serverState;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            string? flags,
 | 
					            string? flags,
 | 
				
			||||||
            string? flagsDescription,
 | 
					            string? flagsDescription,
 | 
				
			||||||
            string? iPAddress,
 | 
					            string? iPAddress,
 | 
				
			||||||
 | 
					            string? i2pDestination,
 | 
				
			||||||
            string? clientId,
 | 
					            string? clientId,
 | 
				
			||||||
            int? port,
 | 
					            int? port,
 | 
				
			||||||
            float? progress,
 | 
					            float? progress,
 | 
				
			||||||
@@ -33,6 +34,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            Flags = flags;
 | 
					            Flags = flags;
 | 
				
			||||||
            FlagsDescription = flagsDescription;
 | 
					            FlagsDescription = flagsDescription;
 | 
				
			||||||
            IPAddress = iPAddress;
 | 
					            IPAddress = iPAddress;
 | 
				
			||||||
 | 
					            I2pDestination = i2pDestination;
 | 
				
			||||||
            ClientId = clientId;
 | 
					            ClientId = clientId;
 | 
				
			||||||
            Port = port;
 | 
					            Port = port;
 | 
				
			||||||
            Progress = progress;
 | 
					            Progress = progress;
 | 
				
			||||||
@@ -71,6 +73,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("ip")]
 | 
					        [JsonPropertyName("ip")]
 | 
				
			||||||
        public string? IPAddress { get; }
 | 
					        public string? IPAddress { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("i2p_dest")]
 | 
				
			||||||
 | 
					        public string? I2pDestination { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("peer_id_client")]
 | 
					        [JsonPropertyName("peer_id_client")]
 | 
				
			||||||
        public string? ClientId { get; }
 | 
					        public string? ClientId { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonConstructor]
 | 
					        [JsonConstructor]
 | 
				
			||||||
        public Preferences(
 | 
					        public Preferences(
 | 
				
			||||||
            bool addToTopOfQueue,
 | 
					            bool addToTopOfQueue,
 | 
				
			||||||
 | 
					            bool addStoppedEnabled,
 | 
				
			||||||
            string addTrackers,
 | 
					            string addTrackers,
 | 
				
			||||||
            bool addTrackersEnabled,
 | 
					            bool addTrackersEnabled,
 | 
				
			||||||
            int altDlLimit,
 | 
					            int altDlLimit,
 | 
				
			||||||
@@ -14,6 +15,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            bool alternativeWebuiEnabled,
 | 
					            bool alternativeWebuiEnabled,
 | 
				
			||||||
            string alternativeWebuiPath,
 | 
					            string alternativeWebuiPath,
 | 
				
			||||||
            string announceIp,
 | 
					            string announceIp,
 | 
				
			||||||
 | 
					            int announcePort,
 | 
				
			||||||
            bool announceToAllTiers,
 | 
					            bool announceToAllTiers,
 | 
				
			||||||
            bool announceToAllTrackers,
 | 
					            bool announceToAllTrackers,
 | 
				
			||||||
            bool anonymousMode,
 | 
					            bool anonymousMode,
 | 
				
			||||||
@@ -85,6 +87,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            int i2pPort,
 | 
					            int i2pPort,
 | 
				
			||||||
            bool idnSupportEnabled,
 | 
					            bool idnSupportEnabled,
 | 
				
			||||||
            bool incompleteFilesExt,
 | 
					            bool incompleteFilesExt,
 | 
				
			||||||
 | 
					            bool useUnwantedFolder,
 | 
				
			||||||
            bool ipFilterEnabled,
 | 
					            bool ipFilterEnabled,
 | 
				
			||||||
            string ipFilterPath,
 | 
					            string ipFilterPath,
 | 
				
			||||||
            bool ipFilterTrackers,
 | 
					            bool ipFilterTrackers,
 | 
				
			||||||
@@ -92,6 +95,8 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            bool limitTcpOverhead,
 | 
					            bool limitTcpOverhead,
 | 
				
			||||||
            bool limitUtpRate,
 | 
					            bool limitUtpRate,
 | 
				
			||||||
            int listenPort,
 | 
					            int listenPort,
 | 
				
			||||||
 | 
					            bool sslEnabled,
 | 
				
			||||||
 | 
					            int sslListenPort,
 | 
				
			||||||
            string locale,
 | 
					            string locale,
 | 
				
			||||||
            bool lsd,
 | 
					            bool lsd,
 | 
				
			||||||
            bool mailNotificationAuthEnabled,
 | 
					            bool mailNotificationAuthEnabled,
 | 
				
			||||||
@@ -160,6 +165,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            string savePath,
 | 
					            string savePath,
 | 
				
			||||||
            bool savePathChangedTmmEnabled,
 | 
					            bool savePathChangedTmmEnabled,
 | 
				
			||||||
            int saveResumeDataInterval,
 | 
					            int saveResumeDataInterval,
 | 
				
			||||||
 | 
					            int saveStatisticsInterval,
 | 
				
			||||||
            Dictionary<string, SaveLocation> scanDirs,
 | 
					            Dictionary<string, SaveLocation> scanDirs,
 | 
				
			||||||
            int scheduleFromHour,
 | 
					            int scheduleFromHour,
 | 
				
			||||||
            int scheduleFromMin,
 | 
					            int scheduleFromMin,
 | 
				
			||||||
@@ -177,12 +183,12 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            int socketReceiveBufferSize,
 | 
					            int socketReceiveBufferSize,
 | 
				
			||||||
            int socketSendBufferSize,
 | 
					            int socketSendBufferSize,
 | 
				
			||||||
            bool ssrfMitigation,
 | 
					            bool ssrfMitigation,
 | 
				
			||||||
            bool startPausedEnabled,
 | 
					 | 
				
			||||||
            int stopTrackerTimeout,
 | 
					            int stopTrackerTimeout,
 | 
				
			||||||
            string tempPath,
 | 
					            string tempPath,
 | 
				
			||||||
            bool tempPathEnabled,
 | 
					            bool tempPathEnabled,
 | 
				
			||||||
            bool torrentChangedTmmEnabled,
 | 
					            bool torrentChangedTmmEnabled,
 | 
				
			||||||
            string torrentContentLayout,
 | 
					            string torrentContentLayout,
 | 
				
			||||||
 | 
					            string torrentContentRemoveOption,
 | 
				
			||||||
            int torrentFileSizeLimit,
 | 
					            int torrentFileSizeLimit,
 | 
				
			||||||
            string torrentStopCondition,
 | 
					            string torrentStopCondition,
 | 
				
			||||||
            int upLimit,
 | 
					            int upLimit,
 | 
				
			||||||
@@ -192,10 +198,12 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            int upnpLeaseDuration,
 | 
					            int upnpLeaseDuration,
 | 
				
			||||||
            bool useCategoryPathsInManualMode,
 | 
					            bool useCategoryPathsInManualMode,
 | 
				
			||||||
            bool useHttps,
 | 
					            bool useHttps,
 | 
				
			||||||
 | 
					            bool ignoreSslErrors,
 | 
				
			||||||
            bool useSubcategories,
 | 
					            bool useSubcategories,
 | 
				
			||||||
            int utpTcpMixedMode,
 | 
					            int utpTcpMixedMode,
 | 
				
			||||||
            bool validateHttpsTrackerCertificate,
 | 
					            bool validateHttpsTrackerCertificate,
 | 
				
			||||||
            string webUiAddress,
 | 
					            string webUiAddress,
 | 
				
			||||||
 | 
					            string webUiApiKey,
 | 
				
			||||||
            int webUiBanDuration,
 | 
					            int webUiBanDuration,
 | 
				
			||||||
            bool webUiClickjackingProtectionEnabled,
 | 
					            bool webUiClickjackingProtectionEnabled,
 | 
				
			||||||
            bool webUiCsrfProtectionEnabled,
 | 
					            bool webUiCsrfProtectionEnabled,
 | 
				
			||||||
@@ -217,6 +225,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            AddToTopOfQueue = addToTopOfQueue;
 | 
					            AddToTopOfQueue = addToTopOfQueue;
 | 
				
			||||||
 | 
					            AddStoppedEnabled = addStoppedEnabled;
 | 
				
			||||||
            AddTrackers = addTrackers;
 | 
					            AddTrackers = addTrackers;
 | 
				
			||||||
            AddTrackersEnabled = addTrackersEnabled;
 | 
					            AddTrackersEnabled = addTrackersEnabled;
 | 
				
			||||||
            AltDlLimit = altDlLimit;
 | 
					            AltDlLimit = altDlLimit;
 | 
				
			||||||
@@ -224,6 +233,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            AlternativeWebuiEnabled = alternativeWebuiEnabled;
 | 
					            AlternativeWebuiEnabled = alternativeWebuiEnabled;
 | 
				
			||||||
            AlternativeWebuiPath = alternativeWebuiPath;
 | 
					            AlternativeWebuiPath = alternativeWebuiPath;
 | 
				
			||||||
            AnnounceIp = announceIp;
 | 
					            AnnounceIp = announceIp;
 | 
				
			||||||
 | 
					            AnnouncePort = announcePort;
 | 
				
			||||||
            AnnounceToAllTiers = announceToAllTiers;
 | 
					            AnnounceToAllTiers = announceToAllTiers;
 | 
				
			||||||
            AnnounceToAllTrackers = announceToAllTrackers;
 | 
					            AnnounceToAllTrackers = announceToAllTrackers;
 | 
				
			||||||
            AnonymousMode = anonymousMode;
 | 
					            AnonymousMode = anonymousMode;
 | 
				
			||||||
@@ -295,6 +305,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            I2pPort = i2pPort;
 | 
					            I2pPort = i2pPort;
 | 
				
			||||||
            IdnSupportEnabled = idnSupportEnabled;
 | 
					            IdnSupportEnabled = idnSupportEnabled;
 | 
				
			||||||
            IncompleteFilesExt = incompleteFilesExt;
 | 
					            IncompleteFilesExt = incompleteFilesExt;
 | 
				
			||||||
 | 
					            UseUnwantedFolder = useUnwantedFolder;
 | 
				
			||||||
            IpFilterEnabled = ipFilterEnabled;
 | 
					            IpFilterEnabled = ipFilterEnabled;
 | 
				
			||||||
            IpFilterPath = ipFilterPath;
 | 
					            IpFilterPath = ipFilterPath;
 | 
				
			||||||
            IpFilterTrackers = ipFilterTrackers;
 | 
					            IpFilterTrackers = ipFilterTrackers;
 | 
				
			||||||
@@ -302,6 +313,8 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            LimitTcpOverhead = limitTcpOverhead;
 | 
					            LimitTcpOverhead = limitTcpOverhead;
 | 
				
			||||||
            LimitUtpRate = limitUtpRate;
 | 
					            LimitUtpRate = limitUtpRate;
 | 
				
			||||||
            ListenPort = listenPort;
 | 
					            ListenPort = listenPort;
 | 
				
			||||||
 | 
					            SslEnabled = sslEnabled;
 | 
				
			||||||
 | 
					            SslListenPort = sslListenPort;
 | 
				
			||||||
            Locale = locale;
 | 
					            Locale = locale;
 | 
				
			||||||
            Lsd = lsd;
 | 
					            Lsd = lsd;
 | 
				
			||||||
            MailNotificationAuthEnabled = mailNotificationAuthEnabled;
 | 
					            MailNotificationAuthEnabled = mailNotificationAuthEnabled;
 | 
				
			||||||
@@ -370,6 +383,7 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            SavePath = savePath;
 | 
					            SavePath = savePath;
 | 
				
			||||||
            SavePathChangedTmmEnabled = savePathChangedTmmEnabled;
 | 
					            SavePathChangedTmmEnabled = savePathChangedTmmEnabled;
 | 
				
			||||||
            SaveResumeDataInterval = saveResumeDataInterval;
 | 
					            SaveResumeDataInterval = saveResumeDataInterval;
 | 
				
			||||||
 | 
					            SaveStatisticsInterval = saveStatisticsInterval;
 | 
				
			||||||
            ScanDirs = scanDirs;
 | 
					            ScanDirs = scanDirs;
 | 
				
			||||||
            ScheduleFromHour = scheduleFromHour;
 | 
					            ScheduleFromHour = scheduleFromHour;
 | 
				
			||||||
            ScheduleFromMin = scheduleFromMin;
 | 
					            ScheduleFromMin = scheduleFromMin;
 | 
				
			||||||
@@ -387,12 +401,12 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            SocketReceiveBufferSize = socketReceiveBufferSize;
 | 
					            SocketReceiveBufferSize = socketReceiveBufferSize;
 | 
				
			||||||
            SocketSendBufferSize = socketSendBufferSize;
 | 
					            SocketSendBufferSize = socketSendBufferSize;
 | 
				
			||||||
            SsrfMitigation = ssrfMitigation;
 | 
					            SsrfMitigation = ssrfMitigation;
 | 
				
			||||||
            StartPausedEnabled = startPausedEnabled;
 | 
					 | 
				
			||||||
            StopTrackerTimeout = stopTrackerTimeout;
 | 
					            StopTrackerTimeout = stopTrackerTimeout;
 | 
				
			||||||
            TempPath = tempPath;
 | 
					            TempPath = tempPath;
 | 
				
			||||||
            TempPathEnabled = tempPathEnabled;
 | 
					            TempPathEnabled = tempPathEnabled;
 | 
				
			||||||
            TorrentChangedTmmEnabled = torrentChangedTmmEnabled;
 | 
					            TorrentChangedTmmEnabled = torrentChangedTmmEnabled;
 | 
				
			||||||
            TorrentContentLayout = torrentContentLayout;
 | 
					            TorrentContentLayout = torrentContentLayout;
 | 
				
			||||||
 | 
					            TorrentContentRemoveOption = torrentContentRemoveOption;
 | 
				
			||||||
            TorrentFileSizeLimit = torrentFileSizeLimit;
 | 
					            TorrentFileSizeLimit = torrentFileSizeLimit;
 | 
				
			||||||
            TorrentStopCondition = torrentStopCondition;
 | 
					            TorrentStopCondition = torrentStopCondition;
 | 
				
			||||||
            UpLimit = upLimit;
 | 
					            UpLimit = upLimit;
 | 
				
			||||||
@@ -402,10 +416,12 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            UpnpLeaseDuration = upnpLeaseDuration;
 | 
					            UpnpLeaseDuration = upnpLeaseDuration;
 | 
				
			||||||
            UseCategoryPathsInManualMode = useCategoryPathsInManualMode;
 | 
					            UseCategoryPathsInManualMode = useCategoryPathsInManualMode;
 | 
				
			||||||
            UseHttps = useHttps;
 | 
					            UseHttps = useHttps;
 | 
				
			||||||
 | 
					            IgnoreSslErrors = ignoreSslErrors;
 | 
				
			||||||
            UseSubcategories = useSubcategories;
 | 
					            UseSubcategories = useSubcategories;
 | 
				
			||||||
            UtpTcpMixedMode = utpTcpMixedMode;
 | 
					            UtpTcpMixedMode = utpTcpMixedMode;
 | 
				
			||||||
            ValidateHttpsTrackerCertificate = validateHttpsTrackerCertificate;
 | 
					            ValidateHttpsTrackerCertificate = validateHttpsTrackerCertificate;
 | 
				
			||||||
            WebUiAddress = webUiAddress;
 | 
					            WebUiAddress = webUiAddress;
 | 
				
			||||||
 | 
					            WebUiApiKey = webUiApiKey;
 | 
				
			||||||
            WebUiBanDuration = webUiBanDuration;
 | 
					            WebUiBanDuration = webUiBanDuration;
 | 
				
			||||||
            WebUiClickjackingProtectionEnabled = webUiClickjackingProtectionEnabled;
 | 
					            WebUiClickjackingProtectionEnabled = webUiClickjackingProtectionEnabled;
 | 
				
			||||||
            WebUiCsrfProtectionEnabled = webUiCsrfProtectionEnabled;
 | 
					            WebUiCsrfProtectionEnabled = webUiCsrfProtectionEnabled;
 | 
				
			||||||
@@ -429,6 +445,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("add_to_top_of_queue")]
 | 
					        [JsonPropertyName("add_to_top_of_queue")]
 | 
				
			||||||
        public bool AddToTopOfQueue { get; }
 | 
					        public bool AddToTopOfQueue { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("add_stopped_enabled")]
 | 
				
			||||||
 | 
					        public bool AddStoppedEnabled { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("add_trackers")]
 | 
					        [JsonPropertyName("add_trackers")]
 | 
				
			||||||
        public string AddTrackers { get; }
 | 
					        public string AddTrackers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -450,6 +469,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("announce_ip")]
 | 
					        [JsonPropertyName("announce_ip")]
 | 
				
			||||||
        public string AnnounceIp { get; }
 | 
					        public string AnnounceIp { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("announce_port")]
 | 
				
			||||||
 | 
					        public int AnnouncePort { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("announce_to_all_tiers")]
 | 
					        [JsonPropertyName("announce_to_all_tiers")]
 | 
				
			||||||
        public bool AnnounceToAllTiers { get; }
 | 
					        public bool AnnounceToAllTiers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -663,6 +685,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("incomplete_files_ext")]
 | 
					        [JsonPropertyName("incomplete_files_ext")]
 | 
				
			||||||
        public bool IncompleteFilesExt { get; }
 | 
					        public bool IncompleteFilesExt { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("use_unwanted_folder")]
 | 
				
			||||||
 | 
					        public bool UseUnwantedFolder { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("ip_filter_enabled")]
 | 
					        [JsonPropertyName("ip_filter_enabled")]
 | 
				
			||||||
        public bool IpFilterEnabled { get; }
 | 
					        public bool IpFilterEnabled { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -684,6 +709,12 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("listen_port")]
 | 
					        [JsonPropertyName("listen_port")]
 | 
				
			||||||
        public int ListenPort { get; }
 | 
					        public int ListenPort { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ssl_enabled")]
 | 
				
			||||||
 | 
					        public bool SslEnabled { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ssl_listen_port")]
 | 
				
			||||||
 | 
					        public int SslListenPort { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("locale")]
 | 
					        [JsonPropertyName("locale")]
 | 
				
			||||||
        public string Locale { get; }
 | 
					        public string Locale { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -888,6 +919,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("save_resume_data_interval")]
 | 
					        [JsonPropertyName("save_resume_data_interval")]
 | 
				
			||||||
        public int SaveResumeDataInterval { get; }
 | 
					        public int SaveResumeDataInterval { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("save_statistics_interval")]
 | 
				
			||||||
 | 
					        public int SaveStatisticsInterval { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("scan_dirs")]
 | 
					        [JsonPropertyName("scan_dirs")]
 | 
				
			||||||
        public Dictionary<string, SaveLocation> ScanDirs { get; }
 | 
					        public Dictionary<string, SaveLocation> ScanDirs { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -939,9 +973,6 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("ssrf_mitigation")]
 | 
					        [JsonPropertyName("ssrf_mitigation")]
 | 
				
			||||||
        public bool SsrfMitigation { get; }
 | 
					        public bool SsrfMitigation { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("start_paused_enabled")]
 | 
					 | 
				
			||||||
        public bool StartPausedEnabled { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("stop_tracker_timeout")]
 | 
					        [JsonPropertyName("stop_tracker_timeout")]
 | 
				
			||||||
        public int StopTrackerTimeout { get; }
 | 
					        public int StopTrackerTimeout { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -957,6 +988,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("torrent_content_layout")]
 | 
					        [JsonPropertyName("torrent_content_layout")]
 | 
				
			||||||
        public string TorrentContentLayout { get; }
 | 
					        public string TorrentContentLayout { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("torrent_content_remove_option")]
 | 
				
			||||||
 | 
					        public string TorrentContentRemoveOption { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("torrent_file_size_limit")]
 | 
					        [JsonPropertyName("torrent_file_size_limit")]
 | 
				
			||||||
        public int TorrentFileSizeLimit { get; }
 | 
					        public int TorrentFileSizeLimit { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -984,6 +1018,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("use_https")]
 | 
					        [JsonPropertyName("use_https")]
 | 
				
			||||||
        public bool UseHttps { get; }
 | 
					        public bool UseHttps { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ignore_ssl_errors")]
 | 
				
			||||||
 | 
					        public bool IgnoreSslErrors { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("use_subcategories")]
 | 
					        [JsonPropertyName("use_subcategories")]
 | 
				
			||||||
        public bool UseSubcategories { get; }
 | 
					        public bool UseSubcategories { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -996,6 +1033,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("web_ui_address")]
 | 
					        [JsonPropertyName("web_ui_address")]
 | 
				
			||||||
        public string WebUiAddress { get; }
 | 
					        public string WebUiAddress { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("web_ui_api_key")]
 | 
				
			||||||
 | 
					        public string WebUiApiKey { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("web_ui_ban_duration")]
 | 
					        [JsonPropertyName("web_ui_ban_duration")]
 | 
				
			||||||
        public int WebUiBanDuration { get; }
 | 
					        public int WebUiBanDuration { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,264 +1,219 @@
 | 
				
			|||||||
using Lantean.QBitTorrentClient.Converters;
 | 
					using Lantean.QBitTorrentClient.Converters;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Text.Json.Serialization;
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBitTorrentClient.Models
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public record Torrent
 | 
					    public record Torrent
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [JsonConstructor]
 | 
					 | 
				
			||||||
        public Torrent(
 | 
					 | 
				
			||||||
            long? addedOn,
 | 
					 | 
				
			||||||
            long? amountLeft,
 | 
					 | 
				
			||||||
            bool? automaticTorrentManagement,
 | 
					 | 
				
			||||||
            float? availability,
 | 
					 | 
				
			||||||
            string? category,
 | 
					 | 
				
			||||||
            long? completed,
 | 
					 | 
				
			||||||
            long? completionOn,
 | 
					 | 
				
			||||||
            string? contentPath,
 | 
					 | 
				
			||||||
            long? downloadLimit,
 | 
					 | 
				
			||||||
            long? downloadSpeed,
 | 
					 | 
				
			||||||
            long? downloaded,
 | 
					 | 
				
			||||||
            long? downloadedSession,
 | 
					 | 
				
			||||||
            long? estimatedTimeOfArrival,
 | 
					 | 
				
			||||||
            bool? firstLastPiecePriority,
 | 
					 | 
				
			||||||
            bool? forceStart,
 | 
					 | 
				
			||||||
            string hash,
 | 
					 | 
				
			||||||
            string? infoHashV1,
 | 
					 | 
				
			||||||
            string? infoHashV2,
 | 
					 | 
				
			||||||
            long? lastActivity,
 | 
					 | 
				
			||||||
            string? magnetUri,
 | 
					 | 
				
			||||||
            float? maxRatio,
 | 
					 | 
				
			||||||
            int? maxSeedingTime,
 | 
					 | 
				
			||||||
            string? name,
 | 
					 | 
				
			||||||
            int? numberComplete,
 | 
					 | 
				
			||||||
            int? numberIncomplete,
 | 
					 | 
				
			||||||
            int? numberLeeches,
 | 
					 | 
				
			||||||
            int? numberSeeds,
 | 
					 | 
				
			||||||
            int? priority,
 | 
					 | 
				
			||||||
            float? progress,
 | 
					 | 
				
			||||||
            float? ratio,
 | 
					 | 
				
			||||||
            float? ratioLimit,
 | 
					 | 
				
			||||||
            string? savePath,
 | 
					 | 
				
			||||||
            long? seedingTime,
 | 
					 | 
				
			||||||
            int? seedingTimeLimit,
 | 
					 | 
				
			||||||
            long? seenComplete,
 | 
					 | 
				
			||||||
            bool? sequentialDownload,
 | 
					 | 
				
			||||||
            long? size,
 | 
					 | 
				
			||||||
            string? state,
 | 
					 | 
				
			||||||
            bool? superSeeding,
 | 
					 | 
				
			||||||
            IReadOnlyList<string>? tags,
 | 
					 | 
				
			||||||
            int? timeActive,
 | 
					 | 
				
			||||||
            long? totalSize,
 | 
					 | 
				
			||||||
            string? tracker,
 | 
					 | 
				
			||||||
            long? uploadLimit,
 | 
					 | 
				
			||||||
            long? uploaded,
 | 
					 | 
				
			||||||
            long? uploadedSession,
 | 
					 | 
				
			||||||
            long? uploadSpeed,
 | 
					 | 
				
			||||||
            long? reannounce,
 | 
					 | 
				
			||||||
            float? inactiveSeedingTimeLimit,
 | 
					 | 
				
			||||||
            float? maxInactiveSeedingTime)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            AddedOn = addedOn;
 | 
					 | 
				
			||||||
            AmountLeft = amountLeft;
 | 
					 | 
				
			||||||
            AutomaticTorrentManagement = automaticTorrentManagement;
 | 
					 | 
				
			||||||
            Availability = availability;
 | 
					 | 
				
			||||||
            Category = category;
 | 
					 | 
				
			||||||
            Completed = completed;
 | 
					 | 
				
			||||||
            CompletionOn = completionOn;
 | 
					 | 
				
			||||||
            ContentPath = contentPath;
 | 
					 | 
				
			||||||
            DownloadLimit = downloadLimit;
 | 
					 | 
				
			||||||
            DownloadSpeed = downloadSpeed;
 | 
					 | 
				
			||||||
            Downloaded = downloaded;
 | 
					 | 
				
			||||||
            DownloadedSession = downloadedSession;
 | 
					 | 
				
			||||||
            EstimatedTimeOfArrival = estimatedTimeOfArrival;
 | 
					 | 
				
			||||||
            FirstLastPiecePriority = firstLastPiecePriority;
 | 
					 | 
				
			||||||
            ForceStart = forceStart;
 | 
					 | 
				
			||||||
            Hash = hash;
 | 
					 | 
				
			||||||
            InfoHashV1 = infoHashV1;
 | 
					 | 
				
			||||||
            InfoHashV2 = infoHashV2;
 | 
					 | 
				
			||||||
            LastActivity = lastActivity;
 | 
					 | 
				
			||||||
            MagnetUri = magnetUri;
 | 
					 | 
				
			||||||
            MaxRatio = maxRatio;
 | 
					 | 
				
			||||||
            MaxSeedingTime = maxSeedingTime;
 | 
					 | 
				
			||||||
            Name = name;
 | 
					 | 
				
			||||||
            NumberComplete = numberComplete;
 | 
					 | 
				
			||||||
            NumberIncomplete = numberIncomplete;
 | 
					 | 
				
			||||||
            NumberLeeches = numberLeeches;
 | 
					 | 
				
			||||||
            NumberSeeds = numberSeeds;
 | 
					 | 
				
			||||||
            Priority = priority;
 | 
					 | 
				
			||||||
            Progress = progress;
 | 
					 | 
				
			||||||
            Ratio = ratio;
 | 
					 | 
				
			||||||
            RatioLimit = ratioLimit;
 | 
					 | 
				
			||||||
            SavePath = savePath;
 | 
					 | 
				
			||||||
            SeedingTime = seedingTime;
 | 
					 | 
				
			||||||
            SeedingTimeLimit = seedingTimeLimit;
 | 
					 | 
				
			||||||
            SeenComplete = seenComplete;
 | 
					 | 
				
			||||||
            SequentialDownload = sequentialDownload;
 | 
					 | 
				
			||||||
            Size = size;
 | 
					 | 
				
			||||||
            State = state;
 | 
					 | 
				
			||||||
            SuperSeeding = superSeeding;
 | 
					 | 
				
			||||||
            Tags = tags ?? [];
 | 
					 | 
				
			||||||
            TimeActive = timeActive;
 | 
					 | 
				
			||||||
            TotalSize = totalSize;
 | 
					 | 
				
			||||||
            Tracker = tracker;
 | 
					 | 
				
			||||||
            UploadLimit = uploadLimit;
 | 
					 | 
				
			||||||
            Uploaded = uploaded;
 | 
					 | 
				
			||||||
            UploadedSession = uploadedSession;
 | 
					 | 
				
			||||||
            UploadSpeed = uploadSpeed;
 | 
					 | 
				
			||||||
            Reannounce = reannounce;
 | 
					 | 
				
			||||||
            InactiveSeedingTimeLimit = inactiveSeedingTimeLimit;
 | 
					 | 
				
			||||||
            MaxInactiveSeedingTime = maxInactiveSeedingTime;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("added_on")]
 | 
					 | 
				
			||||||
        public long? AddedOn { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("amount_left")]
 | 
					 | 
				
			||||||
        public long? AmountLeft { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("auto_tmm")]
 | 
					 | 
				
			||||||
        public bool? AutomaticTorrentManagement { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("availability")]
 | 
					 | 
				
			||||||
        public float? Availability { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("category")]
 | 
					 | 
				
			||||||
        public string? Category { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("completed")]
 | 
					 | 
				
			||||||
        public long? Completed { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("completion_on")]
 | 
					 | 
				
			||||||
        public long? CompletionOn { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("content_path")]
 | 
					 | 
				
			||||||
        public string? ContentPath { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("dl_limit")]
 | 
					 | 
				
			||||||
        public long? DownloadLimit { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("dlspeed")]
 | 
					 | 
				
			||||||
        public long? DownloadSpeed { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("downloaded")]
 | 
					 | 
				
			||||||
        public long? Downloaded { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("downloaded_session")]
 | 
					 | 
				
			||||||
        public long? DownloadedSession { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("eta")]
 | 
					 | 
				
			||||||
        public long? EstimatedTimeOfArrival { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("f_l_piece_prio")]
 | 
					 | 
				
			||||||
        public bool? FirstLastPiecePriority { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("force_start")]
 | 
					 | 
				
			||||||
        public bool? ForceStart { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("hash")]
 | 
					        [JsonPropertyName("hash")]
 | 
				
			||||||
        public string Hash { get; }
 | 
					        public string Hash { get; init; } = string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("infohash_v1")]
 | 
					        [JsonPropertyName("infohash_v1")]
 | 
				
			||||||
        public string? InfoHashV1 { get; }
 | 
					        public string? InfoHashV1 { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("infohash_v2")]
 | 
					        [JsonPropertyName("infohash_v2")]
 | 
				
			||||||
        public string? InfoHashV2 { get; }
 | 
					        public string? InfoHashV2 { get; init; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("last_activity")]
 | 
					 | 
				
			||||||
        public long? LastActivity { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("magnet_uri")]
 | 
					 | 
				
			||||||
        public string? MagnetUri { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("max_ratio")]
 | 
					 | 
				
			||||||
        public float? MaxRatio { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("max_seeding_time")]
 | 
					 | 
				
			||||||
        public int? MaxSeedingTime { get; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("name")]
 | 
					        [JsonPropertyName("name")]
 | 
				
			||||||
        public string? Name { get; }
 | 
					        public string? Name { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("num_complete")]
 | 
					        [JsonPropertyName("magnet_uri")]
 | 
				
			||||||
        public int? NumberComplete { get; }
 | 
					        public string? MagnetUri { get; init; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("num_incomplete")]
 | 
					 | 
				
			||||||
        public int? NumberIncomplete { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("num_leechs")]
 | 
					 | 
				
			||||||
        public int? NumberLeeches { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("num_seeds")]
 | 
					 | 
				
			||||||
        public int? NumberSeeds { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("priority")]
 | 
					 | 
				
			||||||
        public int? Priority { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("progress")]
 | 
					 | 
				
			||||||
        public float? Progress { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("ratio")]
 | 
					 | 
				
			||||||
        public float? Ratio { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("ratio_limit")]
 | 
					 | 
				
			||||||
        public float? RatioLimit { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("save_path")]
 | 
					 | 
				
			||||||
        public string? SavePath { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("seeding_time")]
 | 
					 | 
				
			||||||
        public long? SeedingTime { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("seeding_time_limit")]
 | 
					 | 
				
			||||||
        public int? SeedingTimeLimit { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("seen_complete")]
 | 
					 | 
				
			||||||
        public long? SeenComplete { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("seq_dl")]
 | 
					 | 
				
			||||||
        public bool? SequentialDownload { get; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("size")]
 | 
					        [JsonPropertyName("size")]
 | 
				
			||||||
        public long? Size { get; }
 | 
					        public long? Size { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("progress")]
 | 
				
			||||||
 | 
					        public float? Progress { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("dlspeed")]
 | 
				
			||||||
 | 
					        public long? DownloadSpeed { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("upspeed")]
 | 
				
			||||||
 | 
					        public long? UploadSpeed { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("priority")]
 | 
				
			||||||
 | 
					        public int? Priority { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("num_seeds")]
 | 
				
			||||||
 | 
					        public int? NumberSeeds { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("num_complete")]
 | 
				
			||||||
 | 
					        public int? NumberComplete { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("num_leechs")]
 | 
				
			||||||
 | 
					        public int? NumberLeeches { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("num_incomplete")]
 | 
				
			||||||
 | 
					        public int? NumberIncomplete { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ratio")]
 | 
				
			||||||
 | 
					        public float? Ratio { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("popularity")]
 | 
				
			||||||
 | 
					        public float? Popularity { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("eta")]
 | 
				
			||||||
 | 
					        public long? EstimatedTimeOfArrival { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("state")]
 | 
					        [JsonPropertyName("state")]
 | 
				
			||||||
        public string? State { get; }
 | 
					        public string? State { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("super_seeding")]
 | 
					        [JsonPropertyName("seq_dl")]
 | 
				
			||||||
        public bool? SuperSeeding { get; }
 | 
					        public bool? SequentialDownload { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("f_l_piece_prio")]
 | 
				
			||||||
 | 
					        public bool? FirstLastPiecePriority { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("category")]
 | 
				
			||||||
 | 
					        public string? Category { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("tags")]
 | 
					        [JsonPropertyName("tags")]
 | 
				
			||||||
        [JsonConverter(typeof(CommaSeparatedJsonConverter))]
 | 
					        [JsonConverter(typeof(CommaSeparatedJsonConverter))]
 | 
				
			||||||
        public IReadOnlyList<string>? Tags { get; }
 | 
					        public IReadOnlyList<string> Tags { get; init; } = Array.Empty<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("time_active")]
 | 
					        [JsonPropertyName("super_seeding")]
 | 
				
			||||||
        public int? TimeActive { get; }
 | 
					        public bool? SuperSeeding { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("total_size")]
 | 
					        [JsonPropertyName("force_start")]
 | 
				
			||||||
        public long? TotalSize { get; }
 | 
					        public bool? ForceStart { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("save_path")]
 | 
				
			||||||
 | 
					        public string? SavePath { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("download_path")]
 | 
				
			||||||
 | 
					        public string? DownloadPath { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("content_path")]
 | 
				
			||||||
 | 
					        public string? ContentPath { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("root_path")]
 | 
				
			||||||
 | 
					        public string? RootPath { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("added_on")]
 | 
				
			||||||
 | 
					        public long? AddedOn { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("completion_on")]
 | 
				
			||||||
 | 
					        public long? CompletionOn { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("tracker")]
 | 
					        [JsonPropertyName("tracker")]
 | 
				
			||||||
        public string? Tracker { get; }
 | 
					        public string? Tracker { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("trackers_count")]
 | 
				
			||||||
 | 
					        public int? TrackersCount { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("dl_limit")]
 | 
				
			||||||
 | 
					        public long? DownloadLimit { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("up_limit")]
 | 
					        [JsonPropertyName("up_limit")]
 | 
				
			||||||
        public long? UploadLimit { get; }
 | 
					        public long? UploadLimit { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("downloaded")]
 | 
				
			||||||
 | 
					        public long? Downloaded { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("uploaded")]
 | 
					        [JsonPropertyName("uploaded")]
 | 
				
			||||||
        public long? Uploaded { get; }
 | 
					        public long? Uploaded { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("downloaded_session")]
 | 
				
			||||||
 | 
					        public long? DownloadedSession { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("uploaded_session")]
 | 
					        [JsonPropertyName("uploaded_session")]
 | 
				
			||||||
        public long? UploadedSession { get; }
 | 
					        public long? UploadedSession { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("upspeed")]
 | 
					        [JsonPropertyName("amount_left")]
 | 
				
			||||||
        public long? UploadSpeed { get; }
 | 
					        public long? AmountLeft { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("reannounce")]
 | 
					        [JsonPropertyName("completed")]
 | 
				
			||||||
        public long? Reannounce { get; }
 | 
					        public long? Completed { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("inactive_seeding_time_limit")]
 | 
					        [JsonPropertyName("connections_count")]
 | 
				
			||||||
        public float? InactiveSeedingTimeLimit { get; }
 | 
					        public int? ConnectionsCount { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("connections_limit")]
 | 
				
			||||||
 | 
					        public int? ConnectionsLimit { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("max_ratio")]
 | 
				
			||||||
 | 
					        public float? MaxRatio { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("max_seeding_time")]
 | 
				
			||||||
 | 
					        public int? MaxSeedingTime { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("max_inactive_seeding_time")]
 | 
					        [JsonPropertyName("max_inactive_seeding_time")]
 | 
				
			||||||
        public float? MaxInactiveSeedingTime { get; }
 | 
					        public float? MaxInactiveSeedingTime { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ratio_limit")]
 | 
				
			||||||
 | 
					        public float? RatioLimit { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("seeding_time_limit")]
 | 
				
			||||||
 | 
					        public int? SeedingTimeLimit { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("inactive_seeding_time_limit")]
 | 
				
			||||||
 | 
					        public float? InactiveSeedingTimeLimit { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("share_limit_action")]
 | 
				
			||||||
 | 
					        [JsonConverter(typeof(JsonStringEnumConverter))]
 | 
				
			||||||
 | 
					        public ShareLimitAction? ShareLimitAction { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("seen_complete")]
 | 
				
			||||||
 | 
					        public long? SeenComplete { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("last_activity")]
 | 
				
			||||||
 | 
					        public long? LastActivity { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("total_size")]
 | 
				
			||||||
 | 
					        public long? TotalSize { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("auto_tmm")]
 | 
				
			||||||
 | 
					        public bool? AutomaticTorrentManagement { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("time_active")]
 | 
				
			||||||
 | 
					        public int? TimeActive { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("seeding_time")]
 | 
				
			||||||
 | 
					        public long? SeedingTime { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("availability")]
 | 
				
			||||||
 | 
					        public float? Availability { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("reannounce")]
 | 
				
			||||||
 | 
					        public long? Reannounce { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("comment")]
 | 
				
			||||||
 | 
					        public string? Comment { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("has_metadata")]
 | 
				
			||||||
 | 
					        public bool? HasMetadata { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("created_by")]
 | 
				
			||||||
 | 
					        public string? CreatedBy { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("creation_date")]
 | 
				
			||||||
 | 
					        public long? CreationDate { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("private")]
 | 
				
			||||||
 | 
					        public bool? IsPrivate { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("total_wasted")]
 | 
				
			||||||
 | 
					        public long? TotalWasted { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("pieces_num")]
 | 
				
			||||||
 | 
					        public int? PiecesCount { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("piece_size")]
 | 
				
			||||||
 | 
					        public long? PieceSize { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("pieces_have")]
 | 
				
			||||||
 | 
					        public int? PiecesHave { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("has_tracker_warning")]
 | 
				
			||||||
 | 
					        public bool? HasTrackerWarning { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("has_tracker_error")]
 | 
				
			||||||
 | 
					        public bool? HasTrackerError { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("has_other_announce_error")]
 | 
				
			||||||
 | 
					        public bool? HasOtherAnnounceError { get; init; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										131
									
								
								Lantean.QBitTorrentClient/Models/TorrentCreationTask.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								Lantean.QBitTorrentClient/Models/TorrentCreationTask.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class TorrentCreationTaskRequest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string SourcePath { get; set; } = string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? TorrentFilePath { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int? PieceSize { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool? Private { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool? StartSeeding { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? Comment { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? Source { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IEnumerable<string>? Trackers { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IEnumerable<string>? UrlSeeds { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string? Format { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool? OptimizeAlignment { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int? PaddedFileSizeLimit { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public record TorrentCreationTaskStatus
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [JsonConstructor]
 | 
				
			||||||
 | 
					        public TorrentCreationTaskStatus(
 | 
				
			||||||
 | 
					            string taskID,
 | 
				
			||||||
 | 
					            string? sourcePath,
 | 
				
			||||||
 | 
					            int? pieceSize,
 | 
				
			||||||
 | 
					            bool? @private,
 | 
				
			||||||
 | 
					            string? timeAdded,
 | 
				
			||||||
 | 
					            string? format,
 | 
				
			||||||
 | 
					            bool? optimizeAlignment,
 | 
				
			||||||
 | 
					            int? paddedFileSizeLimit,
 | 
				
			||||||
 | 
					            string? status,
 | 
				
			||||||
 | 
					            string? comment,
 | 
				
			||||||
 | 
					            string? torrentFilePath,
 | 
				
			||||||
 | 
					            string? source,
 | 
				
			||||||
 | 
					            IReadOnlyList<string>? trackers,
 | 
				
			||||||
 | 
					            IReadOnlyList<string>? urlSeeds,
 | 
				
			||||||
 | 
					            string? timeStarted,
 | 
				
			||||||
 | 
					            string? timeFinished,
 | 
				
			||||||
 | 
					            string? errorMessage,
 | 
				
			||||||
 | 
					            double? progress)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            TaskId = taskID;
 | 
				
			||||||
 | 
					            SourcePath = sourcePath;
 | 
				
			||||||
 | 
					            PieceSize = pieceSize;
 | 
				
			||||||
 | 
					            Private = @private;
 | 
				
			||||||
 | 
					            TimeAdded = timeAdded;
 | 
				
			||||||
 | 
					            Format = format;
 | 
				
			||||||
 | 
					            OptimizeAlignment = optimizeAlignment;
 | 
				
			||||||
 | 
					            PaddedFileSizeLimit = paddedFileSizeLimit;
 | 
				
			||||||
 | 
					            Status = status;
 | 
				
			||||||
 | 
					            Comment = comment;
 | 
				
			||||||
 | 
					            TorrentFilePath = torrentFilePath;
 | 
				
			||||||
 | 
					            Source = source;
 | 
				
			||||||
 | 
					            Trackers = trackers ?? Array.Empty<string>();
 | 
				
			||||||
 | 
					            UrlSeeds = urlSeeds ?? Array.Empty<string>();
 | 
				
			||||||
 | 
					            TimeStarted = timeStarted;
 | 
				
			||||||
 | 
					            TimeFinished = timeFinished;
 | 
				
			||||||
 | 
					            ErrorMessage = errorMessage;
 | 
				
			||||||
 | 
					            Progress = progress;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("taskID")]
 | 
				
			||||||
 | 
					        public string TaskId { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("sourcePath")]
 | 
				
			||||||
 | 
					        public string? SourcePath { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("pieceSize")]
 | 
				
			||||||
 | 
					        public int? PieceSize { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("private")]
 | 
				
			||||||
 | 
					        public bool? Private { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("timeAdded")]
 | 
				
			||||||
 | 
					        public string? TimeAdded { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("format")]
 | 
				
			||||||
 | 
					        public string? Format { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("optimizeAlignment")]
 | 
				
			||||||
 | 
					        public bool? OptimizeAlignment { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("paddedFileSizeLimit")]
 | 
				
			||||||
 | 
					        public int? PaddedFileSizeLimit { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("status")]
 | 
				
			||||||
 | 
					        public string? Status { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("comment")]
 | 
				
			||||||
 | 
					        public string? Comment { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("torrentFilePath")]
 | 
				
			||||||
 | 
					        public string? TorrentFilePath { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("source")]
 | 
				
			||||||
 | 
					        public string? Source { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("trackers")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<string> Trackers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("urlSeeds")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<string> UrlSeeds { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("timeStarted")]
 | 
				
			||||||
 | 
					        public string? TimeStarted { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("timeFinished")]
 | 
				
			||||||
 | 
					        public string? TimeFinished { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("errorMessage")]
 | 
				
			||||||
 | 
					        public string? ErrorMessage { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("progress")]
 | 
				
			||||||
 | 
					        public double? Progress { get; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										105
									
								
								Lantean.QBitTorrentClient/Models/TorrentMetadata.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								Lantean.QBitTorrentClient/Models/TorrentMetadata.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public record TorrentMetadata
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [JsonConstructor]
 | 
				
			||||||
 | 
					        public TorrentMetadata(
 | 
				
			||||||
 | 
					            string? infoHashV1,
 | 
				
			||||||
 | 
					            string? infoHashV2,
 | 
				
			||||||
 | 
					            string? hash,
 | 
				
			||||||
 | 
					            TorrentMetadataInfo? info,
 | 
				
			||||||
 | 
					            IReadOnlyList<TorrentMetadataTracker>? trackers,
 | 
				
			||||||
 | 
					            IReadOnlyList<string>? webSeeds,
 | 
				
			||||||
 | 
					            string? createdBy,
 | 
				
			||||||
 | 
					            long? creationDate,
 | 
				
			||||||
 | 
					            string? comment)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            InfoHashV1 = infoHashV1;
 | 
				
			||||||
 | 
					            InfoHashV2 = infoHashV2;
 | 
				
			||||||
 | 
					            Hash = hash;
 | 
				
			||||||
 | 
					            Info = info;
 | 
				
			||||||
 | 
					            Trackers = trackers ?? Array.Empty<TorrentMetadataTracker>();
 | 
				
			||||||
 | 
					            WebSeeds = webSeeds ?? Array.Empty<string>();
 | 
				
			||||||
 | 
					            CreatedBy = createdBy;
 | 
				
			||||||
 | 
					            CreationDate = creationDate;
 | 
				
			||||||
 | 
					            Comment = comment;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("infohash_v1")]
 | 
				
			||||||
 | 
					        public string? InfoHashV1 { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("infohash_v2")]
 | 
				
			||||||
 | 
					        public string? InfoHashV2 { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("hash")]
 | 
				
			||||||
 | 
					        public string? Hash { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("info")]
 | 
				
			||||||
 | 
					        public TorrentMetadataInfo? Info { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("trackers")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<TorrentMetadataTracker> Trackers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("webseeds")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<string> WebSeeds { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("created_by")]
 | 
				
			||||||
 | 
					        public string? CreatedBy { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("creation_date")]
 | 
				
			||||||
 | 
					        public long? CreationDate { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("comment")]
 | 
				
			||||||
 | 
					        public string? Comment { get; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public record TorrentMetadataInfo
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [JsonConstructor]
 | 
				
			||||||
 | 
					        public TorrentMetadataInfo(
 | 
				
			||||||
 | 
					            IReadOnlyList<TorrentMetadataFile>? files,
 | 
				
			||||||
 | 
					            long? length,
 | 
				
			||||||
 | 
					            string? name,
 | 
				
			||||||
 | 
					            long? pieceLength,
 | 
				
			||||||
 | 
					            int? piecesCount,
 | 
				
			||||||
 | 
					            bool? @private)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Files = files ?? Array.Empty<TorrentMetadataFile>();
 | 
				
			||||||
 | 
					            Length = length;
 | 
				
			||||||
 | 
					            Name = name;
 | 
				
			||||||
 | 
					            PieceLength = pieceLength;
 | 
				
			||||||
 | 
					            PiecesCount = piecesCount;
 | 
				
			||||||
 | 
					            Private = @private;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("files")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<TorrentMetadataFile> Files { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("length")]
 | 
				
			||||||
 | 
					        public long? Length { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("name")]
 | 
				
			||||||
 | 
					        public string? Name { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("piece_length")]
 | 
				
			||||||
 | 
					        public long? PieceLength { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("pieces_num")]
 | 
				
			||||||
 | 
					        public int? PiecesCount { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("private")]
 | 
				
			||||||
 | 
					        public bool? Private { get; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public record TorrentMetadataFile(
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("path")] string? Path,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("length")] long? Length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public record TorrentMetadataTracker(
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("url")] string? Url,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("tier")] int? Tier);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
using System.Text.Json.Serialization;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBitTorrentClient.Models
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -13,7 +15,10 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            int seeds,
 | 
					            int seeds,
 | 
				
			||||||
            int leeches,
 | 
					            int leeches,
 | 
				
			||||||
            int downloads,
 | 
					            int downloads,
 | 
				
			||||||
            string message)
 | 
					            string message,
 | 
				
			||||||
 | 
					            long? nextAnnounce,
 | 
				
			||||||
 | 
					            long? minAnnounce,
 | 
				
			||||||
 | 
					            IReadOnlyList<TrackerEndpoint>? endpoints)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Url = url;
 | 
					            Url = url;
 | 
				
			||||||
            Status = status;
 | 
					            Status = status;
 | 
				
			||||||
@@ -23,6 +28,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
            Leeches = leeches;
 | 
					            Leeches = leeches;
 | 
				
			||||||
            Downloads = downloads;
 | 
					            Downloads = downloads;
 | 
				
			||||||
            Message = message;
 | 
					            Message = message;
 | 
				
			||||||
 | 
					            NextAnnounce = nextAnnounce;
 | 
				
			||||||
 | 
					            MinAnnounce = minAnnounce;
 | 
				
			||||||
 | 
					            Endpoints = endpoints ?? Array.Empty<TrackerEndpoint>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("url")]
 | 
					        [JsonPropertyName("url")]
 | 
				
			||||||
@@ -48,5 +56,27 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("msg")]
 | 
					        [JsonPropertyName("msg")]
 | 
				
			||||||
        public string Message { get; }
 | 
					        public string Message { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("next_announce")]
 | 
				
			||||||
 | 
					        public long? NextAnnounce { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("min_announce")]
 | 
				
			||||||
 | 
					        public long? MinAnnounce { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("endpoints")]
 | 
				
			||||||
 | 
					        public IReadOnlyList<TrackerEndpoint> Endpoints { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public record TrackerEndpoint(
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("name")] string? Name,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("updating")] bool? Updating,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("status")] TrackerStatus Status,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("msg")] string? Message,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("bt_version")] int? BitTorrentVersion,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("num_peers")] int? Peers,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("num_seeds")] int? Seeds,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("num_leeches")] int? Leeches,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("num_downloaded")] int? Downloads,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("next_announce")] long? NextAnnounce,
 | 
				
			||||||
 | 
					        [property: JsonPropertyName("min_announce")] long? MinAnnounce);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -7,5 +7,7 @@
 | 
				
			|||||||
        Working = 2,
 | 
					        Working = 2,
 | 
				
			||||||
        Updating = 3,
 | 
					        Updating = 3,
 | 
				
			||||||
        NotWorking = 4,
 | 
					        NotWorking = 4,
 | 
				
			||||||
 | 
					        Error = 5,
 | 
				
			||||||
 | 
					        Unreachable = 6
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.Text.Json.Serialization;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBitTorrentClient.Models
 | 
					namespace Lantean.QBitTorrentClient.Models
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -7,6 +8,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("add_to_top_of_queue")]
 | 
					        [JsonPropertyName("add_to_top_of_queue")]
 | 
				
			||||||
        public bool? AddToTopOfQueue { get; set; }
 | 
					        public bool? AddToTopOfQueue { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("add_stopped_enabled")]
 | 
				
			||||||
 | 
					        public bool? AddStoppedEnabled { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("add_trackers")]
 | 
					        [JsonPropertyName("add_trackers")]
 | 
				
			||||||
        public string? AddTrackers { get; set; }
 | 
					        public string? AddTrackers { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,6 +32,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("announce_ip")]
 | 
					        [JsonPropertyName("announce_ip")]
 | 
				
			||||||
        public string? AnnounceIp { get; set; }
 | 
					        public string? AnnounceIp { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("announce_port")]
 | 
				
			||||||
 | 
					        public int? AnnouncePort { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("announce_to_all_tiers")]
 | 
					        [JsonPropertyName("announce_to_all_tiers")]
 | 
				
			||||||
        public bool? AnnounceToAllTiers { get; set; }
 | 
					        public bool? AnnounceToAllTiers { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -241,6 +248,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("incomplete_files_ext")]
 | 
					        [JsonPropertyName("incomplete_files_ext")]
 | 
				
			||||||
        public bool? IncompleteFilesExt { get; set; }
 | 
					        public bool? IncompleteFilesExt { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("use_unwanted_folder")]
 | 
				
			||||||
 | 
					        public bool? UseUnwantedFolder { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("ip_filter_enabled")]
 | 
					        [JsonPropertyName("ip_filter_enabled")]
 | 
				
			||||||
        public bool? IpFilterEnabled { get; set; }
 | 
					        public bool? IpFilterEnabled { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -262,6 +272,12 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("listen_port")]
 | 
					        [JsonPropertyName("listen_port")]
 | 
				
			||||||
        public int? ListenPort { get; set; }
 | 
					        public int? ListenPort { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ssl_enabled")]
 | 
				
			||||||
 | 
					        public bool? SslEnabled { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ssl_listen_port")]
 | 
				
			||||||
 | 
					        public int? SslListenPort { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("locale")]
 | 
					        [JsonPropertyName("locale")]
 | 
				
			||||||
        public string? Locale { get; set; }
 | 
					        public string? Locale { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -466,6 +482,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("save_resume_data_interval")]
 | 
					        [JsonPropertyName("save_resume_data_interval")]
 | 
				
			||||||
        public int? SaveResumeDataInterval { get; set; }
 | 
					        public int? SaveResumeDataInterval { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("save_statistics_interval")]
 | 
				
			||||||
 | 
					        public int? SaveStatisticsInterval { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("scan_dirs")]
 | 
					        [JsonPropertyName("scan_dirs")]
 | 
				
			||||||
        public Dictionary<string, SaveLocation>? ScanDirs { get; set; }
 | 
					        public Dictionary<string, SaveLocation>? ScanDirs { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -517,9 +536,6 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("ssrf_mitigation")]
 | 
					        [JsonPropertyName("ssrf_mitigation")]
 | 
				
			||||||
        public bool? SsrfMitigation { get; set; }
 | 
					        public bool? SsrfMitigation { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("start_paused_enabled")]
 | 
					 | 
				
			||||||
        public bool? StartPausedEnabled { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [JsonPropertyName("stop_tracker_timeout")]
 | 
					        [JsonPropertyName("stop_tracker_timeout")]
 | 
				
			||||||
        public int? StopTrackerTimeout { get; set; }
 | 
					        public int? StopTrackerTimeout { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -535,6 +551,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("torrent_content_layout")]
 | 
					        [JsonPropertyName("torrent_content_layout")]
 | 
				
			||||||
        public string? TorrentContentLayout { get; set; }
 | 
					        public string? TorrentContentLayout { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("torrent_content_remove_option")]
 | 
				
			||||||
 | 
					        public string? TorrentContentRemoveOption { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("torrent_file_size_limit")]
 | 
					        [JsonPropertyName("torrent_file_size_limit")]
 | 
				
			||||||
        public int? TorrentFileSizeLimit { get; set; }
 | 
					        public int? TorrentFileSizeLimit { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -562,6 +581,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("use_https")]
 | 
					        [JsonPropertyName("use_https")]
 | 
				
			||||||
        public bool? UseHttps { get; set; }
 | 
					        public bool? UseHttps { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("ignore_ssl_errors")]
 | 
				
			||||||
 | 
					        public bool? IgnoreSslErrors { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("use_subcategories")]
 | 
					        [JsonPropertyName("use_subcategories")]
 | 
				
			||||||
        public bool? UseSubcategories { get; set; }
 | 
					        public bool? UseSubcategories { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -574,6 +596,9 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
        [JsonPropertyName("web_ui_address")]
 | 
					        [JsonPropertyName("web_ui_address")]
 | 
				
			||||||
        public string? WebUiAddress { get; set; }
 | 
					        public string? WebUiAddress { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [JsonPropertyName("web_ui_api_key")]
 | 
				
			||||||
 | 
					        public string? WebUiApiKey { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("web_ui_ban_duration")]
 | 
					        [JsonPropertyName("web_ui_ban_duration")]
 | 
				
			||||||
        public int? WebUiBanDuration { get; set; }
 | 
					        public int? WebUiBanDuration { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -627,5 +652,23 @@ namespace Lantean.QBitTorrentClient.Models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        [JsonPropertyName("web_ui_password")]
 | 
					        [JsonPropertyName("web_ui_password")]
 | 
				
			||||||
        public string? WebUiPassword { get; set; }
 | 
					        public string? WebUiPassword { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Validate()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (MaxRatio.HasValue && MaxRatioEnabled.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new InvalidOperationException("Specify either max_ratio or max_ratio_enabled, not both.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (MaxSeedingTime.HasValue && MaxSeedingTimeEnabled.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new InvalidOperationException("Specify either max_seeding_time or max_seeding_time_enabled, not both.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (MaxInactiveSeedingTime.HasValue && MaxInactiveSeedingTimeEnabled.HasValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new InvalidOperationException("Specify either max_inactive_seeding_time or max_inactive_seeding_time_enabled, not both.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user