mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Allow manual Nexus downloads
This commit is contained in:
parent
d8500fd618
commit
6255ec224f
@ -60,6 +60,8 @@ namespace Wabbajack.Common
|
||||
public string MO2ArchiveName { get; internal set; }
|
||||
public Game Game { get; internal set; }
|
||||
public string NexusName { get; internal set; }
|
||||
// Nexus DB id for the game, used in some specific situations
|
||||
public long NexusGameId { get; internal set; }
|
||||
public string MO2Name { get; internal set; }
|
||||
public string GameLocationRegistryKey { get; internal set; }
|
||||
// to get steam ids: https://steamdb.info
|
||||
@ -211,6 +213,7 @@ namespace Wabbajack.Common
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.SkyrimSpecialEdition,
|
||||
NexusName = "skyrimspecialedition",
|
||||
NexusGameId = 1704,
|
||||
MO2Name = "Skyrim Special Edition",
|
||||
MO2ArchiveName = "skyrimse",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim Special Edition",
|
||||
|
@ -17,7 +17,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
public class NexusDownloader : IDownloader, INeedsLogin
|
||||
{
|
||||
private bool _prepared;
|
||||
private SemaphoreSlim _lock = new SemaphoreSlim(1);
|
||||
private AsyncLock _lock = new AsyncLock();
|
||||
private UserStatus _status;
|
||||
private NexusApiClient _client;
|
||||
|
||||
@ -94,9 +94,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
if (!_prepared)
|
||||
{
|
||||
await _lock.WaitAsync();
|
||||
try
|
||||
{
|
||||
using var _ = await _lock.Wait();
|
||||
// Could have become prepared while we waited for the lock
|
||||
if (!_prepared)
|
||||
{
|
||||
@ -108,18 +106,21 @@ namespace Wabbajack.Lib.Downloaders
|
||||
$"Authenticating for the Nexus failed. A nexus account is required to automatically download mods."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
|
||||
if (!await _client.IsPremium())
|
||||
{
|
||||
_lock.Release();
|
||||
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)
|
||||
{
|
||||
Utils.ErrorThrow(new UnconvertedError($"Aborting at the request of the user"));
|
||||
}
|
||||
}
|
||||
|
||||
_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."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class State : AbstractDownloadState
|
||||
|
@ -37,7 +37,7 @@ namespace Wabbajack.Lib.LibCefHelpers
|
||||
return container;
|
||||
}
|
||||
|
||||
public static async Task<Cookie[]> GetCookies(string domainEnding)
|
||||
public static async Task<Cookie[]> GetCookies(string domainEnding = "")
|
||||
{
|
||||
var manager = Cef.GetGlobalCookieManager();
|
||||
var visitor = new CookieVisitor();
|
||||
@ -85,8 +85,14 @@ namespace Wabbajack.Lib.LibCefHelpers
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
// does nothing, but kicks off the static constructor
|
||||
if (Inited) return;
|
||||
Inited = true;
|
||||
CefSettings settings = new CefSettings();
|
||||
settings.CachePath = Path.Combine(Directory.GetCurrentDirectory() + @"\CEF");
|
||||
Cef.Initialize(settings);
|
||||
}
|
||||
|
||||
public static bool Inited { get; set; }
|
||||
}
|
||||
|
||||
public static class ModuleInitializer
|
||||
|
@ -14,6 +14,7 @@ using Wabbajack.Lib.Downloaders;
|
||||
using WebSocketSharp;
|
||||
using static Wabbajack.Lib.NexusApi.NexusApiUtils;
|
||||
using System.Threading;
|
||||
using Wabbajack.Lib.Exceptions;
|
||||
using Wabbajack.Lib.WebAutomation;
|
||||
|
||||
namespace Wabbajack.Lib.NexusApi
|
||||
@ -261,8 +262,25 @@ namespace Wabbajack.Lib.NexusApi
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
|
||||
var url = $"https://api.nexusmods.com/v1/games/{ConvertGameName(archive.GameName)}/mods/{archive.ModID}/files/{archive.FileID}/download_link.json";
|
||||
try
|
||||
{
|
||||
return (await Get<List<DownloadLink>>(url)).First().URI;
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Utils.Log($"Requesting manual download for {archive.ModName}");
|
||||
return (await Utils.Log(await ManuallyDownloadNexusFile.Create(archive)).Task).ToString();
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
Utils.Error(ex, "Manual cancellation of download");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<NexusFileInfo> GetFileInfo(NexusDownloader.State mod)
|
||||
{
|
||||
@ -328,5 +346,10 @@ namespace Wabbajack.Lib.NexusApi
|
||||
}
|
||||
set => _localCacheDir = value;
|
||||
}
|
||||
|
||||
public static Uri ManualDownloadUrl(NexusDownloader.State state)
|
||||
{
|
||||
return new Uri($"https://www.nexusmods.com/{GameRegistry.GetByMO2ArchiveName(state.GameName).NexusName}/mods/{state.ModID}?tab=files");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
Wabbajack.Lib/StatusMessages/ManuallyDownloadNexusFile.cs
Normal file
38
Wabbajack.Lib/StatusMessages/ManuallyDownloadNexusFile.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
|
||||
namespace Wabbajack.Lib
|
||||
{
|
||||
public class ManuallyDownloadNexusFile : AUserIntervention
|
||||
{
|
||||
public NexusDownloader.State State { get; }
|
||||
public override string ShortDescription { get; }
|
||||
public override string ExtendedDescription { get; }
|
||||
|
||||
private TaskCompletionSource<Uri> _tcs = new TaskCompletionSource<Uri>();
|
||||
public Task<Uri> Task => _tcs.Task;
|
||||
|
||||
private ManuallyDownloadNexusFile(NexusDownloader.State state)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
|
||||
public static async Task<ManuallyDownloadNexusFile> Create(NexusDownloader.State state)
|
||||
{
|
||||
var result = new ManuallyDownloadNexusFile(state);
|
||||
return result;
|
||||
}
|
||||
public override void Cancel()
|
||||
{
|
||||
_tcs.SetCanceled();
|
||||
}
|
||||
|
||||
public void Resume(Uri s)
|
||||
{
|
||||
_tcs.SetResult(s);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
|
||||
namespace Wabbajack.Lib.WebAutomation
|
||||
@ -11,7 +13,7 @@ namespace Wabbajack.Lib.WebAutomation
|
||||
public class CefSharpWrapper : IWebDriver
|
||||
{
|
||||
private IWebBrowser _browser;
|
||||
|
||||
public Action<Uri> DownloadHandler { get; set; }
|
||||
public CefSharpWrapper(IWebBrowser browser)
|
||||
{
|
||||
_browser = browser;
|
||||
@ -33,6 +35,7 @@ namespace Wabbajack.Lib.WebAutomation
|
||||
|
||||
_browser.LoadingStateChanged += handler;
|
||||
_browser.Load(uri.ToString());
|
||||
_browser.DownloadHandler = new DownloadHandler(this);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
@ -50,10 +53,34 @@ namespace Wabbajack.Lib.WebAutomation
|
||||
return Helpers.GetCookies(domainPrefix);
|
||||
}
|
||||
|
||||
private const string CefStateName = "cef-state";
|
||||
|
||||
public async Task WaitForInitialized()
|
||||
{
|
||||
while (!_browser.IsBrowserInitialized)
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
public class DownloadHandler : IDownloadHandler
|
||||
{
|
||||
private CefSharpWrapper _wrapper;
|
||||
|
||||
public DownloadHandler(CefSharpWrapper wrapper)
|
||||
{
|
||||
_wrapper = wrapper;
|
||||
}
|
||||
|
||||
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
|
||||
IBeforeDownloadCallback callback)
|
||||
{
|
||||
_wrapper.DownloadHandler(new Uri(downloadItem.Url));
|
||||
}
|
||||
|
||||
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
|
||||
IDownloadItemCallback callback)
|
||||
{
|
||||
callback.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,5 +12,6 @@ namespace Wabbajack.Lib.WebAutomation
|
||||
Task NavigateTo(Uri uri);
|
||||
Task<string> EvaluateJavaScript(string text);
|
||||
Task<Helpers.Cookie[]> GetCookies(string domainPrefix);
|
||||
public Action<Uri> DownloadHandler { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -75,11 +75,14 @@ namespace Wabbajack
|
||||
.Subscribe()
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
var singleton_lock = new AsyncLock();
|
||||
|
||||
Utils.LogMessages
|
||||
.OfType<IUserIntervention>()
|
||||
.ObserveOnGuiThread()
|
||||
.SelectTask(async msg =>
|
||||
{
|
||||
using var _ = await singleton_lock.Wait();
|
||||
try
|
||||
{
|
||||
await UserInterventionHandlers.Handle(msg);
|
||||
|
@ -6,6 +6,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using CefSharp;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
@ -66,6 +67,9 @@ namespace Wabbajack
|
||||
c.Resume(key);
|
||||
});
|
||||
break;
|
||||
case ManuallyDownloadNexusFile c:
|
||||
await WrapBrowserJob(msg, (vm, cancel) => HandleManualNexusDownload(vm, cancel, c));
|
||||
break;
|
||||
case RequestBethesdaNetLogin c:
|
||||
var data = await BethesdaNetDownloader.Login();
|
||||
c.Resume(data);
|
||||
@ -78,14 +82,6 @@ namespace Wabbajack
|
||||
c.Resume(data);
|
||||
});
|
||||
break;
|
||||
case YesNoIntervention c:
|
||||
var result = MessageBox.Show(c.ExtendedDescription, c.ShortDescription, MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
if (result == MessageBoxResult.Yes)
|
||||
c.Confirm();
|
||||
else
|
||||
c.Cancel();
|
||||
break;
|
||||
case CriticalFailureIntervention c:
|
||||
MessageBox.Show(c.ExtendedDescription, c.ShortDescription, MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
@ -98,6 +94,38 @@ namespace Wabbajack
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleManualNexusDownload(WebBrowserVM vm, CancellationTokenSource cancel, ManuallyDownloadNexusFile manuallyDownloadNexusFile)
|
||||
{
|
||||
var state = manuallyDownloadNexusFile.State;
|
||||
var game = GameRegistry.GetByMO2ArchiveName(state.GameName);
|
||||
var hrefs = new[]
|
||||
{
|
||||
$"/Core/Libs/Common/Widgets/DownloadPopUp?id={state.FileID}&game_id={game.NexusGameId}",
|
||||
$"https://www.nexusmods.com/{game.NexusName}/mods/{state.ModID}?tab=files&file_id={state.FileID}",
|
||||
$"/Core/Libs/Common/Widgets/ModRequirementsPopUp?id={state.FileID}&game_id={game.NexusGameId}"
|
||||
};
|
||||
await vm.Driver.WaitForInitialized();
|
||||
IWebDriver browser = new CefSharpWrapper(vm.Browser);
|
||||
vm.Instructions = $"Please Download {state.ModName} - {state.ModID} - {state.FileID}";
|
||||
browser.DownloadHandler = uri =>
|
||||
{
|
||||
manuallyDownloadNexusFile.Resume(uri);
|
||||
};
|
||||
await browser.NavigateTo(NexusApiClient.ManualDownloadUrl(manuallyDownloadNexusFile.State));
|
||||
|
||||
var buttin_href = $"/Core/Libs/Common/Widgets/DownloadPopUp?id={manuallyDownloadNexusFile.State.FileID}&game_id={Game.SkyrimSpecialEdition}";
|
||||
|
||||
while (!cancel.IsCancellationRequested && !manuallyDownloadNexusFile.Task.IsCompleted) {
|
||||
await browser.EvaluateJavaScript(
|
||||
@"Array.from(document.getElementsByClassName('accordion')).forEach(e => Array.from(e.children).forEach(c => c.style=''))");
|
||||
foreach (var href in hrefs)
|
||||
{
|
||||
const string style = "border-thickness: thick; border-color: #ff0000;border-width: medium;border-style: dashed;background-color: teal;padding: 7px";
|
||||
await browser.EvaluateJavaScript($"Array.from(document.querySelectorAll('.accordion a[href=\"{href}\"]')).forEach(e => {{e.scrollIntoView({{behavior: 'smooth', block: 'center', inline: 'nearest'}}); e.setAttribute('style', '{style}');}});");
|
||||
}
|
||||
await Task.Delay(250);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user