mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-10-23 04:52:22 +00:00
Update project name and namespaces
This commit is contained in:
42
Lantean.QBTMud/Helpers/ColumnDefinitionHelper.cs
Normal file
42
Lantean.QBTMud/Helpers/ColumnDefinitionHelper.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Lantean.QBTMud.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMud.Helpers
|
||||
{
|
||||
public static class ColumnDefinitionHelper
|
||||
{
|
||||
public static ColumnDefinition<T> CreateColumnDefinition<T>(string name, Func<T, object?> selector, RenderFragment<RowContext<T>> rowTemplate, bool iconOnly = false, int? width = null, string? tdClass = null, Func<T, string?>? classFunc = null, bool enabled = true, SortDirection initialDirection = SortDirection.None)
|
||||
{
|
||||
var cd = new ColumnDefinition<T>(name, selector, rowTemplate);
|
||||
cd.Class = "no-wrap";
|
||||
if (tdClass is not null)
|
||||
{
|
||||
cd.Class += " " + tdClass;
|
||||
}
|
||||
cd.ClassFunc = classFunc;
|
||||
cd.Width = width;
|
||||
cd.Enabled = enabled;
|
||||
cd.InitialDirection = initialDirection;
|
||||
cd.IconOnly = iconOnly;
|
||||
return cd;
|
||||
}
|
||||
|
||||
public static ColumnDefinition<T> CreateColumnDefinition<T>(string name, Func<T, object?> selector, Func<T, string>? formatter = null, bool iconOnly = false, int? width = null, string? tdClass = null, Func<T, string?>? classFunc = null, bool enabled = true, SortDirection initialDirection = SortDirection.None)
|
||||
{
|
||||
var cd = new ColumnDefinition<T>(name, selector, formatter);
|
||||
cd.Class = "no-wrap";
|
||||
if (tdClass is not null)
|
||||
{
|
||||
cd.Class += " " + tdClass;
|
||||
}
|
||||
cd.ClassFunc = classFunc;
|
||||
cd.Width = width;
|
||||
cd.Enabled = enabled;
|
||||
cd.InitialDirection = initialDirection;
|
||||
cd.IconOnly = iconOnly;
|
||||
|
||||
return cd;
|
||||
}
|
||||
}
|
||||
}
|
426
Lantean.QBTMud/Helpers/DialogHelper.cs
Normal file
426
Lantean.QBTMud/Helpers/DialogHelper.cs
Normal file
@@ -0,0 +1,426 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMud.Components.Dialogs;
|
||||
using Lantean.QBTMud.Filter;
|
||||
using Lantean.QBTMud.Models;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMud.Helpers
|
||||
{
|
||||
public static class DialogHelper
|
||||
{
|
||||
public const long _maxFileSize = 4194304;
|
||||
public static readonly DialogOptions ConfirmDialogOptions = new() { BackgroundClass = "background-blur", MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
public static readonly DialogOptions FormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, BackgroundClass = "background-blur", FullWidth = true };
|
||||
|
||||
public static readonly DialogOptions FullScreenDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.ExtraExtraLarge, BackgroundClass = "background-blur", FullWidth = true };
|
||||
public static readonly DialogOptions NonBlurConfirmDialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
public static readonly DialogOptions NonBlurFormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
|
||||
|
||||
public static async Task<string?> InvokeAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient)
|
||||
{
|
||||
var reference = await dialogService.ShowAsync<CategoryPropertiesDialog>("Add Category", NonBlurFormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var category = (Category)dialogResult.Data;
|
||||
|
||||
await apiClient.AddCategory(category.Name, category.SavePath);
|
||||
|
||||
return category.Name;
|
||||
}
|
||||
|
||||
public static async Task InvokeAddTorrentFileDialog(this IDialogService dialogService, IApiClient apiClient)
|
||||
{
|
||||
var result = await dialogService.ShowAsync<AddTorrentFileDialog>("Upload local torrent", FormDialogOptions);
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = (AddTorrentFileOptions)dialogResult.Data;
|
||||
|
||||
var streams = new List<Stream>();
|
||||
|
||||
var files = new Dictionary<string, Stream>();
|
||||
foreach (var file in options.Files)
|
||||
{
|
||||
var stream = file.OpenReadStream(_maxFileSize);
|
||||
streams.Add(stream);
|
||||
files.Add(file.Name, stream);
|
||||
}
|
||||
|
||||
await apiClient.AddTorrent(
|
||||
urls: null,
|
||||
files,
|
||||
options.SavePath,
|
||||
options.Cookie,
|
||||
options.Category,
|
||||
tags: null,
|
||||
options.SkipHashCheck,
|
||||
!options.StartTorrent,
|
||||
options.ContentLayout,
|
||||
options.RenameTorrent,
|
||||
options.UploadLimit,
|
||||
options.DownloadLimit,
|
||||
ratioLimit: null,
|
||||
seedingTimeLimit: null,
|
||||
options.TorrentManagementMode,
|
||||
options.DownloadInSequentialOrder,
|
||||
options.DownloadFirstAndLastPiecesFirst);
|
||||
|
||||
foreach (var stream in streams)
|
||||
{
|
||||
await stream.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task InvokeAddTorrentLinkDialog(this IDialogService dialogService, IApiClient apiClient, string? url = null)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(AddTorrentLinkDialog.Url), url }
|
||||
};
|
||||
|
||||
var result = await dialogService.ShowAsync<AddTorrentLinkDialog>("Download from URLs", parameters, FormDialogOptions);
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = (AddTorrentLinkOptions)dialogResult.Data;
|
||||
|
||||
await apiClient.AddTorrent(
|
||||
urls: options.Urls,
|
||||
torrents: null,
|
||||
options.SavePath,
|
||||
options.Cookie,
|
||||
options.Category,
|
||||
tags: null,
|
||||
options.SkipHashCheck,
|
||||
!options.StartTorrent,
|
||||
options.ContentLayout,
|
||||
options.RenameTorrent,
|
||||
options.UploadLimit,
|
||||
options.DownloadLimit,
|
||||
ratioLimit: null,
|
||||
seedingTimeLimit: null,
|
||||
options.TorrentManagementMode,
|
||||
options.DownloadInSequentialOrder,
|
||||
options.DownloadFirstAndLastPiecesFirst);
|
||||
}
|
||||
|
||||
public static async Task<bool> InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, params string[] hashes)
|
||||
{
|
||||
if (hashes.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(DeleteDialog.Count), hashes.Length }
|
||||
};
|
||||
|
||||
var reference = await dialogService.ShowAsync<DeleteDialog>($"Remove torrent{(hashes.Length == 1 ? "" : "s")}?", parameters, ConfirmDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await apiClient.DeleteTorrents(hashes, (bool)dialogResult.Data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task InvokeDownloadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable<string> hashes)
|
||||
{
|
||||
Func<long, string> valueDisplayFunc = v => v == Limits.NoLimit ? "∞" : v.ToString();
|
||||
Func<string, long> valueGetFunc = v => v == "∞" ? Limits.NoLimit : long.Parse(v);
|
||||
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(SliderFieldDialog<long>.Min), -1L },
|
||||
{ nameof(SliderFieldDialog<long>.Max), 1000L },
|
||||
{ nameof(SliderFieldDialog<long>.Value), rate / 1024 },
|
||||
{ nameof(SliderFieldDialog<long>.ValueDisplayFunc), valueDisplayFunc },
|
||||
{ nameof(SliderFieldDialog<long>.ValueGetFunc), valueGetFunc },
|
||||
{ nameof(SliderFieldDialog<long>.Label), "Download rate limit" },
|
||||
{ nameof(SliderFieldDialog<long>.Adornment), Adornment.End },
|
||||
{ nameof(SliderFieldDialog<long>.AdornmentText), "KiB/s" },
|
||||
};
|
||||
var result = await dialogService.ShowAsync<SliderFieldDialog<long>>("Download Rate", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var kibs = (long)dialogResult.Data;
|
||||
await apiClient.SetTorrentDownloadLimit(kibs * 1024, null, hashes.ToArray());
|
||||
}
|
||||
|
||||
public static async Task<string?> InvokeEditCategoryDialog(this IDialogService dialogService, IApiClient apiClient, string categoryName)
|
||||
{
|
||||
var category = (await apiClient.GetAllCategories()).FirstOrDefault(c => c.Key == categoryName).Value;
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(CategoryPropertiesDialog.Category), category?.Name },
|
||||
{ nameof(CategoryPropertiesDialog.SavePath), category?.SavePath },
|
||||
};
|
||||
|
||||
var reference = await dialogService.ShowAsync<CategoryPropertiesDialog>("Edit Category", parameters, NonBlurFormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var updatedCategory = (Category)dialogResult.Data;
|
||||
|
||||
await apiClient.EditCategory(updatedCategory.Name, updatedCategory.SavePath);
|
||||
|
||||
return updatedCategory.Name;
|
||||
}
|
||||
|
||||
public static async Task InvokeRenameFilesDialog(this IDialogService dialogService, string hash)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
//{ nameof(RenameFilesDialog.Hash), hash }
|
||||
};
|
||||
|
||||
await dialogService.ShowAsync<RenameFilesDialog>("Rename Files", parameters, FullScreenDialogOptions);
|
||||
}
|
||||
|
||||
public static async Task InvokeRssRulesDialog(this IDialogService dialogService)
|
||||
{
|
||||
await dialogService.ShowAsync<RssRulesDialog>("Edit Rss Auto Downloading Rules", FullScreenDialogOptions);
|
||||
}
|
||||
|
||||
public static async Task InvokeShareRatioDialog(this IDialogService dialogService, IApiClient apiClient, IEnumerable<Torrent> torrents)
|
||||
{
|
||||
var torrentShareRatios = torrents.Select(t => new ShareRatioMax
|
||||
{
|
||||
InactiveSeedingTimeLimit = t.InactiveSeedingTimeLimit,
|
||||
MaxInactiveSeedingTime = t.InactiveSeedingTimeLimit,
|
||||
MaxRatio = t.MaxRatio,
|
||||
MaxSeedingTime = t.MaxSeedingTime,
|
||||
RatioLimit = t.RatioLimit,
|
||||
SeedingTimeLimit = t.SeedingTimeLimit,
|
||||
});
|
||||
|
||||
var torrentsHaveSameShareRatio = torrentShareRatios.Distinct().Count() == 1;
|
||||
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ShareRatioDialog.Value), torrentsHaveSameShareRatio ? torrentShareRatios.FirstOrDefault() : null },
|
||||
};
|
||||
var result = await dialogService.ShowAsync<ShareRatioDialog>("Share ratio", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var shareRatio = (ShareRatio)dialogResult.Data;
|
||||
|
||||
await apiClient.SetTorrentShareLimit(shareRatio.RatioLimit, shareRatio.SeedingTimeLimit, shareRatio.InactiveSeedingTimeLimit, null, torrents.Select(t => t.Hash).ToArray());
|
||||
}
|
||||
|
||||
public static async Task InvokeStringFieldDialog(this IDialogService dialogService, string title, string label, string? value, Func<string, Task> onSuccess)
|
||||
{
|
||||
var result = await dialogService.ShowStringFieldDialog(title, label, value);
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
await onSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task InvokeUploadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable<string> hashes)
|
||||
{
|
||||
Func<long, string> valueDisplayFunc = v => v == Limits.NoLimit ? "∞" : v.ToString();
|
||||
Func<string, long> valueGetFunc = v => v == "∞" ? Limits.NoLimit : long.Parse(v);
|
||||
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(SliderFieldDialog<long>.Min), -1L },
|
||||
{ nameof(SliderFieldDialog<long>.Max), 1000L },
|
||||
{ nameof(SliderFieldDialog<long>.Value), rate / 1024 },
|
||||
{ nameof(SliderFieldDialog<long>.ValueDisplayFunc), valueDisplayFunc },
|
||||
{ nameof(SliderFieldDialog<long>.ValueGetFunc), valueGetFunc },
|
||||
{ nameof(SliderFieldDialog<long>.Label), "Upload rate limit" },
|
||||
{ nameof(SliderFieldDialog<long>.Adornment), Adornment.End },
|
||||
{ nameof(SliderFieldDialog<long>.AdornmentText), "KiB/s" },
|
||||
};
|
||||
var result = await dialogService.ShowAsync<SliderFieldDialog<long>>("Upload Rate", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var kibs = (long)dialogResult.Data;
|
||||
await apiClient.SetTorrentUploadLimit(kibs * 1024, null, hashes.ToArray());
|
||||
}
|
||||
|
||||
public static async Task<HashSet<QBitTorrentClient.Models.PeerId>?> ShowAddPeersDialog(this IDialogService dialogService)
|
||||
{
|
||||
var reference = await dialogService.ShowAsync<AddPeerDialog>("Add Peer", NonBlurFormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var peers = (HashSet<QBitTorrentClient.Models.PeerId>)dialogResult.Data;
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
public static async Task<HashSet<string>?> ShowAddTagsDialog(this IDialogService dialogService)
|
||||
{
|
||||
var reference = await dialogService.ShowAsync<AddTagDialog>("Add Tags", NonBlurFormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var tags = (HashSet<string>)dialogResult.Data;
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static async Task<HashSet<string>?> ShowAddTrackersDialog(this IDialogService dialogService)
|
||||
{
|
||||
var reference = await dialogService.ShowAsync<AddTrackerDialog>("Add Tracker", NonBlurFormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var tags = (HashSet<string>)dialogResult.Data;
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static async Task<(HashSet<string> SelectedColumns, Dictionary<string, int?> ColumnWidths)> ShowColumnsOptionsDialog<T>(this IDialogService dialogService, List<ColumnDefinition<T>> columnDefinitions, HashSet<string> selectedColumns, Dictionary<string, int?> widths)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ColumnOptionsDialog<T>.Columns), columnDefinitions },
|
||||
{ nameof(ColumnOptionsDialog<T>.SelectedColumns), selectedColumns },
|
||||
{ nameof(ColumnOptionsDialog<T>.Widths), widths },
|
||||
};
|
||||
|
||||
var reference = await dialogService.ShowAsync<ColumnOptionsDialog<T>>("Column Options", parameters, FormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return ((HashSet<string>, Dictionary<string, int?>))dialogResult.Data;
|
||||
}
|
||||
|
||||
public static async Task<bool> ShowConfirmDialog(this IDialogService dialogService, string title, string content)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ConfirmDialog.Content), content }
|
||||
};
|
||||
var result = await dialogService.ShowAsync<ConfirmDialog>(title, parameters, ConfirmDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
return dialogResult is not null && !dialogResult.Canceled;
|
||||
}
|
||||
|
||||
public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content, Func<Task> onSuccess)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ConfirmDialog.Content), content }
|
||||
};
|
||||
var result = await dialogService.ShowAsync<ConfirmDialog>(title, parameters, ConfirmDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await onSuccess();
|
||||
}
|
||||
|
||||
public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content, Action onSuccess)
|
||||
{
|
||||
await dialogService.ShowConfirmDialog(title, content, () =>
|
||||
{
|
||||
onSuccess();
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public static async Task<List<PropertyFilterDefinition<T>>?> ShowFilterOptionsDialog<T>(this IDialogService dialogService, List<PropertyFilterDefinition<T>>? propertyFilterDefinitions)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(FilterOptionsDialog<T>.FilterDefinitions), propertyFilterDefinitions },
|
||||
};
|
||||
|
||||
var result = await dialogService.ShowAsync<FilterOptionsDialog<T>>("Filters", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (List<PropertyFilterDefinition<T>>?)dialogResult.Data;
|
||||
}
|
||||
|
||||
public static async Task<string?> ShowStringFieldDialog(this IDialogService dialogService, string title, string label, string? value)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(StringFieldDialog.Label), label },
|
||||
{ nameof(StringFieldDialog.Value), value }
|
||||
};
|
||||
var result = await dialogService.ShowAsync<StringFieldDialog>(title, parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string)dialogResult.Data;
|
||||
}
|
||||
|
||||
public static async Task ShowSubMenu(this IDialogService dialogService, IEnumerable<string> hashes, UIAction parent, Dictionary<string, Torrent> torrents, QBitTorrentClient.Models.Preferences? preferences)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(SubMenuDialog.ParentAction), parent },
|
||||
{ nameof(SubMenuDialog.Hashes), hashes },
|
||||
{ nameof(SubMenuDialog.Torrents), torrents },
|
||||
{ nameof(SubMenuDialog.Preferences), preferences },
|
||||
};
|
||||
|
||||
await dialogService.ShowAsync<SubMenuDialog>(parent.Text, parameters, FormDialogOptions);
|
||||
}
|
||||
}
|
||||
}
|
421
Lantean.QBTMud/Helpers/DisplayHelpers.cs
Normal file
421
Lantean.QBTMud/Helpers/DisplayHelpers.cs
Normal file
@@ -0,0 +1,421 @@
|
||||
using ByteSizeLib;
|
||||
using Lantean.QBTMud.Models;
|
||||
using MudBlazor;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
||||
namespace Lantean.QBTMud.Helpers
|
||||
{
|
||||
public static class DisplayHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats a time period in seconds into an appropriate unit based on the size.
|
||||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public static string Duration(long? seconds, string? prefix = null, string? suffix = null)
|
||||
{
|
||||
if (seconds is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (seconds == 8640000)
|
||||
{
|
||||
return "∞";
|
||||
}
|
||||
|
||||
if (seconds < 60)
|
||||
{
|
||||
return "< 1m";
|
||||
}
|
||||
|
||||
TimeSpan time;
|
||||
try
|
||||
{
|
||||
time = TimeSpan.FromSeconds(seconds.Value);
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
return "∞";
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
if (prefix is not null)
|
||||
{
|
||||
sb.Append(prefix);
|
||||
}
|
||||
if (time.Days > 0)
|
||||
{
|
||||
sb.Append(time.Days);
|
||||
sb.Append('d');
|
||||
|
||||
if (time.Hours != 0)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append(time.Hours);
|
||||
sb.Append('h');
|
||||
}
|
||||
}
|
||||
else if (time.Hours > 0)
|
||||
{
|
||||
sb.Append(time.Hours);
|
||||
sb.Append('h');
|
||||
|
||||
if (time.Minutes != 0)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append(time.Minutes);
|
||||
sb.Append('m');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(time.Minutes);
|
||||
sb.Append('m');
|
||||
}
|
||||
if (suffix is not null)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append(suffix);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a file size in bytes into an appropriate unit based on the size.
|
||||
/// </summary>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public static string Size(long? size, string? prefix = null, string? suffix = null)
|
||||
{
|
||||
if (size is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
size = 0;
|
||||
}
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
if (prefix is not null)
|
||||
{
|
||||
stringBuilder.Append(prefix);
|
||||
}
|
||||
stringBuilder.Append(ByteSize.FromBytes(size.Value).ToString("#.##"));
|
||||
if (suffix is not null)
|
||||
{
|
||||
stringBuilder.Append(suffix);
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a file size in bytes into an appropriate unit based on the size.
|
||||
/// </summary>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public static string Size(object? sizeValue, string? prefix = null, string? suffix = null)
|
||||
{
|
||||
if (sizeValue is not long size)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return Size(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a transfer speed in bytes/s into an appropriate unit based on the size.
|
||||
/// </summary>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public static string Speed(long? size, string? prefix = null, string? suffix = null)
|
||||
{
|
||||
if (size is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (size == -1)
|
||||
{
|
||||
return "∞";
|
||||
}
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
if (prefix is not null)
|
||||
{
|
||||
stringBuilder.Append(prefix);
|
||||
}
|
||||
stringBuilder.Append(ByteSize.FromBytes(size.Value).ToString("#.##"));
|
||||
stringBuilder.Append("/s");
|
||||
if (suffix is not null)
|
||||
{
|
||||
stringBuilder.Append(suffix);
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a value into an empty string if null, otherwise the value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public static string EmptyIfNull<T>(T? value, string? prefix = null, string? suffix = null, [StringSyntax("NumericFormat")] string? format = null) where T : struct, IConvertible
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
if (prefix is not null)
|
||||
{
|
||||
stringBuilder.Append(prefix);
|
||||
}
|
||||
|
||||
if (format is not null)
|
||||
{
|
||||
if (value is long longValue)
|
||||
{
|
||||
stringBuilder.Append(longValue.ToString(format));
|
||||
}
|
||||
else if (value is int intValue)
|
||||
{
|
||||
stringBuilder.Append(intValue.ToString(format));
|
||||
}
|
||||
else if (value is float floatValue)
|
||||
{
|
||||
stringBuilder.Append(floatValue.ToString(format));
|
||||
}
|
||||
else if (value is double doubleValue)
|
||||
{
|
||||
stringBuilder.Append(doubleValue.ToString(format));
|
||||
}
|
||||
else if (value is decimal decimalValue)
|
||||
{
|
||||
stringBuilder.Append(decimalValue.ToString(format));
|
||||
}
|
||||
else if (value is short shortValue)
|
||||
{
|
||||
stringBuilder.Append(shortValue.ToString(format));
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(value.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(value.Value);
|
||||
}
|
||||
|
||||
if (suffix is not null)
|
||||
{
|
||||
stringBuilder.Append(suffix);
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a value into an empty string if null, otherwise the value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public static string EmptyIfNull(string? value, string? prefix = null, string? suffix = null)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
if (prefix is not null)
|
||||
{
|
||||
stringBuilder.Append(prefix);
|
||||
}
|
||||
stringBuilder.Append(value);
|
||||
if (suffix is not null)
|
||||
{
|
||||
stringBuilder.Append(suffix);
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a unix time (in seconds) into a local date time.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="negativeDescription"></param>
|
||||
/// <returns></returns>
|
||||
public static string DateTime(long? value, string negativeDescription = "")
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (value.Value == -1)
|
||||
{
|
||||
return negativeDescription;
|
||||
}
|
||||
|
||||
var dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(value.Value);
|
||||
|
||||
return dateTimeOffset.ToLocalTime().DateTime.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a value into a percentage or empty string if null.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string Percentage(float? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
return "0%";
|
||||
}
|
||||
|
||||
return value.Value.ToString("0.#%");
|
||||
}
|
||||
|
||||
public static string State(string? state)
|
||||
{
|
||||
var status = state switch
|
||||
{
|
||||
"downloading" => "Downloading",
|
||||
"stalledDL" => "Stalled",
|
||||
"metaDL" => "Downloading metadata",
|
||||
"forcedMetaDL" => "[F] Downloading metadata",
|
||||
"forcedDL" => "[F] Downloading",
|
||||
"uploading" or "stalledUP" => "Seeding",
|
||||
"forcedUP" => "[F] Seeding",
|
||||
"queuedDL" or "queuedUP" => "Queued",
|
||||
"checkingDL" or "checkingUP" => "Checking",
|
||||
"queuedForChecking" => "Queued for checking",
|
||||
"checkingResumeData" => "Checking resume data",
|
||||
"pausedDL" => "Paused",
|
||||
"pausedUP" => "Completed",
|
||||
"stoppedDL" => "Stopped",
|
||||
"stoppedUP" => "Completed",
|
||||
"moving" => "Moving",
|
||||
"missingFiles" => "Missing Files",
|
||||
"error" => "Errored",
|
||||
_ => "Unknown",
|
||||
};
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public static (string, Color) GetStateIcon(string? state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case "forcedDL":
|
||||
case "metaDL":
|
||||
case "forcedMetaDL":
|
||||
case "downloading":
|
||||
return (Icons.Material.Filled.Downloading, Color.Success);
|
||||
|
||||
case "forcedUP":
|
||||
case "uploading":
|
||||
return (Icons.Material.Filled.Upload, Color.Info);
|
||||
|
||||
case "stalledUP":
|
||||
return (Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info);
|
||||
|
||||
case "stalledDL":
|
||||
return (Icons.Material.Filled.KeyboardDoubleArrowDown, Color.Success);
|
||||
|
||||
case "pausedDL":
|
||||
return (Icons.Material.Filled.Pause, Color.Success);
|
||||
|
||||
case "pausedUP":
|
||||
return (Icons.Material.Filled.Pause, Color.Info);
|
||||
|
||||
case "stoppedDL":
|
||||
return (Icons.Material.Filled.Stop, Color.Success);
|
||||
|
||||
case "stoppedUP":
|
||||
return (Icons.Material.Filled.Stop, Color.Info);
|
||||
|
||||
case "queuedDL":
|
||||
case "queuedUP":
|
||||
return (Icons.Material.Filled.Queue, Color.Default);
|
||||
|
||||
case "checkingDL":
|
||||
case "checkingUP":
|
||||
return (Icons.Material.Filled.Loop, Color.Info);
|
||||
|
||||
case "queuedForChecking":
|
||||
case "checkingResumeData":
|
||||
return (Icons.Material.Filled.Loop, Color.Warning);
|
||||
|
||||
case "moving":
|
||||
return (Icons.Material.Filled.Moving, Color.Info);
|
||||
|
||||
case "error":
|
||||
case "unknown":
|
||||
case "missingFiles":
|
||||
return (Icons.Material.Filled.Error, Color.Error);
|
||||
|
||||
default:
|
||||
return (Icons.Material.Filled.QuestionMark, Color.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
public static (string, Color) GetStatusIcon(string statusValue)
|
||||
{
|
||||
var status = Enum.Parse<Status>(statusValue);
|
||||
return GetStatusIcon(status);
|
||||
}
|
||||
|
||||
private static (string, Color) GetStatusIcon(Status status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
Status.All => (Icons.Material.Filled.AllOut, Color.Warning),
|
||||
Status.Downloading => (Icons.Material.Filled.Downloading, Color.Success),
|
||||
Status.Seeding => (Icons.Material.Filled.Upload, Color.Info),
|
||||
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.Active => (Icons.Material.Filled.Sort, Color.Success),
|
||||
Status.Inactive => (Icons.Material.Filled.Sort, Color.Error),
|
||||
Status.Stalled => (Icons.Material.Filled.Sort, Color.Info),
|
||||
Status.StalledUploading => (Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info),
|
||||
Status.StalledDownloading => (Icons.Material.Filled.KeyboardDoubleArrowDown, Color.Success),
|
||||
Status.Checking => (Icons.Material.Filled.Loop, Color.Info),
|
||||
Status.Errored => (Icons.Material.Filled.Error, Color.Error),
|
||||
_ => (Icons.Material.Filled.QuestionMark, Color.Inherit),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
287
Lantean.QBTMud/Helpers/FilterHelper.cs
Normal file
287
Lantean.QBTMud/Helpers/FilterHelper.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
using Lantean.QBTMud.Models;
|
||||
|
||||
namespace Lantean.QBTMud.Helpers
|
||||
{
|
||||
public static class FilterHelper
|
||||
{
|
||||
public const string TAG_ALL = "All";
|
||||
public const string TAG_UNTAGGED = "Untagged";
|
||||
public const string CATEGORY_ALL = "All";
|
||||
public const string CATEGORY_UNCATEGORIZED = "Uncategorized";
|
||||
public const string TRACKER_ALL = "All";
|
||||
public const string TRACKER_TRACKERLESS = "Trackerless";
|
||||
|
||||
public static IEnumerable<Torrent> Filter(this IEnumerable<Torrent> torrents, FilterState filterState)
|
||||
{
|
||||
return torrents.Where(t => FilterStatus(t, filterState.Status))
|
||||
.Where(t => FilterTag(t, filterState.Tag))
|
||||
.Where(t => FilterCategory(t, filterState.Category, filterState.UseSubcategories))
|
||||
.Where(t => FilterTracker(t, filterState.Tracker))
|
||||
.Where(t => FilterTerms(t.Name, filterState.Terms));
|
||||
}
|
||||
|
||||
public static HashSet<string> ToHashesHashSet(this IEnumerable<Torrent> torrents)
|
||||
{
|
||||
return torrents.Select(t => t.Hash).ToHashSet();
|
||||
}
|
||||
|
||||
public static bool AddIfTrue(this HashSet<string> hashSet, string value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
return hashSet.Add(value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool RemoveIfTrue(this HashSet<string> hashSet, string value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
return hashSet.Remove(value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool AddIfTrueOrRemove(this HashSet<string> hashSet, string value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
return hashSet.Add(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return hashSet.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ContainsAllTerms(string text, IEnumerable<string> terms)
|
||||
{
|
||||
return terms.Any(t =>
|
||||
{
|
||||
var term = t;
|
||||
var isTermRequired = term[0] == '+';
|
||||
var isTermExcluded = term[0] == '-';
|
||||
|
||||
if (isTermRequired || isTermExcluded)
|
||||
{
|
||||
if (term.Length == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
term = term[1..];
|
||||
}
|
||||
|
||||
var textContainsTerm = text.Contains(term, StringComparison.OrdinalIgnoreCase);
|
||||
return isTermExcluded ? !textContainsTerm : textContainsTerm;
|
||||
});
|
||||
}
|
||||
|
||||
public static bool FilterTerms(string field, string? terms)
|
||||
{
|
||||
if (terms is null || terms == "")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return ContainsAllTerms(field, terms.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
|
||||
}
|
||||
|
||||
public static bool FilterTerms(Torrent torrent, string? terms)
|
||||
{
|
||||
if (terms is null || terms == "")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return ContainsAllTerms(torrent.Name, terms.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
|
||||
}
|
||||
|
||||
public static bool FilterTracker(Torrent torrent, string tracker)
|
||||
{
|
||||
if (tracker == TRACKER_ALL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tracker == TRACKER_TRACKERLESS)
|
||||
{
|
||||
return torrent.Tracker == "";
|
||||
}
|
||||
|
||||
return torrent.Tracker.Contains(tracker);
|
||||
}
|
||||
|
||||
public static bool FilterCategory(Torrent torrent, string category, bool useSubcategories)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case CATEGORY_ALL:
|
||||
break;
|
||||
|
||||
case CATEGORY_UNCATEGORIZED:
|
||||
if (!string.IsNullOrEmpty(torrent.Category))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!useSubcategories)
|
||||
{
|
||||
if (torrent.Category != category)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!torrent.Category.StartsWith(category))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool FilterTag(Torrent torrent, string tag)
|
||||
{
|
||||
if (tag == TAG_ALL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tag == TAG_UNTAGGED)
|
||||
{
|
||||
return torrent.Tags.Count == 0;
|
||||
}
|
||||
|
||||
return torrent.Tags.Contains(tag);
|
||||
}
|
||||
|
||||
public static bool FilterStatus(Torrent torrent, Status status)
|
||||
{
|
||||
return FilterStatus(torrent.State, torrent.UploadSpeed, status);
|
||||
}
|
||||
|
||||
public static bool FilterStatus(string state, long uploadSpeed, Status status)
|
||||
{
|
||||
bool inactive = false;
|
||||
switch (status)
|
||||
{
|
||||
case Status.All:
|
||||
return true;
|
||||
|
||||
case Status.Downloading:
|
||||
if (state != "downloading" && !state.Contains("DL"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Seeding:
|
||||
if (state != "uploading" && state != "forcedUP" && state != "stalledUP" && state != "queuedUP" && state != "checkingUP")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Completed:
|
||||
if (state != "uploading" && !state.Contains("UP"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Status.Resumed:
|
||||
if (!state.Contains("resumed"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Paused:
|
||||
if (!state.Contains("paused") || !state.Contains("stopped"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Inactive:
|
||||
case Status.Active:
|
||||
if (status == Status.Inactive)
|
||||
{
|
||||
inactive = true;
|
||||
}
|
||||
bool check;
|
||||
if (state == "stalledDL")
|
||||
{
|
||||
check = uploadSpeed > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
check = state == "metaDL" || state == "forcedMetaDL" || state == "downloading" || state == "forcedDL" || state == "uploading" || state == "forcedUP";
|
||||
}
|
||||
|
||||
if (check == inactive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Stalled:
|
||||
if (state != "stalledUP" && state != "stalledDL")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.StalledUploading:
|
||||
if (state != "stalledUP")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.StalledDownloading:
|
||||
if (state != "stalledDL")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Checking:
|
||||
if (state != "checkingUP" && state != "checkingDL" && state != "checkingResumeData")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status.Errored:
|
||||
if (state != "error" && state != "unknown" && state != "missingFiles")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetStatusName(this string status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
nameof(Status.StalledUploading) => "Stalled Uploading",
|
||||
nameof(Status.StalledDownloading) => "Stalled Downloading",
|
||||
_ => status,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user