mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-10-23 04:52:22 +00:00
Fix slowness issues with FilesTab when torrents with large file lists are being rendered.
This commit is contained in:
@@ -8,6 +8,7 @@ using Lantean.QBTMud.Models;
|
|||||||
using Lantean.QBTMud.Services;
|
using Lantean.QBTMud.Services;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using MudBlazor;
|
using MudBlazor;
|
||||||
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
@@ -368,16 +369,16 @@ namespace Lantean.QBTMud.Components
|
|||||||
return FileList!.Values.Where(f => f.Name.StartsWith(contentItem.Name + Extensions.DirectorySeparator) && !f.IsFolder);
|
return FileList!.Values.Where(f => f.Name.StartsWith(contentItem.Name + Extensions.DirectorySeparator) && !f.IsFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ContentItem> GetChildren(ContentItem folder, int level)
|
private IEnumerable<ContentItem> GetChildren(ContentItem folder)
|
||||||
{
|
{
|
||||||
level++;
|
var childLevel = folder.Level + 1;
|
||||||
var descendantsKey = folder.GetDescendantsKey(level);
|
var prefix = string.Concat(folder.Name, Extensions.DirectorySeparator);
|
||||||
|
|
||||||
foreach (var item in FileList!.Values.Where(f => f.Name.StartsWith(descendantsKey) && f.Level == level).OrderByDirection(_sortDirection, GetSortSelector()))
|
foreach (var item in FileList!.Values.Where(f => f.Level == childLevel && f.Name.StartsWith(prefix, StringComparison.Ordinal)).OrderByDirection(_sortDirection, GetSortSelector()))
|
||||||
{
|
{
|
||||||
if (item.IsFolder)
|
if (item.IsFolder)
|
||||||
{
|
{
|
||||||
var descendants = GetChildren(item, level);
|
var descendants = GetChildren(item);
|
||||||
// if the filter returns some results then show folder item
|
// if the filter returns some results then show folder item
|
||||||
if (descendants.Any())
|
if (descendants.Any())
|
||||||
{
|
{
|
||||||
@@ -451,8 +452,7 @@ namespace Lantean.QBTMud.Components
|
|||||||
|
|
||||||
if (item.IsFolder && ExpandedNodes.Contains(item.Name))
|
if (item.IsFolder && ExpandedNodes.Contains(item.Name))
|
||||||
{
|
{
|
||||||
var level = 0;
|
var descendants = GetChildren(item);
|
||||||
var descendants = GetChildren(item, level);
|
|
||||||
foreach (var descendant in descendants)
|
foreach (var descendant in descendants)
|
||||||
{
|
{
|
||||||
list.Add(descendant);
|
list.Add(descendant);
|
||||||
@@ -552,4 +552,4 @@ namespace Lantean.QBTMud.Components
|
|||||||
ColumnDefinitionHelper.CreateColumnDefinition<ContentItem>("Availability", c => c.Availability, c => c.Availability.ToString("0.00")),
|
ColumnDefinitionHelper.CreateColumnDefinition<ContentItem>("Availability", c => c.Availability, c => c.Availability.ToString("0.00")),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -625,82 +625,140 @@ namespace Lantean.QBTMud.Services
|
|||||||
|
|
||||||
public Dictionary<string, ContentItem> CreateContentsList(IReadOnlyList<QBitTorrentClient.Models.FileData> files)
|
public Dictionary<string, ContentItem> CreateContentsList(IReadOnlyList<QBitTorrentClient.Models.FileData> files)
|
||||||
{
|
{
|
||||||
var contents = new Dictionary<string, ContentItem>();
|
return BuildContentsTree(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, ContentItem> BuildContentsTree(IReadOnlyList<QBitTorrentClient.Models.FileData> files)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, ContentItem>();
|
||||||
if (files.Count == 0)
|
if (files.Count == 0)
|
||||||
{
|
{
|
||||||
return contents;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var folderIndex = files.Min(f => f.Index) - 1;
|
var folderIndex = files.Min(f => f.Index) - 1;
|
||||||
|
var nodes = new Dictionary<string, ContentTreeNode>(files.Count * 2);
|
||||||
|
var root = new ContentTreeNode(null, null);
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
if (!file.Name.Contains(Extensions.DirectorySeparator))
|
var parent = root;
|
||||||
|
string? parentPath = parent.Item?.Name;
|
||||||
|
|
||||||
|
var segments = file.Name.Split(Extensions.DirectorySeparator);
|
||||||
|
var directoriesLength = segments.Length - 1;
|
||||||
|
|
||||||
|
for (var i = 0; i < directoriesLength; i++)
|
||||||
{
|
{
|
||||||
contents.Add(file.Name, new ContentItem(file.Name, file.Name, file.Index, (Priority)(int)file.Priority, file.Progress, file.Size, file.Availability));
|
var folderName = segments[i];
|
||||||
}
|
if (folderName == ".unwanted")
|
||||||
else
|
|
||||||
{
|
|
||||||
var nameAndPath = file.Name.Split(Extensions.DirectorySeparator);
|
|
||||||
var paths = nameAndPath[..^1];
|
|
||||||
for (var i = 0; i < paths.Length; i++)
|
|
||||||
{
|
{
|
||||||
var directoryName = paths[i];
|
continue;
|
||||||
var directoryPath = string.Join(Extensions.DirectorySeparator, paths[0..(i + 1)]);
|
|
||||||
if (!contents.ContainsKey(directoryPath))
|
|
||||||
{
|
|
||||||
contents.Add(directoryPath, new ContentItem(directoryPath, directoryName, folderIndex--, Priority.Normal, 0, 0, 0, true, i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var displayName = nameAndPath[^1];
|
var folderPath = string.IsNullOrEmpty(parentPath)
|
||||||
|
? folderName
|
||||||
|
: string.Concat(parentPath, Extensions.DirectorySeparator, folderName);
|
||||||
|
|
||||||
contents.Add(file.Name, new ContentItem(file.Name, displayName, file.Index, (Priority)(int)file.Priority, file.Progress, file.Size, file.Availability, false, paths.Length));
|
if (!nodes.TryGetValue(folderPath, out var folderNode))
|
||||||
|
{
|
||||||
|
var level = (parent.Item?.Level ?? -1) + 1;
|
||||||
|
var folderItem = new ContentItem(folderPath, folderName, folderIndex--, Priority.Normal, 0, 0, 0, true, level);
|
||||||
|
folderNode = new ContentTreeNode(folderItem, parent);
|
||||||
|
nodes[folderPath] = folderNode;
|
||||||
|
parent.Children[folderPath] = folderNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = folderNode;
|
||||||
|
parentPath = parent.Item!.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var displayName = segments[^1];
|
||||||
|
var fileLevel = (parent.Item?.Level ?? -1) + 1;
|
||||||
|
var fileItem = new ContentItem(file.Name, displayName, file.Index, (Priority)(int)file.Priority, file.Progress, file.Size, file.Availability, false, fileLevel);
|
||||||
|
var fileNode = new ContentTreeNode(fileItem, parent);
|
||||||
|
nodes[file.Name] = fileNode;
|
||||||
|
parent.Children[fileItem.Name] = fileNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
var directories = contents.Where(c => c.Value.IsFolder).OrderByDescending(c => c.Value.Level);
|
var folders = nodes.Values
|
||||||
|
.Where(n => n.Item is not null && n.Item.IsFolder)
|
||||||
|
.OrderByDescending(n => n.Item!.Level)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
foreach (var directory in directories)
|
foreach (var folder in folders)
|
||||||
{
|
{
|
||||||
var key = directory.Key;
|
var folderItem = folder.Item!;
|
||||||
var level = directory.Value.Level;
|
if (folder.Children.Count == 0)
|
||||||
var filesContents = contents.Where(c => c.Value.Name.StartsWith(key + Extensions.DirectorySeparator) && !c.Value.IsFolder).ToList();
|
|
||||||
var directoriesContents = contents.Where(c => c.Value.Name.StartsWith(key + Extensions.DirectorySeparator) && c.Value.IsFolder && c.Value.Level == level + 1).ToList();
|
|
||||||
var allContents = filesContents.Concat(directoriesContents);
|
|
||||||
var priorities = allContents.Select(d => d.Value.Priority).Distinct();
|
|
||||||
var downloadingContents = allContents.Where(c => c.Value.Priority != Priority.DoNotDownload && !c.Value.IsFolder).ToList();
|
|
||||||
|
|
||||||
long size = 0;
|
|
||||||
float availability = 0;
|
|
||||||
long downloaded = 0;
|
|
||||||
float progress = 0;
|
|
||||||
if (downloadingContents.Count != 0)
|
|
||||||
{
|
{
|
||||||
size = downloadingContents.Sum(c => c.Value.Size);
|
folderItem.Size = 0;
|
||||||
availability = downloadingContents.Average(c => c.Value.Availability);
|
folderItem.Progress = 0;
|
||||||
downloaded = downloadingContents.Sum(c => c.Value.Downloaded);
|
folderItem.Availability = 0;
|
||||||
progress = (float)downloaded / size;
|
folderItem.Priority = Priority.Normal;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contents.TryGetValue(key, out var dir))
|
long sizeSum = 0;
|
||||||
|
double progressSum = 0;
|
||||||
|
double availabilitySum = 0;
|
||||||
|
var firstChild = true;
|
||||||
|
var aggregatedPriority = Priority.Normal;
|
||||||
|
|
||||||
|
foreach (var child in folder.Children.Values)
|
||||||
|
{
|
||||||
|
var childItem = child.Item!;
|
||||||
|
sizeSum += childItem.Size;
|
||||||
|
|
||||||
|
if (firstChild)
|
||||||
|
{
|
||||||
|
aggregatedPriority = childItem.Priority;
|
||||||
|
firstChild = false;
|
||||||
|
}
|
||||||
|
else if (aggregatedPriority != childItem.Priority)
|
||||||
|
{
|
||||||
|
aggregatedPriority = Priority.Mixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childItem.Priority != Priority.DoNotDownload)
|
||||||
|
{
|
||||||
|
progressSum += childItem.Progress * childItem.Size;
|
||||||
|
availabilitySum += childItem.Availability * childItem.Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
folderItem.Size = sizeSum;
|
||||||
|
folderItem.Progress = sizeSum > 0 ? (float)(progressSum / sizeSum) : 0;
|
||||||
|
folderItem.Availability = sizeSum > 0 ? (float)(availabilitySum / sizeSum) : 0;
|
||||||
|
folderItem.Priority = firstChild ? Priority.Normal : aggregatedPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var node in nodes.Values)
|
||||||
|
{
|
||||||
|
if (node.Item is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dir.Availability = availability;
|
|
||||||
dir.Size = size;
|
result[node.Item.Name] = node.Item;
|
||||||
dir.Progress = progress;
|
|
||||||
if (priorities.Count() == 1)
|
|
||||||
{
|
|
||||||
dir.Priority = priorities.First();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dir.Priority = Priority.Mixed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return contents;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ContentTreeNode
|
||||||
|
{
|
||||||
|
public ContentTreeNode(ContentItem? item, ContentTreeNode? parent)
|
||||||
|
{
|
||||||
|
Item = item;
|
||||||
|
Parent = parent;
|
||||||
|
Children = new Dictionary<string, ContentTreeNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentItem? Item { get; }
|
||||||
|
|
||||||
|
public ContentTreeNode? Parent { get; }
|
||||||
|
|
||||||
|
public Dictionary<string, ContentTreeNode> Children { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public QBitTorrentClient.Models.UpdatePreferences MergePreferences(QBitTorrentClient.Models.UpdatePreferences? original, QBitTorrentClient.Models.UpdatePreferences changed)
|
public QBitTorrentClient.Models.UpdatePreferences MergePreferences(QBitTorrentClient.Models.UpdatePreferences? original, QBitTorrentClient.Models.UpdatePreferences changed)
|
||||||
@@ -1175,4 +1233,4 @@ namespace Lantean.QBTMud.Services
|
|||||||
return new RssList(feeds, articles);
|
return new RssList(feeds, articles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,4 +22,4 @@ namespace Lantean.QBTMud.Services
|
|||||||
|
|
||||||
RssList CreateRssList(IReadOnlyDictionary<string, QBitTorrentClient.Models.RssItem> rssItems);
|
RssList CreateRssList(IReadOnlyDictionary<string, QBitTorrentClient.Models.RssItem> rssItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user