Merge multiple project into one solution folder. Update ui and url getter add-on
This commit is contained in:
187
DownloadManager/DownloaderCore/DownloadManagerService.cs
Normal file
187
DownloadManager/DownloaderCore/DownloadManagerService.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using Downloader;
|
||||
using DownloadManager.Models;
|
||||
using DownloadManager.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
|
||||
namespace DownloadManager.DownloaderCore;
|
||||
public class DownloadManagerService()
|
||||
{
|
||||
private readonly ConcurrentQueue<DownloadItemData> _queuedDownloadData = new();
|
||||
private readonly ConcurrentDictionary<Guid, DownloadWorker> _activeWorkers = new();
|
||||
private List<CompletedItemData> _completedDownloadData = new();
|
||||
|
||||
public Action? OnActiveWorkerChanged;
|
||||
public Action? OnQueuedItemChanged;
|
||||
public Action? OnAllWorkerCompleted;
|
||||
public Action<string, string>? OnWorkerFailed;
|
||||
|
||||
public async Task LoadDownloadHistory()
|
||||
{
|
||||
_completedDownloadData = new(await HistoryStorageService.LoadCompletedItemsAsync());
|
||||
}
|
||||
|
||||
public void AddDownloadItem(DownloadItemData itemData)
|
||||
{
|
||||
if (!_queuedDownloadData.Contains(itemData))
|
||||
{
|
||||
_queuedDownloadData.Enqueue(itemData);
|
||||
OnQueuedItemChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertDownloadItem(DownloadItemData itemData, int index)
|
||||
{
|
||||
_queuedDownloadData.Enqueue(itemData);
|
||||
OnQueuedItemChanged?.Invoke();
|
||||
}
|
||||
|
||||
public async Task FindNextDownloadAndStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
var activeWorkersCount = _activeWorkers.Count;
|
||||
var workersToStart = SettingService.Current.MaxActiveDownloadCount - activeWorkersCount;
|
||||
|
||||
if (workersToStart <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var downloadDataToStart = _queuedDownloadData.Take(workersToStart);
|
||||
|
||||
foreach (var downloadData in downloadDataToStart)
|
||||
{
|
||||
var worker = new DownloadWorker(downloadData);
|
||||
if (_activeWorkers.TryAdd(downloadData.Id, worker))
|
||||
{
|
||||
_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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupDownloadWorkerCallbacks(DownloadWorker worker)
|
||||
{
|
||||
worker.OnDownloadStarted = () =>
|
||||
{
|
||||
OnActiveWorkerChanged?.Invoke();
|
||||
OnQueuedItemChanged?.Invoke();
|
||||
};
|
||||
|
||||
worker.OnDownloadFileCompleted = async () =>
|
||||
{
|
||||
_activeWorkers.Remove(worker.DownloadData.Id, out _);
|
||||
|
||||
await AddCompletedItem(worker.DownloadData, worker.DownloadData.Status.ToString(), worker.errorMessage);
|
||||
await FindNextDownloadAndStart();
|
||||
|
||||
OnActiveWorkerChanged?.Invoke();
|
||||
|
||||
if (worker.DownloadData.Status == DownloadStatus.Failed)
|
||||
{
|
||||
OnWorkerFailed?.Invoke(worker.DownloadData.Url, worker.errorMessage);
|
||||
}
|
||||
|
||||
if (_activeWorkers.Count == 0 && _queuedDownloadData.Count == 0)
|
||||
{
|
||||
OnAllWorkerCompleted?.Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
worker.OnDownloadStop = async () =>
|
||||
{
|
||||
_activeWorkers.Remove(worker.DownloadData.Id, out _);
|
||||
await FindNextDownloadAndStart();
|
||||
|
||||
OnActiveWorkerChanged?.Invoke();
|
||||
};
|
||||
}
|
||||
|
||||
public void StopDownload()
|
||||
{
|
||||
foreach (var worker in _activeWorkers)
|
||||
{
|
||||
worker.Value.StopDownload();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddCompletedItem(DownloadItemData downloadData, string status, string errorMessage)
|
||||
{
|
||||
var completedItem = new CompletedItemData
|
||||
{
|
||||
Name = downloadData.FileName,
|
||||
Url = downloadData.Url,
|
||||
SavePath = downloadData.FilePath,
|
||||
FullName = Path.Combine(downloadData.FilePath, downloadData.FileName),
|
||||
Status = status,
|
||||
ErrorMessage = errorMessage == string.Empty ? "No error" : errorMessage
|
||||
};
|
||||
|
||||
_completedDownloadData.Add(completedItem);
|
||||
await SaveCompletedItemsAsync();
|
||||
}
|
||||
|
||||
private async Task SaveCompletedItemsAsync()
|
||||
{
|
||||
await HistoryStorageService.SaveCompletedItemsAsync(_completedDownloadData);
|
||||
}
|
||||
|
||||
public IEnumerable<DownloadItemData> GetDownloadingTask()
|
||||
{
|
||||
return _activeWorkers.Select(x => x.Value.DownloadData);
|
||||
}
|
||||
|
||||
public IEnumerable<DownloadItemData> GetWaitingTask()
|
||||
{
|
||||
return _queuedDownloadData;
|
||||
}
|
||||
|
||||
public IEnumerable<CompletedItemData> GetCompletedTask()
|
||||
{
|
||||
return _completedDownloadData;
|
||||
}
|
||||
|
||||
public async Task ClearCompletedTask()
|
||||
{
|
||||
_completedDownloadData.RemoveAll(x => x.Status == DownloadStatus.Completed.ToString());
|
||||
await SaveCompletedItemsAsync();
|
||||
}
|
||||
|
||||
public async Task ClearFailedTask()
|
||||
{
|
||||
_completedDownloadData.RemoveAll(x => x.Status == DownloadStatus.Failed.ToString());
|
||||
await SaveCompletedItemsAsync();
|
||||
}
|
||||
|
||||
public async Task ClearAllTask()
|
||||
{
|
||||
_completedDownloadData.Clear();
|
||||
await SaveCompletedItemsAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveCompletedTask(CompletedItemData completedItem)
|
||||
{
|
||||
_completedDownloadData.Remove(completedItem);
|
||||
await SaveCompletedItemsAsync();
|
||||
}
|
||||
}
|
||||
155
DownloadManager/DownloaderCore/DownloadWorker.cs
Normal file
155
DownloadManager/DownloaderCore/DownloadWorker.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using Downloader;
|
||||
using DownloadManager.Models;
|
||||
using DownloadManager.Services;
|
||||
using DownloadManager.Utilities;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
||||
namespace DownloadManager.DownloaderCore;
|
||||
public class DownloadWorker(DownloadItemData itemData)
|
||||
{
|
||||
private IDownload? _worker;
|
||||
private DownloadItemData _downloadData = itemData;
|
||||
public DownloadItemData DownloadData => _downloadData;
|
||||
|
||||
public string errorMessage = string.Empty;
|
||||
|
||||
public Action? OnDownloadStarted;
|
||||
public Action? OnDownloadFileCompleted;
|
||||
public Action<string>? OnDownloadFailed;
|
||||
public Action? OnDownloadStop;
|
||||
|
||||
private int retryCount = 0;
|
||||
|
||||
public async Task<bool> BuildDownloadRequestAsync()
|
||||
{
|
||||
var urlGetter = UrlGetterUtilities.CreateUrlGetter(_downloadData.UrlGetterName);
|
||||
|
||||
if (urlGetter == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var fileUrl = await urlGetter.GetFileUrl(_downloadData.Url);
|
||||
|
||||
if (fileUrl.Url == null || fileUrl.Name == null)
|
||||
return false;
|
||||
|
||||
_downloadData.Url = fileUrl.Url;
|
||||
_downloadData.FileName = fileUrl.Name;
|
||||
_downloadData.MD5 = fileUrl.MD5;
|
||||
|
||||
retryCount = _downloadData.DownloadConfiguration.MaxTryAgainOnFailover;
|
||||
|
||||
BuildWorker();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildWorker()
|
||||
{
|
||||
_worker = DownloadBuilder.New()
|
||||
.WithUrl(_downloadData.Url)
|
||||
.WithDirectory(_downloadData.FilePath)
|
||||
.WithFileName(_downloadData.FileName)
|
||||
.WithConfiguration(_downloadData.DownloadConfiguration)
|
||||
.Build();
|
||||
|
||||
_worker.DownloadStarted += DownloadStarted;
|
||||
_worker.DownloadProgressChanged += DownloadProgressChanged;
|
||||
_worker.DownloadFileCompleted += DownloadFileCompleted;
|
||||
}
|
||||
|
||||
private void DownloadStarted(object? sender, DownloadStartedEventArgs e)
|
||||
{
|
||||
_downloadData.Status = DownloadStatus.Running;
|
||||
_downloadData.FileSize = DoubleUtilities.BytesToMegaBytes(e.TotalBytesToReceive, 2);
|
||||
OnDownloadStarted?.Invoke();
|
||||
}
|
||||
|
||||
private void DownloadProgressChanged(object? sender, DownloadProgressChangedEventArgs e)
|
||||
{
|
||||
_downloadData.Progress = (int)e.ProgressPercentage;
|
||||
_downloadData.Speed = DoubleUtilities.BytesToMegaBytes(e.BytesPerSecondSpeed, 2);
|
||||
}
|
||||
|
||||
private void DownloadFileCompleted(object? sender, AsyncCompletedEventArgs e)
|
||||
{
|
||||
_worker?.Dispose();
|
||||
|
||||
if (e.Error != null)
|
||||
{
|
||||
_downloadData.Status = DownloadStatus.Failed;
|
||||
errorMessage = e.Error.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
_downloadData.Status = DownloadStatus.Completed;
|
||||
|
||||
if (SettingService.Current.MD5Check)
|
||||
{
|
||||
// Check md5
|
||||
if (_downloadData.MD5 != null)
|
||||
{
|
||||
var fileFullName = Path.Combine(_downloadData.FilePath, _downloadData.FileName!);
|
||||
var md5 = MD5Utilities.GetMD5HashFromFile(fileFullName);
|
||||
if (md5 != _downloadData.MD5)
|
||||
{
|
||||
_downloadData.Status = DownloadStatus.Failed;
|
||||
errorMessage = "MD5 Check Failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnDownloadFileCompleted?.Invoke();
|
||||
}
|
||||
|
||||
private async Task<bool> RetryDownload()
|
||||
{
|
||||
if (retryCount > 0)
|
||||
{
|
||||
retryCount--;
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
await BuildDownloadRequestAsync();
|
||||
StartDownload();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_downloadData.Status = DownloadStatus.Failed;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartDownload()
|
||||
{
|
||||
_worker?.StartAsync();
|
||||
}
|
||||
|
||||
public void PauseDownload()
|
||||
{
|
||||
_worker?.Pause();
|
||||
OnDownloadStop?.Invoke();
|
||||
}
|
||||
|
||||
public void ResumeDownload()
|
||||
{
|
||||
_worker?.Resume();
|
||||
}
|
||||
|
||||
public void StopDownload()
|
||||
{
|
||||
_worker?.Stop();
|
||||
OnDownloadStop?.Invoke();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user