handle download cancellation better
This commit is contained in:
		| @@ -107,18 +107,20 @@ public class CachedPlayer | |||||||
|         _downloadCancellationTokenSource?.Cancel(); |         _downloadCancellationTokenSource?.Cancel(); | ||||||
|         _downloadCancellationTokenSource = new CancellationTokenSource(); |         _downloadCancellationTokenSource = new CancellationTokenSource(); | ||||||
|         var downloadToken = _downloadCancellationTokenSource.Token; |         var downloadToken = _downloadCancellationTokenSource.Token; | ||||||
|  |         var downloadId = _apiController.GetDownloadId(); | ||||||
|         Task.Run(async () => |         Task.Run(async () => | ||||||
|         { |         { | ||||||
|             List<FileReplacementDto> toDownloadReplacements; |             List<FileReplacementDto> toDownloadReplacements; | ||||||
|  |  | ||||||
|             Dictionary<string, string> moddedPaths; |             Dictionary<string, string> moddedPaths; | ||||||
|             while ((toDownloadReplacements = TryCalculateModdedDictionary(_cache[_lastAppliedEquipmentHash], out moddedPaths)).Count > 0) |             int attempts = 0; | ||||||
|  |             while ((toDownloadReplacements = TryCalculateModdedDictionary(_cache[_lastAppliedEquipmentHash], out moddedPaths)).Count > 0 && attempts++ <= 10) | ||||||
|             { |             { | ||||||
|                 Logger.Debug("Downloading missing files for player " + PlayerName); |                 Logger.Debug("Downloading missing files for player " + PlayerName); | ||||||
|                 await _apiController.DownloadFiles(toDownloadReplacements, downloadToken); |                 await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken); | ||||||
|                 if (downloadToken.IsCancellationRequested) |                 if (downloadToken.IsCancellationRequested) | ||||||
|                 { |                 { | ||||||
|  |                     Logger.Verbose("Detected cancellation"); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -129,7 +131,13 @@ public class CachedPlayer | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             ApplyCharacterData(_cache[_lastAppliedEquipmentHash], moddedPaths); |             ApplyCharacterData(_cache[_lastAppliedEquipmentHash], moddedPaths); | ||||||
|         }, downloadToken); |         }, downloadToken).ContinueWith(task => | ||||||
|  |         { | ||||||
|  |             if (!task.IsCanceled) return; | ||||||
|  |  | ||||||
|  |             Logger.Debug("Download Task was cancelled"); | ||||||
|  |             _apiController.CancelDownload(downloadId); | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private List<FileReplacementDto> TryCalculateModdedDictionary(CharacterCacheDto cache, |     private List<FileReplacementDto> TryCalculateModdedDictionary(CharacterCacheDto cache, | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|   <PropertyGroup> |   <PropertyGroup> | ||||||
|     <Authors></Authors> |     <Authors></Authors> | ||||||
|     <Company></Company> |     <Company></Company> | ||||||
|     <Version>0.1.8.0</Version> |     <Version>0.1.9.0</Version> | ||||||
|     <Description></Description> |     <Description></Description> | ||||||
|     <Copyright></Copyright> |     <Copyright></Copyright> | ||||||
|     <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> |     <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> | ||||||
|   | |||||||
| @@ -367,7 +367,7 @@ namespace MareSynchronos.UI | |||||||
|                 ImGui.Text("No uploads in progress"); |                 ImGui.Text("No uploads in progress"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             var currentDownloads = _apiController.CurrentDownloads.ToList(); |             var currentDownloads = _apiController.CurrentDownloads.SelectMany(k => k.Value).ToList(); | ||||||
|             ImGui.PushFont(UiBuilder.IconFont); |             ImGui.PushFont(UiBuilder.IconFont); | ||||||
|             ImGui.Text(FontAwesomeIcon.Download.ToIconString()); |             ImGui.Text(FontAwesomeIcon.Download.ToIconString()); | ||||||
|             ImGui.PopFont(); |             ImGui.PopFont(); | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ public class DownloadUi : Window, IDisposable | |||||||
|  |  | ||||||
|         if (_apiController.CurrentDownloads.Any()) |         if (_apiController.CurrentDownloads.Any()) | ||||||
|         { |         { | ||||||
|             var currentDownloads = _apiController.CurrentDownloads.ToList(); |             var currentDownloads = _apiController.CurrentDownloads.SelectMany(k => k.Value).ToList(); | ||||||
|             var multBase = currentDownloads.Any() ? 0 : 2; |             var multBase = currentDownloads.Any() ? 0 : 2; | ||||||
|             var doneDownloads = currentDownloads.Count(c => c.IsTransferred); |             var doneDownloads = currentDownloads.Count(c => c.IsTransferred); | ||||||
|             var totalDownloads = currentDownloads.Count; |             var totalDownloads = currentDownloads.Count; | ||||||
|   | |||||||
| @@ -518,7 +518,7 @@ namespace MareSynchronos.UI | |||||||
|             { |             { | ||||||
|                 ImGui.TableSetupColumn( |                 ImGui.TableSetupColumn( | ||||||
|                     $"Uploads ({UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Total))})"); |                     $"Uploads ({UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Total))})"); | ||||||
|                 ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Total))})"); |                 ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(_apiController.CurrentDownloads.SelectMany(k => k.Value).ToList().Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentDownloads.SelectMany(k => k.Value).ToList().Sum(a => a.Total))})"); | ||||||
|  |  | ||||||
|                 ImGui.TableHeadersRow(); |                 ImGui.TableHeadersRow(); | ||||||
|  |  | ||||||
| @@ -553,7 +553,7 @@ namespace MareSynchronos.UI | |||||||
|                     ImGui.TableSetupColumn("Downloaded"); |                     ImGui.TableSetupColumn("Downloaded"); | ||||||
|                     ImGui.TableSetupColumn("Size"); |                     ImGui.TableSetupColumn("Size"); | ||||||
|                     ImGui.TableHeadersRow(); |                     ImGui.TableHeadersRow(); | ||||||
|                     foreach (var transfer in _apiController.CurrentDownloads.ToArray()) |                     foreach (var transfer in _apiController.CurrentDownloads.SelectMany(k => k.Value).ToArray()) | ||||||
|                     { |                     { | ||||||
|                         var color = UiShared.UploadColor((transfer.Transferred, transfer.Total)); |                         var color = UiShared.UploadColor((transfer.Transferred, transfer.Total)); | ||||||
|                         ImGui.PushStyleColor(ImGuiCol.Text, color); |                         ImGui.PushStyleColor(ImGuiCol.Text, color); | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ namespace MareSynchronos.WebAPI | |||||||
| { | { | ||||||
|     public partial class ApiController |     public partial class ApiController | ||||||
|     { |     { | ||||||
|  |         private int _downloadId = 0; | ||||||
|         public void CancelUpload() |         public void CancelUpload() | ||||||
|         { |         { | ||||||
|             if (_uploadCancellationTokenSource != null) |             if (_uploadCancellationTokenSource != null) | ||||||
| @@ -32,7 +33,7 @@ namespace MareSynchronos.WebAPI | |||||||
|             await _fileHub!.SendAsync(FilesHubAPI.SendDeleteAllFiles); |             await _fileHub!.SendAsync(FilesHubAPI.SendDeleteAllFiles); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private async Task<string> DownloadFile(string hash, CancellationToken ct) |         private async Task<string> DownloadFile(int downloadId, string hash, CancellationToken ct) | ||||||
|         { |         { | ||||||
|             var reader = _fileHub!.StreamAsync<byte[]>(FilesHubAPI.StreamDownloadFileAsync, hash, ct); |             var reader = _fileHub!.StreamAsync<byte[]>(FilesHubAPI.StreamDownloadFileAsync, hash, ct); | ||||||
|             string fileName = Path.GetTempFileName(); |             string fileName = Path.GetTempFileName(); | ||||||
| @@ -40,51 +41,44 @@ namespace MareSynchronos.WebAPI | |||||||
|             await foreach (var data in reader.WithCancellation(ct)) |             await foreach (var data in reader.WithCancellation(ct)) | ||||||
|             { |             { | ||||||
|                 //Logger.Debug("Getting chunk of " + hash); |                 //Logger.Debug("Getting chunk of " + hash); | ||||||
|                 CurrentDownloads.Single(f => f.Hash == hash).Transferred += data.Length; |                 CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred += data.Length; | ||||||
|                 await fs.WriteAsync(data, ct); |                 await fs.WriteAsync(data, ct); | ||||||
|             } |             } | ||||||
|             return fileName; |             return fileName; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public async Task DownloadFiles(List<FileReplacementDto> fileReplacementDto, CancellationToken ct) |         public int GetDownloadId() => _downloadId++; | ||||||
|  |  | ||||||
|  |         public async Task DownloadFiles(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct) | ||||||
|         { |         { | ||||||
|             Logger.Debug("Downloading files"); |             Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")"); | ||||||
|             List<FileTransfer> fileTransferList = new List<FileTransfer>(); |  | ||||||
|             List<DownloadFileDto> downloadFiles = new List<DownloadFileDto>(); |             List<DownloadFileDto> downloadFileInfoFromService = new List<DownloadFileDto>(); | ||||||
|             foreach (var file in fileReplacementDto) |             foreach (var file in fileReplacementDto) | ||||||
|             { |             { | ||||||
|                 downloadFiles.Add(await _fileHub!.InvokeAsync<DownloadFileDto>(FilesHubAPI.InvokeGetFileSize, file.Hash, ct)); |                 downloadFileInfoFromService.Add(await _fileHub!.InvokeAsync<DownloadFileDto>(FilesHubAPI.InvokeGetFileSize, file.Hash, ct)); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             downloadFiles = downloadFiles.Distinct().ToList(); |             CurrentDownloads[currentDownloadId] = downloadFileInfoFromService.Distinct().Select(d => new DownloadFileTransfer(d)) | ||||||
|  |                 .Where(d => d.CanBeTransferred).ToList(); | ||||||
|  |  | ||||||
|             foreach (var dto in downloadFiles) |             foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden)) | ||||||
|             { |             { | ||||||
|                 var downloadFileTransfer = new DownloadFileTransfer(dto); |                 if (ForbiddenTransfers.All(f => f.Hash != dto.Hash)) | ||||||
|                 if (CurrentDownloads.All(f => f.Hash != downloadFileTransfer.Hash)) |  | ||||||
|                 { |                 { | ||||||
|                     CurrentDownloads.Add(downloadFileTransfer); |                     ForbiddenTransfers.Add(new DownloadFileTransfer(dto)); | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 fileTransferList.Add(downloadFileTransfer); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             foreach (var file in CurrentDownloads.Where(c => c.IsForbidden)) |  | ||||||
|             { |  | ||||||
|                 if (ForbiddenTransfers.All(f => f.Hash != file.Hash)) |  | ||||||
|                 { |  | ||||||
|                     ForbiddenTransfers.Add(file); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             foreach (var file in fileTransferList.Where(f => f.CanBeTransferred)) |             foreach (var file in CurrentDownloads[currentDownloadId].Where(f => f.CanBeTransferred)) | ||||||
|             { |             { | ||||||
|                 var hash = file.Hash; |                 var hash = file.Hash; | ||||||
|                 var tempFile = await DownloadFile(hash, ct); |                 var tempFile = await DownloadFile(currentDownloadId, hash, ct); | ||||||
|                 if (ct.IsCancellationRequested) |                 if (ct.IsCancellationRequested) | ||||||
|                 { |                 { | ||||||
|                     File.Delete(tempFile); |                     File.Delete(tempFile); | ||||||
|                     CurrentDownloads.RemoveAll(d => fileReplacementDto.Any(f => f.Hash == d.Hash)); |                     Logger.Verbose("Detected cancellation, removing " + currentDownloadId); | ||||||
|  |                     CurrentDownloads.Remove(currentDownloadId); | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -112,18 +106,16 @@ namespace MareSynchronos.WebAPI | |||||||
|             { |             { | ||||||
|                 await using (var db = new FileCacheContext()) |                 await using (var db = new FileCacheContext()) | ||||||
|                 { |                 { | ||||||
|                     allFilesInDb = fileTransferList.Where(c => c.CanBeTransferred).All(h => db.FileCaches.Any(f => f.Hash == h.Hash)); |                     allFilesInDb = CurrentDownloads[currentDownloadId] | ||||||
|  |                         .Where(c => c.CanBeTransferred) | ||||||
|  |                         .All(h => db.FileCaches.Any(f => f.Hash == h.Hash)); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 await Task.Delay(250, ct); |                 await Task.Delay(250, ct); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (ct.IsCancellationRequested) |             Logger.Verbose("Download complete, removing " + currentDownloadId); | ||||||
|             { |             CurrentDownloads.Remove(currentDownloadId); | ||||||
|                 CurrentDownloads.RemoveAll(d => fileReplacementDto.Any(f => f.Hash == d.Hash)); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             CurrentDownloads.RemoveAll(d => d.Transferred == d.Total || !d.CanBeTransferred); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds) |         public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds) | ||||||
| @@ -244,6 +236,11 @@ namespace MareSynchronos.WebAPI | |||||||
|  |  | ||||||
|             await _fileHub!.SendAsync(FilesHubAPI.SendUploadFileStreamAsync, fileHash, AsyncFileData(uploadToken), uploadToken); |             await _fileHub!.SendAsync(FilesHubAPI.SendUploadFileStreamAsync, fileHash, AsyncFileData(uploadToken), uploadToken); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public void CancelDownload(int downloadId) | ||||||
|  |         { | ||||||
|  |             CurrentDownloads.Remove(downloadId); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -96,7 +96,7 @@ namespace MareSynchronos.WebAPI | |||||||
|  |  | ||||||
|         public event SimpleStringDelegate? UnpairedFromOther; |         public event SimpleStringDelegate? UnpairedFromOther; | ||||||
|  |  | ||||||
|         public List<FileTransfer> CurrentDownloads { get; } = new(); |         public Dictionary<int, List<DownloadFileTransfer>> CurrentDownloads { get; } = new(); | ||||||
|  |  | ||||||
|         public List<FileTransfer> CurrentUploads { get; } = new(); |         public List<FileTransfer> CurrentUploads { get; } = new(); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant