wabbajack/Wabbajack.App/Controls/BrowseItemViewModel.cs

187 lines
6.1 KiB
C#
Raw Normal View History

2021-09-27 12:42:46 +00:00
using System;
using System.IO;
using System.Linq;
2021-09-27 12:42:46 +00:00
using System.Net.Http;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Media.Imaging;
using Microsoft.Extensions.Logging;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.App.Messages;
using Wabbajack.App.ViewModels;
using Wabbajack.Common;
using Wabbajack.Downloaders;
2021-10-13 03:59:54 +00:00
using Wabbajack.Downloaders.GameFile;
2021-09-27 12:42:46 +00:00
using Wabbajack.DTOs;
2021-09-30 04:03:43 +00:00
using Wabbajack.DTOs.JsonConverters;
2021-09-27 12:42:46 +00:00
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter;
using Wabbajack.VFS;
2021-10-23 16:51:17 +00:00
namespace Wabbajack.App.Controls;
public enum ModListState
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
Downloaded,
NotDownloaded,
Downloading
}
2021-10-23 16:51:17 +00:00
public class BrowseItemViewModel : ViewModelBase, IActivatableViewModel
{
private readonly HttpClient _client;
private readonly Configuration _configuration;
private readonly DownloadDispatcher _dispatcher;
private readonly IResource<DownloadDispatcher> _downloadLimiter;
private readonly DTOSerializer _dtos;
private readonly FileHashCache _hashCache;
private readonly IResource<HttpClient> _limiter;
private readonly ILogger _logger;
private readonly ModlistMetadata _metadata;
private readonly ModListSummary _summary;
public BrowseItemViewModel(ModlistMetadata metadata, ModListSummary summary, HttpClient client,
IResource<HttpClient> limiter,
FileHashCache hashCache, Configuration configuration, DownloadDispatcher dispatcher,
IResource<DownloadDispatcher> downloadLimiter, GameLocator gameLocator,
DTOSerializer dtos, ILogger logger)
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
Activator = new ViewModelActivator();
_metadata = metadata;
_summary = summary;
_client = client;
_limiter = limiter;
_hashCache = hashCache;
_configuration = configuration;
_dispatcher = dispatcher;
_downloadLimiter = downloadLimiter;
_logger = logger;
_dtos = dtos;
var haveGame = gameLocator.IsInstalled(_metadata.Game);
Tags = metadata.tags
.Select(t => new TagViewModel(t, "ModList"))
.Prepend(new TagViewModel(_metadata.Game.MetaData().HumanFriendlyGameName,
haveGame ? "Game" : "GameNotInstalled"))
.ToArray();
OpenWebsiteCommand = ReactiveCommand.Create(() =>
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
Utils.OpenWebsiteInExternalBrowser(new Uri(_metadata.Links.Readme));
});
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
ExecuteCommand = ReactiveCommand.Create(() =>
2021-09-27 12:42:46 +00:00
{
if (State == ModListState.Downloaded)
{
MessageBus.Current.SendMessage(new StartInstallConfiguration(ModListLocation));
MessageBus.Current.SendMessage(new NavigateTo(typeof(InstallConfigurationViewModel)));
2021-09-27 12:42:46 +00:00
}
else
{
DownloadModList().FireAndForget();
}
2021-10-23 16:51:17 +00:00
},
this.ObservableForProperty(t => t.State)
.Select(c => c.Value != ModListState.Downloading)
.StartWith(true));
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
LoadListImage().FireAndForget();
UpdateState().FireAndForget();
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public string Title => _metadata.ImageContainsTitle ? "" : _metadata.Title;
public string MachineURL => _metadata.Links.MachineURL;
public string Description => _metadata.Description;
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public Uri ImageUri => new(_metadata.Links.ImageUri);
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
[Reactive] public IBitmap Image { get; set; }
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
[Reactive] public ModListState State { get; set; }
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
[Reactive] public ReactiveCommand<Unit, Unit> ExecuteCommand { get; set; }
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
[Reactive] public Percent Progress { get; set; }
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public AbsolutePath ModListLocation => _configuration.ModListsDownloadLocation.Combine(_metadata.Links.MachineURL)
.WithExtension(Ext.Wabbajack);
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public Game Game => _metadata.Game;
public bool IsUtilityList => _metadata.UtilityList;
public bool IsNSFW => _metadata.NSFW;
[Reactive] public TagViewModel[] Tags { get; set; }
public ReactiveCommand<Unit, Unit> OpenWebsiteCommand { get; set; }
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
private async Task DownloadModList()
{
State = ModListState.Downloading;
var state = _dispatcher.Parse(new Uri(_metadata.Links.Download));
var archive = new Archive
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
State = state!,
Hash = _metadata.DownloadMetadata?.Hash ?? default,
Size = _metadata.DownloadMetadata?.Size ?? 0,
Name = ModListLocation.FileName.ToString()
};
using var job = await _downloadLimiter.Begin(state!.PrimaryKeyString, archive.Size, CancellationToken.None);
var hashTask = _dispatcher.Download(archive, ModListLocation, job, CancellationToken.None);
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
while (!hashTask.IsCompleted)
{
Progress = Percent.FactoryPutInRange(job.Current, job.Size ?? 0);
await Task.Delay(100);
2021-09-27 12:42:46 +00:00
}
2021-10-23 16:51:17 +00:00
var hash = await hashTask;
if (hash != _metadata.DownloadMetadata?.Hash)
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
_logger.LogWarning("Hash files didn't match after downloading modlist, deleting modlist");
if (ModListLocation.FileExists())
ModListLocation.Delete();
2021-09-27 12:42:46 +00:00
}
2021-10-23 16:51:17 +00:00
_hashCache.FileHashWriteCache(ModListLocation, hash);
var metadataPath = ModListLocation.WithExtension(Ext.MetaData);
await metadataPath.WriteAllTextAsync(_dtos.Serialize(_metadata));
Progress = Percent.Zero;
await UpdateState();
}
public async Task LoadListImage()
{
using var job = await _limiter.Begin("Loading modlist image", 0, CancellationToken.None);
var response = await _client.GetByteArrayAsync(ImageUri);
Image = new Bitmap(new MemoryStream(response));
}
public async Task<ModListState> GetState()
{
var file = ModListLocation;
if (!file.FileExists())
return ModListState.NotDownloaded;
return await _hashCache.FileHashCachedAsync(file, CancellationToken.None) !=
_metadata.DownloadMetadata?.Hash
? ModListState.NotDownloaded
: ModListState.Downloaded;
}
public async Task UpdateState()
{
State = await GetState();
2021-09-27 12:42:46 +00:00
}
}