diff --git a/gaseous-server/Classes/Common.cs b/gaseous-server/Classes/Common.cs index 3118396..90e4a50 100644 --- a/gaseous-server/Classes/Common.cs +++ b/gaseous-server/Classes/Common.cs @@ -1,9 +1,11 @@ using System.Collections.Concurrent; using System.ComponentModel; using System.Data; +using System.Drawing; using System.IO.Compression; using System.Reflection; using System.Security.Cryptography; +using static gaseous_server.Classes.Metadata.Communications; namespace gaseous_server.Classes { @@ -126,6 +128,27 @@ namespace gaseous_server.Classes typeof(DescriptionAttribute)))?.Description ?? value.ToString(); } + public static Point GetResolution(this Enum value) + { + string width = ((ResolutionAttribute)Attribute.GetCustomAttribute( + value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static) + .Single(x => x.GetValue(null).Equals(value)), + typeof(ResolutionAttribute)))?.width.ToString() ?? value.ToString(); + + string height = ((ResolutionAttribute)Attribute.GetCustomAttribute( + value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static) + .Single(x => x.GetValue(null).Equals(value)), + typeof(ResolutionAttribute)))?.height.ToString() ?? value.ToString(); + + return new Point(int.Parse(width), int.Parse(height)); + } + + public static bool IsNullableEnum(this Type t) + { + Type u = Nullable.GetUnderlyingType(t); + return u != null && u.IsEnum; + } + // compression public static byte[] Compress(byte[] data) { diff --git a/gaseous-server/Classes/ImportGames.cs b/gaseous-server/Classes/ImportGames.cs index 07c5d5e..8799e98 100644 --- a/gaseous-server/Classes/ImportGames.cs +++ b/gaseous-server/Classes/ImportGames.cs @@ -213,30 +213,37 @@ namespace gaseous_server.Classes { Logging.Log(Logging.LogType.Information, "Import Game", " Search type: " + searchType.ToString()); IGDB.Models.Game[] games = Metadata.Games.SearchForGame(SearchCandidate, PlatformId, searchType); - if (games.Length == 1) + if (games != null) { - // exact match! - determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false); - Logging.Log(Logging.LogType.Information, "Import Game", " IGDB game: " + determinedGame.Name); - GameFound = true; - break; - } - else if (games.Length > 0) - { - Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found"); - - // quite likely we've found sequels and alternate types - foreach (Game game in games) + if (games.Length == 1) { - if (game.Name == SearchCandidate) + // exact match! + determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false); + Logging.Log(Logging.LogType.Information, "Import Game", " IGDB game: " + determinedGame.Name); + GameFound = true; + break; + } + else if (games.Length > 0) + { + Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found"); + + // quite likely we've found sequels and alternate types + foreach (Game game in games) { - // found game title matches the search candidate - determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false); - Logging.Log(Logging.LogType.Information, "Import Game", "Found exact match!"); - GameFound = true; - break; + if (game.Name == SearchCandidate) + { + // found game title matches the search candidate + determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false); + Logging.Log(Logging.LogType.Information, "Import Game", "Found exact match!"); + GameFound = true; + break; + } } } + else + { + Logging.Log(Logging.LogType.Information, "Import Game", " No search results found"); + } } else { diff --git a/gaseous-server/Classes/Metadata/Communications.cs b/gaseous-server/Classes/Metadata/Communications.cs index 540f3d4..c6c6480 100644 --- a/gaseous-server/Classes/Metadata/Communications.cs +++ b/gaseous-server/Classes/Metadata/Communications.cs @@ -1,6 +1,7 @@ using System.Collections; using System.ComponentModel; using System.Data; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Net; using System.Reflection; @@ -666,73 +667,73 @@ namespace gaseous_server.Classes.Metadata switch (typeName) { case "agerating": - HasheousClient.Models.Metadata.IGDB.AgeRating ageRatingResult = new AgeRating(); + HasheousClient.Models.Metadata.IGDB.AgeRating ageRatingResult = new HasheousClient.Models.Metadata.IGDB.AgeRating(); ageRatingResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(ageRatingResult) }; case "ageratingcontentdescription": - HasheousClient.Models.Metadata.IGDB.AgeRatingContentDescription ageRatingContentDescriptionResult = new AgeRatingContentDescription(); + HasheousClient.Models.Metadata.IGDB.AgeRatingContentDescription ageRatingContentDescriptionResult = new HasheousClient.Models.Metadata.IGDB.AgeRatingContentDescription(); ageRatingContentDescriptionResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(ageRatingContentDescriptionResult) }; case "alternativename": - HasheousClient.Models.Metadata.IGDB.AlternativeName alternativeNameResult = new AlternativeName(); + HasheousClient.Models.Metadata.IGDB.AlternativeName alternativeNameResult = new HasheousClient.Models.Metadata.IGDB.AlternativeName(); alternativeNameResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(alternativeNameResult) }; case "artwork": - HasheousClient.Models.Metadata.IGDB.Artwork artworkResult = new Artwork(); + HasheousClient.Models.Metadata.IGDB.Artwork artworkResult = new HasheousClient.Models.Metadata.IGDB.Artwork(); artworkResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(artworkResult) }; case "collection": - HasheousClient.Models.Metadata.IGDB.Collection collectionResult = new Collection(); + HasheousClient.Models.Metadata.IGDB.Collection collectionResult = new HasheousClient.Models.Metadata.IGDB.Collection(); collectionResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(collectionResult) }; case "company": - HasheousClient.Models.Metadata.IGDB.Company companyResult = new Company(); + HasheousClient.Models.Metadata.IGDB.Company companyResult = new HasheousClient.Models.Metadata.IGDB.Company(); companyResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(companyResult) }; case "companylogo": - HasheousClient.Models.Metadata.IGDB.CompanyLogo companyLogoResult = new CompanyLogo(); + HasheousClient.Models.Metadata.IGDB.CompanyLogo companyLogoResult = new HasheousClient.Models.Metadata.IGDB.CompanyLogo(); companyLogoResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(companyLogoResult) }; case "companywebsite": - HasheousClient.Models.Metadata.IGDB.CompanyWebsite companyWebsiteResult = new CompanyWebsite(); + HasheousClient.Models.Metadata.IGDB.CompanyWebsite companyWebsiteResult = new HasheousClient.Models.Metadata.IGDB.CompanyWebsite(); companyWebsiteResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(companyWebsiteResult) }; case "cover": - HasheousClient.Models.Metadata.IGDB.Cover coverResult = new Cover(); + HasheousClient.Models.Metadata.IGDB.Cover coverResult = new HasheousClient.Models.Metadata.IGDB.Cover(); coverResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(coverResult) }; case "externalgame": - HasheousClient.Models.Metadata.IGDB.ExternalGame externalGameResult = new ExternalGame(); + HasheousClient.Models.Metadata.IGDB.ExternalGame externalGameResult = new HasheousClient.Models.Metadata.IGDB.ExternalGame(); externalGameResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(externalGameResult) }; case "franchise": - HasheousClient.Models.Metadata.IGDB.Franchise franchiseResult = new Franchise(); + HasheousClient.Models.Metadata.IGDB.Franchise franchiseResult = new HasheousClient.Models.Metadata.IGDB.Franchise(); franchiseResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(franchiseResult) }; case "game": - HasheousClient.Models.Metadata.IGDB.Game gameResult = new Game(); + HasheousClient.Models.Metadata.IGDB.Game gameResult = new HasheousClient.Models.Metadata.IGDB.Game(); if (Fields == "slug") { gameResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, Query.ToString()); @@ -745,61 +746,61 @@ namespace gaseous_server.Classes.Metadata return new T[] { ConvertToIGDBModel(gameResult) }; case "gameengine": - HasheousClient.Models.Metadata.IGDB.GameEngine gameEngineResult = new GameEngine(); + HasheousClient.Models.Metadata.IGDB.GameEngine gameEngineResult = new HasheousClient.Models.Metadata.IGDB.GameEngine(); gameEngineResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(gameEngineResult) }; case "gameenginelogo": - HasheousClient.Models.Metadata.IGDB.GameEngineLogo gameEngineLogoResult = new GameEngineLogo(); + HasheousClient.Models.Metadata.IGDB.GameEngineLogo gameEngineLogoResult = new HasheousClient.Models.Metadata.IGDB.GameEngineLogo(); gameEngineLogoResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(gameEngineLogoResult) }; case "gamelocalization": - HasheousClient.Models.Metadata.IGDB.GameLocalization gameLocalizationResult = new GameLocalization(); + HasheousClient.Models.Metadata.IGDB.GameLocalization gameLocalizationResult = new HasheousClient.Models.Metadata.IGDB.GameLocalization(); gameLocalizationResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(gameLocalizationResult) }; case "gamemode": - HasheousClient.Models.Metadata.IGDB.GameMode gameModeResult = new GameMode(); + HasheousClient.Models.Metadata.IGDB.GameMode gameModeResult = new HasheousClient.Models.Metadata.IGDB.GameMode(); gameModeResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(gameModeResult) }; case "gamevideo": - HasheousClient.Models.Metadata.IGDB.GameVideo gameVideoResult = new GameVideo(); + HasheousClient.Models.Metadata.IGDB.GameVideo gameVideoResult = new HasheousClient.Models.Metadata.IGDB.GameVideo(); gameVideoResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(gameVideoResult) }; case "genre": - HasheousClient.Models.Metadata.IGDB.Genre genreResult = new Genre(); + HasheousClient.Models.Metadata.IGDB.Genre genreResult = new HasheousClient.Models.Metadata.IGDB.Genre(); genreResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(genreResult) }; case "involvedcompany": - HasheousClient.Models.Metadata.IGDB.InvolvedCompany involvedCompanyResult = new InvolvedCompany(); + HasheousClient.Models.Metadata.IGDB.InvolvedCompany involvedCompanyResult = new HasheousClient.Models.Metadata.IGDB.InvolvedCompany(); involvedCompanyResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(involvedCompanyResult) }; case "multiplayermode": - HasheousClient.Models.Metadata.IGDB.MultiplayerMode multiplayerModeResult = new MultiplayerMode(); + HasheousClient.Models.Metadata.IGDB.MultiplayerMode multiplayerModeResult = new HasheousClient.Models.Metadata.IGDB.MultiplayerMode(); multiplayerModeResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(multiplayerModeResult) }; case "platformlogo": - HasheousClient.Models.Metadata.IGDB.PlatformLogo platformLogoResult = new PlatformLogo(); + HasheousClient.Models.Metadata.IGDB.PlatformLogo platformLogoResult = new HasheousClient.Models.Metadata.IGDB.PlatformLogo(); platformLogoResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(platformLogoResult) }; case "platform": - HasheousClient.Models.Metadata.IGDB.Platform platformResult = new Platform(); + HasheousClient.Models.Metadata.IGDB.Platform platformResult = new HasheousClient.Models.Metadata.IGDB.Platform(); if (Fields == "slug") { platformResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, Query.ToString()); @@ -812,31 +813,31 @@ namespace gaseous_server.Classes.Metadata return new T[] { ConvertToIGDBModel(platformResult) }; case "platformversion": - HasheousClient.Models.Metadata.IGDB.PlatformVersion platformVersionResult = new PlatformVersion(); + HasheousClient.Models.Metadata.IGDB.PlatformVersion platformVersionResult = new HasheousClient.Models.Metadata.IGDB.PlatformVersion(); platformVersionResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(platformVersionResult) }; case "playerperspective": - HasheousClient.Models.Metadata.IGDB.PlayerPerspective playerPerspectiveResult = new PlayerPerspective(); + HasheousClient.Models.Metadata.IGDB.PlayerPerspective playerPerspectiveResult = new HasheousClient.Models.Metadata.IGDB.PlayerPerspective(); playerPerspectiveResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(playerPerspectiveResult) }; case "releasedate": - HasheousClient.Models.Metadata.IGDB.ReleaseDate releaseDateResult = new ReleaseDate(); + HasheousClient.Models.Metadata.IGDB.ReleaseDate releaseDateResult = new HasheousClient.Models.Metadata.IGDB.ReleaseDate(); releaseDateResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(releaseDateResult) }; case "screenshot": - HasheousClient.Models.Metadata.IGDB.Screenshot screenshotResult = new Screenshot(); + HasheousClient.Models.Metadata.IGDB.Screenshot screenshotResult = new HasheousClient.Models.Metadata.IGDB.Screenshot(); screenshotResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(screenshotResult) }; case "theme": - HasheousClient.Models.Metadata.IGDB.Theme themeResult = new Theme(); + HasheousClient.Models.Metadata.IGDB.Theme themeResult = new HasheousClient.Models.Metadata.IGDB.Theme(); themeResult = hasheous.GetMetadataProxy(Endpoint, HasheousClient.Hasheous.MetadataProvider.IGDB, long.Parse(Query.ToString())); return new T[] { ConvertToIGDBModel(themeResult) }; @@ -872,12 +873,29 @@ namespace gaseous_server.Classes.Metadata // check if the property is an enum if (property.PropertyType.IsEnum) { - // get the enum type - Type enumType = property.PropertyType; - // get the enum value - object enumValue = Enum.Parse(enumType, input.GetType().GetProperty(property.Name).GetValue(input).ToString()); - // set the enum value - property.SetValue(output, enumValue); + // check if property is null + if (input.GetType().GetProperty(property.Name).GetValue(input) != null) + { + // get the enum type + Type enumType = property.PropertyType; + // get the enum value + object enumValue = Enum.Parse(enumType, input.GetType().GetProperty(property.Name).GetValue(input).ToString()); + // set the enum value + property.SetValue(output, enumValue); + } + } + else if (Common.IsNullableEnum(property.PropertyType)) + { + // check if property is null + if (input.GetType().GetProperty(property.Name).GetValue(input) != null) + { + // get the enum type + Type enumType = property.PropertyType; + // get the enum value + object enumValue = Enum.Parse(enumType.GenericTypeArguments[0], input.GetType().GetProperty(property.Name).GetValue(input).ToString()); + // set the enum value + property.SetValue(output, enumValue); + } } else { @@ -886,17 +904,6 @@ namespace gaseous_server.Classes.Metadata { switch (propertyTypeName) { - case "string": - case "int": - case "int32": - case "int64": - case "long": - case "datetime": - case "datetimeoffset": - case "bool": - property.SetValue(output, inputProperty.GetValue(input)); - break; - case "identityorvalue`1": // create new identityorvalue object, set the id property to the input property value @@ -943,6 +950,10 @@ namespace gaseous_server.Classes.Metadata property.SetValue(output, identitiesOrValues); } break; + + default: + property.SetValue(output, inputProperty.GetValue(input)); + break; } } } @@ -967,6 +978,8 @@ namespace gaseous_server.Classes.Metadata private async Task _DownloadFile(Uri uri, string DestinationFile) { + ConfigureHasheousClient(); + string DestinationDirectory = new FileInfo(DestinationFile).Directory.FullName; if (!Directory.Exists(DestinationDirectory)) { @@ -1036,121 +1049,69 @@ namespace gaseous_server.Classes.Metadata public async Task GetSpecificImageFromServer(string ImagePath, string ImageId, IGDBAPI_ImageSize size, List? FallbackSizes = null) { - string returnPath = ""; + string originalPath = Path.Combine(ImagePath, IGDBAPI_ImageSize.original.ToString(), ImageId + ".jpg"); + string requestedPath = Path.Combine(ImagePath, size.ToString(), ImageId + ".jpg"); - // check for artificial sizes first - switch (size) + // create the directory if it doesn't exist + if (!Directory.Exists(Path.GetDirectoryName(originalPath))) { - case IGDBAPI_ImageSize.screenshot_small: - case IGDBAPI_ImageSize.screenshot_thumb: - string BasePath = Path.Combine(ImagePath, size.ToString()); - if (!Directory.Exists(BasePath)) - { - Directory.CreateDirectory(BasePath); - } - returnPath = Path.Combine(BasePath, ImageId + ".jpg"); - if (!File.Exists(returnPath)) - { - // get original size image and resize - string originalSizePath = await GetSpecificImageFromServer(ImagePath, ImageId, IGDBAPI_ImageSize.original, null); - - int width = 0; - int height = 0; - - switch (size) - { - case IGDBAPI_ImageSize.screenshot_small: - // 235x128 - width = 235; - height = 128; - break; - - case IGDBAPI_ImageSize.screenshot_thumb: - // 165x90 - width = 165; - height = 90; - break; - - } - - using (var image = new ImageMagick.MagickImage(originalSizePath)) - { - image.Resize(width, height); - image.Strip(); - image.Write(returnPath); - } - } - - break; - - default: - // these sizes are IGDB native - if (RateLimitResumeTime > DateTime.UtcNow) - { - Logging.Log(Logging.LogType.Information, "API Connection", "Metadata source rate limit hit. Pausing API communications until " + RateLimitResumeTime.ToString() + ". Attempt " + RetryAttempts + " of " + RetryAttemptsMax + " retries."); - Thread.Sleep(RateLimitRecoveryWaitTime); - } - - if (InRateLimitAvoidanceMode == true) - { - // sleep for a moment to help avoid hitting the rate limiter - Logging.Log(Logging.LogType.Information, "API Connection: Fetch Image", "Metadata source rate limit hit. Pausing API communications for " + RateLimitAvoidanceWait + " milliseconds to avoid rate limiter."); - Thread.Sleep(RateLimitAvoidanceWait); - } - - Communications comms = new Communications(); - List imageSizes = new List - { - size - }; - - // get the image - try - { - returnPath = Path.Combine(ImagePath, size.ToString(), ImageId + ".jpg"); - - // fail early if the file is already downloaded - if (!File.Exists(returnPath)) - { - switch (_MetadataSource) - { - case HasheousClient.Models.MetadataModel.MetadataSources.IGDB: - await comms.IGDBAPI_GetImage(imageSizes, ImageId, ImagePath); - break; - - case HasheousClient.Models.MetadataModel.MetadataSources.Hasheous: - await comms.HasheousAPI_GetImage(imageSizes, ImageId, ImagePath); - break; - - default: - break; - } - } - - } - catch (HttpRequestException ex) - { - if (ex.StatusCode == HttpStatusCode.NotFound) - { - Logging.Log(Logging.LogType.Information, "Image Download", "Image not found, trying a different size."); - - if (FallbackSizes != null) - { - foreach (Communications.IGDBAPI_ImageSize imageSize in FallbackSizes) - { - returnPath = await GetSpecificImageFromServer(ImagePath, ImageId, imageSize, null); - } - } - } - } - - // increment rate limiter avoidance call count - RateLimitAvoidanceCallCount += 1; - - break; + Directory.CreateDirectory(Path.GetDirectoryName(originalPath)); + } + if (!Directory.Exists(Path.GetDirectoryName(requestedPath))) + { + Directory.CreateDirectory(Path.GetDirectoryName(originalPath)); } - return returnPath; + // get the resolution attribute for enum size + Point resolution = Common.GetResolution(size); + + // check if the original image exists + if (!File.Exists(originalPath)) + { + // sleep if the rate limiter is active + if (RateLimitResumeTime > DateTime.UtcNow) + { + Logging.Log(Logging.LogType.Information, "API Connection", "Metadata source rate limit hit. Pausing API communications until " + RateLimitResumeTime.ToString() + ". Attempt " + RetryAttempts + " of " + RetryAttemptsMax + " retries."); + Thread.Sleep(RateLimitRecoveryWaitTime); + } + + if (InRateLimitAvoidanceMode == true) + { + // sleep for a moment to help avoid hitting the rate limiter + Logging.Log(Logging.LogType.Information, "API Connection: Fetch Image", "Metadata source rate limit hit. Pausing API communications for " + RateLimitAvoidanceWait + " milliseconds to avoid rate limiter."); + Thread.Sleep(RateLimitAvoidanceWait); + } + + // get the original image + Communications comms = new Communications(); + switch (_MetadataSource) + { + case HasheousClient.Models.MetadataModel.MetadataSources.IGDB: + await comms.IGDBAPI_GetImage(ImageId, ImagePath); + break; + + case HasheousClient.Models.MetadataModel.MetadataSources.Hasheous: + await comms.HasheousAPI_GetImage(ImageId, ImagePath); + break; + + default: + break; + } + } + + // check if the requested image exists + if (!File.Exists(requestedPath)) + { + // get the original image + using (var image = new ImageMagick.MagickImage(originalPath)) + { + image.Resize(resolution.X, resolution.Y); + image.Strip(); + image.Write(requestedPath); + } + } + + return requestedPath; } public static T? GetSearchCache(string SearchFields, string SearchString) @@ -1208,22 +1169,19 @@ namespace gaseous_server.Classes.Metadata /// /// /// The path to save the downloaded files to - public async Task IGDBAPI_GetImage(List ImageSizes, string ImageId, string OutputPath) + public async Task IGDBAPI_GetImage(string ImageId, string OutputPath) { string urlTemplate = "https://images.igdb.com/igdb/image/upload/t_{size}/{hash}.jpg"; - foreach (IGDBAPI_ImageSize ImageSize in ImageSizes) - { - string url = urlTemplate.Replace("{size}", Common.GetDescription(ImageSize)).Replace("{hash}", ImageId); - string newOutputPath = Path.Combine(OutputPath, Common.GetDescription(ImageSize)); - string OutputFile = ImageId + ".jpg"; - string fullPath = Path.Combine(newOutputPath, OutputFile); + 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); - await _DownloadFile(new Uri(url), fullPath); - } + await _DownloadFile(new Uri(url), fullPath); } - public async Task HasheousAPI_GetImage(List ImageSizes, string ImageId, string OutputPath) + public async Task HasheousAPI_GetImage(string ImageId, string OutputPath) { string urlTemplate = HasheousClient.WebApp.HttpHelper.BaseUri + "api/v1/MetadataProxy/IGDB/Image/{hash}.jpg"; @@ -1241,79 +1199,118 @@ namespace gaseous_server.Classes.Metadata /// 90x128 Fit /// [Description("cover_small")] + [Resolution(90, 128)] cover_small, /// /// 264x374 Fit /// [Description("cover_big")] + [Resolution(264, 374)] cover_big, /// /// 165x90 Lfill, Centre gravity - resized by Gaseous and is not a real IGDB size /// [Description("screenshot_thumb")] + [Resolution(165, 90)] screenshot_thumb, /// /// 235x128 Lfill, Centre gravity - resized by Gaseous and is not a real IGDB size /// [Description("screenshot_small")] + [Resolution(235, 128)] screenshot_small, /// /// 589x320 Lfill, Centre gravity /// [Description("screenshot_med")] + [Resolution(589, 320)] screenshot_med, /// /// 889x500 Lfill, Centre gravity /// [Description("screenshot_big")] + [Resolution(889, 500)] screenshot_big, /// /// 1280x720 Lfill, Centre gravity /// [Description("screenshot_huge")] + [Resolution(1280, 720)] screenshot_huge, /// /// 284x160 Fit /// [Description("logo_med")] + [Resolution(284, 160)] logo_med, /// /// 90x90 Thumb, Centre gravity /// [Description("thumb")] + [Resolution(90, 90)] thumb, /// /// 35x35 Thumb, Centre gravity /// [Description("micro")] + [Resolution(35, 35)] micro, /// /// 1280x720 Fit, Centre gravity /// [Description("720p")] + [Resolution(1280, 720)] r720p, /// /// 1920x1080 Fit, Centre gravity /// [Description("1080p")] + [Resolution(1920, 1080)] r1080p, /// /// The originally uploaded image /// [Description("original")] + [Resolution(0, 0)] original } + + + /// + /// Specifies a resolution for an image size enum + /// + [AttributeUsage(AttributeTargets.All)] + public class ResolutionAttribute : Attribute + { + public static readonly ResolutionAttribute Default = new ResolutionAttribute(); + + public ResolutionAttribute() : this(0, 0) + { + } + + public ResolutionAttribute(int width, int height) + { + ResolutionWidth = width; + ResolutionHeight = height; + } + + public virtual int width => ResolutionWidth; + public virtual int height => ResolutionHeight; + + protected int ResolutionWidth { get; set; } + protected int ResolutionHeight { get; set; } + } } } \ No newline at end of file diff --git a/gaseous-server/Classes/Roms.cs b/gaseous-server/Classes/Roms.cs index 2cf567a..76b99d0 100644 --- a/gaseous-server/Classes/Roms.cs +++ b/gaseous-server/Classes/Roms.cs @@ -271,7 +271,7 @@ namespace gaseous_server.Classes PlatformId = (long)romDR["platformid"], Platform = (string)romDR["platformname"], GameId = (long)romDR["gameid"], - Game = (string)romDR["gamename"], + Game = (string)Common.ReturnValueIfNull(romDR["gamename"], ""), Name = (string)romDR["name"], Size = (long)romDR["size"], Crc = ((string)romDR["crc"]).ToLower(), diff --git a/gaseous-server/wwwroot/scripts/preferences.js b/gaseous-server/wwwroot/scripts/preferences.js index f4a5a84..fb5330e 100644 --- a/gaseous-server/wwwroot/scripts/preferences.js +++ b/gaseous-server/wwwroot/scripts/preferences.js @@ -182,11 +182,12 @@ class PreferencesWindow { ]; for (let j = 0; j < ratingGroupsOrder.length; j++) { let ageGroupValue = AgeRatingGroups[ratingGroupsOrder[j]]; - let iconIdList = ageGroupValue[key]; + let iconIdList = ageGroupValue[key.toLowerCase()]; // loop the age rating icons for (let i = 0; i < iconIdList.length; i++) { let icon = document.createElement('img'); - icon.src = "/images/Ratings/" + key + "/" + AgeRatingStrings[iconIdList[i]] + ".svg"; + // icon.src = "/images/Ratings/" + key + "/" + AgeRatingStrings[iconIdList[i]] + ".svg"; + icon.src = "/images/Ratings/" + key + "/" + iconIdList[i] + ".svg"; icon.title = AgeRatingStrings[iconIdList[i]]; icon.classList.add("rating_image_mini"); classificationIcons.appendChild(icon); diff --git a/gaseous-server/wwwroot/styles/style.css b/gaseous-server/wwwroot/styles/style.css index ebf8b30..5032fb1 100644 --- a/gaseous-server/wwwroot/styles/style.css +++ b/gaseous-server/wwwroot/styles/style.css @@ -2594,6 +2594,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no } .section-header { + position: relative; background-color: var(--section-header-background); padding: 10px; font-weight: bold;