mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Port ModListVM
This commit is contained in:
parent
33161f9817
commit
475e44c895
@ -4,7 +4,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Wabbajack"
|
||||
ShutdownMode="OnExplicitShutdown"
|
||||
StartupUri="Views\MainWindow.xaml">
|
||||
Startup="OnStartup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
@ -1,26 +1,33 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
public partial class App
|
||||
{
|
||||
private readonly ServiceProvider _serviceProvider;
|
||||
public App()
|
||||
{
|
||||
Consts.LogsFolder = LauncherUpdater.CommonFolder.Value.Combine("logs");
|
||||
Consts.LogsFolder.CreateDirectory();
|
||||
|
||||
LoggingSettings.LogToFile = true;
|
||||
Utils.InitializeLogging().Wait();
|
||||
|
||||
CLIOld.ParseOptions(Environment.GetCommandLineArgs());
|
||||
if (CLIArguments.Help)
|
||||
CLIOld.DisplayHelpText();
|
||||
var services = new ServiceCollection();
|
||||
ConfigureServices(services);
|
||||
_serviceProvider = services.BuildServiceProvider();
|
||||
}
|
||||
private void ConfigureServices(ServiceCollection services)
|
||||
{
|
||||
services.AddOSIntegrated();
|
||||
services.AddSingleton<MainWindow>();
|
||||
}
|
||||
private void OnStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
var mainWindow = _serviceProvider.GetService<MainWindow>();
|
||||
mainWindow!.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
using Wabbajack.Compiler;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Wabbajack.Compiler;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Paths;
|
||||
|
@ -3,6 +3,7 @@ using DynamicData.Binding;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reactive.Linq;
|
||||
@ -13,6 +14,8 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Extensions;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
@ -49,6 +52,24 @@ namespace Wabbajack
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void OpenWebsite(Uri url)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {url}")
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
});
|
||||
}
|
||||
|
||||
public static void OpenFolder(AbsolutePath path)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(AbsolutePath.WindowsFolder.Combine("explorer.exe").ToString(), path.ToString())
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static AbsolutePath OpenFileDialog(string filter, string initialDirectory = null)
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ using Wabbajack.Lib.Extensions;
|
||||
using Wabbajack.Lib.Interventions;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.RateLimiter;
|
||||
using Wabbajack.View_Models;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
|
@ -4,25 +4,36 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Installer;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.ModListRegistry;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
using Consts = Wabbajack.Lib.Consts;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class ModListVM : ViewModel
|
||||
{
|
||||
private readonly DTOSerializer _dtos;
|
||||
private readonly ILogger<ModListVM> _logger;
|
||||
public ModList SourceModList { get; private set; }
|
||||
public ModlistMetadata SourceModListMetadata { get; private set; }
|
||||
public Exception Error { get; }
|
||||
|
||||
[Reactive]
|
||||
public Exception Error { get; set; }
|
||||
public AbsolutePath ModListPath { get; }
|
||||
public string Name => SourceModList?.Name;
|
||||
public string Readme => SourceModList?.Readme;
|
||||
public string Author => SourceModList?.Author;
|
||||
public string Description => SourceModList?.Description;
|
||||
public Uri Website => SourceModList?.Website;
|
||||
public ModManager ModManager => SourceModList?.ModManager ?? ModManager.MO2;
|
||||
public Version Version => SourceModList?.Version;
|
||||
public Version WabbajackVersion => SourceModList?.WabbajackVersion;
|
||||
public bool IsNSFW => SourceModList?.IsNSFW ?? false;
|
||||
@ -32,30 +43,37 @@ namespace Wabbajack
|
||||
// and the cached image will automatically be released when the last interested party is gone.
|
||||
public IObservable<BitmapImage> ImageObservable { get; }
|
||||
|
||||
public ModListVM(AbsolutePath modListPath)
|
||||
public ModListVM(ILogger<ModListVM> logger, AbsolutePath modListPath, DTOSerializer dtos)
|
||||
{
|
||||
_dtos = dtos;
|
||||
_logger = logger;
|
||||
|
||||
ModListPath = modListPath;
|
||||
try
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
SourceModList = AInstaller.LoadFromFile(modListPath);
|
||||
var metadataPath = modListPath.WithExtension(Consts.ModlistMetadataExtension);
|
||||
if (metadataPath.Exists)
|
||||
try
|
||||
{
|
||||
try
|
||||
SourceModList = await StandardInstaller.LoadFromFile(_dtos, modListPath);
|
||||
var metadataPath = modListPath.WithExtension(Ext.ModlistMetadataExtension);
|
||||
if (metadataPath.FileExists())
|
||||
{
|
||||
SourceModListMetadata = metadataPath.FromJson<ModlistMetadata>();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
SourceModListMetadata = null;
|
||||
try
|
||||
{
|
||||
SourceModListMetadata = await metadataPath.FromJson<ModlistMetadata>();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
SourceModListMetadata = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Error = ex;
|
||||
Utils.Error(ex, "Exception while loading the modlist!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Error = ex;
|
||||
_logger.LogError(ex, "Exception while loading the modlist!");
|
||||
}
|
||||
});
|
||||
|
||||
ImageObservable = Observable.Return(Unit.Default)
|
||||
// Download and retrieve bytes on background thread
|
||||
@ -64,7 +82,7 @@ namespace Wabbajack
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var fs = await ModListPath.OpenShared();
|
||||
await using var fs = ModListPath.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
using var ar = new ZipArchive(fs, ZipArchiveMode.Read);
|
||||
var ms = new MemoryStream();
|
||||
var entry = ar.GetEntry("modlist-image.png");
|
||||
@ -75,7 +93,7 @@ namespace Wabbajack
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Error(ex, $"Exception while caching Mod List image {Name}");
|
||||
_logger.LogError(ex, "Exception while caching Mod List image {Name}", Name);
|
||||
return default(MemoryStream);
|
||||
}
|
||||
})
|
||||
@ -90,7 +108,7 @@ namespace Wabbajack
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Error(ex, $"Exception while caching Mod List image {Name}");
|
||||
_logger.LogError(ex, "Exception while caching Mod List image {Name}", Name);
|
||||
return default(BitmapImage);
|
||||
}
|
||||
})
|
||||
@ -103,7 +121,7 @@ namespace Wabbajack
|
||||
public void OpenReadme()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Readme)) return;
|
||||
Utils.OpenWebsite(new Uri(Readme));
|
||||
UIUtils.OpenWebsite(new Uri(Readme));
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
@ -1,21 +1,15 @@
|
||||
using DynamicData;
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Media.Imaging;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.DTOs.DownloadStates;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.Extensions;
|
||||
|
||||
namespace Wabbajack
|
||||
namespace Wabbajack.View_Models
|
||||
{
|
||||
public class SlideShow : ViewModel
|
||||
{
|
||||
@ -86,7 +80,7 @@ namespace Wabbajack
|
||||
if (modList?.SourceModList?.Archives == null)
|
||||
{
|
||||
return Observable.Empty<IMetaState>()
|
||||
.ToObservableChangeSet(x => x.URL);
|
||||
.ToObservableChangeSet(x => x.LinkUrl);
|
||||
}
|
||||
return modList.SourceModList.Archives
|
||||
.Select(m => m.State)
|
||||
@ -128,7 +122,7 @@ namespace Wabbajack
|
||||
VisitURLCommand = ReactiveCommand.Create(
|
||||
execute: () =>
|
||||
{
|
||||
Utils.OpenWebsite(TargetMod.State.URL);
|
||||
UIUtils.OpenWebsite(TargetMod.State.URL);
|
||||
return Unit.Default;
|
||||
},
|
||||
canExecute: this.WhenAny(x => x.TargetMod.State.URL)
|
||||
|
@ -1,23 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using CefSharp;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.Interventions;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.WebAutomation;
|
||||
using WebSocketSharp;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
@ -25,9 +14,11 @@ namespace Wabbajack
|
||||
{
|
||||
public MainWindowVM MainWindow { get; }
|
||||
private AsyncLock _browserLock = new();
|
||||
private readonly ILogger<UserInterventionHandlers> _logger;
|
||||
|
||||
public UserInterventionHandlers(MainWindowVM mvm)
|
||||
public UserInterventionHandlers(ILogger<UserInterventionHandlers> logger, MainWindowVM mvm)
|
||||
{
|
||||
_logger = logger;
|
||||
MainWindow = mvm;
|
||||
}
|
||||
|
||||
@ -36,7 +27,7 @@ namespace Wabbajack
|
||||
var wait = await _browserLock.WaitAsync();
|
||||
var cancel = new CancellationTokenSource();
|
||||
var oldPane = MainWindow.ActivePane;
|
||||
using var vm = await WebBrowserVM.GetNew();
|
||||
using var vm = await WebBrowserVM.GetNew(_logger);
|
||||
MainWindow.NavigateTo(vm);
|
||||
vm.BackCommand = ReactiveCommand.Create(() =>
|
||||
{
|
||||
@ -55,7 +46,7 @@ namespace Wabbajack
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Error(ex);
|
||||
_logger.LogError(ex, "During Web browser job");
|
||||
intervention.Cancel();
|
||||
}
|
||||
finally
|
||||
@ -70,6 +61,7 @@ namespace Wabbajack
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
/*
|
||||
case RequestNexusAuthorization c:
|
||||
await WrapBrowserJob(c, async (vm, cancel) =>
|
||||
{
|
||||
@ -100,6 +92,7 @@ namespace Wabbajack
|
||||
|
||||
|
||||
break;
|
||||
*/
|
||||
case CriticalFailureIntervention c:
|
||||
MessageBox.Show(c.ExtendedDescription, c.ShortDescription, MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
@ -112,100 +105,6 @@ namespace Wabbajack
|
||||
throw new NotImplementedException($"No handler for {msg}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OAuthLogin(RequestOAuthLogin oa, WebBrowserVM vm, CancellationTokenSource cancel)
|
||||
{
|
||||
await vm.Driver.WaitForInitialized();
|
||||
vm.Instructions = $"Please log in and allow Wabbajack to access your {oa.SiteName} account";
|
||||
|
||||
var wrapper = new CefSharpWrapper(vm.Browser);
|
||||
var scopes = string.Join(" ", oa.Scopes);
|
||||
var state = Guid.NewGuid().ToString();
|
||||
|
||||
|
||||
var oldHandler = Helpers.SchemeHandler;
|
||||
Helpers.SchemeHandler = (browser, frame, _, request) =>
|
||||
{
|
||||
var req = new Uri(request.Url);
|
||||
Utils.LogStraightToFile($"Got Scheme callback {req}");
|
||||
var parsed = HttpUtility.ParseQueryString(req.Query);
|
||||
if (parsed.Contains("state"))
|
||||
{
|
||||
if (parsed.Get("state") != state)
|
||||
{
|
||||
Utils.Log("Bad OAuth state, state, this shouldn't happen");
|
||||
oa.Cancel();
|
||||
return new ResourceHandler();
|
||||
}
|
||||
}
|
||||
if (parsed.Contains("code"))
|
||||
{
|
||||
Helpers.SchemeHandler = oldHandler;
|
||||
oa.Resume(parsed.Get("code"));
|
||||
}
|
||||
else
|
||||
{
|
||||
oa.Cancel();
|
||||
}
|
||||
return new ResourceHandler();
|
||||
};
|
||||
|
||||
await wrapper.NavigateTo(new Uri(oa.AuthorizationEndpoint + $"?response_type=code&client_id={oa.ClientID}&state={state}&scope={scopes}"));
|
||||
|
||||
while (!oa.Task.IsCanceled && !oa.Task.IsCompleted && !cancel.IsCancellationRequested)
|
||||
await Task.Delay(250);
|
||||
}
|
||||
|
||||
private async Task HandleManualDownload(WebBrowserVM vm, CancellationTokenSource cancel, ManuallyDownloadFile manuallyDownloadFile)
|
||||
{
|
||||
var browser = new CefSharpWrapper(vm.Browser);
|
||||
vm.Instructions = $"Please locate and download {manuallyDownloadFile.State.Url}";
|
||||
|
||||
var result = new TaskCompletionSource<Uri>();
|
||||
|
||||
browser.DownloadHandler = uri =>
|
||||
{
|
||||
//var client = Helpers.GetClient(browser.GetCookies("").Result, browser.Location);
|
||||
result.SetResult(uri);
|
||||
};
|
||||
|
||||
await vm.Driver.WaitForInitialized();
|
||||
|
||||
await browser.NavigateTo(new Uri(manuallyDownloadFile.State.Url));
|
||||
|
||||
while (!cancel.IsCancellationRequested)
|
||||
{
|
||||
if (result.Task.IsCompleted)
|
||||
{
|
||||
var cookies = await Helpers.GetCookies();
|
||||
var referer = browser.Location;
|
||||
var client = Helpers.GetClient(cookies, referer);
|
||||
manuallyDownloadFile.Resume(result.Task.Result, client);
|
||||
break;
|
||||
}
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task HandleManualNexusDownload(WebBrowserVM vm, CancellationTokenSource cancel, ManuallyDownloadNexusFile manuallyDownloadNexusFile)
|
||||
{
|
||||
var state = manuallyDownloadNexusFile.State;
|
||||
var game = state.Game.MetaData();
|
||||
await vm.Driver.WaitForInitialized();
|
||||
IWebDriver browser = new CefSharpWrapper(vm.Browser);
|
||||
vm.Instructions = $"Click the download button to continue (get a NexusMods.com Premium account to automate this)";
|
||||
browser.DownloadHandler = uri =>
|
||||
{
|
||||
manuallyDownloadNexusFile.Resume(uri);
|
||||
browser.DownloadHandler = null;
|
||||
};
|
||||
var url = new Uri(@$"https://www.nexusmods.com/{game.NexusName}/mods/{state.ModID}?tab=files&file_id={state.FileID}");
|
||||
await browser.NavigateTo(url);
|
||||
|
||||
while (!cancel.IsCancellationRequested && !manuallyDownloadNexusFile.Task.IsCompleted) {
|
||||
await Task.Delay(250);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
@ -14,4 +15,10 @@ public static class AbsolutePathExtensions
|
||||
await using var fs = path.Open(FileMode.Open);
|
||||
return await fs.HashingCopy(Stream.Null, token ?? CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task<T> FromJson<T>(this AbsolutePath path, DTOSerializer? dtos = null)
|
||||
{
|
||||
await using var fs = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
return await fs.FromJson<T>(dtos);
|
||||
}
|
||||
}
|
@ -23,4 +23,5 @@ public static class Ext
|
||||
public static Extension CompilerSettings = new(".compiler_settings");
|
||||
public static Extension MO2CompilerSettings = new(".mo2_compiler_settings");
|
||||
public static Extension Temp = new(".temp");
|
||||
public static Extension ModlistMetadataExtension = new(".modlist_metadata");
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
|
||||
namespace Wabbajack.Common;
|
||||
|
||||
@ -57,6 +59,11 @@ public static class StreamExtensions
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
|
||||
public static async Task<T> FromJson<T>(this Stream stream, DTOSerializer? dtos = null)
|
||||
{
|
||||
return (await JsonSerializer.DeserializeAsync<T>(stream, dtos?.Options))!;
|
||||
}
|
||||
|
||||
public static async IAsyncEnumerable<string> ReadLinesAsync(this Stream stream)
|
||||
{
|
||||
using var sr = new StreamReader(stream);
|
||||
|
@ -10,4 +10,6 @@ public interface IMetaState
|
||||
Uri? ImageURL { get; set; }
|
||||
bool IsNSFW { get; set; }
|
||||
string? Description { get; set; }
|
||||
|
||||
Uri? LinkUrl { get; }
|
||||
}
|
Loading…
Reference in New Issue
Block a user