using System.Text; using IGDB; using LANCommander.SDK.Models.Manifest; using Microsoft.Extensions.Logging; namespace LANCommander.Server.Services.Providers.Metadata; public class IgdbMetadataProvider( SettingsProvider settingsProvider, ILogger logger) : IMetadataProvider { private IGDBClient? _client; private const string DefaultFields = "*"; private void Authenticate() { if (_client is not null) return; var clientId = settingsProvider.CurrentValue.Server.IGDB.ClientId; var clientSecret = settingsProvider.CurrentValue.Server.IGDB.ClientSecret; // Client will throw exceptions if no credentials are configured _client = new IGDBClient(clientId, clientSecret); } public string ProviderName => "IGDB"; public bool IsAvailable => !string.IsNullOrWhiteSpace(settingsProvider.CurrentValue.Server.IGDB.ClientId) && !string.IsNullOrWhiteSpace(settingsProvider.CurrentValue.Server.IGDB.ClientSecret); public async Task?> SearchGamesAsync(string input, int limit = 10, int offset = 0) { Authenticate(); var fields = DefaultFields.Split(',').ToList(); fields.Add("involved_companies.developer"); fields.Add("involved_companies.publisher"); fields.Add("involved_companies.company.name"); fields.Add("platforms.*"); var sb = new StringBuilder(); sb.Append($"search \"{input}\";"); sb.Append($"fields {String.Join(',', fields)};"); sb.Append($"limit {limit};"); sb.Append($"offset {offset};"); var results = await _client!.QueryAsync(IGDBClient.Endpoints.Games, sb.ToString()); return new MetadataSearchResultsCollection(results .Where(g => g is not null && g.Id is not null) .Select(g => new MetadataSearchResult(g?.Id?.ToString() ?? string.Empty, ConvertGame(g))).ToList(), results.Length == limit); } public async Task GetGameAsync(string gameId) { var fields = DefaultFields.Split(',').ToList(); fields.Add("genres.*"); fields.Add("game_modes.*"); fields.Add("multiplayer_modes.*"); fields.Add("release_dates.*"); fields.Add("platforms.*"); fields.Add("keywords.*"); fields.Add("involved_companies.developer"); fields.Add("involved_companies.publisher"); fields.Add("involved_companies.company.name"); fields.Add("cover.*"); fields.Add("game_engines.*"); var games = await _client.QueryAsync(IGDBClient.Endpoints.Games, $"fields {String.Join(',', fields)}; where id = {gameId};"); if (games is null) return null; return ConvertGame(games.FirstOrDefault()); } private Game ConvertGame(IGDB.Models.Game? igdbGame) { var game = new Game { Title = igdbGame.Name, Description = igdbGame.Summary, ReleasedOn = igdbGame.FirstReleaseDate.GetValueOrDefault().UtcDateTime, ExternalIds = new List { new GameExternalId { Provider = "IGDB", ExternalId = igdbGame.Id?.ToString() ?? string.Empty } }, }; if (igdbGame.GameModes?.Values?.Any() ?? false) game.Singleplayer = igdbGame.GameModes.Values.Any(gm => gm.Name == "Single player"); if (igdbGame.InvolvedCompanies?.Values?.Any() ?? false) { game.Developers = igdbGame.InvolvedCompanies.Values .Where(c => c.Developer.GetValueOrDefault()) .Select(c => new Company { Name = c.Company?.Value?.Name }) .ToList(); game.Publishers = igdbGame.InvolvedCompanies.Values .Where(c => c.Publisher.GetValueOrDefault()) .Select(c => new Company { Name = c.Company?.Value?.Name }) .ToList(); } if (igdbGame.GameEngines?.Values?.Any() ?? false) { game.Engine = new Engine { Name = igdbGame.GameEngines.Values.FirstOrDefault()?.Name ?? string.Empty }; } if (igdbGame.Platforms?.Values?.Any() ?? false) { game.Platforms = igdbGame.Platforms.Values .Select(p => new Platform { Name = p.Name }) .ToList(); } if (igdbGame.Genres?.Values?.Any() ?? false) { game.Genres = igdbGame.Genres.Values .Select(g => new Genre { Name = g.Name }) .ToList(); } if (igdbGame.Keywords?.Values?.Any() ?? false) { game.Tags = igdbGame.Keywords.Values .Select(k => new Tag { Name = k.Name }) .ToList(); } return game; } }