mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-10-23 04:52:22 +00:00
2137 lines
99 KiB
C#
2137 lines
99 KiB
C#
using Lantean.QBTMud.Helpers;
|
|
using Lantean.QBTMud.Models;
|
|
|
|
namespace Lantean.QBTMud.Services
|
|
{
|
|
public class DataManager : IDataManager
|
|
{
|
|
private static Status[]? _statusArray = null;
|
|
|
|
public PeerList CreatePeerList(QBitTorrentClient.Models.TorrentPeers torrentPeers)
|
|
{
|
|
var peers = new Dictionary<string, Peer>();
|
|
if (torrentPeers.Peers is not null)
|
|
{
|
|
foreach (var (key, peer) in torrentPeers.Peers)
|
|
{
|
|
var newPeer = CreatePeer(key, peer);
|
|
|
|
peers[key] = newPeer;
|
|
}
|
|
}
|
|
|
|
var peerList = new PeerList(peers);
|
|
|
|
return peerList;
|
|
}
|
|
|
|
public MainData CreateMainData(QBitTorrentClient.Models.MainData mainData, string version)
|
|
{
|
|
var majorVersion = VersionHelper.GetMajorVersion(version);
|
|
var torrents = new Dictionary<string, Torrent>(mainData.Torrents?.Count ?? 0);
|
|
if (mainData.Torrents is not null)
|
|
{
|
|
foreach (var (hash, torrent) in mainData.Torrents)
|
|
{
|
|
var newTorrent = CreateTorrent(hash, torrent);
|
|
|
|
torrents[hash] = newTorrent;
|
|
}
|
|
}
|
|
|
|
var tags = new List<string>();
|
|
if (mainData.Tags is not null)
|
|
{
|
|
var seenTags = new HashSet<string>(StringComparer.Ordinal);
|
|
foreach (var tag in mainData.Tags)
|
|
{
|
|
var normalizedTag = NormalizeTag(tag);
|
|
if (string.IsNullOrEmpty(normalizedTag) || !seenTags.Add(normalizedTag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
tags.Add(normalizedTag);
|
|
}
|
|
}
|
|
|
|
var categories = new Dictionary<string, Category>(mainData.Categories?.Count ?? 0);
|
|
if (mainData.Categories is not null)
|
|
{
|
|
foreach (var (name, category) in mainData.Categories)
|
|
{
|
|
var newCategory = CreateCategory(category);
|
|
|
|
categories[name] = newCategory;
|
|
}
|
|
}
|
|
|
|
var trackers = new Dictionary<string, IReadOnlyList<string>>(mainData.Trackers?.Count ?? 0);
|
|
if (mainData.Trackers is not null)
|
|
{
|
|
foreach (var (url, hashes) in mainData.Trackers)
|
|
{
|
|
trackers[url] = hashes;
|
|
}
|
|
}
|
|
|
|
var serverState = CreateServerState(mainData.ServerState);
|
|
|
|
var tagState = new Dictionary<string, HashSet<string>>(tags.Count + 2)
|
|
{
|
|
{ FilterHelper.TAG_ALL, torrents.Keys.ToHashSet() },
|
|
{ FilterHelper.TAG_UNTAGGED, torrents.Values.Where(t => FilterHelper.FilterTag(t, FilterHelper.TAG_UNTAGGED)).ToHashesHashSet() }
|
|
};
|
|
foreach (var tag in tags)
|
|
{
|
|
tagState.Add(tag, torrents.Values.Where(t => FilterHelper.FilterTag(t, tag)).ToHashesHashSet());
|
|
}
|
|
|
|
var categoriesState = new Dictionary<string, HashSet<string>>(categories.Count + 2);
|
|
categoriesState.Add(FilterHelper.CATEGORY_ALL, torrents.Keys.ToHashSet());
|
|
categoriesState.Add(FilterHelper.CATEGORY_UNCATEGORIZED, torrents.Values.Where(t => FilterHelper.FilterCategory(t, FilterHelper.CATEGORY_UNCATEGORIZED, serverState.UseSubcategories)).ToHashesHashSet());
|
|
foreach (var category in categories.Keys)
|
|
{
|
|
categoriesState.Add(category, torrents.Values.Where(t => FilterHelper.FilterCategory(t, category, serverState.UseSubcategories)).ToHashesHashSet());
|
|
}
|
|
|
|
var statuses = GetStatuses(majorVersion).ToArray();
|
|
var statusState = new Dictionary<string, HashSet<string>>(statuses.Length + 2);
|
|
foreach (var status in statuses)
|
|
{
|
|
statusState.Add(status.ToString(), torrents.Values.Where(t => FilterHelper.FilterStatus(t, status)).ToHashesHashSet());
|
|
}
|
|
|
|
var trackersState = new Dictionary<string, HashSet<string>>(trackers.Count + 2);
|
|
trackersState.Add(FilterHelper.TRACKER_ALL, torrents.Keys.ToHashSet());
|
|
trackersState.Add(FilterHelper.TRACKER_TRACKERLESS, torrents.Values.Where(t => FilterHelper.FilterTracker(t, FilterHelper.TRACKER_TRACKERLESS)).ToHashesHashSet());
|
|
foreach (var tracker in trackers.Keys)
|
|
{
|
|
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);
|
|
|
|
return torrentList;
|
|
}
|
|
|
|
private static ServerState CreateServerState(QBitTorrentClient.Models.ServerState? serverState)
|
|
{
|
|
if (serverState is null)
|
|
{
|
|
return new ServerState();
|
|
}
|
|
return new ServerState(
|
|
serverState.AllTimeDownloaded.GetValueOrDefault(),
|
|
serverState.AllTimeUploaded.GetValueOrDefault(),
|
|
serverState.AverageTimeQueue.GetValueOrDefault(),
|
|
serverState.ConnectionStatus!,
|
|
serverState.DHTNodes.GetValueOrDefault(),
|
|
serverState.DownloadInfoData.GetValueOrDefault(),
|
|
serverState.DownloadInfoSpeed.GetValueOrDefault(),
|
|
serverState.DownloadRateLimit.GetValueOrDefault(),
|
|
serverState.FreeSpaceOnDisk.GetValueOrDefault(),
|
|
serverState.GlobalRatio.GetValueOrDefault(),
|
|
serverState.QueuedIOJobs.GetValueOrDefault(),
|
|
serverState.Queuing.GetValueOrDefault(),
|
|
serverState.ReadCacheHits.GetValueOrDefault(),
|
|
serverState.ReadCacheOverload.GetValueOrDefault(),
|
|
serverState.RefreshInterval.GetValueOrDefault(),
|
|
serverState.TotalBuffersSize.GetValueOrDefault(),
|
|
serverState.TotalPeerConnections.GetValueOrDefault(),
|
|
serverState.TotalQueuedSize.GetValueOrDefault(),
|
|
serverState.TotalWastedSession.GetValueOrDefault(),
|
|
serverState.UploadInfoData.GetValueOrDefault(),
|
|
serverState.UploadInfoSpeed.GetValueOrDefault(),
|
|
serverState.UploadRateLimit.GetValueOrDefault(),
|
|
serverState.UseAltSpeedLimits.GetValueOrDefault(),
|
|
serverState.UseSubcategories.GetValueOrDefault(),
|
|
serverState.WriteCacheOverload.GetValueOrDefault());
|
|
}
|
|
|
|
public bool MergeMainData(QBitTorrentClient.Models.MainData mainData, MainData torrentList, out bool filterChanged)
|
|
{
|
|
filterChanged = false;
|
|
var dataChanged = false;
|
|
|
|
if (mainData.CategoriesRemoved is not null)
|
|
{
|
|
foreach (var category in mainData.CategoriesRemoved)
|
|
{
|
|
if (torrentList.Categories.Remove(category))
|
|
{
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
if (torrentList.CategoriesState.Remove(category))
|
|
{
|
|
filterChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainData.TagsRemoved is not null)
|
|
{
|
|
foreach (var tag in mainData.TagsRemoved)
|
|
{
|
|
var normalizedTag = NormalizeTag(tag);
|
|
if (string.IsNullOrEmpty(normalizedTag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (torrentList.TagState.Remove(normalizedTag))
|
|
{
|
|
filterChanged = true;
|
|
}
|
|
torrentList.TagState.Remove(normalizedTag);
|
|
}
|
|
}
|
|
|
|
if (mainData.TrackersRemoved is not null)
|
|
{
|
|
foreach (var tracker in mainData.TrackersRemoved)
|
|
{
|
|
if (torrentList.Trackers.Remove(tracker))
|
|
{
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
if (torrentList.TrackersState.Remove(tracker))
|
|
{
|
|
filterChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainData.TorrentsRemoved is not null)
|
|
{
|
|
foreach (var hash in mainData.TorrentsRemoved)
|
|
{
|
|
if (torrentList.Torrents.Remove(hash))
|
|
{
|
|
RemoveTorrentFromStates(torrentList, hash);
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainData.Categories is not null)
|
|
{
|
|
foreach (var (name, category) in mainData.Categories)
|
|
{
|
|
if (!torrentList.Categories.TryGetValue(name, out var existingCategory))
|
|
{
|
|
var newCategory = CreateCategory(category);
|
|
torrentList.Categories.Add(name, newCategory);
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
else if (UpdateCategory(existingCategory, category))
|
|
{
|
|
dataChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainData.Tags is not null)
|
|
{
|
|
foreach (var tag in mainData.Tags)
|
|
{
|
|
var normalizedTag = NormalizeTag(tag);
|
|
if (string.IsNullOrEmpty(normalizedTag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (torrentList.Tags.Add(normalizedTag))
|
|
{
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
var matchingHashes = torrentList.Torrents
|
|
.Where(pair => FilterHelper.FilterTag(pair.Value, normalizedTag))
|
|
.Select(pair => pair.Key)
|
|
.ToHashSet();
|
|
torrentList.TagState[normalizedTag] = matchingHashes;
|
|
}
|
|
}
|
|
|
|
if (mainData.Trackers is not null)
|
|
{
|
|
foreach (var (url, hashes) in mainData.Trackers)
|
|
{
|
|
if (!torrentList.Trackers.TryGetValue(url, out var existingHashes))
|
|
{
|
|
torrentList.Trackers.Add(url, hashes);
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
else if (!existingHashes.SequenceEqual(hashes))
|
|
{
|
|
torrentList.Trackers[url] = hashes;
|
|
dataChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainData.Torrents is not null)
|
|
{
|
|
foreach (var (hash, torrent) in mainData.Torrents)
|
|
{
|
|
if (!torrentList.Torrents.TryGetValue(hash, out var existingTorrent))
|
|
{
|
|
var newTorrent = CreateTorrent(hash, torrent);
|
|
torrentList.Torrents.Add(hash, newTorrent);
|
|
AddTorrentToStates(torrentList, hash, torrentList.MajorVersion);
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
else
|
|
{
|
|
var previousSnapshot = CreateSnapshot(existingTorrent);
|
|
var updateResult = UpdateTorrent(existingTorrent, torrent);
|
|
if (updateResult.FilterChanged)
|
|
{
|
|
UpdateTorrentStates(torrentList, hash, previousSnapshot, existingTorrent);
|
|
filterChanged = true;
|
|
}
|
|
if (updateResult.DataChanged)
|
|
{
|
|
dataChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainData.ServerState is not null)
|
|
{
|
|
if (UpdateServerState(torrentList.ServerState, mainData.ServerState))
|
|
{
|
|
dataChanged = true;
|
|
}
|
|
}
|
|
|
|
return dataChanged;
|
|
}
|
|
|
|
private static void AddTorrentToStates(MainData torrentList, string hash, int version)
|
|
{
|
|
if (!torrentList.Torrents.TryGetValue(hash, out var torrent))
|
|
{
|
|
return;
|
|
}
|
|
|
|
torrentList.TagState[FilterHelper.TAG_ALL].Add(hash);
|
|
UpdateTagStateForAddition(torrentList, torrent, hash);
|
|
|
|
torrentList.CategoriesState[FilterHelper.CATEGORY_ALL].Add(hash);
|
|
UpdateCategoryState(torrentList, torrent, hash, previousCategory: null);
|
|
|
|
foreach (var status in GetStatuses(version))
|
|
{
|
|
if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusSet))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (FilterHelper.FilterStatus(torrent, status))
|
|
{
|
|
statusSet.Add(hash);
|
|
}
|
|
}
|
|
|
|
torrentList.TrackersState[FilterHelper.TRACKER_ALL].Add(hash);
|
|
UpdateTrackerState(torrentList, torrent, hash, previousTracker: null);
|
|
}
|
|
|
|
private static Status[] GetStatuses(int version)
|
|
{
|
|
if (_statusArray is not null)
|
|
{
|
|
return _statusArray;
|
|
}
|
|
|
|
if (version == 5)
|
|
{
|
|
_statusArray = Enum.GetValues<Status>().Where(s => s != Status.Paused).ToArray();
|
|
}
|
|
else
|
|
{
|
|
_statusArray = Enum.GetValues<Status>().Where(s => s != Status.Stopped).ToArray();
|
|
}
|
|
|
|
return _statusArray;
|
|
}
|
|
|
|
private static void UpdateTorrentStates(MainData torrentList, string hash, TorrentSnapshot previousSnapshot, Torrent updatedTorrent)
|
|
{
|
|
UpdateTagStateForUpdate(torrentList, hash, previousSnapshot.Tags, updatedTorrent.Tags);
|
|
UpdateCategoryState(torrentList, updatedTorrent, hash, previousSnapshot.Category);
|
|
UpdateStatusState(torrentList, hash, previousSnapshot.State, previousSnapshot.UploadSpeed, updatedTorrent.State, updatedTorrent.UploadSpeed);
|
|
UpdateTrackerState(torrentList, updatedTorrent, hash, previousSnapshot.Tracker);
|
|
}
|
|
|
|
private static void RemoveTorrentFromStates(MainData torrentList, string hash)
|
|
{
|
|
if (!torrentList.Torrents.TryGetValue(hash, out var torrent))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var snapshot = CreateSnapshot(torrent);
|
|
|
|
torrentList.TagState[FilterHelper.TAG_ALL].Remove(hash);
|
|
UpdateTagStateForRemoval(torrentList, hash, snapshot.Tags);
|
|
|
|
torrentList.CategoriesState[FilterHelper.CATEGORY_ALL].Remove(hash);
|
|
UpdateCategoryStateForRemoval(torrentList, hash, snapshot.Category);
|
|
|
|
foreach (var status in GetStatuses(torrentList.MajorVersion))
|
|
{
|
|
if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusState))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (FilterHelper.FilterStatus(snapshot.State, snapshot.UploadSpeed, status))
|
|
{
|
|
statusState.Remove(hash);
|
|
}
|
|
}
|
|
|
|
torrentList.TrackersState[FilterHelper.TRACKER_ALL].Remove(hash);
|
|
UpdateTrackerStateForRemoval(torrentList, hash, snapshot.Tracker);
|
|
}
|
|
|
|
private static bool UpdateServerState(ServerState existingServerState, QBitTorrentClient.Models.ServerState serverState)
|
|
{
|
|
var changed = false;
|
|
|
|
if (serverState.AllTimeDownloaded.HasValue && existingServerState.AllTimeDownloaded != serverState.AllTimeDownloaded.Value)
|
|
{
|
|
existingServerState.AllTimeDownloaded = serverState.AllTimeDownloaded.Value;
|
|
changed = true;
|
|
}
|
|
|
|
if (serverState.AllTimeUploaded.HasValue && existingServerState.AllTimeUploaded != serverState.AllTimeUploaded.Value)
|
|
{
|
|
existingServerState.AllTimeUploaded = serverState.AllTimeUploaded.Value;
|
|
changed = true;
|
|
}
|
|
|
|
if (serverState.AverageTimeQueue.HasValue && existingServerState.AverageTimeQueue != serverState.AverageTimeQueue.Value)
|
|
{
|
|
existingServerState.AverageTimeQueue = serverState.AverageTimeQueue.Value;
|
|
changed = true;
|
|
}
|
|
|
|
if (serverState.ConnectionStatus is not null && existingServerState.ConnectionStatus != serverState.ConnectionStatus)
|
|
{
|
|
existingServerState.ConnectionStatus = serverState.ConnectionStatus;
|
|
changed = true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (torrentPeers.PeersRemoved is not null)
|
|
{
|
|
foreach (var key in torrentPeers.PeersRemoved)
|
|
{
|
|
peerList.Peers.Remove(key);
|
|
}
|
|
}
|
|
|
|
if (torrentPeers.Peers is not null)
|
|
{
|
|
foreach (var (key, peer) in torrentPeers.Peers)
|
|
{
|
|
if (!peerList.Peers.TryGetValue(key, out var existingPeer))
|
|
{
|
|
var newPeer = CreatePeer(key, peer);
|
|
peerList.Peers.Add(key, newPeer);
|
|
}
|
|
else
|
|
{
|
|
UpdatePeer(existingPeer, peer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void UpdatePeer(Peer existingPeer, QBitTorrentClient.Models.Peer peer)
|
|
{
|
|
existingPeer.Client = peer.Client ?? existingPeer.Client;
|
|
existingPeer.ClientId = peer.ClientId ?? existingPeer.ClientId;
|
|
existingPeer.Connection = peer.Connection ?? existingPeer.Connection;
|
|
existingPeer.Country = peer.Country ?? existingPeer.Country;
|
|
existingPeer.CountryCode = peer.CountryCode ?? existingPeer.CountryCode;
|
|
existingPeer.Downloaded = peer.Downloaded ?? existingPeer.Downloaded;
|
|
existingPeer.DownloadSpeed = peer.DownloadSpeed ?? existingPeer.DownloadSpeed;
|
|
existingPeer.Files = peer.Files ?? existingPeer.Files;
|
|
existingPeer.Flags = peer.Flags ?? existingPeer.Flags;
|
|
existingPeer.FlagsDescription = peer.FlagsDescription ?? existingPeer.FlagsDescription;
|
|
existingPeer.IPAddress = peer.IPAddress ?? existingPeer.IPAddress;
|
|
existingPeer.Port = peer.Port ?? existingPeer.Port;
|
|
existingPeer.Progress = peer.Progress ?? existingPeer.Progress;
|
|
existingPeer.Relevance = peer.Relevance ?? existingPeer.Relevance;
|
|
existingPeer.Uploaded = peer.Uploaded ?? existingPeer.Uploaded;
|
|
existingPeer.UploadSpeed = peer.UploadSpeed ?? existingPeer.UploadSpeed;
|
|
}
|
|
|
|
private static Category CreateCategory(QBitTorrentClient.Models.Category category)
|
|
{
|
|
return new Category(category.Name, category.SavePath!);
|
|
}
|
|
|
|
private static Peer CreatePeer(string key, QBitTorrentClient.Models.Peer peer)
|
|
{
|
|
return new Peer(
|
|
key,
|
|
peer.Client!,
|
|
peer.ClientId!,
|
|
peer.Connection!,
|
|
peer.Country,
|
|
peer.CountryCode,
|
|
peer.Downloaded.GetValueOrDefault(),
|
|
peer.DownloadSpeed.GetValueOrDefault(),
|
|
peer.Files!,
|
|
peer.Flags!,
|
|
peer.FlagsDescription!,
|
|
peer.IPAddress!,
|
|
peer.Port.GetValueOrDefault(),
|
|
peer.Progress.GetValueOrDefault(),
|
|
peer.Relevance.GetValueOrDefault(),
|
|
peer.Uploaded.GetValueOrDefault(),
|
|
peer.UploadSpeed.GetValueOrDefault());
|
|
}
|
|
|
|
public Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent)
|
|
{
|
|
var normalizedTags = torrent.Tags?
|
|
.Select(NormalizeTag)
|
|
.Where(static tag => !string.IsNullOrEmpty(tag))
|
|
.ToList()
|
|
?? new List<string>();
|
|
|
|
return new Torrent(
|
|
hash,
|
|
torrent.AddedOn.GetValueOrDefault(),
|
|
torrent.AmountLeft.GetValueOrDefault(),
|
|
torrent.AutomaticTorrentManagement.GetValueOrDefault(),
|
|
torrent.Availability.GetValueOrDefault(),
|
|
torrent.Category!,
|
|
torrent.Completed.GetValueOrDefault(),
|
|
torrent.CompletionOn.GetValueOrDefault(),
|
|
torrent.ContentPath!,
|
|
torrent.DownloadLimit.GetValueOrDefault(),
|
|
torrent.DownloadSpeed.GetValueOrDefault(),
|
|
torrent.Downloaded.GetValueOrDefault(),
|
|
torrent.DownloadedSession.GetValueOrDefault(),
|
|
torrent.EstimatedTimeOfArrival.GetValueOrDefault(),
|
|
torrent.FirstLastPiecePriority.GetValueOrDefault(),
|
|
torrent.ForceStart.GetValueOrDefault(),
|
|
torrent.InfoHashV1!,
|
|
torrent.InfoHashV2!,
|
|
torrent.LastActivity.GetValueOrDefault(),
|
|
torrent.MagnetUri!,
|
|
torrent.MaxRatio.GetValueOrDefault(),
|
|
torrent.MaxSeedingTime.GetValueOrDefault(),
|
|
torrent.Name!,
|
|
torrent.NumberComplete.GetValueOrDefault(),
|
|
torrent.NumberIncomplete.GetValueOrDefault(),
|
|
torrent.NumberLeeches.GetValueOrDefault(),
|
|
torrent.NumberSeeds.GetValueOrDefault(),
|
|
torrent.Priority.GetValueOrDefault(),
|
|
torrent.Progress.GetValueOrDefault(),
|
|
torrent.Ratio.GetValueOrDefault(),
|
|
torrent.RatioLimit.GetValueOrDefault(),
|
|
torrent.SavePath!,
|
|
torrent.SeedingTime.GetValueOrDefault(),
|
|
torrent.SeedingTimeLimit.GetValueOrDefault(),
|
|
torrent.SeenComplete.GetValueOrDefault(),
|
|
torrent.SequentialDownload.GetValueOrDefault(),
|
|
torrent.Size.GetValueOrDefault(),
|
|
torrent.State!,
|
|
torrent.SuperSeeding.GetValueOrDefault(),
|
|
normalizedTags,
|
|
torrent.TimeActive.GetValueOrDefault(),
|
|
torrent.TotalSize.GetValueOrDefault(),
|
|
torrent.Tracker!,
|
|
torrent.UploadLimit.GetValueOrDefault(),
|
|
torrent.Uploaded.GetValueOrDefault(),
|
|
torrent.UploadedSession.GetValueOrDefault(),
|
|
torrent.UploadSpeed.GetValueOrDefault(),
|
|
torrent.Reannounce ?? 0,
|
|
torrent.InactiveSeedingTimeLimit.GetValueOrDefault(),
|
|
torrent.MaxInactiveSeedingTime.GetValueOrDefault());
|
|
}
|
|
|
|
private static string NormalizeTag(string? tag)
|
|
{
|
|
if (string.IsNullOrEmpty(tag))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var separatorIndex = tag.IndexOf('\t');
|
|
var normalized = (separatorIndex >= 0) ? tag[..separatorIndex] : tag;
|
|
|
|
return normalized.Trim();
|
|
}
|
|
|
|
private static TorrentSnapshot CreateSnapshot(Torrent torrent)
|
|
{
|
|
return new TorrentSnapshot(
|
|
string.IsNullOrEmpty(torrent.Category) ? null : torrent.Category,
|
|
torrent.Tags.ToList(),
|
|
torrent.Tracker ?? string.Empty,
|
|
torrent.State ?? string.Empty,
|
|
torrent.UploadSpeed);
|
|
}
|
|
|
|
private readonly struct TorrentSnapshot
|
|
{
|
|
public TorrentSnapshot(string? category, List<string> tags, string tracker, string state, long uploadSpeed)
|
|
{
|
|
Category = category;
|
|
Tags = tags;
|
|
Tracker = tracker;
|
|
State = state;
|
|
UploadSpeed = uploadSpeed;
|
|
}
|
|
|
|
public string? Category { get; }
|
|
|
|
public IReadOnlyList<string> Tags { get; }
|
|
|
|
public string Tracker { get; }
|
|
|
|
public string State { get; }
|
|
|
|
public long UploadSpeed { get; }
|
|
}
|
|
|
|
private static void UpdateTagStateForAddition(MainData torrentList, Torrent torrent, string hash)
|
|
{
|
|
if (torrent.Tags.Count == 0)
|
|
{
|
|
torrentList.TagState[FilterHelper.TAG_UNTAGGED].Add(hash);
|
|
return;
|
|
}
|
|
|
|
torrentList.TagState[FilterHelper.TAG_UNTAGGED].Remove(hash);
|
|
foreach (var tag in torrent.Tags)
|
|
{
|
|
if (string.IsNullOrEmpty(tag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
GetOrCreateTagSet(torrentList, tag).Add(hash);
|
|
}
|
|
}
|
|
|
|
private static void UpdateTagStateForUpdate(MainData torrentList, string hash, IReadOnlyList<string> previousTags, IList<string> newTags)
|
|
{
|
|
UpdateTagStateForRemoval(torrentList, hash, previousTags);
|
|
|
|
if (newTags.Count == 0)
|
|
{
|
|
torrentList.TagState[FilterHelper.TAG_UNTAGGED].Add(hash);
|
|
return;
|
|
}
|
|
|
|
torrentList.TagState[FilterHelper.TAG_UNTAGGED].Remove(hash);
|
|
foreach (var tag in newTags)
|
|
{
|
|
if (string.IsNullOrEmpty(tag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
GetOrCreateTagSet(torrentList, tag).Add(hash);
|
|
}
|
|
}
|
|
|
|
private static void UpdateTagStateForRemoval(MainData torrentList, string hash, IReadOnlyList<string> previousTags)
|
|
{
|
|
torrentList.TagState[FilterHelper.TAG_UNTAGGED].Remove(hash);
|
|
|
|
foreach (var tag in previousTags)
|
|
{
|
|
if (string.IsNullOrEmpty(tag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (torrentList.TagState.TryGetValue(tag, out var set))
|
|
{
|
|
set.Remove(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void UpdateCategoryState(MainData torrentList, Torrent updatedTorrent, string hash, string? previousCategory)
|
|
{
|
|
var useSubcategories = torrentList.ServerState.UseSubcategories;
|
|
|
|
if (!string.IsNullOrEmpty(previousCategory))
|
|
{
|
|
foreach (var categoryKey in EnumerateCategoryKeys(previousCategory, useSubcategories))
|
|
{
|
|
if (torrentList.CategoriesState.TryGetValue(categoryKey, out var set))
|
|
{
|
|
set.Remove(hash);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
torrentList.CategoriesState[FilterHelper.CATEGORY_UNCATEGORIZED].Remove(hash);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(updatedTorrent.Category))
|
|
{
|
|
torrentList.CategoriesState[FilterHelper.CATEGORY_UNCATEGORIZED].Add(hash);
|
|
return;
|
|
}
|
|
|
|
foreach (var categoryKey in EnumerateCategoryKeys(updatedTorrent.Category, useSubcategories))
|
|
{
|
|
GetOrCreateCategorySet(torrentList, categoryKey).Add(hash);
|
|
}
|
|
}
|
|
|
|
private static void UpdateCategoryStateForRemoval(MainData torrentList, string hash, string? previousCategory)
|
|
{
|
|
if (string.IsNullOrEmpty(previousCategory))
|
|
{
|
|
torrentList.CategoriesState[FilterHelper.CATEGORY_UNCATEGORIZED].Remove(hash);
|
|
return;
|
|
}
|
|
|
|
foreach (var categoryKey in EnumerateCategoryKeys(previousCategory, torrentList.ServerState.UseSubcategories))
|
|
{
|
|
if (torrentList.CategoriesState.TryGetValue(categoryKey, out var set))
|
|
{
|
|
set.Remove(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void UpdateStatusState(MainData torrentList, string hash, string previousState, long previousUploadSpeed, string newState, long newUploadSpeed)
|
|
{
|
|
foreach (var status in GetStatuses(torrentList.MajorVersion))
|
|
{
|
|
if (!torrentList.StatusState.TryGetValue(status.ToString(), out var statusSet))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var wasMatch = FilterHelper.FilterStatus(previousState, previousUploadSpeed, status);
|
|
var isMatch = FilterHelper.FilterStatus(newState, newUploadSpeed, status);
|
|
|
|
if (wasMatch == isMatch)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (wasMatch)
|
|
{
|
|
statusSet.Remove(hash);
|
|
}
|
|
|
|
if (isMatch)
|
|
{
|
|
statusSet.Add(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void UpdateTrackerState(MainData torrentList, Torrent updatedTorrent, string hash, string? previousTracker)
|
|
{
|
|
if (!string.IsNullOrEmpty(previousTracker))
|
|
{
|
|
if (torrentList.TrackersState.TryGetValue(previousTracker, out var oldSet))
|
|
{
|
|
oldSet.Remove(hash);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
torrentList.TrackersState[FilterHelper.TRACKER_TRACKERLESS].Remove(hash);
|
|
}
|
|
|
|
var tracker = updatedTorrent.Tracker ?? string.Empty;
|
|
if (string.IsNullOrEmpty(tracker))
|
|
{
|
|
torrentList.TrackersState[FilterHelper.TRACKER_TRACKERLESS].Add(hash);
|
|
return;
|
|
}
|
|
|
|
torrentList.TrackersState[FilterHelper.TRACKER_TRACKERLESS].Remove(hash);
|
|
GetOrCreateTrackerSet(torrentList, tracker).Add(hash);
|
|
}
|
|
|
|
private static void UpdateTrackerStateForRemoval(MainData torrentList, string hash, string? previousTracker)
|
|
{
|
|
if (string.IsNullOrEmpty(previousTracker))
|
|
{
|
|
torrentList.TrackersState[FilterHelper.TRACKER_TRACKERLESS].Remove(hash);
|
|
return;
|
|
}
|
|
|
|
if (torrentList.TrackersState.TryGetValue(previousTracker, out var trackerSet))
|
|
{
|
|
trackerSet.Remove(hash);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<string> EnumerateCategoryKeys(string category, bool useSubcategories)
|
|
{
|
|
if (string.IsNullOrEmpty(category))
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
yield return category;
|
|
|
|
if (!useSubcategories)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
var current = category;
|
|
while (true)
|
|
{
|
|
var separatorIndex = current.LastIndexOf('/');
|
|
if (separatorIndex < 0)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
current = current[..separatorIndex];
|
|
yield return current;
|
|
}
|
|
}
|
|
|
|
private static HashSet<string> GetOrCreateTagSet(MainData torrentList, string tag)
|
|
{
|
|
if (!torrentList.TagState.TryGetValue(tag, out var set))
|
|
{
|
|
set = new HashSet<string>(StringComparer.Ordinal);
|
|
torrentList.TagState[tag] = set;
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
private static HashSet<string> GetOrCreateCategorySet(MainData torrentList, string category)
|
|
{
|
|
if (!torrentList.CategoriesState.TryGetValue(category, out var set))
|
|
{
|
|
set = new HashSet<string>(StringComparer.Ordinal);
|
|
torrentList.CategoriesState[category] = set;
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
private static HashSet<string> GetOrCreateTrackerSet(MainData torrentList, string tracker)
|
|
{
|
|
if (!torrentList.TrackersState.TryGetValue(tracker, out var set))
|
|
{
|
|
set = new HashSet<string>(StringComparer.Ordinal);
|
|
torrentList.TrackersState[tracker] = set;
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
private static bool UpdateCategory(Category existingCategory, QBitTorrentClient.Models.Category category)
|
|
{
|
|
if (category.SavePath is not null && existingCategory.SavePath != category.SavePath)
|
|
{
|
|
existingCategory.SavePath = category.SavePath;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private readonly struct TorrentUpdateResult
|
|
{
|
|
public TorrentUpdateResult(bool dataChanged, bool filterChanged)
|
|
{
|
|
DataChanged = dataChanged;
|
|
FilterChanged = filterChanged;
|
|
}
|
|
|
|
public bool DataChanged { get; }
|
|
|
|
public bool FilterChanged { get; }
|
|
}
|
|
|
|
private static TorrentUpdateResult UpdateTorrent(Torrent existingTorrent, QBitTorrentClient.Models.Torrent torrent)
|
|
{
|
|
var dataChanged = false;
|
|
var filterChanged = false;
|
|
|
|
if (torrent.AddedOn.HasValue && existingTorrent.AddedOn != torrent.AddedOn.Value)
|
|
{
|
|
existingTorrent.AddedOn = torrent.AddedOn.Value;
|
|
dataChanged = true;
|
|
}
|
|
|
|
if (torrent.AmountLeft.HasValue && existingTorrent.AmountLeft != torrent.AmountLeft.Value)
|
|
{
|
|
existingTorrent.AmountLeft = torrent.AmountLeft.Value;
|
|
dataChanged = true;
|
|
}
|
|
|
|
if (torrent.AutomaticTorrentManagement.HasValue && existingTorrent.AutomaticTorrentManagement != torrent.AutomaticTorrentManagement.Value)
|
|
{
|
|
existingTorrent.AutomaticTorrentManagement = torrent.AutomaticTorrentManagement.Value;
|
|
dataChanged = true;
|
|
}
|
|
|
|
if (torrent.Availability.HasValue && existingTorrent.Availability != torrent.Availability.Value)
|
|
{
|
|
existingTorrent.Availability = torrent.Availability.Value;
|
|
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)
|
|
{
|
|
var normalizedTags = torrent.Tags.Select(NormalizeTag)
|
|
.Where(static tag => !string.IsNullOrEmpty(tag))
|
|
.ToList();
|
|
|
|
if (!existingTorrent.Tags.SequenceEqual(normalizedTags))
|
|
{
|
|
existingTorrent.Tags.Clear();
|
|
existingTorrent.Tags.AddRange(normalizedTags);
|
|
dataChanged = true;
|
|
filterChanged = true;
|
|
}
|
|
}
|
|
|
|
if (torrent.TimeActive.HasValue && existingTorrent.TimeActive != torrent.TimeActive.Value)
|
|
{
|
|
existingTorrent.TimeActive = torrent.TimeActive.Value;
|
|
dataChanged = true;
|
|
}
|
|
|
|
if (torrent.TotalSize.HasValue && existingTorrent.TotalSize != torrent.TotalSize.Value)
|
|
{
|
|
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)
|
|
{
|
|
return BuildContentsTree(files);
|
|
}
|
|
|
|
private static Dictionary<string, ContentItem> BuildContentsTree(IReadOnlyList<QBitTorrentClient.Models.FileData> files)
|
|
{
|
|
var result = new Dictionary<string, ContentItem>();
|
|
if (files.Count == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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++)
|
|
{
|
|
var folderName = segments[i];
|
|
if (folderName == ".unwanted")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var folderPath = string.IsNullOrEmpty(parentPath)
|
|
? folderName
|
|
: string.Concat(parentPath, Extensions.DirectorySeparator, folderName);
|
|
|
|
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 folders = nodes.Values
|
|
.Where(n => n.Item is not null && n.Item.IsFolder)
|
|
.OrderByDescending(n => n.Item!.Level)
|
|
.ToList();
|
|
|
|
foreach (var folder in folders)
|
|
{
|
|
var folderItem = folder.Item!;
|
|
if (folder.Children.Count == 0)
|
|
{
|
|
folderItem.Size = 0;
|
|
folderItem.Progress = 0;
|
|
folderItem.Availability = 0;
|
|
folderItem.Priority = Priority.Normal;
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
result[node.Item.Name] = node.Item;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static bool UpdateContentItem(ContentItem destination, ContentItem source)
|
|
{
|
|
const float floatTolerance = 0.0001f;
|
|
var changed = false;
|
|
|
|
if (destination.Priority != source.Priority)
|
|
{
|
|
destination.Priority = source.Priority;
|
|
changed = true;
|
|
}
|
|
|
|
if (System.Math.Abs(destination.Progress - source.Progress) > floatTolerance)
|
|
{
|
|
destination.Progress = source.Progress;
|
|
changed = true;
|
|
}
|
|
|
|
if (destination.Size != source.Size)
|
|
{
|
|
destination.Size = source.Size;
|
|
changed = true;
|
|
}
|
|
|
|
if (System.Math.Abs(destination.Availability - source.Availability) > floatTolerance)
|
|
{
|
|
destination.Availability = source.Availability;
|
|
changed = true;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
private struct DirectoryAccumulator
|
|
{
|
|
public long TotalSize { get; private set; }
|
|
|
|
private long _activeSize;
|
|
private double _progressSum;
|
|
private double _availabilitySum;
|
|
private Priority? _priority;
|
|
private bool _mixedPriority;
|
|
|
|
public void Add(Priority priority, float progress, long size, float availability)
|
|
{
|
|
TotalSize += size;
|
|
|
|
if (priority != Priority.DoNotDownload)
|
|
{
|
|
_activeSize += size;
|
|
_progressSum += progress * size;
|
|
_availabilitySum += availability * size;
|
|
}
|
|
|
|
if (!_priority.HasValue)
|
|
{
|
|
_priority = priority;
|
|
}
|
|
else if (_priority.Value != priority)
|
|
{
|
|
_mixedPriority = true;
|
|
}
|
|
}
|
|
|
|
public Priority ResolvePriority()
|
|
{
|
|
if (_mixedPriority)
|
|
{
|
|
return Priority.Mixed;
|
|
}
|
|
|
|
return _priority ?? Priority.Normal;
|
|
}
|
|
|
|
public float ResolveProgress()
|
|
{
|
|
if (_activeSize == 0 || TotalSize == 0)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
var value = _progressSum / _activeSize;
|
|
if (value < 0)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
if (value > 1)
|
|
{
|
|
return 1f;
|
|
}
|
|
|
|
return (float)value;
|
|
}
|
|
|
|
public float ResolveAvailability()
|
|
{
|
|
if (_activeSize == 0 || TotalSize == 0)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
return (float)(_availabilitySum / _activeSize);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (original is null)
|
|
{
|
|
original = new QBitTorrentClient.Models.UpdatePreferences
|
|
{
|
|
AddToTopOfQueue = changed.AddToTopOfQueue,
|
|
AddTrackers = changed.AddTrackers,
|
|
AddTrackersEnabled = changed.AddTrackersEnabled,
|
|
AltDlLimit = changed.AltDlLimit,
|
|
AltUpLimit = changed.AltUpLimit,
|
|
AlternativeWebuiEnabled = changed.AlternativeWebuiEnabled,
|
|
AlternativeWebuiPath = changed.AlternativeWebuiPath,
|
|
AnnounceIp = changed.AnnounceIp,
|
|
AnnounceToAllTiers = changed.AnnounceToAllTiers,
|
|
AnnounceToAllTrackers = changed.AnnounceToAllTrackers,
|
|
AnonymousMode = changed.AnonymousMode,
|
|
AsyncIoThreads = changed.AsyncIoThreads,
|
|
AutoDeleteMode = changed.AutoDeleteMode,
|
|
AutoTmmEnabled = changed.AutoTmmEnabled,
|
|
AutorunEnabled = changed.AutorunEnabled,
|
|
AutorunOnTorrentAddedEnabled = changed.AutorunOnTorrentAddedEnabled,
|
|
AutorunOnTorrentAddedProgram = changed.AutorunOnTorrentAddedProgram,
|
|
AutorunProgram = changed.AutorunProgram,
|
|
BannedIPs = changed.BannedIPs,
|
|
BdecodeDepthLimit = changed.BdecodeDepthLimit,
|
|
BdecodeTokenLimit = changed.BdecodeTokenLimit,
|
|
BittorrentProtocol = changed.BittorrentProtocol,
|
|
BlockPeersOnPrivilegedPorts = changed.BlockPeersOnPrivilegedPorts,
|
|
BypassAuthSubnetWhitelist = changed.BypassAuthSubnetWhitelist,
|
|
BypassAuthSubnetWhitelistEnabled = changed.BypassAuthSubnetWhitelistEnabled,
|
|
BypassLocalAuth = changed.BypassLocalAuth,
|
|
CategoryChangedTmmEnabled = changed.CategoryChangedTmmEnabled,
|
|
CheckingMemoryUse = changed.CheckingMemoryUse,
|
|
ConnectionSpeed = changed.ConnectionSpeed,
|
|
CurrentInterfaceAddress = changed.CurrentInterfaceAddress,
|
|
CurrentInterfaceName = changed.CurrentInterfaceName,
|
|
CurrentNetworkInterface = changed.CurrentNetworkInterface,
|
|
Dht = changed.Dht,
|
|
DiskCache = changed.DiskCache,
|
|
DiskCacheTtl = changed.DiskCacheTtl,
|
|
DiskIoReadMode = changed.DiskIoReadMode,
|
|
DiskIoType = changed.DiskIoType,
|
|
DiskIoWriteMode = changed.DiskIoWriteMode,
|
|
DiskQueueSize = changed.DiskQueueSize,
|
|
DlLimit = changed.DlLimit,
|
|
DontCountSlowTorrents = changed.DontCountSlowTorrents,
|
|
DyndnsDomain = changed.DyndnsDomain,
|
|
DyndnsEnabled = changed.DyndnsEnabled,
|
|
DyndnsPassword = changed.DyndnsPassword,
|
|
DyndnsService = changed.DyndnsService,
|
|
DyndnsUsername = changed.DyndnsUsername,
|
|
EmbeddedTrackerPort = changed.EmbeddedTrackerPort,
|
|
EmbeddedTrackerPortForwarding = changed.EmbeddedTrackerPortForwarding,
|
|
EnableCoalesceReadWrite = changed.EnableCoalesceReadWrite,
|
|
EnableEmbeddedTracker = changed.EnableEmbeddedTracker,
|
|
EnableMultiConnectionsFromSameIp = changed.EnableMultiConnectionsFromSameIp,
|
|
EnablePieceExtentAffinity = changed.EnablePieceExtentAffinity,
|
|
EnableUploadSuggestions = changed.EnableUploadSuggestions,
|
|
Encryption = changed.Encryption,
|
|
ExcludedFileNames = changed.ExcludedFileNames,
|
|
ExcludedFileNamesEnabled = changed.ExcludedFileNamesEnabled,
|
|
ExportDir = changed.ExportDir,
|
|
ExportDirFin = changed.ExportDirFin,
|
|
FileLogAge = changed.FileLogAge,
|
|
FileLogAgeType = changed.FileLogAgeType,
|
|
FileLogBackupEnabled = changed.FileLogBackupEnabled,
|
|
FileLogDeleteOld = changed.FileLogDeleteOld,
|
|
FileLogEnabled = changed.FileLogEnabled,
|
|
FileLogMaxSize = changed.FileLogMaxSize,
|
|
FileLogPath = changed.FileLogPath,
|
|
FilePoolSize = changed.FilePoolSize,
|
|
HashingThreads = changed.HashingThreads,
|
|
I2pAddress = changed.I2pAddress,
|
|
I2pEnabled = changed.I2pEnabled,
|
|
I2pInboundLength = changed.I2pInboundLength,
|
|
I2pInboundQuantity = changed.I2pInboundQuantity,
|
|
I2pMixedMode = changed.I2pMixedMode,
|
|
I2pOutboundLength = changed.I2pOutboundLength,
|
|
I2pOutboundQuantity = changed.I2pOutboundQuantity,
|
|
I2pPort = changed.I2pPort,
|
|
IdnSupportEnabled = changed.IdnSupportEnabled,
|
|
IncompleteFilesExt = changed.IncompleteFilesExt,
|
|
IpFilterEnabled = changed.IpFilterEnabled,
|
|
IpFilterPath = changed.IpFilterPath,
|
|
IpFilterTrackers = changed.IpFilterTrackers,
|
|
LimitLanPeers = changed.LimitLanPeers,
|
|
LimitTcpOverhead = changed.LimitTcpOverhead,
|
|
LimitUtpRate = changed.LimitUtpRate,
|
|
ListenPort = changed.ListenPort,
|
|
Locale = changed.Locale,
|
|
Lsd = changed.Lsd,
|
|
MailNotificationAuthEnabled = changed.MailNotificationAuthEnabled,
|
|
MailNotificationEmail = changed.MailNotificationEmail,
|
|
MailNotificationEnabled = changed.MailNotificationEnabled,
|
|
MailNotificationPassword = changed.MailNotificationPassword,
|
|
MailNotificationSender = changed.MailNotificationSender,
|
|
MailNotificationSmtp = changed.MailNotificationSmtp,
|
|
MailNotificationSslEnabled = changed.MailNotificationSslEnabled,
|
|
MailNotificationUsername = changed.MailNotificationUsername,
|
|
MaxActiveCheckingTorrents = changed.MaxActiveCheckingTorrents,
|
|
MaxActiveDownloads = changed.MaxActiveDownloads,
|
|
MaxActiveTorrents = changed.MaxActiveTorrents,
|
|
MaxActiveUploads = changed.MaxActiveUploads,
|
|
MaxConcurrentHttpAnnounces = changed.MaxConcurrentHttpAnnounces,
|
|
MaxConnec = changed.MaxConnec,
|
|
MaxConnecPerTorrent = changed.MaxConnecPerTorrent,
|
|
MaxInactiveSeedingTime = changed.MaxInactiveSeedingTime,
|
|
MaxInactiveSeedingTimeEnabled = changed.MaxInactiveSeedingTimeEnabled,
|
|
MaxRatio = changed.MaxRatio,
|
|
MaxRatioAct = changed.MaxRatioAct,
|
|
MaxRatioEnabled = changed.MaxRatioEnabled,
|
|
MaxSeedingTime = changed.MaxSeedingTime,
|
|
MaxSeedingTimeEnabled = changed.MaxSeedingTimeEnabled,
|
|
MaxUploads = changed.MaxUploads,
|
|
MaxUploadsPerTorrent = changed.MaxUploadsPerTorrent,
|
|
MemoryWorkingSetLimit = changed.MemoryWorkingSetLimit,
|
|
MergeTrackers = changed.MergeTrackers,
|
|
OutgoingPortsMax = changed.OutgoingPortsMax,
|
|
OutgoingPortsMin = changed.OutgoingPortsMin,
|
|
PeerTos = changed.PeerTos,
|
|
PeerTurnover = changed.PeerTurnover,
|
|
PeerTurnoverCutoff = changed.PeerTurnoverCutoff,
|
|
PeerTurnoverInterval = changed.PeerTurnoverInterval,
|
|
PerformanceWarning = changed.PerformanceWarning,
|
|
Pex = changed.Pex,
|
|
PreallocateAll = changed.PreallocateAll,
|
|
ProxyAuthEnabled = changed.ProxyAuthEnabled,
|
|
ProxyBittorrent = changed.ProxyBittorrent,
|
|
ProxyHostnameLookup = changed.ProxyHostnameLookup,
|
|
ProxyIp = changed.ProxyIp,
|
|
ProxyMisc = changed.ProxyMisc,
|
|
ProxyPassword = changed.ProxyPassword,
|
|
ProxyPeerConnections = changed.ProxyPeerConnections,
|
|
ProxyPort = changed.ProxyPort,
|
|
ProxyRss = changed.ProxyRss,
|
|
ProxyType = changed.ProxyType,
|
|
ProxyUsername = changed.ProxyUsername,
|
|
QueueingEnabled = changed.QueueingEnabled,
|
|
RandomPort = changed.RandomPort,
|
|
ReannounceWhenAddressChanged = changed.ReannounceWhenAddressChanged,
|
|
RecheckCompletedTorrents = changed.RecheckCompletedTorrents,
|
|
RefreshInterval = changed.RefreshInterval,
|
|
RequestQueueSize = changed.RequestQueueSize,
|
|
ResolvePeerCountries = changed.ResolvePeerCountries,
|
|
ResumeDataStorageType = changed.ResumeDataStorageType,
|
|
RssAutoDownloadingEnabled = changed.RssAutoDownloadingEnabled,
|
|
RssDownloadRepackProperEpisodes = changed.RssDownloadRepackProperEpisodes,
|
|
RssMaxArticlesPerFeed = changed.RssMaxArticlesPerFeed,
|
|
RssProcessingEnabled = changed.RssProcessingEnabled,
|
|
RssRefreshInterval = changed.RssRefreshInterval,
|
|
RssSmartEpisodeFilters = changed.RssSmartEpisodeFilters,
|
|
SavePath = changed.SavePath,
|
|
SavePathChangedTmmEnabled = changed.SavePathChangedTmmEnabled,
|
|
SaveResumeDataInterval = changed.SaveResumeDataInterval,
|
|
ScanDirs = changed.ScanDirs,
|
|
ScheduleFromHour = changed.ScheduleFromHour,
|
|
ScheduleFromMin = changed.ScheduleFromMin,
|
|
ScheduleToHour = changed.ScheduleToHour,
|
|
ScheduleToMin = changed.ScheduleToMin,
|
|
SchedulerDays = changed.SchedulerDays,
|
|
SchedulerEnabled = changed.SchedulerEnabled,
|
|
SendBufferLowWatermark = changed.SendBufferLowWatermark,
|
|
SendBufferWatermark = changed.SendBufferWatermark,
|
|
SendBufferWatermarkFactor = changed.SendBufferWatermarkFactor,
|
|
SlowTorrentDlRateThreshold = changed.SlowTorrentDlRateThreshold,
|
|
SlowTorrentInactiveTimer = changed.SlowTorrentInactiveTimer,
|
|
SlowTorrentUlRateThreshold = changed.SlowTorrentUlRateThreshold,
|
|
SocketBacklogSize = changed.SocketBacklogSize,
|
|
SocketReceiveBufferSize = changed.SocketReceiveBufferSize,
|
|
SocketSendBufferSize = changed.SocketSendBufferSize,
|
|
SsrfMitigation = changed.SsrfMitigation,
|
|
StartPausedEnabled = changed.StartPausedEnabled,
|
|
StopTrackerTimeout = changed.StopTrackerTimeout,
|
|
TempPath = changed.TempPath,
|
|
TempPathEnabled = changed.TempPathEnabled,
|
|
TorrentChangedTmmEnabled = changed.TorrentChangedTmmEnabled,
|
|
TorrentContentLayout = changed.TorrentContentLayout,
|
|
TorrentFileSizeLimit = changed.TorrentFileSizeLimit,
|
|
TorrentStopCondition = changed.TorrentStopCondition,
|
|
UpLimit = changed.UpLimit,
|
|
UploadChokingAlgorithm = changed.UploadChokingAlgorithm,
|
|
UploadSlotsBehavior = changed.UploadSlotsBehavior,
|
|
Upnp = changed.Upnp,
|
|
UpnpLeaseDuration = changed.UpnpLeaseDuration,
|
|
UseCategoryPathsInManualMode = changed.UseCategoryPathsInManualMode,
|
|
UseHttps = changed.UseHttps,
|
|
UseSubcategories = changed.UseSubcategories,
|
|
UtpTcpMixedMode = changed.UtpTcpMixedMode,
|
|
ValidateHttpsTrackerCertificate = changed.ValidateHttpsTrackerCertificate,
|
|
WebUiAddress = changed.WebUiAddress,
|
|
WebUiBanDuration = changed.WebUiBanDuration,
|
|
WebUiClickjackingProtectionEnabled = changed.WebUiClickjackingProtectionEnabled,
|
|
WebUiCsrfProtectionEnabled = changed.WebUiCsrfProtectionEnabled,
|
|
WebUiCustomHttpHeaders = changed.WebUiCustomHttpHeaders,
|
|
WebUiDomainList = changed.WebUiDomainList,
|
|
WebUiHostHeaderValidationEnabled = changed.WebUiHostHeaderValidationEnabled,
|
|
WebUiHttpsCertPath = changed.WebUiHttpsCertPath,
|
|
WebUiHttpsKeyPath = changed.WebUiHttpsKeyPath,
|
|
WebUiMaxAuthFailCount = changed.WebUiMaxAuthFailCount,
|
|
WebUiPort = changed.WebUiPort,
|
|
WebUiReverseProxiesList = changed.WebUiReverseProxiesList,
|
|
WebUiReverseProxyEnabled = changed.WebUiReverseProxyEnabled,
|
|
WebUiSecureCookieEnabled = changed.WebUiSecureCookieEnabled,
|
|
WebUiSessionTimeout = changed.WebUiSessionTimeout,
|
|
WebUiUpnp = changed.WebUiUpnp,
|
|
WebUiUseCustomHttpHeadersEnabled = changed.WebUiUseCustomHttpHeadersEnabled,
|
|
WebUiUsername = changed.WebUiUsername
|
|
};
|
|
}
|
|
else
|
|
{
|
|
original.AddToTopOfQueue = changed.AddToTopOfQueue ?? original.AddToTopOfQueue;
|
|
original.AddTrackers = changed.AddTrackers ?? original.AddTrackers;
|
|
original.AddTrackersEnabled = changed.AddTrackersEnabled ?? original.AddTrackersEnabled;
|
|
original.AltDlLimit = changed.AltDlLimit ?? original.AltDlLimit;
|
|
original.AltUpLimit = changed.AltUpLimit ?? original.AltUpLimit;
|
|
original.AlternativeWebuiEnabled = changed.AlternativeWebuiEnabled ?? original.AlternativeWebuiEnabled;
|
|
original.AlternativeWebuiPath = changed.AlternativeWebuiPath ?? original.AlternativeWebuiPath;
|
|
original.AnnounceIp = changed.AnnounceIp ?? original.AnnounceIp;
|
|
original.AnnounceToAllTiers = changed.AnnounceToAllTiers ?? original.AnnounceToAllTiers;
|
|
original.AnnounceToAllTrackers = changed.AnnounceToAllTrackers ?? original.AnnounceToAllTrackers;
|
|
original.AnonymousMode = changed.AnonymousMode ?? original.AnonymousMode;
|
|
original.AsyncIoThreads = changed.AsyncIoThreads ?? original.AsyncIoThreads;
|
|
original.AutoDeleteMode = changed.AutoDeleteMode ?? original.AutoDeleteMode;
|
|
original.AutoTmmEnabled = changed.AutoTmmEnabled ?? original.AutoTmmEnabled;
|
|
original.AutorunEnabled = changed.AutorunEnabled ?? original.AutorunEnabled;
|
|
original.AutorunOnTorrentAddedEnabled = changed.AutorunOnTorrentAddedEnabled ?? original.AutorunOnTorrentAddedEnabled;
|
|
original.AutorunOnTorrentAddedProgram = changed.AutorunOnTorrentAddedProgram ?? original.AutorunOnTorrentAddedProgram;
|
|
original.AutorunProgram = changed.AutorunProgram ?? original.AutorunProgram;
|
|
original.BannedIPs = changed.BannedIPs ?? original.BannedIPs;
|
|
original.BdecodeDepthLimit = changed.BdecodeDepthLimit ?? original.BdecodeDepthLimit;
|
|
original.BdecodeTokenLimit = changed.BdecodeTokenLimit ?? original.BdecodeTokenLimit;
|
|
original.BittorrentProtocol = changed.BittorrentProtocol ?? original.BittorrentProtocol;
|
|
original.BlockPeersOnPrivilegedPorts = changed.BlockPeersOnPrivilegedPorts ?? original.BlockPeersOnPrivilegedPorts;
|
|
original.BypassAuthSubnetWhitelist = changed.BypassAuthSubnetWhitelist ?? original.BypassAuthSubnetWhitelist;
|
|
original.BypassAuthSubnetWhitelistEnabled = changed.BypassAuthSubnetWhitelistEnabled ?? original.BypassAuthSubnetWhitelistEnabled;
|
|
original.BypassLocalAuth = changed.BypassLocalAuth ?? original.BypassLocalAuth;
|
|
original.CategoryChangedTmmEnabled = changed.CategoryChangedTmmEnabled ?? original.CategoryChangedTmmEnabled;
|
|
original.CheckingMemoryUse = changed.CheckingMemoryUse ?? original.CheckingMemoryUse;
|
|
original.ConnectionSpeed = changed.ConnectionSpeed ?? original.ConnectionSpeed;
|
|
original.CurrentInterfaceAddress = changed.CurrentInterfaceAddress ?? original.CurrentInterfaceAddress;
|
|
original.CurrentInterfaceName = changed.CurrentInterfaceName ?? original.CurrentInterfaceName;
|
|
original.CurrentNetworkInterface = changed.CurrentNetworkInterface ?? original.CurrentNetworkInterface;
|
|
original.Dht = changed.Dht ?? original.Dht;
|
|
original.DiskCache = changed.DiskCache ?? original.DiskCache;
|
|
original.DiskCacheTtl = changed.DiskCacheTtl ?? original.DiskCacheTtl;
|
|
original.DiskIoReadMode = changed.DiskIoReadMode ?? original.DiskIoReadMode;
|
|
original.DiskIoType = changed.DiskIoType ?? original.DiskIoType;
|
|
original.DiskIoWriteMode = changed.DiskIoWriteMode ?? original.DiskIoWriteMode;
|
|
original.DiskQueueSize = changed.DiskQueueSize ?? original.DiskQueueSize;
|
|
original.DlLimit = changed.DlLimit ?? original.DlLimit;
|
|
original.DontCountSlowTorrents = changed.DontCountSlowTorrents ?? original.DontCountSlowTorrents;
|
|
original.DyndnsDomain = changed.DyndnsDomain ?? original.DyndnsDomain;
|
|
original.DyndnsEnabled = changed.DyndnsEnabled ?? original.DyndnsEnabled;
|
|
original.DyndnsPassword = changed.DyndnsPassword ?? original.DyndnsPassword;
|
|
original.DyndnsService = changed.DyndnsService ?? original.DyndnsService;
|
|
original.DyndnsUsername = changed.DyndnsUsername ?? original.DyndnsUsername;
|
|
original.EmbeddedTrackerPort = changed.EmbeddedTrackerPort ?? original.EmbeddedTrackerPort;
|
|
original.EmbeddedTrackerPortForwarding = changed.EmbeddedTrackerPortForwarding ?? original.EmbeddedTrackerPortForwarding;
|
|
original.EnableCoalesceReadWrite = changed.EnableCoalesceReadWrite ?? original.EnableCoalesceReadWrite;
|
|
original.EnableEmbeddedTracker = changed.EnableEmbeddedTracker ?? original.EnableEmbeddedTracker;
|
|
original.EnableMultiConnectionsFromSameIp = changed.EnableMultiConnectionsFromSameIp ?? original.EnableMultiConnectionsFromSameIp;
|
|
original.EnablePieceExtentAffinity = changed.EnablePieceExtentAffinity ?? original.EnablePieceExtentAffinity;
|
|
original.EnableUploadSuggestions = changed.EnableUploadSuggestions ?? original.EnableUploadSuggestions;
|
|
original.Encryption = changed.Encryption ?? original.Encryption;
|
|
original.ExcludedFileNames = changed.ExcludedFileNames ?? original.ExcludedFileNames;
|
|
original.ExcludedFileNamesEnabled = changed.ExcludedFileNamesEnabled ?? original.ExcludedFileNamesEnabled;
|
|
original.ExportDir = changed.ExportDir ?? original.ExportDir;
|
|
original.ExportDirFin = changed.ExportDirFin ?? original.ExportDirFin;
|
|
original.FileLogAge = changed.FileLogAge ?? original.FileLogAge;
|
|
original.FileLogAgeType = changed.FileLogAgeType ?? original.FileLogAgeType;
|
|
original.FileLogBackupEnabled = changed.FileLogBackupEnabled ?? original.FileLogBackupEnabled;
|
|
original.FileLogDeleteOld = changed.FileLogDeleteOld ?? original.FileLogDeleteOld;
|
|
original.FileLogEnabled = changed.FileLogEnabled ?? original.FileLogEnabled;
|
|
original.FileLogMaxSize = changed.FileLogMaxSize ?? original.FileLogMaxSize;
|
|
original.FileLogPath = changed.FileLogPath ?? original.FileLogPath;
|
|
original.FilePoolSize = changed.FilePoolSize ?? original.FilePoolSize;
|
|
original.HashingThreads = changed.HashingThreads ?? original.HashingThreads;
|
|
original.I2pAddress = changed.I2pAddress ?? original.I2pAddress;
|
|
original.I2pEnabled = changed.I2pEnabled ?? original.I2pEnabled;
|
|
original.I2pInboundLength = changed.I2pInboundLength ?? original.I2pInboundLength;
|
|
original.I2pInboundQuantity = changed.I2pInboundQuantity ?? original.I2pInboundQuantity;
|
|
original.I2pMixedMode = changed.I2pMixedMode ?? original.I2pMixedMode;
|
|
original.I2pOutboundLength = changed.I2pOutboundLength ?? original.I2pOutboundLength;
|
|
original.I2pOutboundQuantity = changed.I2pOutboundQuantity ?? original.I2pOutboundQuantity;
|
|
original.I2pPort = changed.I2pPort ?? original.I2pPort;
|
|
original.IdnSupportEnabled = changed.IdnSupportEnabled ?? original.IdnSupportEnabled;
|
|
original.IncompleteFilesExt = changed.IncompleteFilesExt ?? original.IncompleteFilesExt;
|
|
original.IpFilterEnabled = changed.IpFilterEnabled ?? original.IpFilterEnabled;
|
|
original.IpFilterPath = changed.IpFilterPath ?? original.IpFilterPath;
|
|
original.IpFilterTrackers = changed.IpFilterTrackers ?? original.IpFilterTrackers;
|
|
original.LimitLanPeers = changed.LimitLanPeers ?? original.LimitLanPeers;
|
|
original.LimitTcpOverhead = changed.LimitTcpOverhead ?? original.LimitTcpOverhead;
|
|
original.LimitUtpRate = changed.LimitUtpRate ?? original.LimitUtpRate;
|
|
original.ListenPort = changed.ListenPort ?? original.ListenPort;
|
|
original.Locale = changed.Locale ?? original.Locale;
|
|
original.Lsd = changed.Lsd ?? original.Lsd;
|
|
original.MailNotificationAuthEnabled = changed.MailNotificationAuthEnabled ?? original.MailNotificationAuthEnabled;
|
|
original.MailNotificationEmail = changed.MailNotificationEmail ?? original.MailNotificationEmail;
|
|
original.MailNotificationEnabled = changed.MailNotificationEnabled ?? original.MailNotificationEnabled;
|
|
original.MailNotificationPassword = changed.MailNotificationPassword ?? original.MailNotificationPassword;
|
|
original.MailNotificationSender = changed.MailNotificationSender ?? original.MailNotificationSender;
|
|
original.MailNotificationSmtp = changed.MailNotificationSmtp ?? original.MailNotificationSmtp;
|
|
original.MailNotificationSslEnabled = changed.MailNotificationSslEnabled ?? original.MailNotificationSslEnabled;
|
|
original.MailNotificationUsername = changed.MailNotificationUsername ?? original.MailNotificationUsername;
|
|
original.MaxActiveCheckingTorrents = changed.MaxActiveCheckingTorrents ?? original.MaxActiveCheckingTorrents;
|
|
original.MaxActiveDownloads = changed.MaxActiveDownloads ?? original.MaxActiveDownloads;
|
|
original.MaxActiveTorrents = changed.MaxActiveTorrents ?? original.MaxActiveTorrents;
|
|
original.MaxActiveUploads = changed.MaxActiveUploads ?? original.MaxActiveUploads;
|
|
original.MaxConcurrentHttpAnnounces = changed.MaxConcurrentHttpAnnounces ?? original.MaxConcurrentHttpAnnounces;
|
|
original.MaxConnec = changed.MaxConnec ?? original.MaxConnec;
|
|
original.MaxConnecPerTorrent = changed.MaxConnecPerTorrent ?? original.MaxConnecPerTorrent;
|
|
original.MaxInactiveSeedingTime = changed.MaxInactiveSeedingTime ?? original.MaxInactiveSeedingTime;
|
|
original.MaxInactiveSeedingTimeEnabled = changed.MaxInactiveSeedingTimeEnabled ?? original.MaxInactiveSeedingTimeEnabled;
|
|
original.MaxRatio = changed.MaxRatio ?? original.MaxRatio;
|
|
original.MaxRatioAct = changed.MaxRatioAct ?? original.MaxRatioAct;
|
|
original.MaxRatioEnabled = changed.MaxRatioEnabled ?? original.MaxRatioEnabled;
|
|
original.MaxSeedingTime = changed.MaxSeedingTime ?? original.MaxSeedingTime;
|
|
original.MaxSeedingTimeEnabled = changed.MaxSeedingTimeEnabled ?? original.MaxSeedingTimeEnabled;
|
|
original.MaxUploads = changed.MaxUploads ?? original.MaxUploads;
|
|
original.MaxUploadsPerTorrent = changed.MaxUploadsPerTorrent ?? original.MaxUploadsPerTorrent;
|
|
original.MemoryWorkingSetLimit = changed.MemoryWorkingSetLimit ?? original.MemoryWorkingSetLimit;
|
|
original.MergeTrackers = changed.MergeTrackers ?? original.MergeTrackers;
|
|
original.OutgoingPortsMax = changed.OutgoingPortsMax ?? original.OutgoingPortsMax;
|
|
original.OutgoingPortsMin = changed.OutgoingPortsMin ?? original.OutgoingPortsMin;
|
|
original.PeerTos = changed.PeerTos ?? original.PeerTos;
|
|
original.PeerTurnover = changed.PeerTurnover ?? original.PeerTurnover;
|
|
original.PeerTurnoverCutoff = changed.PeerTurnoverCutoff ?? original.PeerTurnoverCutoff;
|
|
original.PeerTurnoverInterval = changed.PeerTurnoverInterval ?? original.PeerTurnoverInterval;
|
|
original.PerformanceWarning = changed.PerformanceWarning ?? original.PerformanceWarning;
|
|
original.Pex = changed.Pex ?? original.Pex;
|
|
original.PreallocateAll = changed.PreallocateAll ?? original.PreallocateAll;
|
|
original.ProxyAuthEnabled = changed.ProxyAuthEnabled ?? original.ProxyAuthEnabled;
|
|
original.ProxyBittorrent = changed.ProxyBittorrent ?? original.ProxyBittorrent;
|
|
original.ProxyHostnameLookup = changed.ProxyHostnameLookup ?? original.ProxyHostnameLookup;
|
|
original.ProxyIp = changed.ProxyIp ?? original.ProxyIp;
|
|
original.ProxyMisc = changed.ProxyMisc ?? original.ProxyMisc;
|
|
original.ProxyPassword = changed.ProxyPassword ?? original.ProxyPassword;
|
|
original.ProxyPeerConnections = changed.ProxyPeerConnections ?? original.ProxyPeerConnections;
|
|
original.ProxyPort = changed.ProxyPort ?? original.ProxyPort;
|
|
original.ProxyRss = changed.ProxyRss ?? original.ProxyRss;
|
|
original.ProxyType = changed.ProxyType ?? original.ProxyType;
|
|
original.ProxyUsername = changed.ProxyUsername ?? original.ProxyUsername;
|
|
original.QueueingEnabled = changed.QueueingEnabled ?? original.QueueingEnabled;
|
|
original.RandomPort = changed.RandomPort ?? original.RandomPort;
|
|
original.ReannounceWhenAddressChanged = changed.ReannounceWhenAddressChanged ?? original.ReannounceWhenAddressChanged;
|
|
original.RecheckCompletedTorrents = changed.RecheckCompletedTorrents ?? original.RecheckCompletedTorrents;
|
|
original.RefreshInterval = changed.RefreshInterval ?? original.RefreshInterval;
|
|
original.RequestQueueSize = changed.RequestQueueSize ?? original.RequestQueueSize;
|
|
original.ResolvePeerCountries = changed.ResolvePeerCountries ?? original.ResolvePeerCountries;
|
|
original.ResumeDataStorageType = changed.ResumeDataStorageType ?? original.ResumeDataStorageType;
|
|
original.RssAutoDownloadingEnabled = changed.RssAutoDownloadingEnabled ?? original.RssAutoDownloadingEnabled;
|
|
original.RssDownloadRepackProperEpisodes = changed.RssDownloadRepackProperEpisodes ?? original.RssDownloadRepackProperEpisodes;
|
|
original.RssMaxArticlesPerFeed = changed.RssMaxArticlesPerFeed ?? original.RssMaxArticlesPerFeed;
|
|
original.RssProcessingEnabled = changed.RssProcessingEnabled ?? original.RssProcessingEnabled;
|
|
original.RssRefreshInterval = changed.RssRefreshInterval ?? original.RssRefreshInterval;
|
|
original.RssSmartEpisodeFilters = changed.RssSmartEpisodeFilters ?? original.RssSmartEpisodeFilters;
|
|
original.SavePath = changed.SavePath ?? original.SavePath;
|
|
original.SavePathChangedTmmEnabled = changed.SavePathChangedTmmEnabled ?? original.SavePathChangedTmmEnabled;
|
|
original.SaveResumeDataInterval = changed.SaveResumeDataInterval ?? original.SaveResumeDataInterval;
|
|
original.ScanDirs = changed.ScanDirs ?? original.ScanDirs;
|
|
original.ScheduleFromHour = changed.ScheduleFromHour ?? original.ScheduleFromHour;
|
|
original.ScheduleFromMin = changed.ScheduleFromMin ?? original.ScheduleFromMin;
|
|
original.ScheduleToHour = changed.ScheduleToHour ?? original.ScheduleToHour;
|
|
original.ScheduleToMin = changed.ScheduleToMin ?? original.ScheduleToMin;
|
|
original.SchedulerDays = changed.SchedulerDays ?? original.SchedulerDays;
|
|
original.SchedulerEnabled = changed.SchedulerEnabled ?? original.SchedulerEnabled;
|
|
original.SendBufferLowWatermark = changed.SendBufferLowWatermark ?? original.SendBufferLowWatermark;
|
|
original.SendBufferWatermark = changed.SendBufferWatermark ?? original.SendBufferWatermark;
|
|
original.SendBufferWatermarkFactor = changed.SendBufferWatermarkFactor ?? original.SendBufferWatermarkFactor;
|
|
original.SlowTorrentDlRateThreshold = changed.SlowTorrentDlRateThreshold ?? original.SlowTorrentDlRateThreshold;
|
|
original.SlowTorrentInactiveTimer = changed.SlowTorrentInactiveTimer ?? original.SlowTorrentInactiveTimer;
|
|
original.SlowTorrentUlRateThreshold = changed.SlowTorrentUlRateThreshold ?? original.SlowTorrentUlRateThreshold;
|
|
original.SocketBacklogSize = changed.SocketBacklogSize ?? original.SocketBacklogSize;
|
|
original.SocketReceiveBufferSize = changed.SocketReceiveBufferSize ?? original.SocketReceiveBufferSize;
|
|
original.SocketSendBufferSize = changed.SocketSendBufferSize ?? original.SocketSendBufferSize;
|
|
original.SsrfMitigation = changed.SsrfMitigation ?? original.SsrfMitigation;
|
|
original.StartPausedEnabled = changed.StartPausedEnabled ?? original.StartPausedEnabled;
|
|
original.StopTrackerTimeout = changed.StopTrackerTimeout ?? original.StopTrackerTimeout;
|
|
original.TempPath = changed.TempPath ?? original.TempPath;
|
|
original.TempPathEnabled = changed.TempPathEnabled ?? original.TempPathEnabled;
|
|
original.TorrentChangedTmmEnabled = changed.TorrentChangedTmmEnabled ?? original.TorrentChangedTmmEnabled;
|
|
original.TorrentContentLayout = changed.TorrentContentLayout ?? original.TorrentContentLayout;
|
|
original.TorrentFileSizeLimit = changed.TorrentFileSizeLimit ?? original.TorrentFileSizeLimit;
|
|
original.TorrentStopCondition = changed.TorrentStopCondition ?? original.TorrentStopCondition;
|
|
original.UpLimit = changed.UpLimit ?? original.UpLimit;
|
|
original.UploadChokingAlgorithm = changed.UploadChokingAlgorithm ?? original.UploadChokingAlgorithm;
|
|
original.UploadSlotsBehavior = changed.UploadSlotsBehavior ?? original.UploadSlotsBehavior;
|
|
original.Upnp = changed.Upnp ?? original.Upnp;
|
|
original.UpnpLeaseDuration = changed.UpnpLeaseDuration ?? original.UpnpLeaseDuration;
|
|
original.UseCategoryPathsInManualMode = changed.UseCategoryPathsInManualMode ?? original.UseCategoryPathsInManualMode;
|
|
original.UseHttps = changed.UseHttps ?? original.UseHttps;
|
|
original.UseSubcategories = changed.UseSubcategories ?? original.UseSubcategories;
|
|
original.UtpTcpMixedMode = changed.UtpTcpMixedMode ?? original.UtpTcpMixedMode;
|
|
original.ValidateHttpsTrackerCertificate = changed.ValidateHttpsTrackerCertificate ?? original.ValidateHttpsTrackerCertificate;
|
|
original.WebUiAddress = changed.WebUiAddress ?? original.WebUiAddress;
|
|
original.WebUiBanDuration = changed.WebUiBanDuration ?? original.WebUiBanDuration;
|
|
original.WebUiClickjackingProtectionEnabled = changed.WebUiClickjackingProtectionEnabled ?? original.WebUiClickjackingProtectionEnabled;
|
|
original.WebUiCsrfProtectionEnabled = changed.WebUiCsrfProtectionEnabled ?? original.WebUiCsrfProtectionEnabled;
|
|
original.WebUiCustomHttpHeaders = changed.WebUiCustomHttpHeaders ?? original.WebUiCustomHttpHeaders;
|
|
original.WebUiDomainList = changed.WebUiDomainList ?? original.WebUiDomainList;
|
|
original.WebUiHostHeaderValidationEnabled = changed.WebUiHostHeaderValidationEnabled ?? original.WebUiHostHeaderValidationEnabled;
|
|
original.WebUiHttpsCertPath = changed.WebUiHttpsCertPath ?? original.WebUiHttpsCertPath;
|
|
original.WebUiHttpsKeyPath = changed.WebUiHttpsKeyPath ?? original.WebUiHttpsKeyPath;
|
|
original.WebUiMaxAuthFailCount = changed.WebUiMaxAuthFailCount ?? original.WebUiMaxAuthFailCount;
|
|
original.WebUiPort = changed.WebUiPort ?? original.WebUiPort;
|
|
original.WebUiReverseProxiesList = changed.WebUiReverseProxiesList ?? original.WebUiReverseProxiesList;
|
|
original.WebUiReverseProxyEnabled = changed.WebUiReverseProxyEnabled ?? original.WebUiReverseProxyEnabled;
|
|
original.WebUiSecureCookieEnabled = changed.WebUiSecureCookieEnabled ?? original.WebUiSecureCookieEnabled;
|
|
original.WebUiSessionTimeout = changed.WebUiSessionTimeout ?? original.WebUiSessionTimeout;
|
|
original.WebUiUpnp = changed.WebUiUpnp ?? original.WebUiUpnp;
|
|
original.WebUiUseCustomHttpHeadersEnabled = changed.WebUiUseCustomHttpHeadersEnabled ?? original.WebUiUseCustomHttpHeadersEnabled;
|
|
original.WebUiUsername = changed.WebUiUsername ?? original.WebUiUsername;
|
|
}
|
|
|
|
return original;
|
|
}
|
|
|
|
public bool MergeContentsList(IReadOnlyList<QBitTorrentClient.Models.FileData> files, Dictionary<string, ContentItem> contents)
|
|
{
|
|
if (files.Count == 0)
|
|
{
|
|
if (contents.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
contents.Clear();
|
|
return true;
|
|
}
|
|
|
|
var hasChanges = false;
|
|
var seenPaths = new HashSet<string>(files.Count * 2);
|
|
var directoryAccumulators = new Dictionary<string, DirectoryAccumulator>();
|
|
|
|
var minExistingIndex = contents.Count == 0
|
|
? int.MaxValue
|
|
: contents.Values.Min(c => c.Index);
|
|
var minFileIndex = files.Min(f => f.Index);
|
|
var nextFolderIndex = System.Math.Min(minExistingIndex, minFileIndex) - 1;
|
|
|
|
foreach (var file in files)
|
|
{
|
|
var priority = (Priority)(int)file.Priority;
|
|
var pathSegments = file.Name.Split(Extensions.DirectorySeparator);
|
|
var level = pathSegments.Length - 1;
|
|
var displayName = pathSegments[^1];
|
|
var filePath = file.Name;
|
|
seenPaths.Add(filePath);
|
|
|
|
if (contents.TryGetValue(filePath, out var existingFile))
|
|
{
|
|
var updatedFile = new ContentItem(filePath, displayName, file.Index, priority, file.Progress, file.Size, file.Availability, false, level);
|
|
if (UpdateContentItem(existingFile, updatedFile))
|
|
{
|
|
hasChanges = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var newFile = new ContentItem(filePath, displayName, file.Index, priority, file.Progress, file.Size, file.Availability, false, level);
|
|
contents[filePath] = newFile;
|
|
hasChanges = true;
|
|
}
|
|
|
|
string directoryPath = string.Empty;
|
|
for (var i = 0; i < level; i++)
|
|
{
|
|
var segment = pathSegments[i];
|
|
if (segment == ".unwanted")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
directoryPath = string.IsNullOrEmpty(directoryPath)
|
|
? segment
|
|
: string.Concat(directoryPath, Extensions.DirectorySeparator, segment);
|
|
|
|
seenPaths.Add(directoryPath);
|
|
|
|
if (!contents.TryGetValue(directoryPath, out var directoryItem))
|
|
{
|
|
var newDirectory = new ContentItem(directoryPath, segment, nextFolderIndex--, Priority.Normal, 0, 0, 0, true, i);
|
|
contents[directoryPath] = newDirectory;
|
|
hasChanges = true;
|
|
}
|
|
|
|
if (!directoryAccumulators.TryGetValue(directoryPath, out var accumulator))
|
|
{
|
|
accumulator = new DirectoryAccumulator();
|
|
}
|
|
|
|
accumulator.Add(priority, file.Progress, file.Size, file.Availability);
|
|
directoryAccumulators[directoryPath] = accumulator;
|
|
}
|
|
}
|
|
|
|
var keysToRemove = contents.Keys.Where(key => !seenPaths.Contains(key)).ToList();
|
|
if (keysToRemove.Count != 0)
|
|
{
|
|
hasChanges = true;
|
|
foreach (var key in keysToRemove)
|
|
{
|
|
contents.Remove(key);
|
|
}
|
|
}
|
|
|
|
foreach (var (directoryPath, accumulator) in directoryAccumulators)
|
|
{
|
|
if (!contents.TryGetValue(directoryPath, out var directoryItem))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var updatedDirectory = new ContentItem(
|
|
directoryPath,
|
|
directoryItem.DisplayName,
|
|
directoryItem.Index,
|
|
accumulator.ResolvePriority(),
|
|
accumulator.ResolveProgress(),
|
|
accumulator.TotalSize,
|
|
accumulator.ResolveAvailability(),
|
|
true,
|
|
directoryItem.Level);
|
|
|
|
if (UpdateContentItem(directoryItem, updatedDirectory))
|
|
{
|
|
hasChanges = true;
|
|
}
|
|
}
|
|
|
|
return hasChanges;
|
|
}
|
|
|
|
public RssList CreateRssList(IReadOnlyDictionary<string, QBitTorrentClient.Models.RssItem> rssItems)
|
|
{
|
|
var articles = new List<RssArticle>();
|
|
var feeds = new Dictionary<string, RssFeed>();
|
|
foreach (var (key, rssItem) in rssItems)
|
|
{
|
|
feeds.Add(key, new RssFeed(rssItem.HasError, rssItem.IsLoading, rssItem.LastBuildDate, rssItem.Title, rssItem.Uid, rssItem.Url));
|
|
if (rssItem.Articles is null)
|
|
{
|
|
continue;
|
|
}
|
|
foreach (var rssArticle in rssItem.Articles)
|
|
{
|
|
var article = new RssArticle(
|
|
key,
|
|
rssArticle.Category,
|
|
rssArticle.Comments,
|
|
rssArticle.Date!,
|
|
rssArticle.Description,
|
|
rssArticle.Id!,
|
|
rssArticle.Link,
|
|
rssArticle.Thumbnail,
|
|
rssArticle.Title!,
|
|
rssArticle.TorrentURL!,
|
|
rssArticle.IsRead);
|
|
|
|
articles.Add(article);
|
|
}
|
|
}
|
|
|
|
return new RssList(feeds, articles);
|
|
}
|
|
}
|
|
} |