Merge multiple project into one solution folder. Update ui and url getter add-on

This commit is contained in:
Misaki
2024-07-05 20:50:17 +09:00
parent 939637cc96
commit cb3f021855
126 changed files with 630 additions and 159 deletions

View 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();
}
}

View 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();
}
}