using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using System.Timers; using Aria2NET; using RdtClient.Data.Models.Internal; namespace RdtClient.Service.Services.Downloaders { public class Aria2cDownloader : IDownloader { public event EventHandler DownloadComplete; public event EventHandler DownloadProgress; private const Int32 RetryCount = 5; private readonly String _uri; private readonly String _filePath; private readonly Aria2NetClient _aria2NetClient; private readonly Timer _timer; private String _gid; public Aria2cDownloader(String gid, String uri, String filePath, DbSettings settings) { _gid = gid; _uri = uri; _filePath = filePath; _aria2NetClient = new Aria2NetClient(settings.Aria2cUrl, settings.Aria2cSecret); _timer = new Timer(); _timer.Elapsed += OnTimedEvent; _timer.Interval = 1000; _timer.Enabled = false; } public async Task Download() { var path = Path.GetDirectoryName(_filePath); var fileName = Path.GetFileName(_filePath); if (_gid != null) { try { await _aria2NetClient.TellStatus(_gid); } catch { _gid = null; } } var retryCount = 0; while(true) { try { _gid ??= await _aria2NetClient.AddUri(new List { _uri }, new Dictionary { { "dir", path }, { "out", fileName } }); break; } catch { if (retryCount >= RetryCount) { throw; } await Task.Delay(retryCount * 1000); retryCount++; } } // Add a delay to prevent sending too many Add requests to Aria2 at the same time. await Task.Delay(1000); _timer.Start(); return _gid; } public async Task Cancel() { _timer.Stop(); if (String.IsNullOrWhiteSpace(_gid)) { return; } try { await _aria2NetClient.ForceRemove(_gid); } catch { // ignored } try { await _aria2NetClient.RemoveDownloadResult(_gid); } catch { // ignored } } private async void OnTimedEvent(Object source, ElapsedEventArgs e) { if (_gid == null) { return; } try { var status = await _aria2NetClient.TellStatus(_gid); if (!String.IsNullOrWhiteSpace(status.ErrorMessage) || status.Status == "error") { await Cancel(); DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs { Error = $"{status.ErrorCode}: {status.ErrorMessage}" }); return; } if (status.Status == "complete" || status.Status == "removed") { await Cancel(); DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs()); return; } DownloadProgress?.Invoke(this, new DownloadProgressEventArgs { BytesDone = status.CompletedLength, BytesTotal = status.TotalLength, Speed = status.DownloadSpeed }); } catch { await Cancel(); DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs()); } } } }