From 869b7ea466073681859be71cd6cd3eca5752303e Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Fri, 19 Dec 2025 11:14:43 +1100 Subject: [PATCH] Fixing media importer and adding importer robustness ImportContext wasn't being set on the importers which caused problems during the import process. Fixed that and added a bunch more logging around the place so we can get better tracing into what's happening through the import pipeline --- .../Import/Factories/ImportContextFactory.cs | 2 +- .../Import/ImportContext.cs | 116 +++++++++--------- .../Import/Importers/BaseImporter.cs | 2 +- .../Import/Importers/MediaImporter.cs | 26 ++-- 4 files changed, 76 insertions(+), 70 deletions(-) diff --git a/LANCommander.Launcher.Services/Import/Factories/ImportContextFactory.cs b/LANCommander.Launcher.Services/Import/Factories/ImportContextFactory.cs index f03cadff..5bfd898b 100644 --- a/LANCommander.Launcher.Services/Import/Factories/ImportContextFactory.cs +++ b/LANCommander.Launcher.Services/Import/Factories/ImportContextFactory.cs @@ -7,7 +7,7 @@ public class ImportContextFactory(IServiceProvider serviceProvider) public ImportContext Create() { var scope = serviceProvider.CreateScope(); - + return new ImportContext(scope.ServiceProvider); } } \ No newline at end of file diff --git a/LANCommander.Launcher.Services/Import/ImportContext.cs b/LANCommander.Launcher.Services/Import/ImportContext.cs index 3c0420e8..b14aa8fb 100644 --- a/LANCommander.Launcher.Services/Import/ImportContext.cs +++ b/LANCommander.Launcher.Services/Import/ImportContext.cs @@ -6,10 +6,10 @@ using Microsoft.Extensions.Logging; namespace LANCommander.Launcher.Services.Import; -public class ImportContext +public class ImportContext { - public int Processed; - public int Total; + private int Processed; + private int Total; public AsyncEventHandler OnImportStarted { get; set; } = new(); public AsyncEventHandler OnImportComplete { get; set; } = new(); @@ -18,7 +18,6 @@ public class ImportContext private Queue Queue { get; } = new(); - #region Importers private readonly CollectionImporter _collections; private readonly DeveloperImporter _developers; private readonly EngineImporter _engines; @@ -28,27 +27,27 @@ public class ImportContext private readonly MultiplayerModeImporter _multiplayerModes; private readonly PlatformImporter _platforms; private readonly PublisherImporter _publishers; - private readonly TagImporter _tags; - #endregion - - private readonly ILogger _logger; - - public ImportContext(IServiceProvider serviceProvider) - { - _collections = serviceProvider.GetRequiredService(); - _developers = serviceProvider.GetRequiredService(); - _engines = serviceProvider.GetRequiredService(); - _games = serviceProvider.GetRequiredService(); - _genres = serviceProvider.GetRequiredService(); - _media = serviceProvider.GetRequiredService(); - _multiplayerModes = serviceProvider.GetRequiredService(); - _platforms = serviceProvider.GetRequiredService(); - _publishers = serviceProvider.GetRequiredService(); - _tags = serviceProvider.GetRequiredService(); - - _logger = serviceProvider.GetRequiredService>(); - } - + private readonly TagImporter _tags; + + private readonly ILogger _logger; + + public ImportContext(IServiceProvider serviceProvider) + { + _collections = serviceProvider.GetRequiredService(); + _developers = serviceProvider.GetRequiredService(); + _engines = serviceProvider.GetRequiredService(); + _games = serviceProvider.GetRequiredService(); + _genres = serviceProvider.GetRequiredService(); + _media = serviceProvider.GetRequiredService(); + _multiplayerModes = serviceProvider.GetRequiredService(); + _platforms = serviceProvider.GetRequiredService(); + _publishers = serviceProvider.GetRequiredService(); + _tags = serviceProvider.GetRequiredService(); + _logger = serviceProvider.GetRequiredService>(); + + SetupContextOnImporters(); + } + public async Task AddAsync(Game game) { await AddAsync(game, game.Collections, _collections); @@ -77,7 +76,9 @@ public class ImportContext { var importInfo = await importer.GetImportInfoAsync(record, manifest); - importInfo.Key = importer.GetKey(record); + importInfo.Key = importer.GetKey(record); + + _logger.LogInformation("Queuing item {ItemName} for import with key {Key}", importInfo.Name, importInfo.Key); Queue.Enqueue(importInfo); } @@ -102,6 +103,7 @@ public class ImportContext while (Queue.Count > 0) { + _logger.LogInformation("Importing item {Current} of {Total}", Processed + 1, Total); var queueItem = Queue.Dequeue(); await OnImportStatusUpdate?.InvokeAsync(new ImportStatusUpdate @@ -115,11 +117,13 @@ public class ImportContext if (success) { + _logger.LogInformation("Successfully imported item {ItemName}", queueItem.Name); Processed++; deferred = 0; continue; } - + + _logger.LogInformation("Deferring item {ItemName} for later import", queueItem.Name); Queue.Enqueue(queueItem); deferred++; @@ -134,42 +138,38 @@ public class ImportContext })!; } + private void SetupContextOnImporters() + { + _collections.UseContext(this); + _developers.UseContext(this); + _engines.UseContext(this); + _games.UseContext(this); + _genres.UseContext(this); + _media.UseContext(this); + _multiplayerModes.UseContext(this); + _platforms.UseContext(this); + _publishers.UseContext(this); + _tags.UseContext(this); + } + private async Task TryImportAsync(IImportItemInfo queueItem) { try { - switch (queueItem.Type) + return queueItem.Type switch { - case nameof(Collection): - return await _collections.ImportAsync(queueItem); - - case "Developer": - return await _developers.ImportAsync(queueItem); - - case nameof(Engine): - return await _engines.ImportAsync(queueItem); - - case nameof(Game): - return await _games.ImportAsync(queueItem); - - case nameof(Genre): - return await _genres.ImportAsync(queueItem); - - case nameof(Media): - return await _media.ImportAsync(queueItem); - - case nameof(MultiplayerMode): - return await _multiplayerModes.ImportAsync(queueItem); - - case nameof(Platform): - return await _platforms.ImportAsync(queueItem); - - case "Publisher": - return await _publishers.ImportAsync(queueItem); - - case nameof(Tag): - return await _tags.ImportAsync(queueItem); - } + nameof(Collection) => await _collections.ImportAsync(queueItem), + "Developer" => await _developers.ImportAsync(queueItem), + nameof(Engine) => await _engines.ImportAsync(queueItem), + nameof(Game) => await _games.ImportAsync(queueItem), + nameof(Genre) => await _genres.ImportAsync(queueItem), + nameof(Media) => await _media.ImportAsync(queueItem), + nameof(MultiplayerMode) => await _multiplayerModes.ImportAsync(queueItem), + nameof(Platform) => await _platforms.ImportAsync(queueItem), + "Publisher" => await _publishers.ImportAsync(queueItem), + nameof(Tag) => await _tags.ImportAsync(queueItem), + _ => throw new InvalidOperationException($"No importer found for type {queueItem.Type}"), + }; } catch (Exception ex) { diff --git a/LANCommander.Launcher.Services/Import/Importers/BaseImporter.cs b/LANCommander.Launcher.Services/Import/Importers/BaseImporter.cs index 0e7d3fdf..d9ee126c 100644 --- a/LANCommander.Launcher.Services/Import/Importers/BaseImporter.cs +++ b/LANCommander.Launcher.Services/Import/Importers/BaseImporter.cs @@ -5,7 +5,7 @@ namespace LANCommander.Launcher.Services.Import.Importers; public abstract class BaseImporter : IImporter where TRecord : class { - protected ImportContext ImportContext { get; private set; } + protected ImportContext? ImportContext { get; private set; } public void UseContext(ImportContext importContext) { diff --git a/LANCommander.Launcher.Services/Import/Importers/MediaImporter.cs b/LANCommander.Launcher.Services/Import/Importers/MediaImporter.cs index a58bab0b..38559483 100644 --- a/LANCommander.Launcher.Services/Import/Importers/MediaImporter.cs +++ b/LANCommander.Launcher.Services/Import/Importers/MediaImporter.cs @@ -16,7 +16,7 @@ public class MediaImporter( new() { Type = nameof(Media), - Name = String.IsNullOrWhiteSpace(record.Name) ? record.Type.ToString() : $"{record.Type} - {record.Name}", + Name = string.IsNullOrWhiteSpace(record.Name) ? record.Type.ToString() : $"{record.Type} - {record.Name}", Record = record, Manifest = manifest, }; @@ -35,11 +35,14 @@ public class MediaImporter( { try { - var game = importItemInfo.Manifest as Game; - - if (game == null) + if (importItemInfo.Manifest is not Game game) return false; + if (ImportContext is null) + { + throw new InvalidOperationException("The ImportContext has not been set. Ensure that the UseContext method is called before importing."); + } + if (ImportContext.InQueue(game, gameImporter)) return false; @@ -53,7 +56,7 @@ public class MediaImporter( UpdatedOn = importItemInfo.Record.UpdatedOn, SourceUrl = importItemInfo.Record.SourceUrl, MimeType = importItemInfo.Record.MimeType, - Crc32 = importItemInfo.Record.Crc32 ?? String.Empty, + Crc32 = importItemInfo.Record.Crc32 ?? string.Empty, }; media = await mediaService.AddAsync(media); @@ -62,6 +65,11 @@ public class MediaImporter( return true; } + catch(InvalidOperationException ex) + { + logger.LogError(ex, "Failed to add media due to invalid operation | {Key}", GetKey(importItemInfo.Record)); + return false; + } catch (Exception ex) { logger.LogError(ex, "Could not add media | {Key}", GetKey(importItemInfo.Record)); @@ -88,7 +96,7 @@ public class MediaImporter( existing.UpdatedOn = importItemInfo.Record.UpdatedOn; existing.SourceUrl = importItemInfo.Record.SourceUrl; existing.MimeType = importItemInfo.Record.MimeType; - existing.Crc32 = importItemInfo.Record.Crc32 ?? String.Empty; + existing.Crc32 = importItemInfo.Record.Crc32 ?? string.Empty; await mediaService.UpdateAsync(existing); await mediaService.DownloadAsync(existing); @@ -96,8 +104,6 @@ public class MediaImporter( return true; } - public override async Task ExistsAsync(ImportItemInfo importItemInfo) - { - throw new NotImplementedException(); - } + public override async Task ExistsAsync(ImportItemInfo importItemInfo) => + await mediaService.FileExists(importItemInfo.Record.Id); } \ No newline at end of file