AbstractDownloadState download made async

This commit is contained in:
Justin Swanson 2019-12-05 23:29:17 -06:00
parent e9deda9f44
commit 43dc6953c5
17 changed files with 97 additions and 89 deletions

View File

@ -49,7 +49,7 @@ namespace Compression.BSA.Test
foreach (var info in modIDs)
{
var filename = DownloadMod(info);
var filename = await DownloadMod(info);
var folder = Path.Combine(_bsaFolder, info.Item1.ToString(), info.Item2.ToString());
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
@ -57,7 +57,7 @@ namespace Compression.BSA.Test
}
}
private static string DownloadMod((Game, int) info)
private static async Task<string> DownloadMod((Game, int) info)
{
using (var client = new NexusApiClient())
{
@ -74,7 +74,7 @@ namespace Compression.BSA.Test
GameName = GameRegistry.Games[info.Item1].NexusName,
FileID = file.file_id.ToString()
};
state.Download(src);
await state.Download(src);
return src;
}
}

View File

@ -205,7 +205,7 @@ namespace Wabbajack.Lib
SelectedArchives = await shas.PMap(Queue, sha => ResolveArchive(sha, archives));
}
public Archive ResolveArchive(string sha, IDictionary<string, IndexedArchive> archives)
public async Task<Archive> ResolveArchive(string sha, IDictionary<string, IndexedArchive> archives)
{
if (archives.TryGetValue(sha, out var found))
{
@ -227,7 +227,7 @@ namespace Wabbajack.Lib
Info($"Checking link for {found.Name}");
if (result.State != null && !result.State.Verify())
if (result.State != null && !await result.State.Verify())
Error(
$"Unable to resolve link for {found.Name}. If this is hosted on the Nexus the file may have been removed.");

View File

@ -244,12 +244,12 @@ namespace Wabbajack.Lib
foreach (var a in missing.Where(a => a.State.GetType() == typeof(ManualDownloader.State)))
{
var outputPath = Path.Combine(DownloadFolder, a.Name);
a.State.Download(a, outputPath);
await a.State.Download(a, outputPath);
}
}
await missing.Where(a => a.State.GetType() != typeof(ManualDownloader.State))
.PMap(Queue, archive =>
.PMap(Queue, async archive =>
{
Info($"Downloading {archive.Name}");
var outputPath = Path.Combine(DownloadFolder, archive.Name);
@ -258,16 +258,16 @@ namespace Wabbajack.Lib
if (outputPath.FileExists())
File.Delete(outputPath);
return DownloadArchive(archive, download);
return await DownloadArchive(archive, download);
});
}
public bool DownloadArchive(Archive archive, bool download)
public async Task<bool> DownloadArchive(Archive archive, bool download)
{
try
{
var path = Path.Combine(DownloadFolder, archive.Name);
archive.State.Download(archive, path);
await archive.State.Download(archive, path);
path.FileHashCached();
}

View File

@ -1,4 +1,5 @@
using Alphaleonis.Win32.Filesystem;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using Wabbajack.Lib.Validation;
namespace Wabbajack.Lib.Downloaders
@ -19,18 +20,18 @@ namespace Wabbajack.Lib.Downloaders
/// Downloads this file to the given destination location
/// </summary>
/// <param name="destination"></param>
public abstract void Download(Archive a, string destination);
public abstract Task Download(Archive a, string destination);
public void Download(string destination)
public async Task Download(string destination)
{
Download(new Archive {Name = Path.GetFileName(destination)}, destination);
await Download(new Archive {Name = Path.GetFileName(destination)}, destination);
}
/// <summary>
/// Returns true if this link is still valid
/// </summary>
/// <returns></returns>
public abstract bool Verify();
public abstract Task<bool> Verify();
public abstract IDownloader GetDownloader();

View File

@ -1,5 +1,6 @@
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
@ -40,9 +41,9 @@ namespace Wabbajack.Lib.Downloaders
return whitelist.GoogleIDs.Contains(Id);
}
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
ToHttpState().Download(a, destination);
await ToHttpState().Download(a, destination);
}
private HTTPDownloader.State ToHttpState()
@ -57,9 +58,9 @@ namespace Wabbajack.Lib.Downloaders
return httpState;
}
public override bool Verify()
public override async Task<bool> Verify()
{
return ToHttpState().Verify();
return await ToHttpState().Verify();
}
public override IDownloader GetDownloader()

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Ceras;
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
@ -64,12 +65,12 @@ namespace Wabbajack.Lib.Downloaders
return whitelist.AllowedPrefixes.Any(p => Url.StartsWith(p));
}
public override void Download(Archive a, string destination)
public override Task Download(Archive a, string destination)
{
DoDownload(a, destination, true);
return DoDownload(a, destination, true);
}
public bool DoDownload(Archive a, string destination, bool download)
public async Task<bool> DoDownload(Archive a, string destination, bool download)
{
var client = Client ?? new HttpClient();
client.DefaultRequestHeaders.Add("User-Agent", Consts.UserAgent);
@ -87,18 +88,15 @@ namespace Wabbajack.Lib.Downloaders
var bufferSize = 1024 * 32;
var response = client.GetSync(Url);
var stream = response.Content.ReadAsStreamAsync();
Stream stream;
try
{
stream.Wait();
stream = await response.Content.ReadAsStreamAsync();
}
catch (Exception)
catch (Exception ex)
{
}
if (stream.IsFaulted || response.StatusCode != HttpStatusCode.OK)
{
Utils.Error(stream.Exception, $"While downloading {Url}");
Utils.Error(ex, $"While downloading {Url}");
return false;
}
@ -117,7 +115,7 @@ namespace Wabbajack.Lib.Downloaders
Directory.CreateDirectory(fileInfo.Directory.FullName);
}
using (var webs = stream.Result)
using (var webs = stream)
using (var fs = File.OpenWrite(destination))
{
var buffer = new byte[bufferSize];
@ -135,9 +133,9 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override bool Verify()
public override async Task<bool> Verify()
{
return DoDownload(new Archive {Name = ""}, "", false);
return await DoDownload(new Archive {Name = ""}, "", false);
}
public override IDownloader GetDownloader()

View File

@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using CG.Web.MegaApiClient;
using Wabbajack.Common;
@ -26,7 +27,7 @@ namespace Wabbajack.Lib.Downloaders
public class State : HTTPDownloader.State
{
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
var client = new MegaApiClient();
Utils.Status("Logging into MEGA (as anonymous)");
@ -37,7 +38,7 @@ namespace Wabbajack.Lib.Downloaders
client.DownloadFile(fileLink, destination);
}
public override bool Verify()
public override async Task<bool> Verify()
{
var client = new MegaApiClient();
Utils.Status("Logging into MEGA (as anonymous)");

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Syroot.Windows.IO;
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
@ -76,7 +77,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
var downloader = (ManualDownloader)GetDownloader();
var absPath = Path.Combine(downloader._downloadfolder.Path, a.Name);
@ -107,7 +108,7 @@ namespace Wabbajack.Lib.Downloaders
}
}
public override bool Verify()
public override async Task<bool> Verify()
{
return true;
}

View File

@ -29,14 +29,15 @@ namespace Wabbajack.Lib.Downloaders
return whitelist.AllowedPrefixes.Any(p => Url.StartsWith(p));
}
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
Resolve().Result.Download(a, destination);
var result = await Resolve();
await result.Download(a, destination);
}
public override bool Verify()
public override async Task<bool> Verify()
{
return Resolve().Result != null;
return await Resolve() != null;
}
private async Task<HTTPDownloader.State> Resolve()

View File

@ -1,5 +1,6 @@
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
@ -39,10 +40,10 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
var newURL = GetDownloadUrl();
new HTTPDownloader.State {Url = newURL}.Download(a, destination);
await new HTTPDownloader.State {Url = newURL}.Download(a, destination);
}
private string GetDownloadUrl()
@ -55,10 +56,10 @@ namespace Wabbajack.Lib.Downloaders
return newURL;
}
public override bool Verify()
public override async Task<bool> Verify()
{
var newURL = GetDownloadUrl();
return new HTTPDownloader.State { Url = newURL }.Verify();
return await new HTTPDownloader.State { Url = newURL }.Verify();
}
public override IDownloader GetDownloader()

View File

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.Common.StatusFeed.Errors;
using Wabbajack.Lib.NexusApi;
@ -75,7 +76,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
string url;
try
@ -90,14 +91,14 @@ namespace Wabbajack.Lib.Downloaders
Utils.Log($"Downloading Nexus Archive - {a.Name} - {GameName} - {ModID} - {FileID}");
new HTTPDownloader.State
await new HTTPDownloader.State
{
Url = url
}.Download(a, destination);
}
public override bool Verify()
public override async Task<bool> Verify()
{
try
{

View File

@ -46,7 +46,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override void Download(Archive a, string destination)
public override async Task Download(Archive a, string destination)
{
var currentLib = "";
SteamHandler.Instance.InstallFolders.Where(f => f.Contains(Item.Game.InstallDir)).Do(s => currentLib = s);
@ -77,7 +77,7 @@ namespace Wabbajack.Lib.Downloaders
}
}
public override bool Verify()
public override async Task<bool> Verify()
{
//TODO: find a way to verify steam workshop items
throw new NotImplementedException();

View File

@ -54,7 +54,7 @@ namespace Wabbajack.Test.ListValidation
{
var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
Log($"Downloading {list.Links.MachineURL} - {list.Title}");
state.Download(modlist_path);
await state.Download(modlist_path);
}
else
{
@ -69,10 +69,10 @@ namespace Wabbajack.Test.ListValidation
Log($"{installer.Archives.Count} archives to validate");
var invalids = (await installer.Archives
.PMap(Queue,archive =>
.PMap(Queue, async archive =>
{
Log($"Validating: {archive.Name}");
return new {archive, is_valid = archive.State.Verify()};
return new {archive, is_valid = await archive.State.Verify()};
}))
.Where(a => !a.is_valid)
.ToList();

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common;
using Wabbajack.Lib;
@ -25,7 +26,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void MegaDownload()
public async Task MegaDownload()
{
var ini = @"[General]
directURL=https://mega.nz/#!CsMSFaaJ!-uziC4mbJPRy2e4pPk8Gjb3oDT_38Be9fzZ6Ld4NL-k";
@ -42,13 +43,13 @@ namespace Wabbajack.Test
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist {AllowedPrefixes = new List<string>{"https://mega.nz/#!CsMSFaaJ!-uziC4mbJPRy2e4pPk8Gjb3oDT_38Be9fzZ6Ld4NL-k" } }));
Assert.IsFalse(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string>{ "blerg" }}));
converted.Download(new Archive {Name = "MEGA Test.txt"}, filename);
await converted.Download(new Archive {Name = "MEGA Test.txt"}, filename);
Assert.AreEqual("eSIyd+KOG3s=", Utils.FileHash(filename));
@ -56,7 +57,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void DropboxTests()
public async Task DropboxTests()
{
var ini = @"[General]
directURL=https://www.dropbox.com/s/5hov3m2pboppoc2/WABBAJACK_TEST_FILE.txt?dl=0";
@ -72,13 +73,13 @@ namespace Wabbajack.Test
((HTTPDownloader.State)url_state).Url);
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string> { "https://www.dropbox.com/s/5hov3m2pboppoc2/WABBAJACK_TEST_FILE.txt?" } }));
Assert.IsFalse(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string> { "blerg" } }));
converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
await converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
Assert.AreEqual("eSIyd+KOG3s=", Utils.FileHash(filename));
@ -86,7 +87,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void GoogleDriveTests()
public async Task GoogleDriveTests()
{
var ini = @"[General]
directURL=https://drive.google.com/file/d/1grLRTrpHxlg7VPxATTFNfq2OkU_Plvh_/view?usp=sharing";
@ -102,13 +103,13 @@ namespace Wabbajack.Test
((GoogleDriveDownloader.State)url_state).Id);
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { GoogleIDs = new List<string> { "1grLRTrpHxlg7VPxATTFNfq2OkU_Plvh_" } }));
Assert.IsFalse(converted.IsWhitelisted(new ServerWhitelist { GoogleIDs = new List<string>()}));
converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
await converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
Assert.AreEqual("eSIyd+KOG3s=", Utils.FileHash(filename));
@ -116,7 +117,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void HttpDownload()
public async Task HttpDownload()
{
var ini = @"[General]
directURL=http://build.wabbajack.org/WABBAJACK_TEST_FILE.txt";
@ -131,13 +132,13 @@ namespace Wabbajack.Test
((HTTPDownloader.State)url_state).Url);
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string> { "http://build.wabbajack.org/" } }));
Assert.IsFalse(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string>() }));
converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
await converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
Assert.AreEqual("eSIyd+KOG3s=", Utils.FileHash(filename));
@ -145,7 +146,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void ManualDownload()
public async Task ManualDownload()
{
var ini = @"[General]
manualURL=http://build.wabbajack.org/WABBAJACK_TEST_FILE.zip";
@ -155,12 +156,12 @@ namespace Wabbajack.Test
Assert.IsNotNull(state);
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string> { "http://build.wabbajack.org/" } }));
converted.Download(new Archive { Name = "WABBAJACK_TEST_FILE.zip", Size = 20, Hash = "eSIyd+KOG3s="}, filename);
await converted.Download(new Archive { Name = "WABBAJACK_TEST_FILE.zip", Size = 20, Hash = "eSIyd+KOG3s="}, filename);
Assert.AreEqual("eSIyd+KOG3s=", Utils.FileHash(filename));
@ -168,7 +169,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void MediaFireDownload()
public async Task MediaFireDownload()
{
var ini = @"[General]
directURL=http://www.mediafire.com/file/agiqzm1xwebczpx/WABBAJACK_TEST_FILE.txt";
@ -184,21 +185,21 @@ namespace Wabbajack.Test
((MediaFireDownloader.State) url_state).Url);
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist
{AllowedPrefixes = new List<string> {"http://www.mediafire.com/file/agiqzm1xwebczpx/"}}));
Assert.IsFalse(converted.IsWhitelisted(new ServerWhitelist {AllowedPrefixes = new List<string>()}));
converted.Download(new Archive {Name = "Media Fire Test.txt"}, filename);
await converted.Download(new Archive {Name = "Media Fire Test.txt"}, filename);
Assert.AreEqual(File.ReadAllText(filename), "Cheese for Everyone!");
}
[TestMethod]
public void NexusDownload()
public async Task NexusDownload()
{
var old_val = NexusApiClient.UseLocalCache;
try
@ -215,14 +216,14 @@ namespace Wabbajack.Test
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
// Exercise the cache code
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string> () }));
converted.Download(new Archive { Name = "SkyUI.7z" }, filename);
await converted.Download(new Archive { Name = "SkyUI.7z" }, filename);
Assert.AreEqual(filename.FileHash(), "dF2yafV2Oks=");
@ -234,7 +235,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void ModDbTests()
public async Task ModDbTests()
{
var ini = @"[General]
directURL=https://www.moddb.com/downloads/start/124908?referer=https%3A%2F%2Fwww.moddb.com%2Fmods%2Fautopause";
@ -250,12 +251,12 @@ namespace Wabbajack.Test
((ModDBDownloader.State)url_state).Url);
var converted = state.ViaJSON();
Assert.IsTrue(converted.Verify());
Assert.IsTrue(await converted.Verify());
var filename = Guid.NewGuid().ToString();
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string>() }));
converted.Download(new Archive { Name = "moddbtest.7z" }, filename);
await converted.Download(new Archive { Name = "moddbtest.7z" }, filename);
Assert.AreEqual("2lZt+1h6wxM=", filename.FileHash());
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common;
@ -43,12 +44,12 @@ namespace Wabbajack.Test
}
[TestMethod]
public void CreateModlist()
public async Task CreateModlist()
{
var profile = utils.AddProfile("Default");
var mod = utils.AddMod();
DownloadAndInstall(
await DownloadAndInstall(
"https://github.com/ModOrganizer2/modorganizer/releases/download/v2.2.1/Mod.Organizer.2.2.1.7z",
"Mod.Organizer.2.2.1.7z",
utils.MO2Folder);
@ -59,7 +60,7 @@ namespace Wabbajack.Test
"directURL=https://github.com/ModOrganizer2/modorganizer/releases/download/v2.2.1/Mod.Organizer.2.2.1.7z"
});
DownloadAndInstall(Game.SkyrimSpecialEdition, 12604, "SkyUI");
await DownloadAndInstall(Game.SkyrimSpecialEdition, 12604, "SkyUI");
utils.Configure();
@ -81,13 +82,13 @@ namespace Wabbajack.Test
}
private void DownloadAndInstall(string url, string filename, string mod_name = null)
private async Task DownloadAndInstall(string url, string filename, string mod_name = null)
{
var src = Path.Combine(DOWNLOAD_FOLDER, filename);
if (!File.Exists(src))
{
var state = DownloadDispatcher.ResolveArchive(url);
state.Download(new Archive() { Name = "Unknown"}, src);
await state.Download(new Archive() { Name = "Unknown"}, src);
}
if (!Directory.Exists(utils.DownloadsFolder))
@ -97,11 +98,11 @@ namespace Wabbajack.Test
File.Copy(src, Path.Combine(utils.DownloadsFolder, filename));
FileExtractor.ExtractAll(Queue, src,
await FileExtractor.ExtractAll(Queue, src,
mod_name == null ? utils.MO2Folder : Path.Combine(utils.ModsFolder, mod_name));
}
private void DownloadAndInstall(Game game, int modid, string mod_name)
private async Task DownloadAndInstall(Game game, int modid, string mod_name)
{
utils.AddMod(mod_name);
var client = new NexusApiClient();
@ -132,7 +133,7 @@ namespace Wabbajack.Test
var dest = Path.Combine(utils.DownloadsFolder, file.file_name);
File.Copy(src, dest);
FileExtractor.ExtractAll(Queue, src, Path.Combine(utils.ModsFolder, mod_name));
await FileExtractor.ExtractAll(Queue, src, Path.Combine(utils.ModsFolder, mod_name));
File.WriteAllText(dest + ".meta", ini);
}

View File

@ -1,4 +1,5 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.ModListRegistry;
@ -16,7 +17,7 @@ namespace Wabbajack.Test
}
[TestMethod]
public void VerifyLogoURLs()
public async Task VerifyLogoURLs()
{
var modlists = ModlistMetadata.LoadFromGithub();
@ -24,7 +25,7 @@ namespace Wabbajack.Test
{
var logo_state = DownloadDispatcher.ResolveArchive(modlist.ImageUri);
Assert.IsNotNull(logo_state);
Assert.IsTrue(logo_state.Verify(), $"{modlist.ImageUri} is not valid");
Assert.IsTrue(await logo_state.Verify(), $"{modlist.ImageUri} is not valid");
}
}
}

View File

@ -84,7 +84,7 @@ namespace Wabbajack
queue.QueueTask(async () =>
{
var downloader = DownloadDispatcher.ResolveArchive(Metadata.Links.Download);
downloader.Download(new Archive{ Name = Metadata.Title, Size = Metadata.DownloadMetadata?.Size ?? 0}, Location);
await downloader.Download(new Archive{ Name = Metadata.Title, Size = Metadata.DownloadMetadata?.Size ?? 0}, Location);
Location.FileHashCached();
sub.Dispose();
tcs.SetResult(true);