From ae41fae54f2fd865fed0bf13c49fa74096cf7697 Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Fri, 27 Dec 2024 08:07:10 +1100 Subject: [PATCH] WIP --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 22 ++- gaseous-server/Classes/FileSignature.cs | 25 +++- gaseous-server/Classes/ImportGames.cs | 2 +- .../Classes/Metadata/Communications.cs | 116 ++++++++++++++-- gaseous-server/Classes/Metadata/Games.cs | 22 ++- gaseous-server/Classes/Metadata/Metadata.cs | 14 +- .../Classes/Metadata/PlatformLogos.cs | 5 +- gaseous-server/Classes/Metadata/Platforms.cs | 11 +- .../Controllers/V1.0/PlatformsController.cs | 126 ++++++++++++++++-- gaseous-server/Models/PlatformMapping.cs | 11 +- gaseous-server/Models/Signatures_Games.cs | 22 +++ .../Support/DefaultPlatformLogo.svg | 3 + gaseous-server/gaseous-server.csproj | 3 +- .../wwwroot/pages/settings/mapping.js | 8 ++ gaseous-server/wwwroot/styles/style.css | 18 ++- 16 files changed, 347 insertions(+), 63 deletions(-) create mode 100644 gaseous-server/Support/DefaultPlatformLogo.svg diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c03c62b..2188fd4 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,7 +4,7 @@ FROM mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm RUN apt-get update # download and unzip EmulatorJS from CDN -RUN apt-get install -y p7zip-full +RUN apt-get install -y p7zip-full default-jdk nodejs wget RUN mkdir -p out/wwwroot/emulators/EmulatorJS RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4af9b06..8821eef 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,27 +1,25 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/dotnet { - "name": "C# (.NET)", + "name": "Gaseous C# (.NET)", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile //"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm", "dockerComposeFile": "docker-compose.yml", "service": "development", "workspaceFolder": "/workspace", - // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [5198], + "forwardPorts": [ + 5198 + ], "portsAttributes": { - "5198": { - "protocol": "http" - } + "5198": { + "protocol": "http" + } }, - // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "dotnet restore", - // Configure tool-specific properties. "customizations": { "vscode": { @@ -38,11 +36,11 @@ "ms-dotnettools.vscodeintellicode-csharp", "Zignd.html-css-class-completion", "PWABuilder.pwa-studio", - "ms-azuretools.vscode-docker" + "ms-azuretools.vscode-docker", + "SonarSource.sonarlint-vscode" ] } } - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" -} +} \ No newline at end of file diff --git a/gaseous-server/Classes/FileSignature.cs b/gaseous-server/Classes/FileSignature.cs index b7b9946..826c8c0 100644 --- a/gaseous-server/Classes/FileSignature.cs +++ b/gaseous-server/Classes/FileSignature.cs @@ -337,11 +337,23 @@ namespace gaseous_server.Classes // only IGDB metadata is supported if (metadataResult.Source == HasheousClient.Models.MetadataSources.IGDB) { - if (metadataResult.Id.Length > 0) + if (metadataResult.ImmutableId.Length > 0) { + // use immutable id + Platform hasheousPlatform = Platforms.GetPlatform(long.Parse(metadataResult.ImmutableId)); + signature.MetadataSources.AddPlatform((long)hasheousPlatform.Id, hasheousPlatform.Name, metadataResult.Source); + } + else if (metadataResult.Id.Length > 0) + { + // fall back to id Platform hasheousPlatform = Platforms.GetPlatform(metadataResult.Id); signature.MetadataSources.AddPlatform((long)hasheousPlatform.Id, hasheousPlatform.Name, metadataResult.Source); } + else + { + // no id or immutable id - use unknown platform + signature.MetadataSources.AddPlatform(0, "Unknown Platform", HasheousClient.Models.MetadataSources.None); + } } } } @@ -357,11 +369,20 @@ namespace gaseous_server.Classes // only IGDB metadata is supported if (metadataResult.Source == HasheousClient.Models.MetadataSources.IGDB) { - if (metadataResult.Id.Length > 0) + if (metadataResult.ImmutableId.Length > 0) + { + signature.MetadataSources.AddGame(long.Parse(metadataResult.ImmutableId), HasheousResult.Name, metadataResult.Source); + } + else if (metadataResult.Id.Length > 0) { gaseous_server.Models.Game hasheousGame = Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, metadataResult.Id); signature.MetadataSources.AddGame((long)hasheousGame.Id, hasheousGame.Name, metadataResult.Source); } + else + { + // no id or immutable id - use unknown game + signature.MetadataSources.AddGame(0, "Unknown Game", HasheousClient.Models.MetadataSources.None); + } } } } diff --git a/gaseous-server/Classes/ImportGames.cs b/gaseous-server/Classes/ImportGames.cs index 2a4916f..5813d68 100644 --- a/gaseous-server/Classes/ImportGames.cs +++ b/gaseous-server/Classes/ImportGames.cs @@ -561,7 +561,7 @@ namespace gaseous_server.Classes { gameSlug = game.Slug; } - string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug); + string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, platformSlug); if (!Directory.Exists(DestinationPath)) { Directory.CreateDirectory(DestinationPath); diff --git a/gaseous-server/Classes/Metadata/Communications.cs b/gaseous-server/Classes/Metadata/Communications.cs index 590639e..37cd181 100644 --- a/gaseous-server/Classes/Metadata/Communications.cs +++ b/gaseous-server/Classes/Metadata/Communications.cs @@ -1110,8 +1110,10 @@ namespace gaseous_server.Classes.Metadata public async Task GetSpecificImageFromServer(string ImagePath, string ImageId, IGDBAPI_ImageSize size, List? FallbackSizes = null) { - string originalPath = Path.Combine(ImagePath, IGDBAPI_ImageSize.original.ToString(), ImageId + ".jpg"); - string requestedPath = Path.Combine(ImagePath, size.ToString(), ImageId + ".jpg"); + string originalPath = Path.Combine(ImagePath, _MetadataSource.ToString(), IGDBAPI_ImageSize.original.ToString()); + string originalFilePath = Path.Combine(originalPath, ImageId); + string requestedPath = Path.Combine(ImagePath, _MetadataSource.ToString(), size.ToString()); + string requestedFilePath = Path.Combine(requestedPath, ImageId); // create the directory if it doesn't exist if (!Directory.Exists(Path.GetDirectoryName(originalPath))) @@ -1127,7 +1129,7 @@ namespace gaseous_server.Classes.Metadata Point resolution = Common.GetResolution(size); // check if the original image exists - if (!File.Exists(originalPath)) + if (!File.Exists(originalFilePath)) { // sleep if the rate limiter is active if (RateLimitResumeTime > DateTime.UtcNow) @@ -1147,14 +1149,21 @@ namespace gaseous_server.Classes.Metadata Communications comms = new Communications(); switch (_MetadataSource) { + case HasheousClient.Models.MetadataSources.None: + await comms.API_GetURL(ImageId, originalPath); + + return originalFilePath; + case HasheousClient.Models.MetadataSources.IGDB: + originalFilePath = originalFilePath + ".jpg"; + requestedFilePath = requestedFilePath + ".jpg"; if (Config.MetadataConfiguration.MetadataUseHasheousProxy == false) { - await comms.IGDBAPI_GetImage(ImageId, ImagePath); + await comms.IGDBAPI_GetImage(ImageId, originalPath); } else { - await comms.HasheousAPI_GetImage(ImageId, ImagePath); + await comms.HasheousAPI_GetImage(ImageId, originalPath); } break; @@ -1164,10 +1173,10 @@ namespace gaseous_server.Classes.Metadata } // check if the requested image exists - if (!File.Exists(requestedPath)) + if (!File.Exists(requestedFilePath)) { // get the original image - using (var image = new ImageMagick.MagickImage(originalPath)) + using (var image = new ImageMagick.MagickImage(originalFilePath)) { image.Resize(resolution.X, resolution.Y); image.Strip(); @@ -1175,7 +1184,7 @@ namespace gaseous_server.Classes.Metadata } } - return requestedPath; + return requestedFilePath; } public static T? GetSearchCache(string SearchFields, string SearchString) @@ -1228,6 +1237,80 @@ namespace gaseous_server.Classes.Metadata db.ExecuteNonQuery(sql, dbDict); } + public static void PopulateHasheousPlatformData(long Id) + { + // fetch all platforms + ConfigureHasheousClient(ref hasheous); + var hasheousPlatforms = hasheous.GetPlatforms(); + + foreach (var hasheousPlatform in hasheousPlatforms) + { + // check the metadata attribute for a igdb platform id + if (hasheousPlatform.Metadata != null) + { + foreach (var metadata in hasheousPlatform.Metadata) + { + if (metadata.Source == HasheousClient.Models.MetadataSources.IGDB) + { + if (metadata.ImmutableId.Length > 0) + { + long objId = 0; + long.TryParse(metadata.ImmutableId, out objId); + if (objId == Id) + { + // we have a match - check hasheousPlatform attributes for a logo + foreach (var hasheousPlatformAttribute in hasheousPlatform.Attributes) + { + if ( + hasheousPlatformAttribute.attributeType == HasheousClient.Models.AttributeItem.AttributeType.ImageId && + hasheousPlatformAttribute.attributeName == HasheousClient.Models.AttributeItem.AttributeName.Logo && + hasheousPlatformAttribute.Value != null + ) + { + Uri logoUrl = new Uri( + new Uri(HasheousClient.WebApp.HttpHelper.BaseUri, UriKind.Absolute), + new Uri("/api/v1/images/" + hasheousPlatformAttribute.Value, UriKind.Relative)); + + // generate a platform logo object + HasheousClient.Models.Metadata.IGDB.PlatformLogo platformLogo = new HasheousClient.Models.Metadata.IGDB.PlatformLogo + { + AlphaChannel = false, + Animated = false, + ImageId = (string)hasheousPlatformAttribute.Value, + Url = logoUrl.ToString() + }; + + // generate a long id from the value + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(platformLogo.ImageId); + long longId = BitConverter.ToInt64(bytes, 0); + platformLogo.Id = longId; + + // store the platform logo object + Storage.CacheStatus cacheStatus = Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.None, "PlatformLogo", longId); + switch (cacheStatus) + { + case Storage.CacheStatus.NotPresent: + Storage.NewCacheValue(HasheousClient.Models.MetadataSources.None, platformLogo, false); + break; + } + + // update the platform object + Platform? platform = Platforms.GetPlatform(Id); + if (platform != null) + { + platform.PlatformLogo = platformLogo.Id; + Storage.NewCacheValue(HasheousClient.Models.MetadataSources.None, platform, true); + } + } + } + } + } + } + } + } + } + } + /// /// See https://api-docs.igdb.com/?javascript#images for more information about the image url structure /// @@ -1238,9 +1321,8 @@ namespace gaseous_server.Classes.Metadata string urlTemplate = "https://images.igdb.com/igdb/image/upload/t_{size}/{hash}.jpg"; string url = urlTemplate.Replace("{size}", "original").Replace("{hash}", ImageId); - string newOutputPath = Path.Combine(OutputPath, "original"); string OutputFile = ImageId + ".jpg"; - string fullPath = Path.Combine(newOutputPath, OutputFile); + string fullPath = Path.Combine(OutputPath, OutputFile); await _DownloadFile(new Uri(url), fullPath); } @@ -1250,9 +1332,19 @@ namespace gaseous_server.Classes.Metadata string urlTemplate = HasheousClient.WebApp.HttpHelper.BaseUri + "api/v1/MetadataProxy/IGDB/Image/{hash}.jpg"; string url = urlTemplate.Replace("{hash}", ImageId); - string newOutputPath = Path.Combine(OutputPath, "original"); string OutputFile = ImageId + ".jpg"; - string fullPath = Path.Combine(newOutputPath, OutputFile); + string fullPath = Path.Combine(OutputPath, OutputFile); + + await _DownloadFile(new Uri(url), fullPath); + } + + public async Task API_GetURL(string FileName, string OutputPath) + { + string urlTemplate = HasheousClient.WebApp.HttpHelper.BaseUri + "api/v1/images/{imageid}"; + + string url = urlTemplate.Replace("{imageid}", FileName); + string OutputFile = FileName; + string fullPath = Path.Combine(OutputPath, OutputFile); await _DownloadFile(new Uri(url), fullPath); } diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs index 49dc206..4f49bbd 100644 --- a/gaseous-server/Classes/Metadata/Games.cs +++ b/gaseous-server/Classes/Metadata/Games.cs @@ -294,12 +294,24 @@ ORDER BY Platform.`Name`;"; if (platform.Id != 0) { Models.PlatformMapping.PlatformMapItem platformMap = PlatformMapping.GetPlatformMap((long)platform.Id); - emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration + if (platformMap != null) { - EmulatorType = platformMap.WebEmulator.Type, - Core = platformMap.WebEmulator.Core, - EnableBIOSFiles = platformMap.EnabledBIOSHashes - }; + emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration + { + EmulatorType = platformMap.WebEmulator.Type, + Core = platformMap.WebEmulator.Core, + EnableBIOSFiles = platformMap.EnabledBIOSHashes + }; + } + else + { + emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration + { + EmulatorType = "", + Core = "", + EnableBIOSFiles = new List() + }; + } } } diff --git a/gaseous-server/Classes/Metadata/Metadata.cs b/gaseous-server/Classes/Metadata/Metadata.cs index d58df4d..3af5891 100644 --- a/gaseous-server/Classes/Metadata/Metadata.cs +++ b/gaseous-server/Classes/Metadata/Metadata.cs @@ -93,21 +93,13 @@ namespace gaseous_server.Classes.Metadata // check cached metadata status // if metadata is not cached or expired, get it from the source. Otherwise, return the cached metadata Storage.CacheStatus? cacheStatus; - if (SourceType == HasheousClient.Models.MetadataSources.None) + if (idType == IdType.Long) { - // if source is None, set cache status to current - cacheStatus = Storage.CacheStatus.Current; + cacheStatus = Storage.GetCacheStatus(SourceType, type, (long)Id); } else { - if (idType == IdType.Long) - { - cacheStatus = Storage.GetCacheStatus(SourceType, type, (long)Id); - } - else - { - cacheStatus = Storage.GetCacheStatus(SourceType, type, (string)Id); - } + cacheStatus = Storage.GetCacheStatus(SourceType, type, (string)Id); } // if ForceRefresh is true, set cache status to expired if it is current diff --git a/gaseous-server/Classes/Metadata/PlatformLogos.cs b/gaseous-server/Classes/Metadata/PlatformLogos.cs index 89d56ad..969baf6 100644 --- a/gaseous-server/Classes/Metadata/PlatformLogos.cs +++ b/gaseous-server/Classes/Metadata/PlatformLogos.cs @@ -1,5 +1,6 @@ using System; using HasheousClient.Models.Metadata.IGDB; +using static gaseous_server.Models.PlatformMapping; namespace gaseous_server.Classes.Metadata @@ -12,7 +13,7 @@ namespace gaseous_server.Classes.Metadata { } - public static PlatformLogo? GetPlatformLogo(long? Id) + public static PlatformLogo? GetPlatformLogo(long? Id, HasheousClient.Models.MetadataSources SourceType = HasheousClient.Models.MetadataSources.IGDB) { if ((Id == 0) || (Id == null)) { @@ -20,7 +21,7 @@ namespace gaseous_server.Classes.Metadata } else { - PlatformLogo? RetVal = Metadata.GetMetadata(HasheousClient.Models.MetadataSources.IGDB, (long)Id, false); + PlatformLogo? RetVal = Metadata.GetMetadata(SourceType, (long)Id, false); return RetVal; } } diff --git a/gaseous-server/Classes/Metadata/Platforms.cs b/gaseous-server/Classes/Metadata/Platforms.cs index 1992b7f..9b42194 100644 --- a/gaseous-server/Classes/Metadata/Platforms.cs +++ b/gaseous-server/Classes/Metadata/Platforms.cs @@ -22,7 +22,16 @@ namespace gaseous_server.Classes.Metadata } else { - Platform? RetVal = Metadata.GetMetadata(HasheousClient.Models.MetadataSources.IGDB, (long)Id, false); + Platform? RetVal = new Platform(); + if (Config.MetadataConfiguration.DefaultMetadataSource == HasheousClient.Models.MetadataSources.None) + { + + RetVal = (Platform?)Storage.GetCacheValue(HasheousClient.Models.MetadataSources.None, RetVal, "Id", (long)Id); + } + else + { + RetVal = Metadata.GetMetadata(HasheousClient.Models.MetadataSources.IGDB, (long)Id, false); + } return RetVal; } } diff --git a/gaseous-server/Controllers/V1.0/PlatformsController.cs b/gaseous-server/Controllers/V1.0/PlatformsController.cs index ffacb69..6c65c0d 100644 --- a/gaseous-server/Controllers/V1.0/PlatformsController.cs +++ b/gaseous-server/Controllers/V1.0/PlatformsController.cs @@ -127,7 +127,7 @@ namespace gaseous_server.Controllers try { - logoObject = PlatformLogos.GetPlatformLogo((long)platformObject.PlatformLogo); + logoObject = PlatformLogos.GetPlatformLogo((long)platformObject.PlatformLogo, Communications.MetadataSource); } catch { @@ -141,17 +141,17 @@ namespace gaseous_server.Controllers } else { - return NotFound(); + return GetDummyImage(); } } else { - return NotFound(); + return GetDummyImage(); } } - string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject)); - string imagePath = Path.Combine(basePath, size.ToString(), logoObject.ImageId + ".jpg"); + string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject), Communications.MetadataSource.ToString()); + string imagePath = Path.Combine(basePath, size.ToString(), logoObject.ImageId); if (!System.IO.File.Exists(imagePath)) { @@ -171,9 +171,71 @@ namespace gaseous_server.Controllers if (System.IO.File.Exists(imagePath)) { - string filename = logoObject.ImageId + ".jpg"; + // get image info + var info = new ImageMagick.MagickImageInfo(imagePath); + string extension = ".jpg"; + string mimeType = "image/jpg"; + switch (info.Format) + { + case ImageMagick.MagickFormat.Jpeg: + extension = ".jpg"; + mimeType = "image/jpg"; + break; + + case ImageMagick.MagickFormat.Png: + extension = ".png"; + mimeType = "image/png"; + break; + + case ImageMagick.MagickFormat.Gif: + extension = ".gif"; + mimeType = "image/gif"; + break; + + case ImageMagick.MagickFormat.Bmp: + extension = ".bmp"; + mimeType = "image/bmp"; + break; + + case ImageMagick.MagickFormat.Tiff: + extension = ".tiff"; + mimeType = "image/tiff"; + break; + + case ImageMagick.MagickFormat.Unknown: + extension = ".jpg"; + mimeType = "image/jpg"; + break; + + case ImageMagick.MagickFormat.WebP: + extension = ".webp"; + mimeType = "image/webp"; + break; + + case ImageMagick.MagickFormat.Heic: + extension = ".heic"; + mimeType = "image/heic"; + break; + + case ImageMagick.MagickFormat.Heif: + extension = ".heif"; + mimeType = "image/heif"; + break; + + case ImageMagick.MagickFormat.Svg: + extension = ".svg"; + mimeType = "image/svg+xml"; + break; + + default: + extension = ".jpg"; + mimeType = "image/jpg"; + break; + } + + string filename = logoObject.ImageId + extension; string filepath = imagePath; - string contentType = "image/jpg"; + string contentType = mimeType; var cd = new System.Net.Mime.ContentDisposition { @@ -195,14 +257,60 @@ namespace gaseous_server.Controllers return File(filedata, contentType); } - - return NotFound(); + else + { + return NotFound(); + } } catch { return NotFound(); } } + + private ActionResult GetDummyImage() + { + // return resource named DefaultPlatformLogo.svg + var assembly = Assembly.GetExecutingAssembly(); + string resourceName = "gaseous_server.Support.DefaultPlatformLogo.svg"; + string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); + if (resources.Contains(resourceName)) + { + string svgData = ""; + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + using (StreamReader reader = new StreamReader(stream)) + { + svgData = reader.ReadToEnd(); + } + + var cd = new System.Net.Mime.ContentDisposition + { + FileName = "DefaultPlatformLogo.svg", + Inline = true, + }; + + Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Cache-Control", "public, max-age=604800"); + + byte[] filedata = null; + using (MemoryStream ms = new MemoryStream()) + { + using (StreamWriter writer = new StreamWriter(ms)) + { + writer.Write(svgData); + writer.Flush(); + ms.Position = 0; + filedata = ms.ToArray(); + } + } + + return File(filedata, "image/svg+xml"); + } + else + { + return NotFound(); + } + } } } diff --git a/gaseous-server/Models/PlatformMapping.cs b/gaseous-server/Models/PlatformMapping.cs index 422610a..c0d978e 100644 --- a/gaseous-server/Models/PlatformMapping.cs +++ b/gaseous-server/Models/PlatformMapping.cs @@ -102,9 +102,16 @@ namespace gaseous_server.Models AlternativeName = mapItem.AlternateNames.FirstOrDefault() }; - if (Storage.GetCacheStatus(Communications.MetadataSource, "Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent) + if (Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.None, "Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent) { - Storage.NewCacheValue(Communications.MetadataSource, platform); + Storage.NewCacheValue(HasheousClient.Models.MetadataSources.None, platform); + } + + Communications.PopulateHasheousPlatformData(mapItem.IGDBId); + + if (Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.IGDB, "Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent) + { + Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, platform); } return platform; diff --git a/gaseous-server/Models/Signatures_Games.cs b/gaseous-server/Models/Signatures_Games.cs index 157e78f..3215e91 100644 --- a/gaseous-server/Models/Signatures_Games.cs +++ b/gaseous-server/Models/Signatures_Games.cs @@ -54,6 +54,21 @@ namespace gaseous_server.Models } } + if (_flags.PlatformId == 0) + { + // fall back to the IGDB source if present + foreach (SourceValues.SourceValueItem source in MetadataSources.Platforms) + { + if (source.Source == HasheousClient.Models.MetadataSources.IGDB) + { + _flags.PlatformId = source.Id; + _flags.PlatformName = source.Name; + _flags.PlatformMetadataSource = source.Source; + break; + } + } + } + foreach (SourceValues.SourceValueItem source in MetadataSources.Games) { if (source.Source == Config.MetadataConfiguration.DefaultMetadataSource) @@ -65,6 +80,13 @@ namespace gaseous_server.Models } } + if (_flags.GameId == null || _flags.GameId == 0) + { + _flags.GameId = 0; + _flags.GameName = "Unknown Game"; + _flags.GameMetadataSource = HasheousClient.Models.MetadataSources.None; + } + return _flags; } } diff --git a/gaseous-server/Support/DefaultPlatformLogo.svg b/gaseous-server/Support/DefaultPlatformLogo.svg new file mode 100644 index 0000000..0e1945d --- /dev/null +++ b/gaseous-server/Support/DefaultPlatformLogo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/gaseous-server/gaseous-server.csproj b/gaseous-server/gaseous-server.csproj index 8b3b9d6..f11a46f 100644 --- a/gaseous-server/gaseous-server.csproj +++ b/gaseous-server/gaseous-server.csproj @@ -20,7 +20,7 @@ - + @@ -92,6 +92,7 @@ + diff --git a/gaseous-server/wwwroot/pages/settings/mapping.js b/gaseous-server/wwwroot/pages/settings/mapping.js index 9191ed8..8c16203 100644 --- a/gaseous-server/wwwroot/pages/settings/mapping.js +++ b/gaseous-server/wwwroot/pages/settings/mapping.js @@ -15,6 +15,7 @@ function loadPlatformMapping(Overwrite) { createTableRow( true, [ + '', 'Platform', 'Supported File Extensions', 'Unique File Extensions', @@ -27,6 +28,12 @@ function loadPlatformMapping(Overwrite) { ); for (let i = 0; i < result.length; i++) { + let logo = document.createElement('img'); + logo.src = '/api/v1.1/Platforms/' + result[i].igdbId + '/platformlogo/original/logo.png'; + logo.alt = result[i].igdbName; + logo.title = result[i].igdbName; + logo.classList.add('platform_image'); + let hasWebEmulator = ''; if (result[i].webEmulator.type.length > 0) { hasWebEmulator = 'Yes'; @@ -49,6 +56,7 @@ function loadPlatformMapping(Overwrite) { } let newRow = [ + logo, result[i].igdbName, result[i].extensions.supportedFileExtensions.join(', '), result[i].extensions.uniqueFileExtensions.join(', '), diff --git a/gaseous-server/wwwroot/styles/style.css b/gaseous-server/wwwroot/styles/style.css index 03c5b28..0674f01 100644 --- a/gaseous-server/wwwroot/styles/style.css +++ b/gaseous-server/wwwroot/styles/style.css @@ -2930,6 +2930,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no cursor: pointer; display: flex; gap: 20px; + padding-left: 0; align-items: center; position: relative; overflow: hidden; @@ -2946,15 +2947,24 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no .platform_item_background:hover {} .platform_image_container { - margin-left: 10px; margin-right: 10px; - max-width: 50px; - max-height: 50px; + width: 70px; + height: 70px; flex-grow: 0; + /* text-align: center; + vertical-align: middle; + line-height: 75px; */ + display: flex; + justify-content: center; + align-items: center; + + padding-left: 20px; + padding-right: 20px; + background-color: white; } .platform_image { - width: 40px; + width: 70px; } .platform_name_container {