2019-10-12 21:37:16 +00:00
using System ;
using System.Collections.Generic ;
2020-05-20 03:25:41 +00:00
using System.IO ;
2019-10-12 21:37:16 +00:00
using System.Linq ;
2020-05-20 03:25:41 +00:00
using System.Net.Http ;
2019-12-07 03:50:50 +00:00
using System.Threading.Tasks ;
2020-02-27 13:46:34 +00:00
using Alphaleonis.Win32.Filesystem ;
2019-11-04 23:21:58 +00:00
using Wabbajack.Common ;
2020-02-08 23:53:11 +00:00
using Wabbajack.Lib.Downloaders.UrlDownloaders ;
2019-10-12 21:37:16 +00:00
2019-10-16 03:10:34 +00:00
namespace Wabbajack.Lib.Downloaders
2019-10-12 21:37:16 +00:00
{
public static class DownloadDispatcher
{
2019-10-13 14:18:21 +00:00
public static readonly List < IDownloader > Downloaders = new List < IDownloader > ( )
2019-10-12 21:37:16 +00:00
{
2019-12-14 17:30:52 +00:00
new GameFileSourceDownloader ( ) ,
2019-10-12 21:37:16 +00:00
new MegaDownloader ( ) ,
new DropboxDownloader ( ) ,
new GoogleDriveDownloader ( ) ,
2019-10-12 22:31:07 +00:00
new ModDBDownloader ( ) ,
2019-10-12 22:15:20 +00:00
new NexusDownloader ( ) ,
2019-10-24 01:19:11 +00:00
new MediaFireDownloader ( ) ,
2019-12-08 17:00:22 +00:00
new LoversLabDownloader ( ) ,
2020-01-06 00:21:05 +00:00
new VectorPlexusDownloader ( ) ,
2020-01-06 15:08:54 +00:00
new DeadlyStreamDownloader ( ) ,
2020-01-29 04:17:24 +00:00
new BethesdaNetDownloader ( ) ,
2020-01-22 09:50:09 +00:00
new TESAllianceDownloader ( ) ,
2020-05-28 23:44:58 +00:00
new TESAllDownloader ( ) ,
2020-03-03 21:53:29 +00:00
new YouTubeDownloader ( ) ,
2020-05-09 22:16:16 +00:00
new WabbajackCDNDownloader ( ) ,
2020-05-28 22:31:01 +00:00
new YandexDownloader ( ) ,
2019-11-07 00:29:53 +00:00
new HTTPDownloader ( ) ,
2019-10-12 22:31:07 +00:00
new ManualDownloader ( ) ,
2019-10-12 21:37:16 +00:00
} ;
2020-02-08 23:53:11 +00:00
public static readonly List < IUrlInferencer > Inferencers = new List < IUrlInferencer > ( )
{
2020-03-03 21:53:29 +00:00
new BethesdaNetInferencer ( ) ,
2020-05-09 22:16:16 +00:00
new YoutubeInferencer ( ) ,
new WabbajackCDNInfluencer ( )
2020-02-08 23:53:11 +00:00
} ;
2019-10-12 22:31:07 +00:00
private static readonly Dictionary < Type , IDownloader > IndexedDownloaders ;
2019-10-12 21:37:16 +00:00
static DownloadDispatcher ( )
{
2019-10-12 22:31:07 +00:00
IndexedDownloaders = Downloaders . ToDictionary ( d = > d . GetType ( ) ) ;
2019-10-12 21:37:16 +00:00
}
2020-04-10 01:29:53 +00:00
public static async Task < AbstractDownloadState ? > Infer ( Uri uri )
2020-02-08 23:53:11 +00:00
{
2020-03-03 21:53:29 +00:00
foreach ( var inf in Inferencers )
{
var state = await inf . Infer ( uri ) ;
if ( state ! = null )
return state ;
}
return null ;
2020-02-08 23:53:11 +00:00
}
2019-12-08 17:00:22 +00:00
public static T GetInstance < T > ( ) where T : IDownloader
2019-10-12 21:37:16 +00:00
{
2019-12-08 17:00:22 +00:00
var inst = ( T ) IndexedDownloaders [ typeof ( T ) ] ;
return inst ;
2019-10-12 21:37:16 +00:00
}
2020-04-03 03:57:59 +00:00
public static async Task < AbstractDownloadState > ResolveArchive ( dynamic ini , bool quickMode = false )
2019-10-12 21:37:16 +00:00
{
2020-04-03 03:57:59 +00:00
var states = await Task . WhenAll ( Downloaders . Select ( d = > ( Task < AbstractDownloadState > ) d . GetDownloaderState ( ini , quickMode ) ) ) ;
2019-12-07 03:50:50 +00:00
return states . FirstOrDefault ( result = > result ! = null ) ;
2019-10-12 21:37:16 +00:00
}
2019-10-16 11:44:45 +00:00
/// <summary>
/// Reduced version of Resolve archive that requires less information, but only works
/// with a single URL string
/// </summary>
/// <param name="ini"></param>
/// <returns></returns>
2020-04-10 01:29:53 +00:00
public static AbstractDownloadState ? ResolveArchive ( string url )
2019-10-16 11:44:45 +00:00
{
return Downloaders . OfType < IUrlDownloader > ( ) . Select ( d = > d . GetDownloaderState ( url ) ) . FirstOrDefault ( result = > result ! = null ) ;
}
2020-04-10 19:06:32 +00:00
public static async Task PrepareAll ( IEnumerable < AbstractDownloadState > states )
2019-11-04 23:21:58 +00:00
{
2020-04-10 19:06:32 +00:00
await Task . WhenAll ( states . Select ( s = > s . GetDownloader ( ) . GetType ( ) )
2019-11-04 23:21:58 +00:00
. Distinct ( )
2020-04-10 19:06:32 +00:00
. Select ( t = > Downloaders . First ( d = > d . GetType ( ) = = t ) . Prepare ( ) ) ) ;
2019-11-04 23:21:58 +00:00
}
2020-02-27 13:46:34 +00:00
2020-03-25 22:30:43 +00:00
public static async Task < bool > DownloadWithPossibleUpgrade ( Archive archive , AbsolutePath destination )
2020-02-27 13:46:34 +00:00
{
var success = await Download ( archive , destination ) ;
if ( success )
{
await destination . FileHashCachedAsync ( ) ;
return true ;
}
2020-05-20 03:25:41 +00:00
if ( ! ( archive . State is IUpgradingState ) )
2020-02-27 13:46:34 +00:00
{
2020-05-20 03:25:41 +00:00
Utils . Log ( $"Download failed for {archive.Name} and no upgrade from this download source is possible" ) ;
2020-02-27 13:46:34 +00:00
return false ;
}
2020-05-20 03:25:41 +00:00
var upgrade = ( IUpgradingState ) archive . State ;
2020-02-27 13:46:34 +00:00
2020-05-20 03:25:41 +00:00
Utils . Log ( $"Trying to find solution to broken download for {archive.Name}" ) ;
var result = await upgrade . FindUpgrade ( archive ) ;
if ( result = = default )
2020-02-27 13:46:34 +00:00
{
2020-05-20 03:25:41 +00:00
Utils . Log (
$"No solution for broken download {archive.Name} {archive.State.PrimaryKeyString} could be found" ) ;
return false ;
2020-02-27 13:46:34 +00:00
2020-05-20 03:25:41 +00:00
}
2020-05-30 21:05:26 +00:00
Utils . Log ( $"Looking for patch for {archive.Name} ({(long)archive.Hash} {archive.Hash.ToHex()} -> {(long)result.Archive!.Hash} {result.Archive!.Hash.ToHex()})" ) ;
2020-05-20 03:25:41 +00:00
var patchResult = await ClientAPI . GetModUpgrade ( archive , result . Archive ! ) ;
2020-02-27 13:46:34 +00:00
2020-05-20 03:25:41 +00:00
Utils . Log ( $"Downloading patch for {archive.Name}" ) ;
var tempFile = new TempFile ( ) ;
using var response = await ( new Common . Http . Client ( ) ) . GetAsync ( patchResult ) ;
await tempFile . Path . WriteAllAsync ( await response . Content . ReadAsStreamAsync ( ) ) ;
response . Dispose ( ) ;
Utils . Log ( $"Applying patch to {archive.Name}" ) ;
2020-05-25 14:31:56 +00:00
await using ( var src = await result . NewFile . Path . OpenShared ( ) )
2020-05-25 17:34:25 +00:00
await using ( var final = await destination . Create ( ) )
2020-02-27 13:46:34 +00:00
{
2020-05-25 14:31:56 +00:00
Utils . ApplyPatch ( src , ( ) = > tempFile . Path . OpenShared ( ) . Result , final ) ;
2020-02-27 13:46:34 +00:00
}
2020-05-20 03:25:41 +00:00
var hash = await destination . FileHashCachedAsync ( ) ;
if ( hash ! = archive . Hash & & archive . Hash ! = default )
{
Utils . Log ( "Archive hash didn't match after patching" ) ;
return false ;
}
2020-02-27 13:46:34 +00:00
return true ;
}
2020-03-25 22:30:43 +00:00
private static async Task < bool > Download ( Archive archive , AbsolutePath destination )
2020-02-27 13:46:34 +00:00
{
try
{
var result = await archive . State . Download ( archive , destination ) ;
if ( ! result ) return false ;
2020-03-27 03:10:23 +00:00
if ( ! archive . Hash . IsValid ) return true ;
2020-02-27 13:46:34 +00:00
var hash = await destination . FileHashCachedAsync ( ) ;
if ( hash = = archive . Hash ) return true ;
Utils . Log ( $"Hashed download is incorrect" ) ;
return false ;
}
2020-03-28 13:33:39 +00:00
catch ( Exception )
2020-02-27 13:46:34 +00:00
{
return false ;
}
}
2019-10-12 21:37:16 +00:00
}
}