Refactor download progress calculation to use centralized stats and simplify logic across services.
- Fixes an issue where progress goes back to 50% as soon as downloads are done - Prevents copy and update of torrent/nzb list by not updating download stats on every use, most callers don't require this data, notable perf/memory improvement.
This commit is contained in:
parent
a5f9cd353b
commit
40be170677
5 changed files with 58 additions and 68 deletions
|
|
@ -40,6 +40,7 @@ public class SabnzbdTest
|
||||||
};
|
};
|
||||||
|
|
||||||
_torrentsMock.Setup(t => t.Get()).ReturnsAsync(torrentList);
|
_torrentsMock.Setup(t => t.Get()).ReturnsAsync(torrentList);
|
||||||
|
_torrentsMock.Setup(t => t.GetDownloadStats(It.IsAny<Guid>())).Returns((0, 1000, 500));
|
||||||
|
|
||||||
var sabnzbd = new Sabnzbd(_loggerMock.Object, _torrentsMock.Object, _appSettings);
|
var sabnzbd = new Sabnzbd(_loggerMock.Object, _torrentsMock.Object, _appSettings);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -223,29 +223,28 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
|
||||||
var speed = torrent.RdSpeed ?? 0;
|
var speed = torrent.RdSpeed ?? 0;
|
||||||
var bytesDone = (Int64)(bytesTotal * rdProgress);
|
var bytesDone = (Int64)(bytesTotal * rdProgress);
|
||||||
|
|
||||||
Double downloadProgress = 0;
|
Double progress;
|
||||||
|
|
||||||
if (torrent.Downloads is { Count: > 0 })
|
if (torrent.Downloads is { Count: > 0 })
|
||||||
{
|
{
|
||||||
var dlBytesDone = torrent.Downloads.Sum(m => m.BytesDone);
|
var dlStats = torrent.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
|
||||||
var dlBytesTotal = torrent.Downloads.Sum(m => m.BytesTotal);
|
var dlBytesDone = dlStats.Sum(m => m.BytesDone);
|
||||||
speed = (Int32)torrent.Downloads.Average(m => m.Speed);
|
var dlBytesTotal = dlStats.Sum(m => m.BytesTotal);
|
||||||
downloadProgress = bytesTotal > 0 ? Math.Clamp((Double)dlBytesDone / dlBytesTotal, 0.0, 1.0) : 0;
|
speed = (Int32)(dlStats.Any() ? dlStats.Average(m => m.Speed) : 0);
|
||||||
|
var downloadProgress = dlBytesTotal > 0 ? Math.Clamp((Double)dlBytesDone / dlBytesTotal, 0.0, 1.0) : 0;
|
||||||
|
progress = (rdProgress + downloadProgress) / 2.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progress = rdProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
var progress = (rdProgress + downloadProgress) / 2.0;
|
|
||||||
var remaining = TimeSpan.Zero;
|
var remaining = TimeSpan.Zero;
|
||||||
var bytesRemaining = bytesTotal - bytesDone;
|
if (progress > 0 && progress < 1.0)
|
||||||
|
|
||||||
if (speed > 0 && bytesRemaining > 0)
|
|
||||||
{
|
{
|
||||||
remaining = TimeSpan.FromSeconds(bytesRemaining / (Double)speed);
|
var startTime = torrent.Retry > torrent.Added ? torrent.Retry.Value : torrent.Added;
|
||||||
|
var elapsed = DateTimeOffset.UtcNow - startTime;
|
||||||
// In case there is clock skew
|
var totalEstimatedTime = TimeSpan.FromTicks((Int64)(elapsed.Ticks / progress));
|
||||||
if (remaining < TimeSpan.Zero)
|
remaining = totalEstimatedTime - elapsed;
|
||||||
{
|
|
||||||
remaining = TimeSpan.Zero;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new TorrentInfo
|
var result = new TorrentInfo
|
||||||
|
|
@ -361,9 +360,10 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
|
||||||
|
|
||||||
if (torrent.Downloads.Count > 0)
|
if (torrent.Downloads.Count > 0)
|
||||||
{
|
{
|
||||||
bytesDone = torrent.Downloads.Sum(m => m.BytesDone);
|
var dlStats = torrent.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
|
||||||
bytesTotal = torrent.Downloads.Sum(m => m.BytesTotal);
|
bytesDone = dlStats.Sum(m => m.BytesDone);
|
||||||
speed = (Int32)torrent.Downloads.Average(m => m.Speed);
|
bytesTotal = dlStats.Sum(m => m.BytesTotal);
|
||||||
|
speed = (Int32)(dlStats.Any() ? dlStats.Average(m => m.Speed) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new TorrentProperties
|
var result = new TorrentProperties
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,21 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
||||||
NoOfSlots = activeTorrents.Count,
|
NoOfSlots = activeTorrents.Count,
|
||||||
Slots = activeTorrents.Select((t, index) =>
|
Slots = activeTorrents.Select((t, index) =>
|
||||||
{
|
{
|
||||||
Double downloadProgress = 0;
|
|
||||||
var rdProgress = Math.Clamp(t.RdProgress ?? 0.0, 0.0, 100.0) / 100.0;
|
var rdProgress = Math.Clamp(t.RdProgress ?? 0.0, 0.0, 100.0) / 100.0;
|
||||||
|
Double progress;
|
||||||
|
|
||||||
if (t.Downloads is { Count: > 0 })
|
var dlStats = t.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
|
||||||
|
if (dlStats.Count > 0)
|
||||||
{
|
{
|
||||||
var bytesDone = t.Downloads.Sum(m => m.BytesDone);
|
var bytesDone = dlStats.Sum(m => m.BytesDone);
|
||||||
var bytesTotal = t.Downloads.Sum(m => m.BytesTotal);
|
var bytesTotal = dlStats.Sum(m => m.BytesTotal);
|
||||||
downloadProgress = bytesTotal > 0 ? Math.Clamp((Double)bytesDone / bytesTotal, 0.0, 1.0) : 0;
|
var downloadProgress = bytesTotal > 0 ? Math.Clamp((Double)bytesDone / bytesTotal, 0.0, 1.0) : 0;
|
||||||
|
progress = (rdProgress + downloadProgress) / 2.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progress = rdProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
var progress = (rdProgress + downloadProgress) / 2.0;
|
|
||||||
|
|
||||||
var timeLeft = "0:00:00";
|
var timeLeft = "0:00:00";
|
||||||
var startTime = t.Retry > t.Added ? t.Retry.Value : t.Added;
|
var startTime = t.Retry > t.Added ? t.Retry.Value : t.Added;
|
||||||
|
|
@ -50,8 +54,8 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
||||||
Index = index,
|
Index = index,
|
||||||
NzoId = t.Hash,
|
NzoId = t.Hash,
|
||||||
Filename = t.RdName ?? t.Hash,
|
Filename = t.RdName ?? t.Hash,
|
||||||
Size = FileSizeHelper.FormatSize(t.Downloads.Sum(d => d.BytesTotal)),
|
Size = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal)),
|
||||||
SizeLeft = FileSizeHelper.FormatSize(t.Downloads.Sum(d => d.BytesTotal - d.BytesDone)),
|
SizeLeft = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal - d.BytesDone)),
|
||||||
Percentage = (progress * 100.0).ToString("0"),
|
Percentage = (progress * 100.0).ToString("0"),
|
||||||
|
|
||||||
Status = t.RdStatus switch
|
Status = t.RdStatus switch
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,21 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
||||||
|
|
||||||
public static Boolean IsPausedForLowDiskSpace { get; set; }
|
public static Boolean IsPausedForLowDiskSpace { get; set; }
|
||||||
|
|
||||||
|
public static (Int64 Speed, Int64 BytesTotal, Int64 BytesDone) GetStats(Guid downloadId)
|
||||||
|
{
|
||||||
|
if (ActiveDownloadClients.TryGetValue(downloadId, out var downloadClient))
|
||||||
|
{
|
||||||
|
return (downloadClient.Speed, downloadClient.BytesTotal, downloadClient.BytesDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ActiveUnpackClients.TryGetValue(downloadId, out var unpackClient))
|
||||||
|
{
|
||||||
|
return (0, 100, unpackClient.Progess);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Initialize()
|
public async Task Initialize()
|
||||||
{
|
{
|
||||||
Log("Initializing TorrentRunner");
|
Log("Initializing TorrentRunner");
|
||||||
|
|
@ -665,8 +680,8 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
||||||
|
|
||||||
var completePerc = 0;
|
var completePerc = 0;
|
||||||
|
|
||||||
var totalDownloadBytes = torrent.Downloads.Sum(m => m.BytesTotal);
|
var totalDownloadBytes = torrent.Downloads.Sum(m => GetStats(m.DownloadId).BytesTotal);
|
||||||
var totalDoneBytes = torrent.Downloads.Sum(m => m.BytesDone);
|
var totalDoneBytes = torrent.Downloads.Sum(m => GetStats(m.DownloadId).BytesDone);
|
||||||
|
|
||||||
if (totalDownloadBytes > 0)
|
if (totalDownloadBytes > 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -58,29 +58,15 @@ public class Torrents(
|
||||||
|
|
||||||
private static readonly SemaphoreSlim TorrentResetLock = new(1, 1);
|
private static readonly SemaphoreSlim TorrentResetLock = new(1, 1);
|
||||||
|
|
||||||
|
public virtual (Int64 Speed, Int64 BytesTotal, Int64 BytesDone) GetDownloadStats(Guid downloadId)
|
||||||
|
{
|
||||||
|
return TorrentRunner.GetStats(downloadId);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task<IList<Torrent>> Get()
|
public virtual async Task<IList<Torrent>> Get()
|
||||||
{
|
{
|
||||||
var torrents = await torrentData.Get();
|
var torrents = await torrentData.Get();
|
||||||
|
|
||||||
foreach (var torrent in torrents)
|
|
||||||
{
|
|
||||||
foreach (var download in torrent.Downloads)
|
|
||||||
{
|
|
||||||
if (TorrentRunner.ActiveDownloadClients.TryGetValue(download.DownloadId, out var downloadClient))
|
|
||||||
{
|
|
||||||
download.Speed = downloadClient.Speed;
|
|
||||||
download.BytesTotal = downloadClient.BytesTotal;
|
|
||||||
download.BytesDone = downloadClient.BytesDone;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TorrentRunner.ActiveUnpackClients.TryGetValue(download.DownloadId, out var unpackClient))
|
|
||||||
{
|
|
||||||
download.BytesTotal = 100;
|
|
||||||
download.BytesDone = unpackClient.Progess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return torrents;
|
return torrents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -663,8 +649,7 @@ public class Torrents(
|
||||||
{
|
{
|
||||||
Category = Settings.Get.Provider.Default.Category,
|
Category = Settings.Get.Provider.Default.Category,
|
||||||
DownloadClient = Settings.Get.DownloadClient.Client,
|
DownloadClient = Settings.Get.DownloadClient.Client,
|
||||||
DownloadAction =
|
DownloadAction = Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||||
Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
|
||||||
HostDownloadAction = Settings.Get.Provider.Default.HostDownloadAction,
|
HostDownloadAction = Settings.Get.Provider.Default.HostDownloadAction,
|
||||||
FinishedActionDelay = Settings.Get.Provider.Default.FinishedActionDelay,
|
FinishedActionDelay = Settings.Get.Provider.Default.FinishedActionDelay,
|
||||||
FinishedAction = Settings.Get.Provider.Default.FinishedAction,
|
FinishedAction = Settings.Get.Provider.Default.FinishedAction,
|
||||||
|
|
@ -881,22 +866,6 @@ public class Torrents(
|
||||||
|
|
||||||
await UpdateTorrentClientData(torrent);
|
await UpdateTorrentClientData(torrent);
|
||||||
|
|
||||||
foreach (var download in torrent.Downloads)
|
|
||||||
{
|
|
||||||
if (TorrentRunner.ActiveDownloadClients.TryGetValue(download.DownloadId, out var downloadClient))
|
|
||||||
{
|
|
||||||
download.Speed = downloadClient.Speed;
|
|
||||||
download.BytesTotal = downloadClient.BytesTotal;
|
|
||||||
download.BytesDone = downloadClient.BytesDone;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TorrentRunner.ActiveUnpackClients.TryGetValue(download.DownloadId, out var unpackClient))
|
|
||||||
{
|
|
||||||
download.BytesTotal = 100;
|
|
||||||
download.BytesDone = unpackClient.Progess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return torrent;
|
return torrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1084,6 +1053,7 @@ public class Torrents(
|
||||||
|
|
||||||
logger.LogDebug(message);
|
logger.LogDebug(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String ComputeSha1Hash(String input)
|
private static String ComputeSha1Hash(String input)
|
||||||
{
|
{
|
||||||
using var sha1 = System.Security.Cryptography.SHA1.Create();
|
using var sha1 = System.Security.Cryptography.SHA1.Create();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue