Merge branch '2.0.0' into dotnet10-api14-migration

This commit is contained in:
defnotken
2025-11-26 16:20:38 -06:00
116 changed files with 20468 additions and 3311 deletions

View File

@@ -1,4 +1,4 @@
using LightlessSync.API.Data;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Enum;
using LightlessSync.FileCache;
using LightlessSync.Services.CharaData;
@@ -38,21 +38,16 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
public int TotalFiles { get; internal set; }
internal Dictionary<ObjectKind, Dictionary<string, FileDataEntry>> LastAnalysis { get; } = [];
public CharacterAnalysisSummary LatestSummary => _latestSummary;
public void CancelAnalyze()
{
_analysisCts?.CancelDispose();
_analysisCts = null;
}
public async Task ComputeAnalysis(bool print = true, bool recalculate = false)
{
Logger.LogDebug("=== Calculating Character Analysis ===");
_analysisCts = _analysisCts?.CancelRecreate() ?? new();
var cancelToken = _analysisCts.Token;
var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList();
var remaining = allFiles.Where(c => !c.IsComputed || recalculate).ToList();
@@ -98,27 +93,42 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
}
RecalculateSummary();
Mediator.Publish(new CharacterDataAnalyzedMessage());
_analysisCts.CancelDispose();
_analysisCts = null;
if (print) PrintAnalysis();
}
public void Dispose()
{
_analysisCts.CancelDispose();
_baseAnalysisCts.Dispose();
}
public async Task UpdateFileEntriesAsync(IEnumerable<string> filePaths, CancellationToken token)
{
var normalized = new HashSet<string>(
filePaths.Where(path => !string.IsNullOrWhiteSpace(path)),
StringComparer.OrdinalIgnoreCase);
if (normalized.Count == 0)
{
return;
}
foreach (var objectEntries in LastAnalysis.Values)
{
foreach (var entry in objectEntries.Values)
{
if (!entry.FilePaths.Any(path => normalized.Contains(path)))
{
continue;
}
token.ThrowIfCancellationRequested();
await entry.ComputeSizes(_fileCacheManager, token).ConfigureAwait(false);
}
}
}
private async Task BaseAnalysis(CharacterData charaData, CancellationToken token)
{
if (string.Equals(charaData.DataHash.Value, _lastDataHash, StringComparison.Ordinal)) return;
LastAnalysis.Clear();
foreach (var obj in charaData.FileReplacements)
{
Dictionary<string, FileDataEntry> data = new(StringComparer.OrdinalIgnoreCase);
@@ -128,7 +138,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
var fileCacheEntries = (await _fileCacheManager.GetAllFileCachesByHashAsync(fileEntry.Hash, ignoreCacheEntries: true, validate: false, token).ConfigureAwait(false)).ToList();
if (fileCacheEntries.Count == 0) continue;
var filePath = fileCacheEntries[0].ResolvedFilepath;
FileInfo fi = new(filePath);
string ext = "unk?";
@@ -140,9 +149,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
{
Logger.LogWarning(ex, "Could not identify extension for {path}", filePath);
}
var tris = await _xivDataAnalyzer.GetTrianglesByHash(fileEntry.Hash).ConfigureAwait(false);
foreach (var entry in fileCacheEntries)
{
data[fileEntry.Hash] = new FileDataEntry(fileEntry.Hash, ext,
@@ -153,17 +160,13 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
tris);
}
}
LastAnalysis[obj.Key] = data;
}
RecalculateSummary();
Mediator.Publish(new CharacterDataAnalyzedMessage());
_lastDataHash = charaData.DataHash.Value;
}
private void RecalculateSummary()
{
var builder = ImmutableDictionary.CreateBuilder<ObjectKind, CharacterAnalysisObjectSummary>();
@@ -189,7 +192,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
_latestSummary = new CharacterAnalysisSummary(builder.ToImmutable());
}
private void PrintAnalysis()
{
if (LastAnalysis.Count == 0) return;
@@ -198,7 +200,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
int fileCounter = 1;
int totalFiles = kvp.Value.Count;
Logger.LogInformation("=== Analysis for {obj} ===", kvp.Key);
foreach (var entry in kvp.Value.OrderBy(b => b.Value.GamePaths.OrderBy(p => p, StringComparer.Ordinal).First(), StringComparer.Ordinal))
{
Logger.LogInformation("File {x}/{y}: {hash}", fileCounter++, totalFiles, entry.Key);
@@ -227,7 +228,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
Logger.LogInformation("Total files: {count}, size extracted: {size}, size compressed: {sizeComp}", kvp.Value.Count,
UiSharedService.ByteToString(kvp.Value.Sum(v => v.Value.OriginalSize)), UiSharedService.ByteToString(kvp.Value.Sum(v => v.Value.CompressedSize)));
}
Logger.LogInformation("=== Total summary for all currently present objects ===");
Logger.LogInformation("Total files: {count}, size extracted: {size}, size compressed: {sizeComp}",
LastAnalysis.Values.Sum(v => v.Values.Count),
@@ -235,7 +235,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.CompressedSize))));
Logger.LogInformation("IMPORTANT NOTES:\n\r- For Lightless up- and downloads only the compressed size is relevant.\n\r- An unusually high total files count beyond 200 and up will also increase your download time to others significantly.");
}
internal sealed record FileDataEntry(string Hash, string FileType, List<string> GamePaths, List<string> FilePaths, long OriginalSize, long CompressedSize, long Triangles)
{
public bool IsComputed => OriginalSize > 0 && CompressedSize > 0;
@@ -255,7 +254,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
public long OriginalSize { get; private set; } = OriginalSize;
public long CompressedSize { get; private set; } = CompressedSize;
public long Triangles { get; private set; } = Triangles;
public Lazy<string> Format = new(() =>
{
switch (FileType)