Working validate files dialog

Very basic, but it will compare local files against what's available on the server and allow you to selectively replace them. Save paths are ignored to avoid accidental replacement.

Implements #142
This commit is contained in:
Pat Hartl 2024-11-14 20:53:08 -06:00
parent a734ef9793
commit 65be58fbf2
6 changed files with 77 additions and 14 deletions

View file

@ -192,12 +192,11 @@ else
{
Title = $"File Conflicts",
Maximizable = false,
DefaultMaximized = false,
DefaultMaximized = true,
Closable = true,
Draggable = true,
Centered = true,
OkText = "Replace",
Footer = null,
OkText = "Replace Selected",
};
var modalRef = ModalService.CreateModal<ValidateFilesDialog, Game>(modalOptions, Model.DataItem as Game);

View file

@ -5,13 +5,34 @@
@inject SDK.Client Client
@inject IMessageService MessageService
<Table DataSource="Conflicts" Size="TableSize.Small" RowKey="c => c.FullName" @bind-SelectedRows="Selected">
<Table DataSource="Conflicts" Resizable Size="TableSize.Small" RowKey="c => c.FullName" @bind-SelectedRows="Selected" Loading="Loading">
<Selection Type="checkbox" />
<PropertyColumn Property="c => c.FullName" Title="Name" />
<Column TData="string" Title="Created">
@context.LocalFileInfo?.CreationTime
</Column>
<Column TData="string" Title="Modified">
@context.LocalFileInfo?.LastWriteTime
</Column>
<Column TData="string" Title="Local Size">
@if (context.LocalFileInfo?.Length > 0)
{
@ByteSizeLib.ByteSize.FromBytes(context.LocalFileInfo.Length)
}
</Column>
<PropertyColumn Property="c => c.Length" Title="Original Size">
@if (context.Length > 0)
{
@ByteSizeLib.ByteSize.FromBytes(context.Length)
}
</PropertyColumn>
</Table>
@code {
IEnumerable<ArchiveEntry> Conflicts = new List<ArchiveEntry>();
IEnumerable<ArchiveEntry> Selected = new List<ArchiveEntry>();
IEnumerable<ArchiveValidationConflict> Conflicts = new List<ArchiveValidationConflict>();
IEnumerable<ArchiveValidationConflict> Selected = new List<ArchiveValidationConflict>();
bool Loading = true;
protected override async Task OnInitializedAsync()
{
@ -26,12 +47,22 @@
async Task LoadData()
{
Conflicts = await Client.Games.ValidateFilesAsync(Options.InstallDirectory, Options.Id);
Loading = false;
}
public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args)
{
Loading = true;
StateHasChanged();
await Task.Yield();
await Client.Games.DownloadFilesAsync(Options.InstallDirectory, Options.Id, Selected.Select(e => e.FullName));
Loading = false;
StateHasChanged();
await Task.Yield();
await base.CloseFeedbackAsync();
}
}

View file

@ -3,4 +3,5 @@ public class ArchiveEntry
public string FullName { get; set; }
public string Name { get; set; }
public uint Crc32 { get; set; }
public long Length { get; set; }
}

View file

@ -0,0 +1,13 @@
using System.IO;
namespace LANCommander.SDK.Models
{
public class ArchiveValidationConflict
{
public string FullName { get; set; }
public string Name { get; set; }
public uint Crc32 { get; set; }
public long Length { get; set; }
public FileInfo LocalFileInfo { get; set; }
}
}

View file

@ -1025,20 +1025,33 @@ namespace LANCommander.SDK.Services
/// <param name="installDirectory">The game's install directory</param>
/// <param name="gameId">The game's ID</param>
/// <returns>List of file conflicts</returns>
public async Task<IEnumerable<ArchiveEntry>> ValidateFilesAsync(string installDirectory, Guid gameId)
public async Task<IEnumerable<ArchiveValidationConflict>> ValidateFilesAsync(string installDirectory, Guid gameId)
{
var manifest = await ManifestHelper.ReadAsync(installDirectory, gameId);
var archive = await Client.GetRequestAsync<Archive>($"/api/Archives/Contents/{gameId}/{manifest.Version}");
var entries = await Client.GetRequestAsync<IEnumerable<ArchiveEntry>>($"/api/Archives/{archive.Id}");
var entries = await Client.GetRequestAsync<IEnumerable<ArchiveEntry>>($"/api/Archives/Contents/{gameId}/{manifest.Version}");
var conflictedEntries = new List<ArchiveEntry>();
var conflictedEntries = new List<ArchiveValidationConflict>();
var savePathEntries = manifest.SavePaths?.SelectMany(p => Client.Saves.GetFileSavePathEntries(p, installDirectory)) ?? new List<SavePathEntry>();
foreach (var entry in entries)
{
if (savePathEntries.Any(e => e.ArchivePath.Equals(entry.FullName, StringComparison.OrdinalIgnoreCase)))
continue;
if (entry.FullName.EndsWith('/'))
continue;
var localFile = Path.Combine(installDirectory, entry.FullName.Replace('/', Path.DirectorySeparatorChar));
if (!Path.Exists(localFile))
conflictedEntries.Add(entry);
conflictedEntries.Add(new ArchiveValidationConflict
{
Name = entry.Name,
FullName = entry.FullName,
Crc32 = entry.Crc32,
Length = entry.Length,
});
else
{
uint crc = 0;
@ -1062,7 +1075,13 @@ namespace LANCommander.SDK.Services
}
if (crc == 0 || crc != entry.Crc32)
conflictedEntries.Add(entry);
conflictedEntries.Add(new ArchiveValidationConflict
{
Name = entry.Name,
FullName = entry.FullName,
Crc32 = entry.Crc32,
LocalFileInfo = new FileInfo(localFile)
});
}
}
@ -1096,7 +1115,6 @@ namespace LANCommander.SDK.Services
{
Overwrite = true,
PreserveFileTime = true,
PreserveAttributes = true,
});
}
else // Skip to next entry

View file

@ -110,7 +110,8 @@ namespace LANCommander.Server.Controllers.Api
{
FullName = entry.FullName,
Name = entry.Name,
Crc32 = entry.Crc32
Crc32 = entry.Crc32,
Length = entry.Length,
});
}
}