From 0a8793e4f905f879d195eea4ec13c289be374791 Mon Sep 17 00:00:00 2001 From: Roger Far Date: Wed, 13 Jan 2021 11:04:42 -0700 Subject: [PATCH] Add simple retry mechanism for network connection drops. --- .../app/torrent-row/torrent-row.component.ts | 5 +- client/src/app/torrent-status.pipe.ts | 20 ++--- server/RdtClient.Data/Data/DownloadData.cs | 5 ++ .../Services/DownloadClient.cs | 74 +++++++++++++------ .../Services/TorrentRunner.cs | 10 ++- server/RdtClient.Service/Services/Torrents.cs | 9 +++ 6 files changed, 87 insertions(+), 36 deletions(-) diff --git a/client/src/app/torrent-row/torrent-row.component.ts b/client/src/app/torrent-row/torrent-row.component.ts index ed15e59..17e5088 100644 --- a/client/src/app/torrent-row/torrent-row.component.ts +++ b/client/src/app/torrent-row/torrent-row.component.ts @@ -54,7 +54,10 @@ export class TorrentRowComponent implements OnInit { } public canDownload(): boolean { - return this.torrent.rdStatus === RealDebridStatus.Finished && this.torrent.downloads.length === 0; + return ( + (this.torrent.rdStatus === RealDebridStatus.Finished && this.torrent.downloads.length === 0) || + (this.torrent.downloads.length > 0 && this.torrent.downloads.any((m) => m.error != null)) + ); } public canUnpack(): boolean { diff --git a/client/src/app/torrent-status.pipe.ts b/client/src/app/torrent-status.pipe.ts index 7a3f9da..5d81a0a 100644 --- a/client/src/app/torrent-status.pipe.ts +++ b/client/src/app/torrent-status.pipe.ts @@ -9,23 +9,19 @@ export class TorrentStatusPipe implements PipeTransform { constructor(private pipe: FileSizePipe) {} transform(torrent: Torrent): string { - if (torrent.completed) { - return 'Finished'; - } - if (torrent.downloads.length > 0) { - const allFinished = torrent.downloads.all((m) => m.completed != null); - - if (allFinished) { - return 'Finished'; - } - const errors = torrent.downloads.where((m) => m.error != null); if (errors.length > 0) { return 'Error'; } + const allFinished = torrent.downloads.all((m) => m.completed != null); + + if (allFinished) { + return 'Finished'; + } + const downloading = torrent.downloads.where((m) => m.downloadStarted && !m.downloadFinished); if (downloading.length > 0) { @@ -80,6 +76,10 @@ export class TorrentStatusPipe implements PipeTransform { } } + if (torrent.completed) { + return 'Finished'; + } + switch (torrent.rdStatus) { case RealDebridStatus.Downloading: const speed = this.pipe.transform(torrent.rdSpeed, 'filesize'); diff --git a/server/RdtClient.Data/Data/DownloadData.cs b/server/RdtClient.Data/Data/DownloadData.cs index 2e18692..9465785 100644 --- a/server/RdtClient.Data/Data/DownloadData.cs +++ b/server/RdtClient.Data/Data/DownloadData.cs @@ -89,6 +89,11 @@ namespace RdtClient.Data.Data var dbDownload = await _dataContext.Downloads .FirstOrDefaultAsync(m => m.DownloadId == downloadId); + if (dbDownload == null) + { + return; + } + dbDownload.DownloadFinished = dateTime; await _dataContext.SaveChangesAsync(); diff --git a/server/RdtClient.Service/Services/DownloadClient.cs b/server/RdtClient.Service/Services/DownloadClient.cs index e73596f..de7e336 100644 --- a/server/RdtClient.Service/Services/DownloadClient.cs +++ b/server/RdtClient.Service/Services/DownloadClient.cs @@ -84,6 +84,7 @@ namespace RdtClient.Service.Services // Determine the file size var webRequest = WebRequest.Create(uri); webRequest.Method = "HEAD"; + webRequest.Timeout = 5000; Int64 responseLength; using (var webResponse = await webRequest.GetResponseAsync()) @@ -91,36 +92,67 @@ namespace RdtClient.Service.Services responseLength = Int64.Parse(webResponse.Headers.Get("Content-Length")); } - var request = WebRequest.Create(uri); - using var response = await request.GetResponseAsync(); + var timeout = DateTimeOffset.UtcNow.AddHours(1); - await using var stream = response.GetResponseStream(); - await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write); - var buffer = new Byte[4096]; - - while (fileStream.Length < response.ContentLength) + while (timeout > DateTimeOffset.UtcNow) { - var read = await stream.ReadAsync(buffer, 0, buffer.Length); - - if (read > 0) + try { - fileStream.Write(buffer, 0, read); + var request = WebRequest.Create(uri); + using var response = await request.GetResponseAsync(); - BytesDone = fileStream.Length; - BytesTotal = responseLength; + await using var stream = response.GetResponseStream(); - if (DateTime.UtcNow > _nextUpdate) + if (stream == null) { - Speed = fileStream.Length - _bytesLastUpdate; - - _nextUpdate = DateTime.UtcNow.AddSeconds(1); - _bytesLastUpdate = fileStream.Length; + throw new IOException("No stream"); } - } - else - { + + await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write); + var buffer = new Byte[4096]; + + while (fileStream.Length < response.ContentLength) + { + var read = await stream.ReadAsync(buffer, 0, buffer.Length); + + if (read > 0) + { + fileStream.Write(buffer, 0, read); + + BytesDone = fileStream.Length; + BytesTotal = responseLength; + + if (DateTime.UtcNow > _nextUpdate) + { + Speed = fileStream.Length - _bytesLastUpdate; + + _nextUpdate = DateTime.UtcNow.AddSeconds(1); + _bytesLastUpdate = fileStream.Length; + + timeout = DateTimeOffset.UtcNow.AddHours(1); + } + } + else + { + break; + } + } + break; } + catch (IOException) + { + await Task.Delay(1000); + } + catch (WebException) + { + await Task.Delay(1000); + } + } + + if (timeout <= DateTimeOffset.UtcNow) + { + throw new Exception($"Download timed out"); } Speed = 0; diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index d6ff1ed..07b901b 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -134,7 +134,7 @@ namespace RdtClient.Service.Services // Check if there are any downloads that are queued and can be started. var queuedDownloads = torrents.SelectMany(m => m.Downloads) - .Where(m => m.DownloadQueued != null && m.DownloadStarted == null) + .Where(m => m.Completed == null && m.DownloadQueued != null && m.DownloadStarted == null) .OrderBy(m => m.DownloadQueued); foreach (var download in queuedDownloads) @@ -144,7 +144,7 @@ namespace RdtClient.Service.Services // Check if there are any unpacks that are queued and can be started. var queuedUnpacks = torrents.SelectMany(m => m.Downloads) - .Where(m => m.UnpackingQueued != null && m.UnpackingStarted == null) + .Where(m => m.Completed == null && m.UnpackingQueued != null && m.UnpackingStarted == null) .OrderBy(m => m.DownloadQueued); foreach (var download in queuedUnpacks) @@ -197,7 +197,8 @@ namespace RdtClient.Service.Services // If the torrent has any files that need starting to be downloaded, download them. var downloadsPending = torrent.Downloads - .Where(m => m.DownloadStarted == null && + .Where(m => m.Completed == null && + m.DownloadStarted == null && m.DownloadFinished == null) .ToList(); @@ -216,7 +217,8 @@ namespace RdtClient.Service.Services { // If all files are finished downloading, move to the unpacking step. var unpackingPending = torrent.Downloads - .Where(m => m.DownloadFinished != null && + .Where(m => m.Completed == null && + m.DownloadFinished != null && m.UnpackingStarted == null && m.UnpackingFinished == null) .ToList(); diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index fb64178..0ff83c7 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -214,6 +214,15 @@ namespace RdtClient.Service.Services var downloadPath = await DownloadPath(download.Torrent); + download.DownloadFinished = null; + await _downloads.UpdateDownloadStarted(download.DownloadId, null); + + download.Completed = null; + await _downloads.UpdateCompleted(download.DownloadId, null); + + download.Error = null; + await _downloads.UpdateError(download.DownloadId, null); + // Check if we have reached the download limit, if so queue the download, but don't start it. if (TorrentRunner.ActiveDownloadClients.Count >= settingDownloadLimit) {