2019-10-12 21:37:16 +00:00
using System ;
2019-12-20 20:51:10 +00:00
using System.ComponentModel ;
2019-10-12 21:37:16 +00:00
using System.Linq ;
2020-01-07 05:44:05 +00:00
using System.Reactive ;
2019-12-20 20:51:10 +00:00
using System.Reactive.Linq ;
2019-12-14 05:46:20 +00:00
using System.Threading ;
2019-12-06 05:29:17 +00:00
using System.Threading.Tasks ;
2019-12-20 20:51:10 +00:00
using System.Windows.Input ;
2020-03-04 12:10:49 +00:00
using Ceras ;
2020-03-10 20:41:45 +00:00
using MongoDB.Bson.Serialization.Attributes ;
2019-12-20 20:51:10 +00:00
using ReactiveUI ;
2019-10-12 21:37:16 +00:00
using Wabbajack.Common ;
2019-12-04 04:12:08 +00:00
using Wabbajack.Common.StatusFeed.Errors ;
2019-10-16 03:10:34 +00:00
using Wabbajack.Lib.NexusApi ;
using Wabbajack.Lib.Validation ;
2020-03-04 12:10:49 +00:00
using Game = Wabbajack . Common . Game ;
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
{
2020-01-05 01:01:43 +00:00
public class NexusDownloader : IDownloader , INeedsLogin
2019-10-12 21:37:16 +00:00
{
2019-12-14 05:46:20 +00:00
private bool _prepared ;
2020-02-06 05:30:31 +00:00
private AsyncLock _lock = new AsyncLock ( ) ;
2019-12-14 05:46:20 +00:00
private UserStatus _status ;
private NexusApiClient _client ;
2019-12-20 20:51:10 +00:00
public IObservable < bool > IsLoggedIn = > Utils . HaveEncryptedJsonObservable ( "nexusapikey" ) ;
public string SiteName = > "Nexus Mods" ;
2020-01-06 04:49:11 +00:00
public IObservable < string > MetaInfo = > Observable . Return ( "" ) ;
2019-12-20 20:51:10 +00:00
public Uri SiteURL = > new Uri ( "https://www.nexusmods.com" ) ;
public Uri IconUri = > new Uri ( "https://www.nexusmods.com/favicon.ico" ) ;
2020-01-05 00:05:31 +00:00
2020-01-07 05:44:05 +00:00
public ReactiveCommand < Unit , Unit > TriggerLogin { get ; }
public ReactiveCommand < Unit , Unit > ClearLogin { get ; }
2019-12-20 20:51:10 +00:00
2020-01-05 00:05:31 +00:00
public NexusDownloader ( )
{
2020-01-05 13:14:53 +00:00
if ( CLIArguments . ApiKey ! = null )
{
CLIArguments . ApiKey . ToEcryptedJson ( "nexusapikey" ) ;
}
2020-01-05 00:05:31 +00:00
TriggerLogin = ReactiveCommand . CreateFromTask (
execute : ( ) = > Utils . CatchAndLog ( NexusApiClient . RequestAndCacheAPIKey ) ,
2020-01-09 03:22:49 +00:00
canExecute : IsLoggedIn . Select ( b = > ! b ) . ObserveOnGuiThread ( ) ) ;
2020-01-05 00:05:31 +00:00
ClearLogin = ReactiveCommand . Create (
execute : ( ) = > Utils . CatchAndLog ( ( ) = > Utils . DeleteEncryptedJson ( "nexusapikey" ) ) ,
2020-01-09 03:22:49 +00:00
canExecute : IsLoggedIn . ObserveOnGuiThread ( ) ) ;
2020-01-05 00:05:31 +00:00
}
2019-12-07 03:50:50 +00:00
public async Task < AbstractDownloadState > GetDownloaderState ( dynamic archiveINI )
2019-10-12 21:37:16 +00:00
{
2019-11-21 15:51:57 +00:00
var general = archiveINI ? . General ;
2019-10-12 21:37:16 +00:00
if ( general . modID ! = null & & general . fileID ! = null & & general . gameName ! = null )
{
2019-11-08 01:36:01 +00:00
var name = ( string ) general . gameName ;
2019-11-09 14:57:35 +00:00
var gameMeta = GameRegistry . GetByMO2ArchiveName ( name ) ;
var game = gameMeta ! = null ? GameRegistry . GetByMO2ArchiveName ( name ) . Game : GameRegistry . GetByNexusName ( name ) . Game ;
2019-12-07 03:50:50 +00:00
var client = await NexusApiClient . Get ( ) ;
2020-02-04 02:06:35 +00:00
dynamic info ;
try
{
info = await client . GetModInfo ( game , general . modID ) ;
}
catch ( Exception )
{
Utils . Error ( $"Error getting mod info for Nexus mod with {general.modID}" ) ;
throw ;
}
2020-03-04 12:10:49 +00:00
2019-10-12 21:37:16 +00:00
return new State
{
2020-03-04 12:10:49 +00:00
Name = NexusApiUtils . FixupSummary ( info . name ) ,
Author = NexusApiUtils . FixupSummary ( info . author ) ,
Version = general . version ? ? "0.0.0.0" ,
ImageURL = info . picture_url ,
IsNSFW = info . contains_adult_content ,
Description = NexusApiUtils . FixupSummary ( info . summary ) ,
2019-10-12 21:37:16 +00:00
GameName = general . gameName ,
ModID = general . modID ,
2020-03-04 12:10:49 +00:00
FileID = general . fileID
2019-10-12 21:37:16 +00:00
} ;
}
return null ;
}
2019-12-07 02:45:13 +00:00
public async Task Prepare ( )
2019-10-12 22:15:20 +00:00
{
2019-12-14 05:46:20 +00:00
if ( ! _prepared )
2019-10-12 22:15:20 +00:00
{
2020-02-06 05:30:31 +00:00
using var _ = await _lock . Wait ( ) ;
// Could have become prepared while we waited for the lock
if ( ! _prepared )
2019-12-14 05:46:20 +00:00
{
2020-02-06 05:30:31 +00:00
_client = await NexusApiClient . Get ( ) ;
_status = await _client . GetUserStatus ( ) ;
if ( ! _client . IsAuthenticated )
{
Utils . ErrorThrow ( new UnconvertedError (
$"Authenticating for the Nexus failed. A nexus account is required to automatically download mods." ) ) ;
return ;
}
if ( ! await _client . IsPremium ( ) )
2019-12-14 05:46:20 +00:00
{
2020-02-06 05:30:31 +00:00
var result = await Utils . Log ( new YesNoIntervention (
"Wabbajack can operate without a premium account, but downloads will be slower and the install process will require more user interactions (you will have to start each download by hand). Are you sure you wish to continue?" ,
"Continue without Premium?" ) ) . Task ;
if ( result = = ConfirmationIntervention . Choice . Abort )
2019-12-14 05:46:20 +00:00
{
2020-02-06 05:30:31 +00:00
Utils . ErrorThrow ( new UnconvertedError ( $"Aborting at the request of the user" ) ) ;
2019-12-14 05:46:20 +00:00
}
}
2020-02-26 05:05:33 +00:00
_prepared = true ;
2019-12-14 05:46:20 +00:00
}
2019-10-12 22:15:20 +00:00
}
}
2020-03-10 20:41:45 +00:00
[BsonIgnoreExtraElements]
2020-03-04 12:10:49 +00:00
public class State : AbstractDownloadState , IMetaState
2019-10-12 21:37:16 +00:00
{
2020-03-04 12:10:49 +00:00
public string URL = > $"http://nexusmods.com/{NexusApiUtils.ConvertGameName(GameName)}/mods/{ModID}" ;
public string Name { get ; set ; }
2020-01-01 16:19:06 +00:00
public string Author { get ; set ; }
2020-03-04 12:10:49 +00:00
public string Version { get ; set ; }
public string ImageURL { get ; set ; }
public bool IsNSFW { get ; set ; }
public string Description { get ; set ; }
public async Task < bool > LoadMetaData ( )
{
return true ;
}
2020-01-01 16:19:06 +00:00
public string GameName { get ; set ; }
public string ModID { get ; set ; }
2020-03-04 12:10:49 +00:00
public string FileID { get ; set ; }
2020-01-01 16:19:06 +00:00
public override object [ ] PrimaryKey { get = > new object [ ] { GameName , ModID , FileID } ; }
2019-10-12 21:37:16 +00:00
public override bool IsWhitelisted ( ServerWhitelist whitelist )
{
// Nexus files are always whitelisted
return true ;
}
2020-01-18 19:57:26 +00:00
public override async Task < bool > Download ( Archive a , string destination )
2019-10-12 21:37:16 +00:00
{
string url ;
try
{
2019-12-07 03:50:50 +00:00
var client = await NexusApiClient . Get ( ) ;
2020-01-02 00:11:13 +00:00
url = await client . GetNexusDownloadLink ( this ) ;
2019-10-12 21:37:16 +00:00
}
catch ( Exception ex )
{
2020-01-13 21:11:07 +00:00
Utils . Log ( $"{a.Name} - Error getting Nexus download URL - {ex.Message}" ) ;
2020-01-18 19:57:26 +00:00
return false ;
2019-10-12 21:37:16 +00:00
}
Utils . Log ( $"Downloading Nexus Archive - {a.Name} - {GameName} - {ModID} - {FileID}" ) ;
2020-01-18 19:57:26 +00:00
return await new HTTPDownloader . State
2019-10-12 21:37:16 +00:00
{
2020-02-02 12:15:29 +00:00
Url = url
2019-10-12 21:37:16 +00:00
} . Download ( a , destination ) ;
}
2020-01-13 22:55:55 +00:00
public override async Task < bool > Verify ( Archive a )
2019-10-12 21:37:16 +00:00
{
try
{
2019-12-01 14:44:01 +00:00
var gameMeta = GameRegistry . GetByMO2ArchiveName ( GameName ) ? ? GameRegistry . GetByNexusName ( GameName ) ;
if ( gameMeta = = null )
return false ;
var game = gameMeta . Game ;
if ( ! int . TryParse ( ModID , out var modID ) )
return false ;
2019-12-07 03:50:50 +00:00
var client = await NexusApiClient . Get ( ) ;
var modFiles = await client . GetModFiles ( game , modID ) ;
2019-12-01 14:44:01 +00:00
if ( ! ulong . TryParse ( FileID , out var fileID ) )
return false ;
var found = modFiles . files
. FirstOrDefault ( file = > file . file_id = = fileID & & file . category_name ! = null ) ;
2019-11-21 05:57:48 +00:00
return found ! = null ;
2019-10-12 21:37:16 +00:00
}
catch ( Exception ex )
{
2020-03-04 12:10:49 +00:00
Utils . Log ( $"{Name} - {GameName} - {ModID} - {FileID} - Error getting Nexus download URL - {ex}" ) ;
2019-10-12 21:37:16 +00:00
return false ;
}
}
2019-10-12 22:15:20 +00:00
public override IDownloader GetDownloader ( )
{
return DownloadDispatcher . GetInstance < NexusDownloader > ( ) ;
}
2019-10-12 22:54:25 +00:00
2020-02-02 12:23:07 +00:00
public override string GetManifestURL ( Archive a )
2020-02-02 12:15:29 +00:00
{
2020-02-02 12:23:07 +00:00
return $"http://nexusmods.com/{NexusApiUtils.ConvertGameName(GameName)}/mods/{ModID}" ;
2020-02-02 12:15:29 +00:00
}
2020-01-11 04:15:53 +00:00
public override string [ ] GetMetaIni ( )
{
return new [ ] { "[General]" , $"gameName={GameName}" , $"modID={ModID}" , $"fileID={FileID}" } ;
}
2019-10-12 21:37:16 +00:00
}
}
}