mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Update the launcher to try the Nexus for updates before Github
This commit is contained in:
parent
43778f41ea
commit
9e4c272c1c
@ -1,6 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Wabbajack.Launcher.ViewModels;
|
||||
using Wabbajack.Launcher.Views;
|
||||
|
||||
@ -15,10 +16,11 @@ public class App : Application
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainWindowViewModel()
|
||||
DataContext = Program.Services.GetRequiredService<MainWindowViewModel>()
|
||||
};
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
|
40
Wabbajack.Launcher/Models/LegacyNexusApiKey.cs
Normal file
40
Wabbajack.Launcher/Models/LegacyNexusApiKey.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using Wabbajack.DTOs.Logins;
|
||||
using Wabbajack.Networking.Http.Interfaces;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
|
||||
namespace Wabbajack.Launcher.Models;
|
||||
|
||||
public class LegacyNexusApiKey : ITokenProvider<NexusApiState>
|
||||
{
|
||||
private AbsolutePath TokenPath => KnownFolders.WabbajackAppLocal.Combine("nexusapikey");
|
||||
public async ValueTask<NexusApiState?> Get()
|
||||
{
|
||||
var data = await TokenPath.ReadAllBytesAsync();
|
||||
var decoded = ProtectedData.Unprotect(data, Encoding.UTF8.GetBytes("nexusapikey"), DataProtectionScope.LocalMachine);
|
||||
var apiKey = JsonSerializer.Deserialize<string>(decoded)!;
|
||||
return new NexusApiState()
|
||||
{
|
||||
ApiKey = apiKey
|
||||
};
|
||||
}
|
||||
|
||||
public ValueTask SetToken(NexusApiState val)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask<bool> Delete()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HaveToken()
|
||||
{
|
||||
return TokenPath.FileExists();
|
||||
}
|
||||
}
|
@ -1,5 +1,22 @@
|
||||
using Avalonia;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.Downloaders.Http;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.DTOs.Logins;
|
||||
using Wabbajack.Launcher.Models;
|
||||
using Wabbajack.Launcher.ViewModels;
|
||||
using Wabbajack.Networking.Http;
|
||||
using Wabbajack.Networking.Http.Interfaces;
|
||||
using Wabbajack.Networking.NexusApi;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.RateLimiter;
|
||||
|
||||
namespace Wabbajack.Launcher;
|
||||
|
||||
@ -11,9 +28,40 @@ internal class Program
|
||||
// yet and stuff might break.
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||
.ConfigureLogging(c => { c.ClearProviders(); })
|
||||
.ConfigureServices((host, services) =>
|
||||
{
|
||||
services.AddNexusApi();
|
||||
services.AddDTOConverters();
|
||||
services.AddDTOSerializer();
|
||||
services.AddSingleton<MainWindowViewModel>();
|
||||
services.AddSingleton<HttpClient>();
|
||||
services.AddSingleton<ITokenProvider<NexusApiState>, LegacyNexusApiKey>();
|
||||
services.AddSingleton<HttpDownloader>();
|
||||
services.AddAllSingleton<IResource, IResource<HttpClient>>(s => new Resource<HttpClient>("Web Requests", 4));
|
||||
services.AddAllSingleton<IHttpDownloader, SingleThreadedDownloader>();
|
||||
|
||||
var version =
|
||||
$"{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Patch}{ThisAssembly.Git.SemVer.DashLabel}";
|
||||
services.AddSingleton(s => new ApplicationInfo
|
||||
{
|
||||
ApplicationSlug = "Wabbajack",
|
||||
ApplicationName = Environment.ProcessPath?.ToAbsolutePath().FileName.ToString() ?? "Wabbajack",
|
||||
ApplicationSha = ThisAssembly.Git.Sha,
|
||||
Platform = RuntimeInformation.ProcessArchitecture.ToString(),
|
||||
OperatingSystemDescription = RuntimeInformation.OSDescription,
|
||||
RuntimeIdentifier = RuntimeInformation.RuntimeIdentifier,
|
||||
OSVersion = Environment.OSVersion.VersionString,
|
||||
Version = version
|
||||
});
|
||||
}).Build();
|
||||
Services = host.Services;
|
||||
|
||||
BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
public static IServiceProvider Services { get; set; }
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
|
@ -7,8 +7,14 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Compression.Zip;
|
||||
using Wabbajack.Downloaders.Http;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.DownloadStates;
|
||||
using Wabbajack.Networking.NexusApi;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
|
||||
@ -19,12 +25,16 @@ public class MainWindowViewModel : ViewModelBase
|
||||
private readonly WebClient _client = new();
|
||||
private readonly List<string> _errors = new();
|
||||
|
||||
private Release _version;
|
||||
private (Version Version, long Size, Func<Task<Uri>> Uri) _version;
|
||||
public Uri GITHUB_REPO = new("https://api.github.com/repos/wabbajack-tools/wabbajack/releases");
|
||||
private readonly NexusApi _nexusApi;
|
||||
private readonly HttpDownloader _downloader;
|
||||
|
||||
public MainWindowViewModel()
|
||||
public MainWindowViewModel(NexusApi nexusApi, HttpDownloader downloader)
|
||||
{
|
||||
_nexusApi = nexusApi;
|
||||
Status = "Checking for new versions";
|
||||
_downloader = downloader;
|
||||
var tsk = CheckForUpdates();
|
||||
}
|
||||
|
||||
@ -37,13 +47,15 @@ public class MainWindowViewModel : ViewModelBase
|
||||
|
||||
try
|
||||
{
|
||||
var releases = await GetReleases();
|
||||
_version = releases.OrderByDescending(r =>
|
||||
var nexusRelease = await GetNexusReleases(CancellationToken.None);
|
||||
if (nexusRelease != default)
|
||||
_version = nexusRelease;
|
||||
else
|
||||
{
|
||||
if (r.Tag.Split(".").Length == 4 && Version.TryParse(r.Tag, out var v))
|
||||
return v;
|
||||
return new Version(0, 0, 0, 0);
|
||||
}).FirstOrDefault();
|
||||
_version = await GetGithubRelease(CancellationToken.None);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -51,32 +63,28 @@ public class MainWindowViewModel : ViewModelBase
|
||||
await FinishAndExit();
|
||||
}
|
||||
|
||||
if (_version == null)
|
||||
if (_version == default)
|
||||
{
|
||||
_errors.Add("Unable to parse Github releases");
|
||||
_errors.Add("Unable to find releases");
|
||||
await FinishAndExit();
|
||||
}
|
||||
|
||||
Status = "Looking for Updates";
|
||||
|
||||
var base_folder = Path.Combine(Directory.GetCurrentDirectory(), _version.Tag);
|
||||
var baseFolder = KnownFolders.CurrentDirectory.Combine(_version.Version.ToString());
|
||||
|
||||
if (File.Exists(Path.Combine(base_folder, "Wabbajack.exe"))) await FinishAndExit();
|
||||
if (baseFolder.Combine("Wabbajack.exe").FileExists()) await FinishAndExit();
|
||||
|
||||
var asset = _version.Assets.FirstOrDefault(a => a.Name == _version.Tag + ".zip");
|
||||
if (asset == null)
|
||||
{
|
||||
_errors.Add("No zip file for release " + _version.Tag);
|
||||
await FinishAndExit();
|
||||
}
|
||||
Status = $"Getting download Uri for {_version.Version}";
|
||||
var uri = await _version.Uri();
|
||||
|
||||
var wc = new WebClient();
|
||||
wc.DownloadProgressChanged += UpdateProgress;
|
||||
Status = $"Downloading {_version.Tag} ...";
|
||||
Status = $"Downloading {_version.Version} ...";
|
||||
byte[] data;
|
||||
try
|
||||
{
|
||||
data = await wc.DownloadDataTaskAsync(asset.BrowserDownloadUrl);
|
||||
data = await wc.DownloadDataTaskAsync(uri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -84,7 +92,7 @@ public class MainWindowViewModel : ViewModelBase
|
||||
// Something went wrong so fallback to original URL
|
||||
try
|
||||
{
|
||||
data = await wc.DownloadDataTaskAsync(asset.BrowserDownloadUrl);
|
||||
data = await wc.DownloadDataTaskAsync(uri);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
@ -96,23 +104,21 @@ public class MainWindowViewModel : ViewModelBase
|
||||
|
||||
try
|
||||
{
|
||||
using (var zip = new ZipArchive(new MemoryStream(data), ZipArchiveMode.Read))
|
||||
{
|
||||
using var zip = new ZipArchive(new MemoryStream(data), ZipArchiveMode.Read);
|
||||
foreach (var entry in zip.Entries)
|
||||
{
|
||||
Status = $"Extracting: {entry.Name}";
|
||||
var outPath = Path.Combine(base_folder, entry.FullName);
|
||||
if (!Directory.Exists(Path.GetDirectoryName(outPath)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
var outPath = baseFolder.Combine(entry.FullName.ToRelativePath());
|
||||
if (!outPath.Parent.DirectoryExists())
|
||||
outPath.Parent.CreateDirectory();
|
||||
|
||||
if (entry.FullName.EndsWith("/") || entry.FullName.EndsWith("\\"))
|
||||
continue;
|
||||
await using var o = entry.Open();
|
||||
await using var of = File.Create(outPath);
|
||||
await using var of = outPath.Open(FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
await o.CopyToAsync(of);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errors.Add(ex.Message);
|
||||
@ -175,10 +181,34 @@ public class MainWindowViewModel : ViewModelBase
|
||||
|
||||
private void UpdateProgress(object sender, DownloadProgressChangedEventArgs e)
|
||||
{
|
||||
Status = $"Downloading {_version.Tag} ({e.ProgressPercentage}%)...";
|
||||
Status = $"Downloading {_version.Version} ({e.ProgressPercentage}%)...";
|
||||
}
|
||||
|
||||
private async Task<Release[]> GetReleases()
|
||||
private async Task<(Version Version, long Size, Func<Task<Uri>> Uri)> GetGithubRelease(CancellationToken token)
|
||||
{
|
||||
var releases = await GetGithubReleases();
|
||||
|
||||
|
||||
var version = releases.Select(r =>
|
||||
{
|
||||
if (r.Tag.Split(".").Length == 4 && Version.TryParse(r.Tag, out var v))
|
||||
return (v, r);
|
||||
return (new Version(0, 0, 0, 0), r);
|
||||
})
|
||||
.OrderByDescending(r => r.Item1)
|
||||
.FirstOrDefault();
|
||||
|
||||
var asset = version.r.Assets.FirstOrDefault(a => a.Name == version.Item1 + ".zip");
|
||||
if (asset == null)
|
||||
{
|
||||
Status = $"Error, no asset found for Github Release {version.r}";
|
||||
return default;
|
||||
}
|
||||
|
||||
return (version.Item1, asset.Size, async () => asset!.BrowserDownloadUrl);
|
||||
}
|
||||
|
||||
private async Task<Release[]> GetGithubReleases()
|
||||
{
|
||||
Status = "Checking GitHub Repository";
|
||||
var data = await _client.DownloadStringTaskAsync(GITHUB_REPO);
|
||||
@ -186,6 +216,30 @@ public class MainWindowViewModel : ViewModelBase
|
||||
return JsonSerializer.Deserialize<Release[]>(data)!;
|
||||
}
|
||||
|
||||
private async Task<(Version Version, long Size, Func<Task<Uri>> uri)> GetNexusReleases(CancellationToken token)
|
||||
{
|
||||
Status = "Checking Nexus for updates";
|
||||
if (!await _nexusApi.IsPremium(token))
|
||||
return default;
|
||||
|
||||
var data = await _nexusApi.ModFiles("site", 403, token);
|
||||
Status = "Parsing Response";
|
||||
//return JsonSerializer.Deserialize<Release[]>(data)!;
|
||||
|
||||
var found = data.info.Files.Where(f => f.CategoryId == 5)
|
||||
.Where(f => f.Name.EndsWith(".zip"))
|
||||
.Select(f => Version.TryParse(f.Name[..^4], out var version) ? (version, f.SizeInBytes ?? f.Size, f.FileId) : default)
|
||||
.FirstOrDefault(f => f != default);
|
||||
if (found == default) return default;
|
||||
|
||||
return (found.version, found.Item2, async () =>
|
||||
{
|
||||
var link = await _nexusApi.DownloadLink("site", 403, found.FileId, token);
|
||||
|
||||
return link.info.First().URI;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class Release
|
||||
{
|
||||
@ -200,5 +254,8 @@ public class MainWindowViewModel : ViewModelBase
|
||||
public Uri BrowserDownloadUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("name")] public string Name { get; set; }
|
||||
|
||||
|
||||
[JsonPropertyName("size")] public long Size { get; set; }
|
||||
}
|
||||
}
|
@ -7,17 +7,24 @@
|
||||
<AssemblyName>Wabbajack</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GitInfo" Version="2.2.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Avalonia" Version="0.10.14" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.14" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.14" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.14" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="18.0.10" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wabbajack.Downloaders.Http\Wabbajack.Downloaders.Http.csproj" />
|
||||
<ProjectReference Include="..\Wabbajack.Downloaders.Nexus\Wabbajack.Downloaders.Nexus.csproj" />
|
||||
<ProjectReference Include="..\Wabbajack.Paths.IO\Wabbajack.Paths.IO.csproj" />
|
||||
<ProjectReference Include="..\Wabbajack.Paths\Wabbajack.Paths.csproj" />
|
||||
</ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user