rdt-client/server/RdtClient.Service/Services/DownloadManager.cs
2020-04-07 12:08:17 -06:00

186 lines
No EOL
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using RdtClient.Data.Data;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data;
using SharpCompress.Common;
using SharpCompress.Readers;
namespace RdtClient.Service.Services
{
public static class DownloadManager
{
public static readonly Dictionary<Guid, Download> ActiveDownloads = new Dictionary<Guid, Download>();
static DownloadManager()
{
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.MaxServicePointIdleTime = 1000;
}
public static async Task Download(Download download, String destinationFolderPath)
{
await UpdateStatus(download.DownloadId, DownloadStatus.Downloading, TorrentStatus.Downloading);
if (!ActiveDownloads.TryAdd(download.DownloadId, download))
{
return;
}
download.Progress = 0;
download.BytesLastUpdate = 0;
download.NextUpdate = DateTime.UtcNow.AddSeconds(1);
download.Speed = 0;
var fileUrl = download.Link;
var uri = new Uri(fileUrl);
var filePath = Path.Combine(destinationFolderPath, uri.Segments.Last());
if (!Directory.Exists(destinationFolderPath))
{
Directory.CreateDirectory(destinationFolderPath);
}
var webRequest = WebRequest.Create(fileUrl);
webRequest.Method = "HEAD";
Int64 responseLength;
using (var webResponse = await webRequest.GetResponseAsync())
{
responseLength = Int64.Parse(webResponse.Headers.Get("Content-Length"));
}
if (File.Exists(filePath))
{
File.Delete(filePath);
}
var request = WebRequest.Create(fileUrl);
using (var response = await request.GetResponseAsync())
{
await using var stream = response.GetResponseStream();
await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write);
var buffer = new Byte[4096];
while (fileStream.Length < response.ContentLength)
{
var read = stream.Read(buffer, 0, buffer.Length);
if (read > 0)
{
fileStream.Write(buffer, 0, read);
ActiveDownloads[download.DownloadId].Progress = (Int32) (fileStream.Length * 100 / responseLength);
if (DateTime.UtcNow > ActiveDownloads[download.DownloadId]
.NextUpdate)
{
ActiveDownloads[download.DownloadId].Speed = fileStream.Length - ActiveDownloads[download.DownloadId].BytesLastUpdate;
ActiveDownloads[download.DownloadId].NextUpdate = DateTime.UtcNow.AddSeconds(1);
ActiveDownloads[download.DownloadId].BytesLastUpdate = fileStream.Length;
Debug.WriteLine($"{fileStream.Length}/{responseLength} {ActiveDownloads[download.DownloadId].Speed}");
}
}
else
{
break;
}
}
}
ActiveDownloads[download.DownloadId].Speed = 0;
try
{
if (filePath.EndsWith(".rar"))
{
await UpdateStatus(download.DownloadId, DownloadStatus.Unpacking, TorrentStatus.Downloading);
await using (Stream stream = File.OpenRead(filePath))
{
var reader = ReaderFactory.Open(stream);
while (reader.MoveToNextEntry())
{
if (reader.Entry.IsDirectory)
{
continue;
}
reader.WriteEntryToDirectory(destinationFolderPath,
new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = true
});
}
}
var retryCount = 0;
while (File.Exists(filePath) && retryCount < 10)
{
retryCount++;
try
{
File.Delete(filePath);
}
catch
{
await Task.Delay(1000);
}
}
}
}
catch
{
// ignored
}
await UpdateStatus(download.DownloadId, DownloadStatus.Finished, TorrentStatus.Finished);
ActiveDownloads.Remove(download.DownloadId);
}
private static async Task UpdateStatus(Guid downloadId, DownloadStatus downloadStatus, TorrentStatus torrentStatus)
{
await using var context = new DataContext();
var download = await context.Downloads.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
download.Status = downloadStatus;
await context.SaveChangesAsync();
var torrent = await context.Torrents.FirstOrDefaultAsync(m => m.TorrentId == download.TorrentId);
if (torrentStatus == TorrentStatus.Finished)
{
var allDownloads = await context.Downloads.Where(m => m.TorrentId == download.TorrentId)
.ToListAsync();
if (allDownloads.All(m => m.Status == DownloadStatus.Finished))
{
torrent.Status = TorrentStatus.Finished;
}
else
{
torrent.Status = TorrentStatus.Downloading;
}
}
else
{
torrent.Status = torrentStatus;
}
await context.SaveChangesAsync();
}
}
}