Improve torrent list performance

This commit is contained in:
ahjephson
2025-10-19 15:21:22 +01:00
parent a9a8a4eba8
commit e4dac8556e
4 changed files with 727 additions and 114 deletions

View File

@@ -1,4 +1,6 @@
using Lantean.QBitTorrentClient; using System;
using System.Linq;
using Lantean.QBitTorrentClient;
using Lantean.QBTMud.Components; using Lantean.QBTMud.Components;
using Lantean.QBTMud.Helpers; using Lantean.QBTMud.Helpers;
using Lantean.QBTMud.Models; using Lantean.QBTMud.Models;
@@ -52,22 +54,35 @@ namespace Lantean.QBTMud.Layout
protected string? SearchText { get; set; } protected string? SearchText { get; set; }
protected IEnumerable<Torrent> Torrents => GetTorrents(); protected IReadOnlyList<Torrent> Torrents => GetTorrents();
protected bool IsAuthenticated { get; set; } protected bool IsAuthenticated { get; set; }
protected bool LostConnection { get; set; } protected bool LostConnection { get; set; }
private List<Torrent> GetTorrents() private IReadOnlyList<Torrent> _visibleTorrents = Array.Empty<Torrent>();
private bool _torrentsDirty = true;
private IReadOnlyList<Torrent> GetTorrents()
{ {
if (!_torrentsDirty)
{
return _visibleTorrents;
}
if (MainData is null) if (MainData is null)
{ {
return []; _visibleTorrents = Array.Empty<Torrent>();
_torrentsDirty = false;
return _visibleTorrents;
} }
var filterState = new FilterState(Category, Status, Tag, Tracker, MainData.ServerState.UseSubcategories, SearchText); var filterState = new FilterState(Category, Status, Tag, Tracker, MainData.ServerState.UseSubcategories, SearchText);
_visibleTorrents = MainData.Torrents.Values.Filter(filterState).ToList();
_torrentsDirty = false;
return MainData.Torrents.Values.Filter(filterState).ToList(); return _visibleTorrents;
} }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@@ -84,6 +99,7 @@ namespace Lantean.QBTMud.Layout
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, Version);
MarkTorrentsDirty();
_requestId = data.ResponseId; _requestId = data.ResponseId;
_refreshInterval = MainData.ServerState.RefreshInterval; _refreshInterval = MainData.ServerState.RefreshInterval;
@@ -126,32 +142,47 @@ namespace Lantean.QBTMud.Layout
return; return;
} }
var shouldRender = false;
if (MainData is null || data.FullUpdate) if (MainData is null || data.FullUpdate)
{ {
MainData = DataManager.CreateMainData(data, Version); MainData = DataManager.CreateMainData(data, Version);
MarkTorrentsDirty();
shouldRender = true;
} }
else else
{ {
DataManager.MergeMainData(data, MainData); var dataChanged = DataManager.MergeMainData(data, MainData, out var filterChanged);
if (filterChanged)
{
MarkTorrentsDirty();
}
shouldRender = dataChanged;
} }
if (MainData is not null)
{
_refreshInterval = MainData.ServerState.RefreshInterval; _refreshInterval = MainData.ServerState.RefreshInterval;
}
_requestId = data.ResponseId; _requestId = data.ResponseId;
if (shouldRender)
{
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
} }
} }
} }
}
protected EventCallback<string> CategoryChanged => EventCallback.Factory.Create<string>(this, category => Category = category); protected EventCallback<string> CategoryChanged => EventCallback.Factory.Create<string>(this, OnCategoryChanged);
protected EventCallback<Status> StatusChanged => EventCallback.Factory.Create<Status>(this, status => Status = status); protected EventCallback<Status> StatusChanged => EventCallback.Factory.Create<Status>(this, OnStatusChanged);
protected EventCallback<string> TagChanged => EventCallback.Factory.Create<string>(this, tag => Tag = tag); protected EventCallback<string> TagChanged => EventCallback.Factory.Create<string>(this, OnTagChanged);
protected EventCallback<string> TrackerChanged => EventCallback.Factory.Create<string>(this, tracker => Tracker = tracker); protected EventCallback<string> TrackerChanged => EventCallback.Factory.Create<string>(this, OnTrackerChanged);
protected EventCallback<string> SearchTermChanged => EventCallback.Factory.Create<string>(this, term => SearchText = term); protected EventCallback<string> SearchTermChanged => EventCallback.Factory.Create<string>(this, OnSearchTermChanged);
protected EventCallback<string> SortColumnChanged => EventCallback.Factory.Create<string>(this, columnId => SortColumn = columnId); protected EventCallback<string> SortColumnChanged => EventCallback.Factory.Create<string>(this, columnId => SortColumn = columnId);
@@ -167,6 +198,67 @@ namespace Lantean.QBTMud.Layout
return (Icons.Material.Outlined.SignalWifi4Bar, Color.Success); return (Icons.Material.Outlined.SignalWifi4Bar, Color.Success);
} }
private void OnCategoryChanged(string category)
{
if (Category == category)
{
return;
}
Category = category;
MarkTorrentsDirty();
}
private void OnStatusChanged(Status status)
{
if (Status == status)
{
return;
}
Status = status;
MarkTorrentsDirty();
}
private void OnTagChanged(string tag)
{
if (Tag == tag)
{
return;
}
Tag = tag;
MarkTorrentsDirty();
}
private void OnTrackerChanged(string tracker)
{
if (Tracker == tracker)
{
return;
}
Tracker = tracker;
MarkTorrentsDirty();
}
private void OnSearchTermChanged(string term)
{
if (SearchText == term)
{
return;
}
SearchText = term;
MarkTorrentsDirty();
}
private void MarkTorrentsDirty()
{
_torrentsDirty = true;
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!_disposedValue) if (!_disposedValue)

View File

@@ -35,11 +35,14 @@ namespace Lantean.QBTMud.Pages
public QBitTorrentClient.Models.Preferences? Preferences { get; set; } public QBitTorrentClient.Models.Preferences? Preferences { get; set; }
[CascadingParameter] [CascadingParameter]
public IEnumerable<Torrent>? Torrents { get; set; } public IReadOnlyList<Torrent>? Torrents { get; set; }
[CascadingParameter] [CascadingParameter]
public MainData MainData { get; set; } = default!; public MainData MainData { get; set; } = default!;
[CascadingParameter(Name = "LostConnection")]
public bool LostConnection { get; set; }
[CascadingParameter(Name = "SearchTermChanged")] [CascadingParameter(Name = "SearchTermChanged")]
public EventCallback<string> SearchTermChanged { get; set; } public EventCallback<string> SearchTermChanged { get; set; }
@@ -64,6 +67,13 @@ namespace Lantean.QBTMud.Pages
protected ContextMenu? ContextMenu { get; set; } protected ContextMenu? ContextMenu { get; set; }
private object? _lastRenderedTorrents;
private QBitTorrentClient.Models.Preferences? _lastPreferences;
private bool _lastLostConnection;
private bool _hasRendered;
private bool _pendingSelectionChange;
private int _lastSelectionCount;
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender) if (firstRender)
@@ -73,9 +83,61 @@ namespace Lantean.QBTMud.Pages
} }
} }
protected override bool ShouldRender()
{
if (!_hasRendered)
{
_hasRendered = true;
_lastRenderedTorrents = Torrents;
_lastPreferences = Preferences;
_lastLostConnection = LostConnection;
_pendingSelectionChange = false;
_lastSelectionCount = SelectedItems.Count;
return true;
}
if (_pendingSelectionChange)
{
_pendingSelectionChange = false;
_lastSelectionCount = SelectedItems.Count;
return true;
}
if (!ReferenceEquals(_lastRenderedTorrents, Torrents))
{
_lastRenderedTorrents = Torrents;
_lastPreferences = Preferences;
_lastLostConnection = LostConnection;
_lastSelectionCount = SelectedItems.Count;
return true;
}
if (!ReferenceEquals(_lastPreferences, Preferences))
{
_lastPreferences = Preferences;
_lastSelectionCount = SelectedItems.Count;
return true;
}
if (_lastLostConnection != LostConnection)
{
_lastLostConnection = LostConnection;
_lastSelectionCount = SelectedItems.Count;
return true;
}
return false;
}
protected void SelectedItemsChanged(HashSet<Torrent> selectedItems) protected void SelectedItemsChanged(HashSet<Torrent> selectedItems)
{ {
SelectedItems = selectedItems; SelectedItems = selectedItems;
if (_lastSelectionCount != SelectedItems.Count)
{
_pendingSelectionChange = true;
_lastSelectionCount = SelectedItems.Count;
InvokeAsync(StateHasChanged);
}
} }
protected async Task SortDirectionChangedHandler(SortDirection sortDirection) protected async Task SortDirectionChangedHandler(SortDirection sortDirection)

View File

@@ -1,5 +1,6 @@
using Lantean.QBTMud.Helpers; using Lantean.QBTMud.Helpers;
using Lantean.QBTMud.Models; using Lantean.QBTMud.Models;
using System.Linq;
namespace Lantean.QBTMud.Services namespace Lantean.QBTMud.Services
{ {
@@ -142,14 +143,24 @@ namespace Lantean.QBTMud.Services
serverState.WriteCacheOverload.GetValueOrDefault()); serverState.WriteCacheOverload.GetValueOrDefault());
} }
public void MergeMainData(QBitTorrentClient.Models.MainData mainData, MainData torrentList) public bool MergeMainData(QBitTorrentClient.Models.MainData mainData, MainData torrentList, out bool filterChanged)
{ {
filterChanged = false;
var dataChanged = false;
if (mainData.CategoriesRemoved is not null) if (mainData.CategoriesRemoved is not null)
{ {
foreach (var category in mainData.CategoriesRemoved) foreach (var category in mainData.CategoriesRemoved)
{ {
torrentList.Categories.Remove(category); if (torrentList.Categories.Remove(category))
torrentList.CategoriesState.Remove(category); {
dataChanged = true;
filterChanged = true;
}
if (torrentList.CategoriesState.Remove(category))
{
filterChanged = true;
}
} }
} }
@@ -157,8 +168,15 @@ namespace Lantean.QBTMud.Services
{ {
foreach (var tag in mainData.TagsRemoved) foreach (var tag in mainData.TagsRemoved)
{ {
torrentList.Tags.Remove(tag); if (torrentList.Tags.Remove(tag))
torrentList.TagState.Remove(tag); {
dataChanged = true;
filterChanged = true;
}
if (torrentList.TagState.Remove(tag))
{
filterChanged = true;
}
} }
} }
@@ -166,17 +184,28 @@ namespace Lantean.QBTMud.Services
{ {
foreach (var tracker in mainData.TrackersRemoved) foreach (var tracker in mainData.TrackersRemoved)
{ {
torrentList.Trackers.Remove(tracker); if (torrentList.Trackers.Remove(tracker))
torrentList.TrackersState.Remove(tracker); {
dataChanged = true;
filterChanged = true;
}
if (torrentList.TrackersState.Remove(tracker))
{
filterChanged = true;
}
} }
} }
if (mainData.TorrentsRemoved is not null) if (mainData.TorrentsRemoved is not null)
{ {
foreach (var hash in mainData.TorrentsRemoved) foreach (var hash in mainData.TorrentsRemoved)
{
if (torrentList.Torrents.Remove(hash))
{ {
RemoveTorrentFromStates(torrentList, hash); RemoveTorrentFromStates(torrentList, hash);
torrentList.Torrents.Remove(hash); dataChanged = true;
filterChanged = true;
}
} }
} }
@@ -188,10 +217,12 @@ namespace Lantean.QBTMud.Services
{ {
var newCategory = CreateCategory(category); var newCategory = CreateCategory(category);
torrentList.Categories.Add(name, newCategory); torrentList.Categories.Add(name, newCategory);
dataChanged = true;
filterChanged = true;
} }
else else if (UpdateCategory(existingCategory, category))
{ {
UpdateCategory(existingCategory, category); dataChanged = true;
} }
} }
} }
@@ -200,7 +231,11 @@ namespace Lantean.QBTMud.Services
{ {
foreach (var tag in mainData.Tags) foreach (var tag in mainData.Tags)
{ {
torrentList.Tags.Add(tag); if (torrentList.Tags.Add(tag))
{
dataChanged = true;
filterChanged = true;
}
} }
} }
@@ -208,13 +243,16 @@ namespace Lantean.QBTMud.Services
{ {
foreach (var (url, hashes) in mainData.Trackers) foreach (var (url, hashes) in mainData.Trackers)
{ {
if (!torrentList.Trackers.TryGetValue(url, out _)) if (!torrentList.Trackers.TryGetValue(url, out var existingHashes))
{ {
torrentList.Trackers.Add(url, hashes); torrentList.Trackers.Add(url, hashes);
dataChanged = true;
filterChanged = true;
} }
else else if (!existingHashes.SequenceEqual(hashes))
{ {
torrentList.Trackers[url] = hashes; torrentList.Trackers[url] = hashes;
dataChanged = true;
} }
} }
} }
@@ -228,21 +266,36 @@ 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, torrentList.MajorVersion);
dataChanged = true;
filterChanged = true;
} }
else else
{
var updateResult = UpdateTorrent(existingTorrent, torrent);
if (updateResult.FilterChanged)
{ {
UpdateTorrentStates(torrentList, hash); UpdateTorrentStates(torrentList, hash);
UpdateTorrent(existingTorrent, torrent); filterChanged = true;
}
if (updateResult.DataChanged)
{
dataChanged = true;
}
} }
} }
} }
if (mainData.ServerState is not null) if (mainData.ServerState is not null)
{ {
UpdateServerState(torrentList.ServerState, mainData.ServerState); if (UpdateServerState(torrentList.ServerState, mainData.ServerState))
{
dataChanged = true;
} }
} }
return dataChanged;
}
private static void AddTorrentToStates(MainData torrentList, string hash, int version) private static void AddTorrentToStates(MainData torrentList, string hash, int version)
{ {
var torrent = torrentList.Torrents[hash]; var torrent = torrentList.Torrents[hash];
@@ -403,33 +456,161 @@ namespace Lantean.QBTMud.Services
} }
} }
private static void UpdateServerState(ServerState existingServerState, QBitTorrentClient.Models.ServerState serverState) private static bool UpdateServerState(ServerState existingServerState, QBitTorrentClient.Models.ServerState serverState)
{ {
existingServerState.AllTimeDownloaded = serverState.AllTimeDownloaded ?? existingServerState.AllTimeDownloaded; var changed = false;
existingServerState.AllTimeUploaded = serverState.AllTimeUploaded ?? existingServerState.AllTimeUploaded;
existingServerState.AverageTimeQueue = serverState.AverageTimeQueue ?? existingServerState.AverageTimeQueue; if (serverState.AllTimeDownloaded.HasValue && existingServerState.AllTimeDownloaded != serverState.AllTimeDownloaded.Value)
existingServerState.ConnectionStatus = serverState.ConnectionStatus ?? existingServerState.ConnectionStatus; {
existingServerState.DHTNodes = serverState.DHTNodes ?? existingServerState.DHTNodes; existingServerState.AllTimeDownloaded = serverState.AllTimeDownloaded.Value;
existingServerState.DownloadInfoData = serverState.DownloadInfoData ?? existingServerState.DownloadInfoData; changed = true;
existingServerState.DownloadInfoSpeed = serverState.DownloadInfoSpeed ?? existingServerState.DownloadInfoSpeed; }
existingServerState.DownloadRateLimit = serverState.DownloadRateLimit ?? existingServerState.DownloadRateLimit;
existingServerState.FreeSpaceOnDisk = serverState.FreeSpaceOnDisk ?? existingServerState.FreeSpaceOnDisk; if (serverState.AllTimeUploaded.HasValue && existingServerState.AllTimeUploaded != serverState.AllTimeUploaded.Value)
existingServerState.GlobalRatio = serverState.GlobalRatio ?? existingServerState.GlobalRatio; {
existingServerState.QueuedIOJobs = serverState.QueuedIOJobs ?? existingServerState.QueuedIOJobs; existingServerState.AllTimeUploaded = serverState.AllTimeUploaded.Value;
existingServerState.Queuing = serverState.Queuing ?? existingServerState.Queuing; changed = true;
existingServerState.ReadCacheHits = serverState.ReadCacheHits ?? existingServerState.ReadCacheHits; }
existingServerState.ReadCacheOverload = serverState.ReadCacheOverload ?? existingServerState.ReadCacheOverload;
existingServerState.RefreshInterval = serverState.RefreshInterval ?? existingServerState.RefreshInterval; if (serverState.AverageTimeQueue.HasValue && existingServerState.AverageTimeQueue != serverState.AverageTimeQueue.Value)
existingServerState.TotalBuffersSize = serverState.TotalBuffersSize ?? existingServerState.TotalBuffersSize; {
existingServerState.TotalPeerConnections = serverState.TotalPeerConnections ?? existingServerState.TotalPeerConnections; existingServerState.AverageTimeQueue = serverState.AverageTimeQueue.Value;
existingServerState.TotalQueuedSize = serverState.TotalQueuedSize ?? existingServerState.TotalQueuedSize; changed = true;
existingServerState.TotalWastedSession = serverState.TotalWastedSession ?? existingServerState.TotalWastedSession; }
existingServerState.UploadInfoData = serverState.UploadInfoData ?? existingServerState.UploadInfoData;
existingServerState.UploadInfoSpeed = serverState.UploadInfoSpeed ?? existingServerState.UploadInfoSpeed; if (serverState.ConnectionStatus is not null && existingServerState.ConnectionStatus != serverState.ConnectionStatus)
existingServerState.UploadRateLimit = serverState.UploadRateLimit ?? existingServerState.UploadRateLimit; {
existingServerState.UseAltSpeedLimits = serverState.UseAltSpeedLimits ?? existingServerState.UseAltSpeedLimits; existingServerState.ConnectionStatus = serverState.ConnectionStatus;
existingServerState.UseSubcategories = serverState.UseSubcategories ?? existingServerState.UseSubcategories; changed = true;
existingServerState.WriteCacheOverload = serverState.WriteCacheOverload ?? existingServerState.WriteCacheOverload; }
if (serverState.DHTNodes.HasValue && existingServerState.DHTNodes != serverState.DHTNodes.Value)
{
existingServerState.DHTNodes = serverState.DHTNodes.Value;
changed = true;
}
if (serverState.DownloadInfoData.HasValue && existingServerState.DownloadInfoData != serverState.DownloadInfoData.Value)
{
existingServerState.DownloadInfoData = serverState.DownloadInfoData.Value;
changed = true;
}
if (serverState.DownloadInfoSpeed.HasValue && existingServerState.DownloadInfoSpeed != serverState.DownloadInfoSpeed.Value)
{
existingServerState.DownloadInfoSpeed = serverState.DownloadInfoSpeed.Value;
changed = true;
}
if (serverState.DownloadRateLimit.HasValue && existingServerState.DownloadRateLimit != serverState.DownloadRateLimit.Value)
{
existingServerState.DownloadRateLimit = serverState.DownloadRateLimit.Value;
changed = true;
}
if (serverState.FreeSpaceOnDisk.HasValue && existingServerState.FreeSpaceOnDisk != serverState.FreeSpaceOnDisk.Value)
{
existingServerState.FreeSpaceOnDisk = serverState.FreeSpaceOnDisk.Value;
changed = true;
}
if (serverState.GlobalRatio.HasValue && existingServerState.GlobalRatio != serverState.GlobalRatio.Value)
{
existingServerState.GlobalRatio = serverState.GlobalRatio.Value;
changed = true;
}
if (serverState.QueuedIOJobs.HasValue && existingServerState.QueuedIOJobs != serverState.QueuedIOJobs.Value)
{
existingServerState.QueuedIOJobs = serverState.QueuedIOJobs.Value;
changed = true;
}
if (serverState.Queuing.HasValue && existingServerState.Queuing != serverState.Queuing.Value)
{
existingServerState.Queuing = serverState.Queuing.Value;
changed = true;
}
if (serverState.ReadCacheHits.HasValue && existingServerState.ReadCacheHits != serverState.ReadCacheHits.Value)
{
existingServerState.ReadCacheHits = serverState.ReadCacheHits.Value;
changed = true;
}
if (serverState.ReadCacheOverload.HasValue && existingServerState.ReadCacheOverload != serverState.ReadCacheOverload.Value)
{
existingServerState.ReadCacheOverload = serverState.ReadCacheOverload.Value;
changed = true;
}
if (serverState.RefreshInterval.HasValue && existingServerState.RefreshInterval != serverState.RefreshInterval.Value)
{
existingServerState.RefreshInterval = serverState.RefreshInterval.Value;
changed = true;
}
if (serverState.TotalBuffersSize.HasValue && existingServerState.TotalBuffersSize != serverState.TotalBuffersSize.Value)
{
existingServerState.TotalBuffersSize = serverState.TotalBuffersSize.Value;
changed = true;
}
if (serverState.TotalPeerConnections.HasValue && existingServerState.TotalPeerConnections != serverState.TotalPeerConnections.Value)
{
existingServerState.TotalPeerConnections = serverState.TotalPeerConnections.Value;
changed = true;
}
if (serverState.TotalQueuedSize.HasValue && existingServerState.TotalQueuedSize != serverState.TotalQueuedSize.Value)
{
existingServerState.TotalQueuedSize = serverState.TotalQueuedSize.Value;
changed = true;
}
if (serverState.TotalWastedSession.HasValue && existingServerState.TotalWastedSession != serverState.TotalWastedSession.Value)
{
existingServerState.TotalWastedSession = serverState.TotalWastedSession.Value;
changed = true;
}
if (serverState.UploadInfoData.HasValue && existingServerState.UploadInfoData != serverState.UploadInfoData.Value)
{
existingServerState.UploadInfoData = serverState.UploadInfoData.Value;
changed = true;
}
if (serverState.UploadInfoSpeed.HasValue && existingServerState.UploadInfoSpeed != serverState.UploadInfoSpeed.Value)
{
existingServerState.UploadInfoSpeed = serverState.UploadInfoSpeed.Value;
changed = true;
}
if (serverState.UploadRateLimit.HasValue && existingServerState.UploadRateLimit != serverState.UploadRateLimit.Value)
{
existingServerState.UploadRateLimit = serverState.UploadRateLimit.Value;
changed = true;
}
if (serverState.UseAltSpeedLimits.HasValue && existingServerState.UseAltSpeedLimits != serverState.UseAltSpeedLimits.Value)
{
existingServerState.UseAltSpeedLimits = serverState.UseAltSpeedLimits.Value;
changed = true;
}
if (serverState.UseSubcategories.HasValue && existingServerState.UseSubcategories != serverState.UseSubcategories.Value)
{
existingServerState.UseSubcategories = serverState.UseSubcategories.Value;
changed = true;
}
if (serverState.WriteCacheOverload.HasValue && existingServerState.WriteCacheOverload != serverState.WriteCacheOverload.Value)
{
existingServerState.WriteCacheOverload = serverState.WriteCacheOverload.Value;
changed = true;
}
return changed;
} }
public void MergeTorrentPeers(QBitTorrentClient.Models.TorrentPeers torrentPeers, PeerList peerList) public void MergeTorrentPeers(QBitTorrentClient.Models.TorrentPeers torrentPeers, PeerList peerList)
@@ -561,66 +742,344 @@ namespace Lantean.QBTMud.Services
torrent.MaxInactiveSeedingTime.GetValueOrDefault()); torrent.MaxInactiveSeedingTime.GetValueOrDefault());
} }
private static void UpdateCategory(Category existingCategory, QBitTorrentClient.Models.Category category) private static bool UpdateCategory(Category existingCategory, QBitTorrentClient.Models.Category category)
{ {
existingCategory.SavePath = category.SavePath ?? existingCategory.SavePath; if (category.SavePath is not null && existingCategory.SavePath != category.SavePath)
{
existingCategory.SavePath = category.SavePath;
return true;
} }
private static void UpdateTorrent(Torrent existingTorrent, QBitTorrentClient.Models.Torrent torrent) return false;
}
private readonly struct TorrentUpdateResult
{ {
existingTorrent.AddedOn = torrent.AddedOn ?? existingTorrent.AddedOn; public TorrentUpdateResult(bool dataChanged, bool filterChanged)
existingTorrent.AmountLeft = torrent.AmountLeft ?? existingTorrent.AmountLeft; {
existingTorrent.AutomaticTorrentManagement = torrent.AutomaticTorrentManagement ?? existingTorrent.AutomaticTorrentManagement; DataChanged = dataChanged;
existingTorrent.Availability = torrent.Availability ?? existingTorrent.Availability; FilterChanged = filterChanged;
existingTorrent.Category = torrent.Category ?? existingTorrent.Category; }
existingTorrent.Completed = torrent.Completed ?? existingTorrent.Completed;
existingTorrent.CompletionOn = torrent.CompletionOn ?? existingTorrent.CompletionOn; public bool DataChanged { get; }
existingTorrent.ContentPath = torrent.ContentPath ?? existingTorrent.ContentPath;
existingTorrent.Downloaded = torrent.Downloaded ?? existingTorrent.Downloaded; public bool FilterChanged { get; }
existingTorrent.DownloadedSession = torrent.DownloadedSession ?? existingTorrent.DownloadedSession; }
existingTorrent.DownloadLimit = torrent.DownloadLimit ?? existingTorrent.DownloadLimit;
existingTorrent.DownloadSpeed = torrent.DownloadSpeed ?? existingTorrent.DownloadSpeed; private static TorrentUpdateResult UpdateTorrent(Torrent existingTorrent, QBitTorrentClient.Models.Torrent torrent)
existingTorrent.EstimatedTimeOfArrival = torrent.EstimatedTimeOfArrival ?? existingTorrent.EstimatedTimeOfArrival; {
existingTorrent.FirstLastPiecePriority = torrent.FirstLastPiecePriority ?? existingTorrent.FirstLastPiecePriority; var dataChanged = false;
existingTorrent.ForceStart = torrent.ForceStart ?? existingTorrent.ForceStart; var filterChanged = false;
existingTorrent.InfoHashV1 = torrent.InfoHashV1 ?? existingTorrent.InfoHashV1;
existingTorrent.InfoHashV2 = torrent.InfoHashV2 ?? existingTorrent.InfoHashV2; if (torrent.AddedOn.HasValue && existingTorrent.AddedOn != torrent.AddedOn.Value)
existingTorrent.LastActivity = torrent.LastActivity ?? existingTorrent.LastActivity; {
existingTorrent.MagnetUri = torrent.MagnetUri ?? existingTorrent.MagnetUri; existingTorrent.AddedOn = torrent.AddedOn.Value;
existingTorrent.MaxRatio = torrent.MaxRatio ?? existingTorrent.MaxRatio; dataChanged = true;
existingTorrent.MaxSeedingTime = torrent.MaxSeedingTime ?? existingTorrent.MaxSeedingTime; }
existingTorrent.Name = torrent.Name ?? existingTorrent.Name;
existingTorrent.NumberComplete = torrent.NumberComplete ?? existingTorrent.NumberComplete; if (torrent.AmountLeft.HasValue && existingTorrent.AmountLeft != torrent.AmountLeft.Value)
existingTorrent.NumberIncomplete = torrent.NumberIncomplete ?? existingTorrent.NumberIncomplete; {
existingTorrent.NumberLeeches = torrent.NumberLeeches ?? existingTorrent.NumberLeeches; existingTorrent.AmountLeft = torrent.AmountLeft.Value;
existingTorrent.NumberSeeds = torrent.NumberSeeds ?? existingTorrent.NumberSeeds; dataChanged = true;
existingTorrent.Priority = torrent.Priority ?? existingTorrent.Priority; }
existingTorrent.Progress = torrent.Progress ?? existingTorrent.Progress;
existingTorrent.Ratio = torrent.Ratio ?? existingTorrent.Ratio; if (torrent.AutomaticTorrentManagement.HasValue && existingTorrent.AutomaticTorrentManagement != torrent.AutomaticTorrentManagement.Value)
existingTorrent.RatioLimit = torrent.RatioLimit ?? existingTorrent.RatioLimit; {
existingTorrent.SavePath = torrent.SavePath ?? existingTorrent.SavePath; existingTorrent.AutomaticTorrentManagement = torrent.AutomaticTorrentManagement.Value;
existingTorrent.SeedingTime = torrent.SeedingTime ?? existingTorrent.SeedingTime; dataChanged = true;
existingTorrent.SeedingTimeLimit = torrent.SeedingTimeLimit ?? existingTorrent.SeedingTimeLimit; }
existingTorrent.SeenComplete = torrent.SeenComplete ?? existingTorrent.SeenComplete;
existingTorrent.SequentialDownload = torrent.SequentialDownload ?? existingTorrent.SequentialDownload; if (torrent.Availability.HasValue && existingTorrent.Availability != torrent.Availability.Value)
existingTorrent.Size = torrent.Size ?? existingTorrent.Size; {
existingTorrent.State = torrent.State ?? existingTorrent.State; existingTorrent.Availability = torrent.Availability.Value;
existingTorrent.SuperSeeding = torrent.SuperSeeding ?? existingTorrent.SuperSeeding; dataChanged = true;
}
if (torrent.Category is not null && existingTorrent.Category != torrent.Category)
{
existingTorrent.Category = torrent.Category;
dataChanged = true;
filterChanged = true;
}
if (torrent.Completed.HasValue && existingTorrent.Completed != torrent.Completed.Value)
{
existingTorrent.Completed = torrent.Completed.Value;
dataChanged = true;
}
if (torrent.CompletionOn.HasValue && existingTorrent.CompletionOn != torrent.CompletionOn.Value)
{
existingTorrent.CompletionOn = torrent.CompletionOn.Value;
dataChanged = true;
}
if (torrent.ContentPath is not null && existingTorrent.ContentPath != torrent.ContentPath)
{
existingTorrent.ContentPath = torrent.ContentPath;
dataChanged = true;
}
if (torrent.Downloaded.HasValue && existingTorrent.Downloaded != torrent.Downloaded.Value)
{
existingTorrent.Downloaded = torrent.Downloaded.Value;
dataChanged = true;
}
if (torrent.DownloadedSession.HasValue && existingTorrent.DownloadedSession != torrent.DownloadedSession.Value)
{
existingTorrent.DownloadedSession = torrent.DownloadedSession.Value;
dataChanged = true;
}
if (torrent.DownloadLimit.HasValue && existingTorrent.DownloadLimit != torrent.DownloadLimit.Value)
{
existingTorrent.DownloadLimit = torrent.DownloadLimit.Value;
dataChanged = true;
}
if (torrent.DownloadSpeed.HasValue && existingTorrent.DownloadSpeed != torrent.DownloadSpeed.Value)
{
existingTorrent.DownloadSpeed = torrent.DownloadSpeed.Value;
dataChanged = true;
}
if (torrent.EstimatedTimeOfArrival.HasValue && existingTorrent.EstimatedTimeOfArrival != torrent.EstimatedTimeOfArrival.Value)
{
existingTorrent.EstimatedTimeOfArrival = torrent.EstimatedTimeOfArrival.Value;
dataChanged = true;
}
if (torrent.FirstLastPiecePriority.HasValue && existingTorrent.FirstLastPiecePriority != torrent.FirstLastPiecePriority.Value)
{
existingTorrent.FirstLastPiecePriority = torrent.FirstLastPiecePriority.Value;
dataChanged = true;
}
if (torrent.ForceStart.HasValue && existingTorrent.ForceStart != torrent.ForceStart.Value)
{
existingTorrent.ForceStart = torrent.ForceStart.Value;
dataChanged = true;
}
if (torrent.InfoHashV1 is not null && existingTorrent.InfoHashV1 != torrent.InfoHashV1)
{
existingTorrent.InfoHashV1 = torrent.InfoHashV1;
dataChanged = true;
}
if (torrent.InfoHashV2 is not null && existingTorrent.InfoHashV2 != torrent.InfoHashV2)
{
existingTorrent.InfoHashV2 = torrent.InfoHashV2;
dataChanged = true;
}
if (torrent.LastActivity.HasValue && existingTorrent.LastActivity != torrent.LastActivity.Value)
{
existingTorrent.LastActivity = torrent.LastActivity.Value;
dataChanged = true;
}
if (torrent.MagnetUri is not null && existingTorrent.MagnetUri != torrent.MagnetUri)
{
existingTorrent.MagnetUri = torrent.MagnetUri;
dataChanged = true;
}
if (torrent.MaxRatio.HasValue && existingTorrent.MaxRatio != torrent.MaxRatio.Value)
{
existingTorrent.MaxRatio = torrent.MaxRatio.Value;
dataChanged = true;
}
if (torrent.MaxSeedingTime.HasValue && existingTorrent.MaxSeedingTime != torrent.MaxSeedingTime.Value)
{
existingTorrent.MaxSeedingTime = torrent.MaxSeedingTime.Value;
dataChanged = true;
}
if (torrent.Name is not null && existingTorrent.Name != torrent.Name)
{
existingTorrent.Name = torrent.Name;
dataChanged = true;
filterChanged = true;
}
if (torrent.NumberComplete.HasValue && existingTorrent.NumberComplete != torrent.NumberComplete.Value)
{
existingTorrent.NumberComplete = torrent.NumberComplete.Value;
dataChanged = true;
}
if (torrent.NumberIncomplete.HasValue && existingTorrent.NumberIncomplete != torrent.NumberIncomplete.Value)
{
existingTorrent.NumberIncomplete = torrent.NumberIncomplete.Value;
dataChanged = true;
}
if (torrent.NumberLeeches.HasValue && existingTorrent.NumberLeeches != torrent.NumberLeeches.Value)
{
existingTorrent.NumberLeeches = torrent.NumberLeeches.Value;
dataChanged = true;
}
if (torrent.NumberSeeds.HasValue && existingTorrent.NumberSeeds != torrent.NumberSeeds.Value)
{
existingTorrent.NumberSeeds = torrent.NumberSeeds.Value;
dataChanged = true;
}
if (torrent.Priority.HasValue && existingTorrent.Priority != torrent.Priority.Value)
{
existingTorrent.Priority = torrent.Priority.Value;
dataChanged = true;
}
if (torrent.Progress.HasValue && existingTorrent.Progress != torrent.Progress.Value)
{
existingTorrent.Progress = torrent.Progress.Value;
dataChanged = true;
}
if (torrent.Ratio.HasValue && existingTorrent.Ratio != torrent.Ratio.Value)
{
existingTorrent.Ratio = torrent.Ratio.Value;
dataChanged = true;
}
if (torrent.RatioLimit.HasValue && existingTorrent.RatioLimit != torrent.RatioLimit.Value)
{
existingTorrent.RatioLimit = torrent.RatioLimit.Value;
dataChanged = true;
}
if (torrent.SavePath is not null && existingTorrent.SavePath != torrent.SavePath)
{
existingTorrent.SavePath = torrent.SavePath;
dataChanged = true;
}
if (torrent.SeedingTime.HasValue && existingTorrent.SeedingTime != torrent.SeedingTime.Value)
{
existingTorrent.SeedingTime = torrent.SeedingTime.Value;
dataChanged = true;
}
if (torrent.SeedingTimeLimit.HasValue && existingTorrent.SeedingTimeLimit != torrent.SeedingTimeLimit.Value)
{
existingTorrent.SeedingTimeLimit = torrent.SeedingTimeLimit.Value;
dataChanged = true;
}
if (torrent.SeenComplete.HasValue && existingTorrent.SeenComplete != torrent.SeenComplete.Value)
{
existingTorrent.SeenComplete = torrent.SeenComplete.Value;
dataChanged = true;
}
if (torrent.SequentialDownload.HasValue && existingTorrent.SequentialDownload != torrent.SequentialDownload.Value)
{
existingTorrent.SequentialDownload = torrent.SequentialDownload.Value;
dataChanged = true;
}
if (torrent.Size.HasValue && existingTorrent.Size != torrent.Size.Value)
{
existingTorrent.Size = torrent.Size.Value;
dataChanged = true;
}
if (torrent.State is not null && existingTorrent.State != torrent.State)
{
existingTorrent.State = torrent.State;
dataChanged = true;
filterChanged = true;
}
if (torrent.SuperSeeding.HasValue && existingTorrent.SuperSeeding != torrent.SuperSeeding.Value)
{
existingTorrent.SuperSeeding = torrent.SuperSeeding.Value;
dataChanged = true;
}
if (torrent.Tags is not null) if (torrent.Tags is not null)
{
if (!existingTorrent.Tags.SequenceEqual(torrent.Tags))
{ {
existingTorrent.Tags.Clear(); existingTorrent.Tags.Clear();
existingTorrent.Tags.AddRange(torrent.Tags); existingTorrent.Tags.AddRange(torrent.Tags);
dataChanged = true;
filterChanged = true;
} }
existingTorrent.TimeActive = torrent.TimeActive ?? existingTorrent.TimeActive; }
existingTorrent.TotalSize = torrent.TotalSize ?? existingTorrent.TotalSize;
existingTorrent.Tracker = torrent.Tracker ?? existingTorrent.Tracker; if (torrent.TimeActive.HasValue && existingTorrent.TimeActive != torrent.TimeActive.Value)
existingTorrent.UploadLimit = torrent.UploadLimit ?? existingTorrent.UploadLimit; {
existingTorrent.Uploaded = torrent.Uploaded ?? existingTorrent.Uploaded; existingTorrent.TimeActive = torrent.TimeActive.Value;
existingTorrent.UploadedSession = torrent.UploadedSession ?? existingTorrent.UploadedSession; dataChanged = true;
existingTorrent.UploadSpeed = torrent.UploadSpeed ?? existingTorrent.UploadSpeed; }
existingTorrent.Reannounce = torrent.Reannounce ?? existingTorrent.Reannounce;
existingTorrent.InactiveSeedingTimeLimit = torrent.InactiveSeedingTimeLimit ?? existingTorrent.InactiveSeedingTimeLimit; if (torrent.TotalSize.HasValue && existingTorrent.TotalSize != torrent.TotalSize.Value)
existingTorrent.MaxInactiveSeedingTime = torrent.MaxInactiveSeedingTime ?? existingTorrent.MaxInactiveSeedingTime; {
existingTorrent.TotalSize = torrent.TotalSize.Value;
dataChanged = true;
}
if (torrent.Tracker is not null && existingTorrent.Tracker != torrent.Tracker)
{
existingTorrent.Tracker = torrent.Tracker;
dataChanged = true;
filterChanged = true;
}
if (torrent.UploadLimit.HasValue && existingTorrent.UploadLimit != torrent.UploadLimit.Value)
{
existingTorrent.UploadLimit = torrent.UploadLimit.Value;
dataChanged = true;
}
if (torrent.Uploaded.HasValue && existingTorrent.Uploaded != torrent.Uploaded.Value)
{
existingTorrent.Uploaded = torrent.Uploaded.Value;
dataChanged = true;
}
if (torrent.UploadedSession.HasValue && existingTorrent.UploadedSession != torrent.UploadedSession.Value)
{
existingTorrent.UploadedSession = torrent.UploadedSession.Value;
dataChanged = true;
}
var previousUploadSpeed = existingTorrent.UploadSpeed;
if (torrent.UploadSpeed.HasValue && previousUploadSpeed != torrent.UploadSpeed.Value)
{
existingTorrent.UploadSpeed = torrent.UploadSpeed.Value;
dataChanged = true;
if ((previousUploadSpeed > 0) != (torrent.UploadSpeed.Value > 0))
{
filterChanged = true;
}
}
if (torrent.Reannounce.HasValue && existingTorrent.Reannounce != torrent.Reannounce.Value)
{
existingTorrent.Reannounce = torrent.Reannounce.Value;
dataChanged = true;
}
if (torrent.InactiveSeedingTimeLimit.HasValue && existingTorrent.InactiveSeedingTimeLimit != torrent.InactiveSeedingTimeLimit.Value)
{
existingTorrent.InactiveSeedingTimeLimit = torrent.InactiveSeedingTimeLimit.Value;
dataChanged = true;
}
if (torrent.MaxInactiveSeedingTime.HasValue && existingTorrent.MaxInactiveSeedingTime != torrent.MaxInactiveSeedingTime.Value)
{
existingTorrent.MaxInactiveSeedingTime = torrent.MaxInactiveSeedingTime.Value;
dataChanged = true;
}
return new TorrentUpdateResult(dataChanged, filterChanged);
} }
public Dictionary<string, ContentItem> CreateContentsList(IReadOnlyList<QBitTorrentClient.Models.FileData> files) public Dictionary<string, ContentItem> CreateContentsList(IReadOnlyList<QBitTorrentClient.Models.FileData> files)

View File

@@ -8,7 +8,7 @@ namespace Lantean.QBTMud.Services
Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent); Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent);
void MergeMainData(QBitTorrentClient.Models.MainData mainData, MainData torrentList); bool MergeMainData(QBitTorrentClient.Models.MainData mainData, MainData torrentList, out bool filterChanged);
PeerList CreatePeerList(QBitTorrentClient.Models.TorrentPeers torrentPeers); PeerList CreatePeerList(QBitTorrentClient.Models.TorrentPeers torrentPeers);