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 ;
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 ;
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 ;
private SemaphoreSlim _lock = new SemaphoreSlim ( 1 ) ;
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 ( ) ;
var info = await client . GetModInfo ( game , general . modID ) ;
2019-10-12 21:37:16 +00:00
return new State
{
GameName = general . gameName ,
FileID = general . fileID ,
ModID = general . modID ,
Version = general . version ? ? "0.0.0.0" ,
Author = info . author ,
UploadedBy = info . uploaded_by ,
UploaderProfile = info . uploaded_users_profile_url ,
ModName = info . name ,
SlideShowPic = info . picture_url ,
2019-11-08 01:36:01 +00:00
NexusURL = NexusApiUtils . GetModURL ( game , info . mod_id ) ,
2019-10-12 21:37:16 +00:00
Summary = info . summary ,
Adult = info . contains_adult_content
} ;
}
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
{
2019-12-14 05:46:20 +00:00
await _lock . WaitAsync ( ) ;
try
{
// Could have become prepared while we waited for the lock
if ( ! _prepared )
{
_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 ;
}
}
}
finally
{
_lock . Release ( ) ;
}
2019-10-12 22:15:20 +00:00
}
2019-12-14 05:46:20 +00:00
_prepared = true ;
if ( _status . is_premium ) return ;
Utils . ErrorThrow ( new UnconvertedError ( $"Automated installs with Wabbajack requires a premium nexus account. {await _client.Username()} is not a premium account." ) ) ;
2019-10-12 22:15:20 +00:00
}
2019-10-12 21:37:16 +00:00
public class State : AbstractDownloadState
{
2020-01-01 16:19:06 +00:00
public string Author { get ; set ; }
public string FileID { get ; set ; }
public string GameName { get ; set ; }
public string ModID { get ; set ; }
public string UploadedBy { get ; set ; }
public string UploaderProfile { get ; set ; }
public string Version { get ; set ; }
public string SlideShowPic { get ; set ; }
public string ModName { get ; set ; }
public string NexusURL { get ; set ; }
public string Summary { get ; set ; }
public bool Adult { get ; set ; }
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-01-13 21:11:07 +00:00
Utils . Log ( $"{ModName} - {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
}
}
}