Implement mod list maintainer

This commit is contained in:
Timothy Baldridge 2021-12-27 08:37:20 -07:00
parent 9a13413472
commit bf5d092f43
4 changed files with 102 additions and 9 deletions

View File

@ -13,6 +13,7 @@ using System.Windows.Input;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using Alphaleonis.Win32.Filesystem; using Alphaleonis.Win32.Filesystem;
using DynamicData; using DynamicData;
using Microsoft.Extensions.Logging;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Wabbajack.Common; using Wabbajack.Common;
@ -23,7 +24,9 @@ using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.Extensions; using Wabbajack.Lib.Extensions;
using Wabbajack.Lib.ModListRegistry; using Wabbajack.Lib.ModListRegistry;
using Wabbajack.Paths; using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter; using Wabbajack.RateLimiter;
using Wabbajack.Services.OSIntegrated.Services;
namespace Wabbajack namespace Wabbajack
{ {
@ -84,12 +87,17 @@ namespace Wabbajack
public bool LoadingImage => _LoadingImage.Value; public bool LoadingImage => _LoadingImage.Value;
private Subject<bool> IsLoadingIdle; private Subject<bool> IsLoadingIdle;
private readonly ILogger<ModListMetadataVM> _logger;
private readonly ModListDownloadMaintainer _maintainer;
public ModListMetadataVM(ModListGalleryVM parent, ModlistMetadata metadata) public ModListMetadataVM(ILogger<ModListMetadataVM> logger, ModListGalleryVM parent, ModlistMetadata metadata,
ModListDownloadMaintainer maintainer)
{ {
_logger = logger;
_parent = parent; _parent = parent;
_maintainer = maintainer;
Metadata = metadata; Metadata = metadata;
Location = LauncherUpdater.CommonFolder.Value.Combine("downloaded_mod_lists", Metadata.Links.MachineURL + (string)Consts.ModListExtension); Location = LauncherUpdater.CommonFolder.Value.Combine("downloaded_mod_lists", Metadata.Links.MachineURL).WithExtension(Ext.Wabbajack);
ModListTagList = new List<ModListTag>(); ModListTagList = new List<ModListTag>();
Metadata.tags.ForEach(tag => Metadata.tags.ForEach(tag =>
@ -103,7 +111,7 @@ namespace Wabbajack
VersionText = "Modlist version : " + Metadata.Version; VersionText = "Modlist version : " + Metadata.Version;
IsBroken = metadata.ValidationSummary.HasFailures || metadata.ForceDown; IsBroken = metadata.ValidationSummary.HasFailures || metadata.ForceDown;
//https://www.wabbajack.org/#/modlists/info?machineURL=eldersouls //https://www.wabbajack.org/#/modlists/info?machineURL=eldersouls
OpenWebsiteCommand = ReactiveCommand.Create(() => Utils.OpenWebsite(new Uri($"https://www.wabbajack.org/#/modlists/info?machineURL={Metadata.Links.MachineURL}"))); OpenWebsiteCommand = ReactiveCommand.Create(() => UIUtils.OpenWebsite(new Uri($"https://www.wabbajack.org/#/modlists/info?machineURL={Metadata.Links.MachineURL}")));
IsLoadingIdle = new Subject<bool>(); IsLoadingIdle = new Subject<bool>();
@ -152,7 +160,7 @@ namespace Wabbajack
return false; return false;
} }
// Return an updated check on exists // Return an updated check on exists
return Location.Exists; return Location.FileExists();
} }
return exists; return exists;
}) })
@ -175,7 +183,7 @@ namespace Wabbajack
} }
catch (Exception ex) catch (Exception ex)
{ {
Utils.Error(ex); _logger.LogError(ex, "While opening modlist README");
} }
}); });
}) })
@ -190,7 +198,7 @@ namespace Wabbajack
{ {
try try
{ {
return !IsDownloading && !(await metadata.NeedsDownload(Location)); return !IsDownloading && !(await maintainer.HaveModList(metadata));
} }
catch (Exception) catch (Exception)
{ {
@ -200,7 +208,7 @@ namespace Wabbajack
.ToGuiProperty(this, nameof(Exists)); .ToGuiProperty(this, nameof(Exists));
var imageObs = Observable.Return(Metadata.Links.ImageUri) var imageObs = Observable.Return(Metadata.Links.ImageUri)
.DownloadBitmapImage((ex) => Utils.Log($"Error downloading modlist image {Metadata.Title}")); .DownloadBitmapImage((ex) => _logger.LogError("Error downloading modlist image {Title}", Metadata.Title));
_Image = imageObs _Image = imageObs
.ToGuiProperty(this, nameof(Image)); .ToGuiProperty(this, nameof(Image));
@ -227,8 +235,8 @@ namespace Wabbajack
try try
{ {
IsDownloading = true; IsDownloading = true;
Utils.Log($"Starting Download of {Metadata.Links.MachineURL}"); _logger.LogInformation("Starting Download of {MachineUrl}", Metadata.Links.MachineURL);
var downloader = DownloadDispatcher.ResolveArchive(Metadata.Links.Download); var downloader = await DownloadDispatcher.ResolveArchive(Metadata.Links.Download);
var result = await downloader.Download( var result = await downloader.Download(
new Archive(state: null!) new Archive(state: null!)
{ {

View File

@ -27,10 +27,14 @@ public class Job<T> : IJob, IDisposable
{ {
await Resource.Report(this, processedSize, token); await Resource.Report(this, processedSize, token);
Current += processedSize; Current += processedSize;
OnUpdate?.Invoke(this, (Percent.FactoryPutInRange(Current, Size ?? 1), Current));
} }
public void ReportNoWait(int processedSize) public void ReportNoWait(int processedSize)
{ {
Resource.ReportNoWait(this, processedSize); Resource.ReportNoWait(this, processedSize);
OnUpdate?.Invoke(this, (Percent.FactoryPutInRange(Current, Size ?? 1), Current));
} }
public event EventHandler<(Percent Progress, long Processed)> OnUpdate;
} }

View File

@ -19,6 +19,7 @@ using Wabbajack.Networking.WabbajackClientApi;
using Wabbajack.Paths; using Wabbajack.Paths;
using Wabbajack.Paths.IO; using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter; using Wabbajack.RateLimiter;
using Wabbajack.Services.OSIntegrated.Services;
using Wabbajack.Services.OSIntegrated.TokenProviders; using Wabbajack.Services.OSIntegrated.TokenProviders;
using Wabbajack.VFS; using Wabbajack.VFS;
@ -99,6 +100,8 @@ public static class ServiceExtensions
service.AddScoped<Context>(); service.AddScoped<Context>();
service.AddSingleton<FileExtractor.FileExtractor>(); service.AddSingleton<FileExtractor.FileExtractor>();
service.AddSingleton<ModListDownloadMaintainer>();
// Networking // Networking
service.AddSingleton<HttpClient>(); service.AddSingleton<HttpClient>();
service.AddAllSingleton<IHttpDownloader, SingleThreadedDownloader>(); service.AddAllSingleton<IHttpDownloader, SingleThreadedDownloader>();

View File

@ -0,0 +1,78 @@
using System;
using System.Reactive.Subjects;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Wabbajack.Common;
using Wabbajack.Downloaders;
using Wabbajack.DTOs;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter;
using Wabbajack.VFS;
namespace Wabbajack.Services.OSIntegrated.Services;
public class ModListDownloadMaintainer
{
private readonly ILogger<ModListDownloadMaintainer> _logger;
private readonly Configuration _configuration;
private readonly DownloadDispatcher _dispatcher;
private readonly FileHashCache _hashCache;
private readonly IResource<DownloadDispatcher> _rateLimiter;
public ModListDownloadMaintainer(ILogger<ModListDownloadMaintainer> logger, Configuration configuration,
DownloadDispatcher dispatcher, FileHashCache hashCache, IResource<DownloadDispatcher> rateLimiter)
{
_logger = logger;
_configuration = configuration;
_dispatcher = dispatcher;
_hashCache = hashCache;
_rateLimiter = rateLimiter;
}
public AbsolutePath ModListPath(ModlistMetadata metadata)
{
return _configuration.ModListsDownloadLocation.Combine(metadata.Links.MachineURL).WithExtension(Ext.Wabbajack);
}
public async Task<bool> HaveModList(ModlistMetadata metadata, CancellationToken? token = null)
{
token ??= CancellationToken.None;
var path = ModListPath(metadata);
if (!path.FileExists()) return false;
return await _hashCache.FileHashCachedAsync(path, token.Value) == metadata.DownloadMetadata!.Hash;
}
public (IObservable<Percent> Progress, Task Task) DownloadModlist(ModlistMetadata metadata, CancellationToken? token = null)
{
var path = ModListPath(metadata);
token ??= CancellationToken.None;
var progress = new Subject<Percent>();
progress.OnNext(Percent.Zero);
var tsk = Task.Run(async () =>
{
var job = await _rateLimiter.Begin($"Downloading {metadata.Title}", metadata.DownloadMetadata!.Size, token.Value);
job.OnUpdate += (_, pr) =>
{
progress.OnNext(pr.Progress);
};
var hash = await _dispatcher.Download(new Archive()
{
State = _dispatcher.Parse(new Uri(metadata.Links.Download))!,
Size = metadata.DownloadMetadata.Size,
Hash = metadata.DownloadMetadata.Hash
}, path, job, token.Value);
_hashCache.FileHashWriteCache(path, hash);
});
return (progress, tsk);
}
}