mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge branch 'master' into issue-606
This commit is contained in:
commit
ae5ac5fa13
@ -1,10 +1,12 @@
|
||||
### Changelog
|
||||
|
||||
#### Version - 1.1.0.0 -
|
||||
#### Version - Next
|
||||
* Binary Patching stores temporary and patch data on disk instead of memory (reducing memory usage)
|
||||
* Fix a memory leak with diffing progress reporting
|
||||
* Fix a bug with bad data in inferred game INI files.
|
||||
* Building BSAs now leverage Virtual Memory resulting in a 32x reduction in memory usage during installation
|
||||
* Fix a bug with bad data in inferred game INI files.
|
||||
* Added download support for YouTube
|
||||
* Slideshow can now display mods from non-Nexus sites
|
||||
* Building BSAs now leverage Virtual Memory resulting in a 32x reduction in memory usage during installation (#609)
|
||||
|
||||
#### Verison - 1.0.0.0 - 2/29/2020
|
||||
* 1.0, first non-beta release
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BunnyCDN.Net.Storage" Version="1.0.2" />
|
||||
<PackageReference Include="Dapper" Version="2.0.30" />
|
||||
<PackageReference Include="FluentFTP" Version="31.3.0" />
|
||||
<PackageReference Include="FluentFTP" Version="31.3.2" />
|
||||
<PackageReference Include="graphiql" Version="1.2.0" />
|
||||
<PackageReference Include="GraphQL" Version="3.0.0-preview-1352" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="2.2.0" />
|
||||
|
@ -106,5 +106,8 @@ namespace Wabbajack.Common
|
||||
|
||||
public static string LogsFolder = "logs";
|
||||
public static int MaxOldLogs = 50;
|
||||
|
||||
public static string SettingsFile => Path.Combine(LocalAppDataPath, "settings.json");
|
||||
public static byte SettingsVersion => 1;
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ namespace Wabbajack.Common
|
||||
|
||||
return sha.Hash.ToBase64();
|
||||
}
|
||||
|
||||
|
||||
public static string StringSHA256Hex(this string s)
|
||||
{
|
||||
var sha = new SHA256Managed();
|
||||
|
@ -84,6 +84,27 @@ namespace Wabbajack.Lib
|
||||
return id;
|
||||
}
|
||||
|
||||
public async Task<bool> GatherMetaData()
|
||||
{
|
||||
Utils.Log($"Getting meta data for {SelectedArchives.Count} archives");
|
||||
await SelectedArchives.PMap(Queue, async a =>
|
||||
{
|
||||
if (a.State is IMetaState metaState)
|
||||
{
|
||||
var b = await metaState.LoadMetaData();
|
||||
Utils.Log(b
|
||||
? $"Getting meta data for {a.Name} was successful!"
|
||||
: $"Getting meta data for {a.Name} failed!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Log($"Archive {a.Name} is not an AbstractMetaState!");
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ExportModList()
|
||||
{
|
||||
Utils.Log($"Exporting ModList to {ModListOutputFile}");
|
||||
|
@ -30,7 +30,7 @@ namespace Wabbajack.Lib
|
||||
typeof(PropertyFile), typeof(SteamMeta), typeof(SteamWorkshopDownloader), typeof(SteamWorkshopDownloader.State),
|
||||
typeof(LoversLabDownloader.State), typeof(GameFileSourceDownloader.State), typeof(VectorPlexusDownloader.State),
|
||||
typeof(DeadlyStreamDownloader.State), typeof(AFKModsDownloader.State), typeof(TESAllianceDownloader.State),
|
||||
typeof(TES3ArchiveState), typeof(TES3FileState), typeof(BethesdaNetDownloader.State), typeof(YouTubeDownloader)
|
||||
typeof(TES3ArchiveState), typeof(TES3FileState), typeof(BethesdaNetDownloader.State), typeof(YouTubeDownloader), typeof(IMetaState)
|
||||
},
|
||||
};
|
||||
Config.VersionTolerance.Mode = VersionToleranceMode.Standard;
|
||||
|
@ -7,10 +7,23 @@ using Wabbajack.Lib.Validation;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
public interface IMetaState
|
||||
{
|
||||
string URL { get; }
|
||||
string Name { get; set; }
|
||||
string Author { get; set; }
|
||||
string Version { get; set; }
|
||||
string ImageURL { get; set; }
|
||||
bool IsNSFW { get; set; }
|
||||
string Description { get; set; }
|
||||
|
||||
Task<bool> LoadMetaData();
|
||||
}
|
||||
|
||||
public abstract class AbstractDownloadState
|
||||
{
|
||||
|
||||
public static List<Type> KnownSubTypes = new List<Type>()
|
||||
public static List<Type> KnownSubTypes = new List<Type>
|
||||
{
|
||||
typeof(HTTPDownloader.State),
|
||||
typeof(GameFileSourceDownloader.State),
|
||||
|
@ -5,6 +5,7 @@ using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Validation;
|
||||
@ -15,8 +16,8 @@ namespace Wabbajack.Lib.Downloaders
|
||||
// IPS4 is the site used by LoversLab, VectorPlexus, etc. the general mechanics of each site are the
|
||||
// same, so we can fairly easily abstract the state.
|
||||
// Pass in the state type via TState
|
||||
public abstract class AbstractIPS4Downloader<TDownloader, TState> : AbstractNeedsLoginDownloader, IDownloader
|
||||
where TState : AbstractIPS4Downloader<TDownloader, TState>.State<TDownloader>, new()
|
||||
public abstract class AbstractIPS4Downloader<TDownloader, TState> : AbstractNeedsLoginDownloader, IDownloader
|
||||
where TState : AbstractIPS4Downloader<TDownloader, TState>.State<TDownloader>, new()
|
||||
where TDownloader : IDownloader
|
||||
{
|
||||
public override string SiteName { get; }
|
||||
@ -61,7 +62,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
}
|
||||
|
||||
|
||||
public class State<TDownloader> : AbstractDownloadState where TDownloader : IDownloader
|
||||
public class State<TDownloader> : AbstractDownloadState, IMetaState where TDownloader : IDownloader
|
||||
{
|
||||
public string FileID { get; set; }
|
||||
public string FileName { get; set; }
|
||||
@ -69,10 +70,12 @@ namespace Wabbajack.Lib.Downloaders
|
||||
private static bool IsHTTPS => Downloader.SiteURL.AbsolutePath.StartsWith("https://");
|
||||
private static string URLPrefix => IsHTTPS ? "https://" : "http://";
|
||||
|
||||
private static string Site => string.IsNullOrWhiteSpace(Downloader.SiteURL.Query)
|
||||
public static string Site => string.IsNullOrWhiteSpace(Downloader.SiteURL.Query)
|
||||
? $"{URLPrefix}{Downloader.SiteURL.Host}"
|
||||
: Downloader.SiteURL.ToString();
|
||||
|
||||
public static AbstractNeedsLoginDownloader Downloader => (AbstractNeedsLoginDownloader)(object)DownloadDispatcher.GetInstance<TDownloader>();
|
||||
|
||||
public override object[] PrimaryKey
|
||||
{
|
||||
get
|
||||
@ -100,13 +103,13 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
private async Task<Stream> ResolveDownloadStream()
|
||||
{
|
||||
var downloader = (AbstractNeedsLoginDownloader)(object)DownloadDispatcher.GetInstance<TDownloader>();
|
||||
//var downloader = (AbstractNeedsLoginDownloader)(object)DownloadDispatcher.GetInstance<TDownloader>();
|
||||
|
||||
TOP:
|
||||
var csrfurl = FileID == null
|
||||
? $"{Site}/files/file/{FileName}/?do=download"
|
||||
: $"{Site}/files/file/{FileName}/?do=download&r={FileID}";
|
||||
var html = await downloader.AuthedClient.GetStringAsync(csrfurl);
|
||||
var html = await Downloader.AuthedClient.GetStringAsync(csrfurl);
|
||||
|
||||
var pattern = new Regex("(?<=csrfKey=).*(?=[&\"\'])|(?<=csrfKey: \").*(?=[&\"\'])");
|
||||
var matches = pattern.Matches(html).Cast<Match>();
|
||||
@ -122,10 +125,10 @@ namespace Wabbajack.Lib.Downloaders
|
||||
: $"{Site}/files/file/{FileName}/{sep}do=download&r={FileID}&confirm=1&t=1&csrfKey={csrfKey}";
|
||||
|
||||
|
||||
var streamResult = await downloader.AuthedClient.GetAsync(url);
|
||||
var streamResult = await Downloader.AuthedClient.GetAsync(url);
|
||||
if (streamResult.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
Utils.ErrorThrow(new InvalidOperationException(), $"{downloader.SiteName} servers reported an error for file: {FileID}");
|
||||
Utils.ErrorThrow(new InvalidOperationException(), $"{Downloader.SiteName} servers reported an error for file: {FileID}");
|
||||
}
|
||||
|
||||
var contentType = streamResult.Content.Headers.ContentType;
|
||||
@ -138,7 +141,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
var secs = times.Download - times.CurrentTime;
|
||||
for (int x = 0; x < secs; x++)
|
||||
{
|
||||
Utils.Status($"Waiting for {secs} at the request of {downloader.SiteName}", Percent.FactoryPutInRange(x, secs));
|
||||
Utils.Status($"Waiting for {secs} at the request of {Downloader.SiteName}", Percent.FactoryPutInRange(x, secs));
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
streamResult.Dispose();
|
||||
@ -200,7 +203,19 @@ namespace Wabbajack.Lib.Downloaders
|
||||
};
|
||||
}
|
||||
|
||||
private static AbstractNeedsLoginDownloader Downloader => (AbstractNeedsLoginDownloader)(object)DownloadDispatcher.GetInstance<TDownloader>();
|
||||
// from IMetaState
|
||||
public string URL => $"{Site}/files/file/{FileName}";
|
||||
public string Name { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string ImageURL { get; set; }
|
||||
public virtual bool IsNSFW { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
public virtual async Task<bool> LoadMetaData()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractIPS4Downloader(Uri loginUri, string encryptedKeyName, string cookieDomain) :
|
||||
|
@ -1,24 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Windows.Input;
|
||||
using CefSharp;
|
||||
using ReactiveUI;
|
||||
using HtmlAgilityPack;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.Validation;
|
||||
using Wabbajack.Lib.WebAutomation;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
@ -29,7 +14,6 @@ namespace Wabbajack.Lib.Downloaders
|
||||
public override Uri SiteURL => new Uri("https://www.loverslab.com");
|
||||
public override Uri IconUri => new Uri("https://www.loverslab.com/favicon.ico");
|
||||
#endregion
|
||||
|
||||
public LoversLabDownloader() : base(new Uri("https://www.loverslab.com/login"),
|
||||
"loverslabcookies", "loverslab.com")
|
||||
{
|
||||
@ -46,8 +30,31 @@ namespace Wabbajack.Lib.Downloaders
|
||||
Utils.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public class State : State<LoversLabDownloader>
|
||||
{
|
||||
public override bool IsNSFW => true;
|
||||
|
||||
public override async Task<bool> LoadMetaData()
|
||||
{
|
||||
var html = await Downloader.AuthedClient.GetStringAsync(URL);
|
||||
var doc = new HtmlDocument();
|
||||
doc.LoadHtml(html);
|
||||
var node = doc.DocumentNode;
|
||||
Name = node.SelectNodes("//h1[@class='ipsType_pageTitle ipsContained_container']/span")?.First().InnerHtml;
|
||||
Author = node
|
||||
.SelectNodes(
|
||||
"//div[@class='ipsBox_alt']/div[@class='ipsPhotoPanel ipsPhotoPanel_tiny ipsClearfix ipsSpacer_bottom']/div/p[@class='ipsType_reset ipsType_large ipsType_blendLinks']/a")
|
||||
?.First().InnerHtml;
|
||||
Version = node.SelectNodes("//section/h2[@class='ipsType_sectionHead']/span[@data-role='versionTitle']")
|
||||
?
|
||||
.First().InnerHtml;
|
||||
ImageURL = node
|
||||
.SelectNodes(
|
||||
"//div[@class='ipsBox ipsSpacer_top ipsSpacer_double']/section/div[@class='ipsPad ipsAreaBackground']/div[@class='ipsCarousel ipsClearfix']/div[@class='ipsCarousel_inner']/ul[@class='cDownloadsCarousel ipsClearfix']/li[@class='ipsCarousel_item ipsAreaBackground_reset ipsPad_half']/span[@class='ipsThumb ipsThumb_medium ipsThumb_bg ipsCursor_pointer']")
|
||||
?.First().GetAttributeValue("data-fullurl", "none");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,13 @@ using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Ceras;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StatusFeed.Errors;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.Validation;
|
||||
using Game = Wabbajack.Common.Game;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
@ -69,21 +71,18 @@ namespace Wabbajack.Lib.Downloaders
|
||||
Utils.Error($"Error getting mod info for Nexus mod with {general.modID}");
|
||||
throw;
|
||||
}
|
||||
|
||||
return new State
|
||||
{
|
||||
GameName = general.gameName,
|
||||
FileID = general.fileID,
|
||||
ModID = general.modID,
|
||||
Name = NexusApiUtils.FixupSummary(info.name),
|
||||
Author = NexusApiUtils.FixupSummary(info.author),
|
||||
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,
|
||||
NexusURL = NexusApiUtils.GetModURL(game, info.mod_id),
|
||||
Summary = info.summary,
|
||||
Adult = info.contains_adult_content
|
||||
|
||||
ImageURL = info.picture_url,
|
||||
IsNSFW = info.contains_adult_content,
|
||||
Description = NexusApiUtils.FixupSummary(info.summary),
|
||||
GameName = general.gameName,
|
||||
ModID = general.modID,
|
||||
FileID = general.fileID
|
||||
};
|
||||
}
|
||||
|
||||
@ -123,20 +122,30 @@ namespace Wabbajack.Lib.Downloaders
|
||||
}
|
||||
}
|
||||
|
||||
public class State : AbstractDownloadState
|
||||
public class State : AbstractDownloadState, IMetaState
|
||||
{
|
||||
public string URL => $"http://nexusmods.com/{NexusApiUtils.ConvertGameName(GameName)}/mods/{ModID}";
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Author { get; set; }
|
||||
public string FileID { get; set; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 string FileID { get; set; }
|
||||
|
||||
public override object[] PrimaryKey { get => new object[]{GameName, ModID, FileID};}
|
||||
|
||||
@ -192,7 +201,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Log($"{ModName} - {GameName} - {ModID} - {FileID} - Error getting Nexus download URL - {ex}");
|
||||
Utils.Log($"{Name} - {GameName} - {ModID} - {FileID} - Error getting Nexus download URL - {ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ namespace Wabbajack.Lib
|
||||
protected override async Task<bool> _Begin(CancellationToken cancel)
|
||||
{
|
||||
if (cancel.IsCancellationRequested) return false;
|
||||
ConfigureProcessor(19, ConstructDynamicNumThreads(await RecommendQueueSize()));
|
||||
ConfigureProcessor(20, ConstructDynamicNumThreads(await RecommendQueueSize()));
|
||||
UpdateTracker.Reset();
|
||||
UpdateTracker.NextStep("Gathering information");
|
||||
Info("Looking for other profiles");
|
||||
@ -280,6 +280,9 @@ namespace Wabbajack.Lib
|
||||
UpdateTracker.NextStep("Building Patches");
|
||||
await BuildPatches();
|
||||
|
||||
UpdateTracker.NextStep("Gathering Metadata");
|
||||
await GatherMetaData();
|
||||
|
||||
ModList = new ModList
|
||||
{
|
||||
GameType = CompilingGame.Game,
|
||||
|
@ -284,7 +284,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
|
||||
try
|
||||
{
|
||||
Utils.Log($"Requesting manual download for {archive.ModName}");
|
||||
Utils.Log($"Requesting manual download for {archive.Name}");
|
||||
return (await Utils.Log(await ManuallyDownloadNexusFile.Create(archive)).Task).ToString();
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
|
@ -113,7 +113,7 @@ namespace Wabbajack.Lib.Validation
|
||||
if (nexus_mod_permissions.TryGetValue(p.ArchiveHashPath[0], out var archive))
|
||||
{
|
||||
var ext = Path.GetExtension(p.ArchiveHashPath.Last());
|
||||
var url = (archive.archive.State as NexusDownloader.State).NexusURL;
|
||||
var url = (archive.archive.State as NexusDownloader.State).URL;
|
||||
if (Consts.AssetFileExtensions.Contains(ext) && !(archive.permissions.CanModifyAssets ?? true))
|
||||
{
|
||||
ValidationErrors.Push($"{p.To} from {url} is set to disallow asset modification");
|
||||
@ -131,7 +131,7 @@ namespace Wabbajack.Lib.Validation
|
||||
{
|
||||
if (nexus_mod_permissions.TryGetValue(p.ArchiveHashPath[0], out var archive))
|
||||
{
|
||||
var url = (archive.archive.State as NexusDownloader.State).NexusURL;
|
||||
var url = (archive.archive.State as NexusDownloader.State).URL;
|
||||
if (!(archive.permissions.CanExtractBSAs ?? true) &&
|
||||
p.ArchiveHashPath.Skip(1).ButLast().Any(a => Consts.SupportedBSAs.Contains(Path.GetExtension(a).ToLower())))
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
<Version>4.1.7</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Fody">
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Genbox.AlphaFS">
|
||||
<Version>2.2.2.1</Version>
|
||||
@ -25,7 +25,7 @@
|
||||
<Version>2.0.0-alpha.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="HtmlAgilityPack">
|
||||
<Version>1.11.20</Version>
|
||||
<Version>1.11.21</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MegaApiClient">
|
||||
<Version>1.7.1</Version>
|
||||
|
@ -1,10 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Subjects;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
@ -14,7 +12,7 @@ namespace Wabbajack
|
||||
[JsonObject(MemberSerialization.OptOut)]
|
||||
public class MainSettings
|
||||
{
|
||||
private static string _filename = "settings.json";
|
||||
public byte Version { get; set; }
|
||||
|
||||
public double PosX { get; set; }
|
||||
public double PosY { get; set; }
|
||||
@ -30,13 +28,26 @@ namespace Wabbajack
|
||||
|
||||
public static bool TryLoadTypicalSettings(out MainSettings settings)
|
||||
{
|
||||
if (!File.Exists(_filename))
|
||||
if (!File.Exists(Consts.SettingsFile))
|
||||
{
|
||||
settings = default;
|
||||
return false;
|
||||
}
|
||||
settings = JsonConvert.DeserializeObject<MainSettings>(File.ReadAllText(_filename));
|
||||
return true;
|
||||
|
||||
// Version check
|
||||
settings = JsonConvert.DeserializeObject<MainSettings>(File.ReadAllText(Consts.SettingsFile));
|
||||
if (settings.Version == Consts.SettingsVersion)
|
||||
return true;
|
||||
|
||||
var backup = Consts.SettingsFile + "-backup.json";
|
||||
if(File.Exists(backup))
|
||||
File.Delete(backup);
|
||||
|
||||
File.Copy(Consts.SettingsFile, backup);
|
||||
File.Delete(Consts.SettingsFile);
|
||||
|
||||
settings = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SaveSettings(MainSettings settings)
|
||||
@ -48,7 +59,7 @@ namespace Wabbajack
|
||||
//settings._saveSignal.OnCompleted();
|
||||
//await settings._saveSignal;
|
||||
|
||||
File.WriteAllText(_filename, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
File.WriteAllText(Consts.SettingsFile, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,14 +87,14 @@ namespace Wabbajack
|
||||
[JsonObject(MemberSerialization.OptOut)]
|
||||
public class PerformanceSettings : ViewModel
|
||||
{
|
||||
private bool _Manual = false;
|
||||
public bool Manual { get => _Manual; set => this.RaiseAndSetIfChanged(ref _Manual, value); }
|
||||
private bool _manual;
|
||||
public bool Manual { get => _manual; set => RaiseAndSetIfChanged(ref _manual, value); }
|
||||
|
||||
private byte _MaxCores = byte.MaxValue;
|
||||
public byte MaxCores { get => _MaxCores; set => this.RaiseAndSetIfChanged(ref _MaxCores, value); }
|
||||
private byte _maxCores = byte.MaxValue;
|
||||
public byte MaxCores { get => _maxCores; set => RaiseAndSetIfChanged(ref _maxCores, value); }
|
||||
|
||||
private Percent _TargetUsage = Percent.One;
|
||||
public Percent TargetUsage { get => _TargetUsage; set => this.RaiseAndSetIfChanged(ref _TargetUsage, value); }
|
||||
private Percent _targetUsage = Percent.One;
|
||||
public Percent TargetUsage { get => _targetUsage; set => RaiseAndSetIfChanged(ref _targetUsage, value); }
|
||||
|
||||
public void AttachToBatchProcessor(ABatchProcessor processor)
|
||||
{
|
||||
|
@ -274,7 +274,7 @@ namespace Wabbajack
|
||||
_titleText = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModList)
|
||||
.Select(modList => modList?.Name ?? string.Empty),
|
||||
this.WhenAny(x => x.Slideshow.TargetMod.ModName)
|
||||
this.WhenAny(x => x.Slideshow.TargetMod.State.Name)
|
||||
.StartWith(default(string)),
|
||||
this.WhenAny(x => x.Installing),
|
||||
resultSelector: (modList, mod, installing) => installing ? mod : modList)
|
||||
@ -282,7 +282,7 @@ namespace Wabbajack
|
||||
_authorText = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModList)
|
||||
.Select(modList => modList?.Author ?? string.Empty),
|
||||
this.WhenAny(x => x.Slideshow.TargetMod.ModAuthor)
|
||||
this.WhenAny(x => x.Slideshow.TargetMod.State.Author)
|
||||
.StartWith(default(string)),
|
||||
this.WhenAny(x => x.Installing),
|
||||
resultSelector: (modList, mod, installing) => installing ? mod : modList)
|
||||
@ -290,7 +290,7 @@ namespace Wabbajack
|
||||
_description = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModList)
|
||||
.Select(modList => modList?.Description ?? string.Empty),
|
||||
this.WhenAny(x => x.Slideshow.TargetMod.ModDescription)
|
||||
this.WhenAny(x => x.Slideshow.TargetMod.State.Description)
|
||||
.StartWith(default(string)),
|
||||
this.WhenAny(x => x.Installing),
|
||||
resultSelector: (modList, mod, installing) => installing ? mod : modList)
|
||||
|
@ -38,6 +38,7 @@ namespace Wabbajack
|
||||
catch (Exception ex)
|
||||
{
|
||||
Error = ex;
|
||||
Utils.Error(ex, "Exception while loading the modlist!");
|
||||
}
|
||||
|
||||
ImageObservable = Observable.Return(Unit.Default)
|
||||
|
@ -1,49 +1,29 @@
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class ModVM : ViewModel
|
||||
{
|
||||
public string ModName { get; }
|
||||
|
||||
public string ModID { get; }
|
||||
|
||||
public string ModDescription { get; }
|
||||
|
||||
public string ModAuthor { get; }
|
||||
|
||||
public bool IsNSFW { get; }
|
||||
|
||||
public string ModURL { get; }
|
||||
|
||||
public string ImageURL { get; }
|
||||
public IMetaState State { get; }
|
||||
|
||||
// Image isn't exposed as a direct property, but as an observable.
|
||||
// This acts as a caching mechanism, as interested parties will trigger it to be created,
|
||||
// and the cached image will automatically be released when the last interested party is gone.
|
||||
public IObservable<BitmapImage> ImageObservable { get; }
|
||||
|
||||
public ModVM(NexusDownloader.State m)
|
||||
public ModVM(IMetaState state)
|
||||
{
|
||||
ModName = NexusApiUtils.FixupSummary(m.ModName);
|
||||
ModID = m.ModID;
|
||||
ModDescription = NexusApiUtils.FixupSummary(m.Summary);
|
||||
ModAuthor = NexusApiUtils.FixupSummary(m.Author);
|
||||
IsNSFW = m.Adult;
|
||||
ModURL = m.NexusURL;
|
||||
ImageURL = m.SlideShowPic;
|
||||
ImageObservable = Observable.Return(ImageURL)
|
||||
State = state;
|
||||
|
||||
ImageObservable = Observable.Return(State.ImageURL)
|
||||
.ObserveOn(RxApp.TaskpoolScheduler)
|
||||
.DownloadBitmapImage((ex) => Utils.Log($"Skipping slide for mod {ModName} ({ModID})"))
|
||||
.DownloadBitmapImage((ex) => Utils.Log($"Skipping slide for mod {State.Name}"))
|
||||
.Replay(1)
|
||||
.RefCount(TimeSpan.FromMilliseconds(5000));
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ 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.Lib;
|
||||
@ -33,7 +34,7 @@ namespace Wabbajack
|
||||
public ModVM TargetMod => _targetMod.Value;
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SlideShowNextItemCommand { get; } = ReactiveCommand.Create(() => { });
|
||||
public ReactiveCommand<Unit, Unit> VisitNexusSiteCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> VisitURLCommand { get; }
|
||||
|
||||
public const int PreloadAmount = 4;
|
||||
|
||||
@ -82,23 +83,24 @@ namespace Wabbajack
|
||||
{
|
||||
if (modList?.SourceModList?.Archives == null)
|
||||
{
|
||||
return Observable.Empty<NexusDownloader.State>()
|
||||
.ToObservableChangeSet(x => x.ModID);
|
||||
return Observable.Empty<IMetaState>()
|
||||
.ToObservableChangeSet(x => x.URL);
|
||||
}
|
||||
return modList.SourceModList.Archives
|
||||
.Select(m => m.State)
|
||||
.OfType<NexusDownloader.State>()
|
||||
.OfType<IMetaState>()
|
||||
.DistinctBy(x => x.URL)
|
||||
// Shuffle it
|
||||
.Shuffle(_random)
|
||||
.AsObservableChangeSet(x => x.ModID);
|
||||
.AsObservableChangeSet(x => x.URL);
|
||||
})
|
||||
// Switch to the new list after every ModList change
|
||||
.Switch()
|
||||
.Transform(nexus => new ModVM(nexus))
|
||||
.Transform(mod => new ModVM(mod))
|
||||
.DisposeMany()
|
||||
// Filter out any NSFW slides if we don't want them
|
||||
.AutoRefreshOnObservable(slide => this.WhenAny(x => x.ShowNSFW))
|
||||
.Filter(slide => !slide.IsNSFW || ShowNSFW)
|
||||
.Filter(slide => !slide.State.IsNSFW || ShowNSFW)
|
||||
.RefCount();
|
||||
|
||||
// Find target mod to display by combining dynamic list with currently desired index
|
||||
@ -120,14 +122,18 @@ namespace Wabbajack
|
||||
.Switch()
|
||||
.ToGuiProperty(this, nameof(Image));
|
||||
|
||||
VisitNexusSiteCommand = ReactiveCommand.Create(
|
||||
VisitURLCommand = ReactiveCommand.Create(
|
||||
execute: () =>
|
||||
{
|
||||
Utils.OpenWebsite(TargetMod.ModURL);
|
||||
Utils.OpenWebsite(TargetMod.State.URL);
|
||||
return Unit.Default;
|
||||
},
|
||||
canExecute: this.WhenAny(x => x.TargetMod.ModURL)
|
||||
.Select(x => x?.StartsWith("https://") ?? false)
|
||||
canExecute: this.WhenAny(x => x.TargetMod.State.URL)
|
||||
.Select(x =>
|
||||
{
|
||||
var regex = new Regex("^(http|https):\\/\\/");
|
||||
return x != null && regex.Match(x).Success;
|
||||
})
|
||||
.ObserveOnGuiThread());
|
||||
|
||||
// Preload upcoming images
|
||||
|
@ -157,7 +157,7 @@ namespace Wabbajack
|
||||
};
|
||||
await vm.Driver.WaitForInitialized();
|
||||
IWebDriver browser = new CefSharpWrapper(vm.Browser);
|
||||
vm.Instructions = $"Please Download {state.ModName} - {state.ModID} - {state.FileID}";
|
||||
vm.Instructions = $"Please Download {state.Name} - {state.ModID} - {state.FileID}";
|
||||
browser.DownloadHandler = uri =>
|
||||
{
|
||||
manuallyDownloadNexusFile.Resume(uri);
|
||||
|
@ -73,7 +73,7 @@ namespace Wabbajack
|
||||
})
|
||||
.BindToStrict(this, x => x.PlayPauseButton.ToolTip)
|
||||
.DisposeWith(dispose);
|
||||
this.WhenAny(x => x.ViewModel.Slideshow.VisitNexusSiteCommand)
|
||||
this.WhenAny(x => x.ViewModel.Slideshow.VisitURLCommand)
|
||||
.BindToStrict(this, x => x.OpenWebsite.Command)
|
||||
.DisposeWith(dispose);
|
||||
this.BindStrict(this.ViewModel, x => x.Slideshow.ShowNSFW, x => x.ShowNSFWButton.IsChecked,
|
||||
|
@ -59,7 +59,10 @@ namespace Wabbajack
|
||||
};
|
||||
if (CLIArguments.NoSettings || !MainSettings.TryLoadTypicalSettings(out var settings))
|
||||
{
|
||||
_settings = new MainSettings();
|
||||
_settings = new MainSettings
|
||||
{
|
||||
Version = Consts.SettingsVersion
|
||||
};
|
||||
RunWhenLoaded(DefaultSettings);
|
||||
}
|
||||
else
|
||||
|
@ -55,7 +55,7 @@
|
||||
<PackageReference Include="CefSharp.Wpf" Version="79.1.350" />
|
||||
<PackageReference Include="DynamicData" Version="6.14.8" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="3.8.1" />
|
||||
<PackageReference Include="Fody" Version="6.1.0">
|
||||
<PackageReference Include="Fody" Version="6.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
Loading…
Reference in New Issue
Block a user