From 3f238124428371c1caa9b1de2d6d71a6a75321bf Mon Sep 17 00:00:00 2001 From: Al <26797547+Al12rs@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:23:19 +0200 Subject: [PATCH 1/5] Various FlexPanel related layout fixes (#2088) --- .../Collections/CollectionCardView.axaml | 4 +- .../CollectionCardStyles.axaml | 47 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/NexusMods.App.UI/Pages/LibraryPage/Collections/CollectionCardView.axaml b/src/NexusMods.App.UI/Pages/LibraryPage/Collections/CollectionCardView.axaml index c27f98567d..8a0d157560 100644 --- a/src/NexusMods.App.UI/Pages/LibraryPage/Collections/CollectionCardView.axaml +++ b/src/NexusMods.App.UI/Pages/LibraryPage/Collections/CollectionCardView.axaml @@ -28,9 +28,9 @@ - + - + diff --git a/src/Themes/NexusMods.Themes.NexusFluentDark/Styles/UserControls/CollectionCards/CollectionCardStyles.axaml b/src/Themes/NexusMods.Themes.NexusFluentDark/Styles/UserControls/CollectionCards/CollectionCardStyles.axaml index f81bb93feb..2d32f2af4b 100644 --- a/src/Themes/NexusMods.Themes.NexusFluentDark/Styles/UserControls/CollectionCards/CollectionCardStyles.axaml +++ b/src/Themes/NexusMods.Themes.NexusFluentDark/Styles/UserControls/CollectionCards/CollectionCardStyles.axaml @@ -42,14 +42,14 @@ - - + + From dedc86e021a6d317b9a363008d5960373ff4773e Mon Sep 17 00:00:00 2001 From: erri120 Date: Sat, 28 Sep 2024 14:55:47 +0200 Subject: [PATCH 2/5] Find GOG games installed with the Heroic launcher Part of #1695. This only finds the games, we still need a way to run them through the launcher. --- Directory.Packages.props | 3 +- .../HeroicGogLocator.cs | 53 +++++++++++++++++++ .../NexusMods.StandardGameLocators.csproj | 1 + .../Services.cs | 3 ++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/NexusMods.StandardGameLocators/HeroicGogLocator.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index a9c14aa18c..08d9f4499e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,7 @@ + @@ -120,7 +121,7 @@ - + diff --git a/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs b/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs new file mode 100644 index 0000000000..9a9f5abbcb --- /dev/null +++ b/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs @@ -0,0 +1,53 @@ +using GameFinder.Launcher.Heroic; +using GameFinder.StoreHandlers.GOG; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NexusMods.Abstractions.GameLocators; +using NexusMods.Abstractions.GameLocators.Stores.GOG; +using NexusMods.Abstractions.Games.Stores.GOG; + +namespace NexusMods.StandardGameLocators; + +/// +/// Find GOG games installed with the Heroic launcher. +/// +public class HeroicGogLocator : IGameLocator +{ + private readonly ILogger _logger; + + private readonly HeroicGOGHandler _handler; + private IReadOnlyDictionary? _cachedGames; + + /// + /// Constructor. + /// + public HeroicGogLocator(IServiceProvider provider) + { + _logger = provider.GetRequiredService>(); + _handler = provider.GetRequiredService(); + } + + public IEnumerable Find(ILocatableGame game) + { + if (game is not IGogGame tg) yield break; + + if (_cachedGames is null) + { + _cachedGames = _handler.FindAllGamesById(out var errors); + if (errors.Any()) + { + foreach (var error in errors) + _logger.LogError("While looking for games: {Error}", error); + } + } + + foreach (var id in tg.GogIds) + { + if (!_cachedGames.TryGetValue(GOGGameId.From(id), out var found)) continue; + yield return new GameLocatorResult(found.Path, GameStore.GOG, new GOGLocatorResultMetadata + { + Id = id, + }); + } + } +} diff --git a/src/NexusMods.StandardGameLocators/NexusMods.StandardGameLocators.csproj b/src/NexusMods.StandardGameLocators/NexusMods.StandardGameLocators.csproj index 27a773f608..853d0bf61a 100644 --- a/src/NexusMods.StandardGameLocators/NexusMods.StandardGameLocators.csproj +++ b/src/NexusMods.StandardGameLocators/NexusMods.StandardGameLocators.csproj @@ -4,6 +4,7 @@ + diff --git a/src/NexusMods.StandardGameLocators/Services.cs b/src/NexusMods.StandardGameLocators/Services.cs index 9c50d35589..b93cd89377 100644 --- a/src/NexusMods.StandardGameLocators/Services.cs +++ b/src/NexusMods.StandardGameLocators/Services.cs @@ -1,5 +1,6 @@ using System.Text.Json.Serialization; using GameFinder.Common; +using GameFinder.Launcher.Heroic; using GameFinder.RegistryUtils; using GameFinder.StoreHandlers.EADesktop; using GameFinder.StoreHandlers.EADesktop.Crypto; @@ -61,6 +62,7 @@ public static IServiceCollection AddStandardGameLocators( onLinux: () => { services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -92,6 +94,7 @@ public static IServiceCollection AddStandardGameLocators( onLinux: () => { services.AddSingleton>(provider => new SteamHandler(provider.GetRequiredService(), registry: null)); + services.AddSingleton(provider => new HeroicGOGHandler(provider.GetRequiredService())); services.AddSingleton>(provider => new DefaultWinePrefixManager(provider.GetRequiredService())); services.AddSingleton>(provider => new BottlesWinePrefixManager(provider.GetRequiredService())); From d56347fadac052142928c71db076989016522f90 Mon Sep 17 00:00:00 2001 From: erri120 Date: Sat, 28 Sep 2024 15:08:32 +0200 Subject: [PATCH 3/5] Fix tests --- tests/NexusMods.StandardGameLocators.TestHelpers/Services.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/NexusMods.StandardGameLocators.TestHelpers/Services.cs b/tests/NexusMods.StandardGameLocators.TestHelpers/Services.cs index 73d4e62872..5b970ad346 100644 --- a/tests/NexusMods.StandardGameLocators.TestHelpers/Services.cs +++ b/tests/NexusMods.StandardGameLocators.TestHelpers/Services.cs @@ -1,4 +1,5 @@ using GameFinder.Common; +using GameFinder.Launcher.Heroic; using GameFinder.StoreHandlers.EADesktop; using GameFinder.StoreHandlers.EGS; using GameFinder.StoreHandlers.GOG; @@ -75,6 +76,8 @@ public static IServiceCollection AddStubbedGameLocators(this IServiceCollection if (OSInformation.Shared.IsLinux) { + coll.AddSingleton(s => new HeroicGOGHandler(s.GetRequiredService())); + coll.AddSingleton>(s => new StubbedWinePrefixManager(s.GetRequiredService(), tfm => new WinePrefix From 0b1255444cee25212014eda1a76d21df6ddf74d4 Mon Sep 17 00:00:00 2001 From: erri120 Date: Sun, 29 Sep 2024 17:56:32 +0200 Subject: [PATCH 4/5] Run through heroic --- .../Stores/GOG/GOGLocatorResultMetadata.cs | 3 +++ .../RunGameTool.cs | 23 ++++++++++++++++--- .../HeroicGogLocator.cs | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/GOG/GOGLocatorResultMetadata.cs b/src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/GOG/GOGLocatorResultMetadata.cs index d5cba47d39..1c471ca5cb 100644 --- a/src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/GOG/GOGLocatorResultMetadata.cs +++ b/src/Abstractions/NexusMods.Abstractions.GameLocators/Stores/GOG/GOGLocatorResultMetadata.cs @@ -14,3 +14,6 @@ public record GOGLocatorResultMetadata : IGameLocatorResultMetadata /// public required long Id { get; init; } } + +[PublicAPI] +public record HeroicGOGLocatorResultMetadata : GOGLocatorResultMetadata; diff --git a/src/Abstractions/NexusMods.Abstractions.Games/RunGameTool.cs b/src/Abstractions/NexusMods.Abstractions.Games/RunGameTool.cs index 39c9851350..1748a1835a 100644 --- a/src/Abstractions/NexusMods.Abstractions.Games/RunGameTool.cs +++ b/src/Abstractions/NexusMods.Abstractions.Games/RunGameTool.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging; using NexusMods.Abstractions.GameLocators; using NexusMods.Abstractions.Games.DTO; +using NexusMods.Abstractions.Games.Stores.GOG; using NexusMods.Abstractions.Games.Stores.Steam; using NexusMods.Abstractions.Loadouts; using NexusMods.CrossPlatform.Process; @@ -60,10 +61,18 @@ public async Task Execute(Loadout.ReadOnly loadout, CancellationToken cancellati var program = await GetGamePath(loadout); var primaryFile = _game.GetPrimaryFile(loadout.InstallationInstance.Store).CombineChecked(loadout.InstallationInstance); - if (OSInformation.Shared.IsLinux && program.Equals(primaryFile) && loadout.InstallationInstance.LocatorResultMetadata is SteamLocatorResultMetadata steamLocatorResultMetadata) + if (OSInformation.Shared.IsLinux && program.Equals(primaryFile)) { - await RunThroughSteam(steamLocatorResultMetadata.AppId, cancellationToken); - return; + var locator = loadout.InstallationInstance.LocatorResultMetadata; + switch (locator) + { + case SteamLocatorResultMetadata steamLocatorResultMetadata: + await RunThroughSteam(steamLocatorResultMetadata.AppId, cancellationToken); + return; + case HeroicGOGLocatorResultMetadata heroicGOGLocatorResultMetadata: + await RunThroughHeroic("gog", heroicGOGLocatorResultMetadata.Id, cancellationToken); + return; + } } var names = new HashSet @@ -177,6 +186,14 @@ private async Task RunThroughSteam(uint appId, CancellationToken cancellationTok await reaper.WaitForExitAsync(cancellationToken); } + private async Task RunThroughHeroic(string type, long appId, CancellationToken cancellationToken) + { + Debug.Assert(OSInformation.Shared.IsLinux); + + // TODO: track process + await _osInterop.OpenUrl(new Uri($"heroic://launch/{type}/{appId.ToString(CultureInfo.InvariantCulture)}"), fireAndForget: true, cancellationToken: cancellationToken); + } + private async ValueTask WaitForProcessToStart( string processName, TimeSpan timeout, diff --git a/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs b/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs index 9a9f5abbcb..e7e80b1947 100644 --- a/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs +++ b/src/NexusMods.StandardGameLocators/HeroicGogLocator.cs @@ -44,7 +44,7 @@ public IEnumerable Find(ILocatableGame game) foreach (var id in tg.GogIds) { if (!_cachedGames.TryGetValue(GOGGameId.From(id), out var found)) continue; - yield return new GameLocatorResult(found.Path, GameStore.GOG, new GOGLocatorResultMetadata + yield return new GameLocatorResult(found.Path, GameStore.GOG, new HeroicGOGLocatorResultMetadata { Id = id, }); From b7ab9933382b6a158b57331c9062dc643ee9071e Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Mon, 30 Sep 2024 06:40:25 -0600 Subject: [PATCH 5/5] Switch GraphQL calls to fragments (#2102) * Switch GraphQL calls to fragments * Re-add missing GQL file --- .../GraphQLResolver.cs | 6 +- .../Extensions/FragmentExtensions.cs | 70 +++++++++++++++++++ .../GraphQL/CollectionRevisionInfo.graphql | 19 ++--- .../GraphQL/CommonFragments.graphql | 24 +++++++ .../GraphQL/ModInfo.graphql | 12 ++++ .../NexusModsLibrary.cs | 63 ++++------------- 6 files changed, 127 insertions(+), 67 deletions(-) create mode 100644 src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs create mode 100644 src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CommonFragments.graphql create mode 100644 src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/ModInfo.graphql diff --git a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/GraphQLResolver.cs b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/GraphQLResolver.cs index 053b2cba0f..e278def657 100644 --- a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/GraphQLResolver.cs +++ b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/GraphQLResolver.cs @@ -20,14 +20,14 @@ public readonly struct GraphQLResolver(ITransaction Tx, ReadOnlyModel Model) /// /// Create a new resolver using the given primary key attribute and value. /// - public static GraphQLResolver Create(IDb referenceDb, ITransaction tx, ScalarAttribute primaryKeyAttribute, THighLevel primaryKeyValue) where THighLevel : notnull + public static GraphQLResolver Create(IDb db, ITransaction tx, ScalarAttribute primaryKeyAttribute, THighLevel primaryKeyValue) where THighLevel : notnull { - var existing = referenceDb.Datoms(primaryKeyAttribute, primaryKeyValue); + var existing = db.Datoms(primaryKeyAttribute, primaryKeyValue); var exists = existing.Count > 0; var id = existing.Count == 0 ? tx.TempId() : existing[0].E; if (!exists) tx.Add(id, primaryKeyAttribute, primaryKeyValue); - return new GraphQLResolver(tx, new ReadOnlyModel(referenceDb, id)); + return new GraphQLResolver(tx, new ReadOnlyModel(db, id)); } /// diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs b/src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs new file mode 100644 index 0000000000..4151a111e1 --- /dev/null +++ b/src/Networking/NexusMods.Networking.NexusWebApi/Extensions/FragmentExtensions.cs @@ -0,0 +1,70 @@ +using NexusMods.Abstractions.Games.DTO; +using NexusMods.Abstractions.NexusModsLibrary; +using NexusMods.Abstractions.NexusModsLibrary.Models; +using NexusMods.Abstractions.NexusWebApi.Types; +using NexusMods.MnemonicDB.Abstractions; +using NexusMods.Paths; + +namespace NexusMods.Networking.NexusWebApi.Extensions; + +/// +/// Extensions to GraphQL fragments. +/// +public static class FragmentExtensions +{ + /// + /// Resolves the IUserFragment to an entity in the database, inserting or updating as necessary. + /// + public static async Task Resolve(this IUserFragment userFragment, IDb db, ITransaction tx, HttpClient client, CancellationToken token) + { + var userResolver = GraphQLResolver.Create(db, tx, User.NexusId, (ulong)userFragment.MemberId); + userResolver.Add(User.Name, userFragment.Name); + userResolver.Add(User.Avatar, new Uri(userFragment.Avatar)); + + var avatarImage = await DownloadImage(client, userFragment.Avatar, token); + userResolver.Add(User.AvatarImage,avatarImage); + return userResolver.Id; + } + + /// + /// Resolves the IModFragment to an entity in the database, inserting or updating as necessary. + /// + public static EntityId Resolve(this IModFileFragment modFileFragment, IDb db, ITransaction tx, EntityId modEId) + { + var nexusFileResolver = GraphQLResolver.Create(db, tx, (NexusModsFileMetadata.FileId, FileId.From((ulong)modFileFragment.FileId)), (NexusModsFileMetadata.ModPageId, modEId)); + nexusFileResolver.Add(NexusModsFileMetadata.ModPageId, modEId); + nexusFileResolver.Add(NexusModsFileMetadata.Name, modFileFragment.Name); + nexusFileResolver.Add(NexusModsFileMetadata.Version, modFileFragment.Version); + if (ulong.TryParse(modFileFragment.SizeInBytes, out var size)) + nexusFileResolver.Add(NexusModsFileMetadata.Size, Size.From(size)); + return nexusFileResolver.Id; + } + + /// + /// Resolves the IModFragment to an entity in the database, inserting or updating as necessary. + /// + public static EntityId Resolve(this IModFragment modFragment, IDb db, ITransaction tx) + { + var nexusModResolver = GraphQLResolver.Create(db, tx, NexusModsModPageMetadata.ModId, + ModId.From((ulong)modFragment.ModId)); + + nexusModResolver.Add(NexusModsModPageMetadata.Name, modFragment.Name); + nexusModResolver.Add(NexusModsModPageMetadata.GameDomain, GameDomain.From(modFragment.Game.DomainName)); + + if (Uri.TryCreate(modFragment.PictureUrl, UriKind.Absolute, out var fullSizedPictureUri)) + nexusModResolver.Add(NexusModsModPageMetadata.FullSizedPictureUri, fullSizedPictureUri); + + if (Uri.TryCreate(modFragment.ThumbnailUrl, UriKind.Absolute, out var thumbnailUri)) + nexusModResolver.Add(NexusModsModPageMetadata.ThumbnailUri, thumbnailUri); + return nexusModResolver.Id; + } + + private static async Task DownloadImage(HttpClient client, string? uri, CancellationToken token) + { + if (uri is null) return []; + if (!Uri.TryCreate(uri, UriKind.Absolute, out var imageUri)) return []; + + return await client.GetByteArrayAsync(imageUri, token); + } + +} diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CollectionRevisionInfo.graphql b/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CollectionRevisionInfo.graphql index 8473b39dda..efea25fd58 100644 --- a/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CollectionRevisionInfo.graphql +++ b/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CollectionRevisionInfo.graphql @@ -1,3 +1,4 @@ +#import { UserFragment, ModFragment, ModFileFragment } from './CommonFragments.graphql'; # Pulls all the information we need about a collection revision. query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultContent: Boolean!) @@ -20,18 +21,9 @@ query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultCo gameId, fileId, file { - name, - modId, - fileId, - version, - sizeInBytes, + ...ModFileFragment mod { - name - game { - domainName - } - thumbnailUrl - pictureUrl + ...ModFragment } } updatePolicy, @@ -53,11 +45,8 @@ query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultCo id } user { - name - avatar - memberId + ...UserFragment } - } } } diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CommonFragments.graphql b/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CommonFragments.graphql new file mode 100644 index 0000000000..f27b5fada4 --- /dev/null +++ b/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/CommonFragments.graphql @@ -0,0 +1,24 @@ + +fragment UserFragment on User { + name + avatar + memberId +} + +fragment ModFileFragment on ModFile { + name, + modId, + fileId, + version, + sizeInBytes +} + +fragment ModFragment on Mod { + modId + name + game { + domainName + } + thumbnailUrl + pictureUrl +} diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/ModInfo.graphql b/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/ModInfo.graphql new file mode 100644 index 0000000000..894a14b68c --- /dev/null +++ b/src/Networking/NexusMods.Networking.NexusWebApi/GraphQL/ModInfo.graphql @@ -0,0 +1,12 @@ +#include { ModFragment } from './CommonFragments.graphql' + +query ModInfo($gameDomain: String!, $modId: Int!) +{ + legacyModsByDomain(ids: [{gameDomain: $gameDomain, modId: $modId}]) + { + nodes + { + ...ModFragment + } + } +} diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs index 08880a2093..e378abe252 100644 --- a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs +++ b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs @@ -12,6 +12,7 @@ using NexusMods.Extensions.BCL; using NexusMods.MnemonicDB.Abstractions; using NexusMods.Networking.HttpDownloader; +using NexusMods.Networking.NexusWebApi.Extensions; using NexusMods.Paths; using User = NexusMods.Abstractions.NexusModsLibrary.Models.User; @@ -48,28 +49,15 @@ public NexusModsLibrary(IServiceProvider serviceProvider) using var tx = _connection.BeginTransaction(); - var modInfo = await _apiClient.ModInfoAsync(gameDomain.ToString(), modId, cancellationToken); - - var newModPage = new NexusModsModPageMetadata.New(tx) - { - Name = modInfo.Data.Name, - ModId = modId, - GameDomain = gameDomain, - }; - - if (Uri.TryCreate(modInfo.Data.PictureUrl, UriKind.Absolute, out var fullSizedPictureUri)) + var modInfo = await _gqlClient.ModInfo.ExecuteAsync(gameDomain.ToString(), (int)modId.Value, cancellationToken); + EntityId first = default; + foreach (var node in modInfo.Data!.LegacyModsByDomain.Nodes) { - newModPage.FullSizedPictureUri = fullSizedPictureUri; - - var thumbnailUrl = modInfo.Data.PictureUrl.Replace("/images/", "/images/thumbnails/", StringComparison.OrdinalIgnoreCase); - if (Uri.TryCreate(thumbnailUrl, UriKind.Absolute, out var thumbnailUri)) - { - newModPage.ThumbnailUri = thumbnailUri; - } + first = node.Resolve(_connection.Db, tx); } - + var txResults = await tx.Commit(); - return txResults.Remap(newModPage); + return NexusModsModPageMetadata.Load(txResults.Db, txResults[first]); } /// @@ -88,7 +76,6 @@ public NexusModsLibrary(IServiceProvider serviceProvider) var db = _connection.Db; var collectionInfo = info.Data!.CollectionRevision.Collection; var collectionTileImage = DownloadImage(collectionInfo.TileImage?.ThumbnailUrl, token); - var avatarImage = DownloadImage(collectionInfo.User.Avatar, token); var collectionBackgroundImage = DownloadImage(collectionInfo.HeaderImage?.Url, token); // Remap the collection info @@ -99,13 +86,8 @@ public NexusModsLibrary(IServiceProvider serviceProvider) collectionResolver.Add(CollectionMetadata.TileImage, await collectionTileImage); collectionResolver.Add(CollectionMetadata.BackgroundImage, await collectionBackgroundImage); - // Remap the user info - var userResolver = GraphQLResolver.Create(db, tx, User.NexusId, (ulong)collectionInfo.User.MemberId); - userResolver.Add(User.Name, collectionInfo.User.Name); - userResolver.Add(User.Avatar, new Uri(collectionInfo.User.Avatar)); - userResolver.Add(User.AvatarImage, await avatarImage); - - collectionResolver.Add(CollectionMetadata.Author, userResolver.Id); + var user = await collectionInfo.User.Resolve(db, tx, _httpClient, token); + collectionResolver.Add(CollectionMetadata.Author, user); // Remap the revision info var revisionInfo = info.Data!.CollectionRevision; @@ -121,38 +103,21 @@ public NexusModsLibrary(IServiceProvider serviceProvider) foreach (var file in revisionInfo.ModFiles) { var fileInfo = file.File!; - var modInfo = fileInfo.Mod; - var nexusModResolver = GraphQLResolver.Create(db, tx, NexusModsModPageMetadata.ModId, ModId.From((ulong)fileInfo.ModId)); - nexusModResolver.Add(NexusModsModPageMetadata.Name, modInfo.Name); - nexusModResolver.Add(NexusModsModPageMetadata.GameDomain, GameDomain.From(modInfo.Game.DomainName)); - if (Uri.TryCreate(modInfo.PictureUrl, UriKind.Absolute, out var fullSizedPictureUri)) - nexusModResolver.Add(NexusModsModPageMetadata.FullSizedPictureUri, fullSizedPictureUri); - - if (Uri.TryCreate(modInfo.ThumbnailUrl, UriKind.Absolute, out var thumbnailUri)) - nexusModResolver.Add(NexusModsModPageMetadata.ThumbnailUri, thumbnailUri); - - - var nexusFileResolver = GraphQLResolver.Create(db, tx, (NexusModsFileMetadata.FileId, FileId.From((ulong)fileInfo.FileId)), (NexusModsFileMetadata.ModPageId, nexusModResolver.Id)); - nexusFileResolver.Add(NexusModsFileMetadata.ModPageId, nexusModResolver.Id); - nexusFileResolver.Add(NexusModsFileMetadata.Name, fileInfo.Name); - nexusFileResolver.Add(NexusModsFileMetadata.Version, fileInfo.Version); - nexusFileResolver.Add(NexusModsFileMetadata.Size, Size.FromLong(long.Parse(fileInfo.SizeInBytes!))); + var modEId = fileInfo.Mod.Resolve(db, tx); + var modfile = fileInfo.Resolve(db, tx, modEId); var revisionFileResolver = GraphQLResolver.Create(db, tx, CollectionRevisionModFile.FileId, ulong.Parse(file.Id)); revisionFileResolver.Add(CollectionRevisionModFile.CollectionRevision, revisionResolver.Id); - revisionFileResolver.Add(CollectionRevisionModFile.NexusModFile, nexusFileResolver.Id); + revisionFileResolver.Add(CollectionRevisionModFile.NexusModFile, modfile); revisionFileResolver.Add(CollectionRevisionModFile.IsOptional, file.Optional); } var txResults = await tx.Commit(); return CollectionRevisionMetadata.Load(txResults.Db, txResults[revisionResolver.Id]); } - - /// - /// Load an image from a URI - /// - public async Task DownloadImage(string? uri, CancellationToken token) + + private async Task DownloadImage(string? uri, CancellationToken token) { if (uri is null) return []; if (!Uri.TryCreate(uri, UriKind.Absolute, out var imageUri)) return [];