using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; using Wabbajack.Common; using Wabbajack.Lib.Downloaders.UrlDownloaders; namespace Wabbajack.Lib.Downloaders { public static class DownloadDispatcher { public static readonly List Downloaders = new List() { new GameFileSourceDownloader(), new MegaDownloader(), new DropboxDownloader(), new GoogleDriveDownloader(), new ModDBDownloader(), new NexusDownloader(), new MediaFireDownloader(), new LoversLabDownloader(), new VectorPlexusDownloader(), new DeadlyStreamDownloader(), new BethesdaNetDownloader(), new TESAllianceDownloader(), new YouTubeDownloader(), new WabbajackCDNDownloader(), new HTTPDownloader(), new ManualDownloader(), }; public static readonly List Inferencers = new List() { new BethesdaNetInferencer(), new YoutubeInferencer(), new WabbajackCDNInfluencer() }; private static readonly Dictionary IndexedDownloaders; static DownloadDispatcher() { IndexedDownloaders = Downloaders.ToDictionary(d => d.GetType()); } public static async Task Infer(Uri uri) { foreach (var inf in Inferencers) { var state = await inf.Infer(uri); if (state != null) return state; } return null; } public static T GetInstance() where T : IDownloader { var inst = (T)IndexedDownloaders[typeof(T)]; return inst; } public static async Task ResolveArchive(dynamic ini, bool quickMode = false) { var states = await Task.WhenAll(Downloaders.Select(d => (Task)d.GetDownloaderState(ini, quickMode))); return states.FirstOrDefault(result => result != null); } /// /// Reduced version of Resolve archive that requires less information, but only works /// with a single URL string /// /// /// public static AbstractDownloadState? ResolveArchive(string url) { return Downloaders.OfType().Select(d => d.GetDownloaderState(url)).FirstOrDefault(result => result != null); } public static async Task PrepareAll(IEnumerable states) { await Task.WhenAll(states.Select(s => s.GetDownloader().GetType()) .Distinct() .Select(t => Downloaders.First(d => d.GetType() == t).Prepare())); } public static async Task DownloadWithPossibleUpgrade(Archive archive, AbsolutePath destination) { var success = await Download(archive, destination); if (success) { await destination.FileHashCachedAsync(); return true; } Utils.Log($"Download failed, looking for upgrade"); var upgrade = await ClientAPI.GetModUpgrade(archive.Hash); if (upgrade == null) { Utils.Log($"No upgrade found for {archive.Hash}"); return false; } Utils.Log($"Upgrading via {upgrade.State.PrimaryKeyString}"); Utils.Log($"Upgrading {archive.Hash}"); var upgradePath = destination.Parent.Combine("_Upgrade_" + archive.Name); var upgradeResult = await Download(upgrade, upgradePath); if (!upgradeResult) return false; var patchName = $"{archive.Hash.ToHex()}_{upgrade.Hash.ToHex()}"; var patchPath = destination.Parent.Combine("_Patch_" + patchName); var patchState = new Archive(new HTTPDownloader.State($"https://wabbajackcdn.b-cdn.net/updates/{patchName}")) { Name = patchName, }; var patchResult = await Download(patchState, patchPath); if (!patchResult) return false; Utils.Status($"Applying Upgrade to {archive.Hash}"); await using (var patchStream = patchPath.OpenRead()) await using (var srcStream = upgradePath.OpenRead()) await using (var destStream = destination.Create()) { OctoDiff.Apply(srcStream, patchStream, destStream); } await destination.FileHashCachedAsync(); return true; } private static async Task Download(Archive archive, AbsolutePath destination) { try { var result = await archive.State.Download(archive, destination); if (!result) return false; if (!archive.Hash.IsValid) return true; var hash = await destination.FileHashCachedAsync(); if (hash == archive.Hash) return true; Utils.Log($"Hashed download is incorrect"); return false; } catch (Exception) { return false; } } } }