2021-09-27 12:42:46 +00:00
|
|
|
using System;
|
|
|
|
using System.IO;
|
2021-09-29 22:24:21 +00:00
|
|
|
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;
|
2021-10-27 21:47:58 +00:00
|
|
|
using Wabbajack.App.Models;
|
2021-09-27 12:42:46 +00:00
|
|
|
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,
|
2021-10-27 21:47:58 +00:00
|
|
|
Downloading,
|
|
|
|
Disabled
|
2021-10-23 16:51:17 +00:00
|
|
|
}
|
2021-09-29 22:24:21 +00:00
|
|
|
|
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;
|
2021-10-27 21:47:58 +00:00
|
|
|
private readonly ImageCache _imageCache;
|
2021-10-23 16:51:17 +00:00
|
|
|
|
|
|
|
public BrowseItemViewModel(ModlistMetadata metadata, ModListSummary summary, HttpClient client,
|
|
|
|
IResource<HttpClient> limiter,
|
|
|
|
FileHashCache hashCache, Configuration configuration, DownloadDispatcher dispatcher,
|
2021-10-27 21:47:58 +00:00
|
|
|
IResource<DownloadDispatcher> downloadLimiter, GameLocator gameLocator, ImageCache imageCache,
|
2021-10-23 16:51:17 +00:00
|
|
|
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;
|
2021-10-27 21:47:58 +00:00
|
|
|
_imageCache = imageCache;
|
2021-10-23 16:51:17 +00:00
|
|
|
_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)
|
|
|
|
{
|
2021-10-27 04:28:01 +00:00
|
|
|
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)
|
2021-10-27 21:47:58 +00:00
|
|
|
.Select(c => c.Value != ModListState.Downloading && c.Value != ModListState.Disabled)
|
2021-10-23 16:51:17 +00:00
|
|
|
.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;
|
2021-10-27 21:47:58 +00:00
|
|
|
public string Description => State == ModListState.Disabled ? "Disabled: Under Construction \n " + _metadata.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()
|
|
|
|
{
|
2021-10-27 23:03:49 +00:00
|
|
|
Image = await _imageCache.From(ImageUri, 540, 300);
|
2021-10-23 16:51:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<ModListState> GetState()
|
|
|
|
{
|
2021-10-27 21:47:58 +00:00
|
|
|
if (_metadata.ForceDown || _summary.HasFailures)
|
|
|
|
return ModListState.Disabled;
|
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|