Initial upload

This commit is contained in:
Misaki
2025-01-15 23:16:34 +09:00
parent d239fa2010
commit cf05b1407b
21 changed files with 315 additions and 183 deletions

View File

@@ -4,9 +4,9 @@ using DownloadManager.Services;
using System.Collections.Concurrent;
namespace DownloadManager.DownloaderCore;
public partial class DownloadManagerService()
public partial class DownloadManagerService(HistoryStorageService historyService)
{
private readonly ConcurrentQueue<DownloadItemData> _queuedDownloadData = new();
private ConcurrentQueue<DownloadItemData> _queuedDownloadData = new();
private readonly ConcurrentDictionary<Guid, DownloadWorker> _activeWorkers = new();
private List<CompletedItemData> _completedDownloadData = new();
@@ -16,9 +16,9 @@ public partial class DownloadManagerService()
public Action<string, string>? OnWorkerFailed;
public Action<string>? OnWorkerComplete;
public async Task LoadDownloadHistory()
public void LoadDownloadHistory()
{
_completedDownloadData = new(await HistoryStorageService.LoadCompletedItemsAsync());
_completedDownloadData = new(historyService.LoadCompletedItems());
}
public void AddDownloadItem(DownloadItemData itemData)
@@ -36,7 +36,7 @@ public partial class DownloadManagerService()
OnQueuedItemChanged?.Invoke();
}
public async Task FindNextDownloadAndStart()
public async Task FindNextDownloadAndStartAsync()
{
try
{
@@ -48,31 +48,29 @@ public partial class DownloadManagerService()
return;
}
var downloadDataToStart = _queuedDownloadData.Take(workersToStart);
var downloadDataToStart = _queuedDownloadData.Take(workersToStart).ToArray();
foreach (var downloadData in downloadDataToStart)
await Parallel.ForEachAsync(downloadDataToStart, new ParallelOptions() { MaxDegreeOfParallelism = SettingService.Current.MaxActiveDownloadCount }, async (downloadData, cts) =>
{
var worker = new DownloadWorker(downloadData);
if (_activeWorkers.TryAdd(downloadData.Id, worker))
_activeWorkers.TryAdd(downloadData.Id, worker);
_queuedDownloadData.TryDequeue(out _);
SetupDownloadWorkerCallbacks(worker);
if (await worker.BuildDownloadRequestAsync())
{
_queuedDownloadData.TryDequeue(out _);
SetupDownloadWorkerCallbacks(worker);
if (await worker.BuildDownloadRequestAsync())
{
worker.StartDownload();
}
else
{
_activeWorkers.Remove(downloadData.Id, out _);
await AddCompletedItem(downloadData, DownloadStatus.Failed.ToString(), $"Not able to get file url using {downloadData.UrlGetterName} url getter");
await FindNextDownloadAndStart();
OnWorkerFailed?.Invoke(downloadData.Url, $"Not able to get file url using {downloadData.UrlGetterName} url getter");
}
await worker.StartAsync();
}
}
else
{
_activeWorkers.Remove(downloadData.Id, out _);
AddCompletedItem(downloadData, DownloadStatus.Failed.ToString(), $"Not able to get file url using {downloadData.UrlGetterName} url getter");
await FindNextDownloadAndStartAsync();
OnWorkerFailed?.Invoke(downloadData.Url, $"Not able to get file url using {downloadData.UrlGetterName} url getter");
}
});
}
catch (Exception)
{
@@ -88,12 +86,12 @@ public partial class DownloadManagerService()
OnQueuedItemChanged?.Invoke();
};
worker.OnDownloadFileCompleted = async () =>
worker.OnDownloadCompleted = async () =>
{
_activeWorkers.Remove(worker.DownloadData.Id, out _);
await AddCompletedItem(worker.DownloadData, worker.DownloadData.Status.ToString(), worker.errorMessage);
await FindNextDownloadAndStart();
AddCompletedItem(worker.DownloadData, worker.DownloadData.Status.ToString(), worker.errorMessage);
await FindNextDownloadAndStartAsync();
OnActiveWorkerChanged?.Invoke();
@@ -110,12 +108,14 @@ public partial class DownloadManagerService()
{
OnAllWorkerCompleted?.Invoke();
}
worker.Dispose();
};
worker.OnDownloadStop = async () =>
{
_activeWorkers.Remove(worker.DownloadData.Id, out _);
await FindNextDownloadAndStart();
await FindNextDownloadAndStartAsync();
OnActiveWorkerChanged?.Invoke();
};
@@ -128,4 +128,10 @@ public partial class DownloadManagerService()
worker.Value.StopDownload();
}
}
internal void RemoveTask(DownloadItemData task)
{
_queuedDownloadData = new(_queuedDownloadData.Where(x => x != task));
OnQueuedItemChanged?.Invoke();
}
}

View File

@@ -1,12 +1,12 @@
using Downloader;
using DownloadManager.Models;
using DownloadManager.Services;
using LiteDB;
namespace DownloadManager.DownloaderCore
{
public partial class DownloadManagerService
{
private async Task AddCompletedItem(DownloadItemData downloadData, string status, string errorMessage)
private void AddCompletedItem(DownloadItemData downloadData, string status, string errorMessage)
{
var completedItem = new CompletedItemData
{
@@ -15,16 +15,12 @@ namespace DownloadManager.DownloaderCore
SavePath = downloadData.FilePath,
FullName = System.IO.Path.Combine(downloadData.FilePath, downloadData.FileName),
Status = status,
CompletedDate = DateOnly.FromDateTime(DateTime.Now),
ErrorMessage = errorMessage == string.Empty ? "No error" : errorMessage
};
_completedDownloadData.Add(completedItem);
await SaveCompletedItemsAsync();
}
private async Task SaveCompletedItemsAsync()
{
await HistoryStorageService.SaveCompletedItemsAsync(_completedDownloadData);
historyService.SaveCompletedItem(completedItem);
}
public IEnumerable<DownloadItemData> GetDownloadingTask()
@@ -42,31 +38,33 @@ namespace DownloadManager.DownloaderCore
return _completedDownloadData;
}
public async Task ClearCompletedTask()
public void ClearCompletedTask()
{
var completedTasks = _completedDownloadData.Where(x => x.Status == DownloadStatus.Completed.ToString()).ToArray();
_completedDownloadData.RemoveAll(x => x.Status == DownloadStatus.Completed.ToString());
await SaveCompletedItemsAsync();
historyService.RemoveCompletedItemList(completedTasks);
}
public async Task ClearFailedTask()
public void ClearFailedTask()
{
var failedTasks = _completedDownloadData.Where(x => x.Status == DownloadStatus.Failed.ToString()).ToArray();
_completedDownloadData.RemoveAll(x => x.Status == DownloadStatus.Failed.ToString());
await SaveCompletedItemsAsync();
historyService.RemoveCompletedItemList(failedTasks);
}
public async Task ClearAllTask()
public void ClearAllTask()
{
_completedDownloadData.Clear();
await SaveCompletedItemsAsync();
historyService.ClearCompletedItems();
}
public async Task RemoveCompletedTask(CompletedItemData completedItem)
public void RemoveCompletedTask(CompletedItemData completedItem)
{
_completedDownloadData.Remove(completedItem);
await SaveCompletedItemsAsync();
historyService.RemoveCompletedItem(completedItem);
}
public async Task RetryFailedTask()
public void RetryFailedTask()
{
var failedTasks = _completedDownloadData.Where(x => x.Status == DownloadStatus.Failed.ToString()).ToArray();
foreach (var failedTask in failedTasks)
@@ -76,7 +74,7 @@ namespace DownloadManager.DownloaderCore
_completedDownloadData.Remove(failedTask);
}
await SaveCompletedItemsAsync();
historyService.RemoveCompletedItemList(failedTasks);
}
}
}
}

View File

@@ -6,7 +6,7 @@ using System.ComponentModel;
using System.IO;
namespace DownloadManager.DownloaderCore;
public class DownloadWorker(DownloadItemData itemData)
public class DownloadWorker(DownloadItemData itemData) : IDisposable
{
private IDownload? _worker;
private DownloadItemData _downloadData = itemData;
@@ -15,12 +15,9 @@ public class DownloadWorker(DownloadItemData itemData)
public string errorMessage = string.Empty;
public Action? OnDownloadStarted;
public Action? OnDownloadFileCompleted;
public Action<string>? OnDownloadFailed;
public Action? OnDownloadCompleted;
public Action? OnDownloadStop;
private int retryCount = 0;
public async Task<bool> BuildDownloadRequestAsync()
{
var urlGetter = UrlGetterUtilities.CreateUrlGetter(_downloadData.UrlGetterName);
@@ -39,8 +36,6 @@ public class DownloadWorker(DownloadItemData itemData)
_downloadData.FileName = fileUrl.Name;
_downloadData.MD5 = fileUrl.MD5;
retryCount = _downloadData.DownloadConfiguration.MaxTryAgainOnFailover;
BuildWorker();
return true;
@@ -81,8 +76,6 @@ public class DownloadWorker(DownloadItemData itemData)
private void DownloadFileCompleted(object? sender, AsyncCompletedEventArgs e)
{
_worker?.Dispose();
if (e.Error != null)
{
_downloadData.Status = DownloadStatus.Failed;
@@ -97,8 +90,7 @@ public class DownloadWorker(DownloadItemData itemData)
// Check md5
if (_downloadData.MD5 != null)
{
var fileFullName = Path.Combine(_downloadData.FilePath, _downloadData.FileName!);
var md5 = MD5Utilities.GetMD5HashFromFile(fileFullName);
var md5 = MD5Utilities.GetMD5HashFromFile(_downloadData.FileFullName);
if (md5 != _downloadData.MD5)
{
_downloadData.Status = DownloadStatus.Failed;
@@ -108,37 +100,42 @@ public class DownloadWorker(DownloadItemData itemData)
}
}
OnDownloadFileCompleted?.Invoke();
OnDownloadCompleted?.Invoke();
}
private async Task<bool> RetryDownload()
public async Task StartAsync()
{
if (retryCount > 0)
if (_worker == null)
{
retryCount--;
await Task.Delay(1000);
await BuildDownloadRequestAsync();
StartDownload();
return true;
return;
}
else
if (File.Exists(_downloadData.FileFullName))
{
_downloadData.Status = DownloadStatus.Failed;
return false;
if (SettingService.Current.FileOverwrite)
{
File.Delete(_downloadData.FileFullName);
}
else
{
_downloadData.Status = DownloadStatus.Failed;
errorMessage = "File already exists";
OnDownloadCompleted?.Invoke();
return;
}
}
}
public void StartDownload()
{
_worker?.StartAsync();
await _worker.StartAsync();
}
public void PauseDownload()
{
_worker?.Pause();
if (_worker == null)
{
return;
}
_worker.Pause();
OnDownloadStop?.Invoke();
}
@@ -149,7 +146,18 @@ public class DownloadWorker(DownloadItemData itemData)
public void StopDownload()
{
_worker?.Stop();
if (_worker == null)
{
return;
}
_worker.Stop();
OnDownloadStop?.Invoke();
}
public void Dispose()
{
_worker?.Dispose();
GC.SuppressFinalize(this);
}
}