From ee4e5d4037984db5c19c78e8052997d34830d615 Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Tue, 27 Jun 2023 23:31:29 +1000 Subject: [PATCH] refactor: sped up game list loading code, plus many small bug fixes --- gaseous-server/Classes/Metadata/Games.cs | 14 +- gaseous-server/Classes/Metadata/Storage.cs | 389 ++++++++++-------- .../Controllers/FilterController.cs | 1 + gaseous-server/Controllers/GamesController.cs | 23 +- gaseous-server/Program.cs | 24 ++ gaseous-server/gaseous-server.csproj | 2 + .../fonts/Commodore Pixelized v1.2.ttf | Bin 0 -> 20324 bytes gaseous-server/wwwroot/images/YouTube.svg | 1 + gaseous-server/wwwroot/index.html | 4 +- gaseous-server/wwwroot/pages/game.html | 37 +- gaseous-server/wwwroot/styles/style.css | 26 +- gaseous-tools/Database/MySQL/gaseous-1000.sql | 4 +- 12 files changed, 315 insertions(+), 210 deletions(-) create mode 100644 gaseous-server/wwwroot/fonts/Commodore Pixelized v1.2.ttf create mode 100644 gaseous-server/wwwroot/images/YouTube.svg diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs index 016eed1..f83a351 100644 --- a/gaseous-server/Classes/Metadata/Games.cs +++ b/gaseous-server/Classes/Metadata/Games.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using gaseous_tools; using IGDB; using IGDB.Models; @@ -25,7 +26,7 @@ namespace gaseous_server.Classes.Metadata if (Id == 0) { Game returnValue = new Game(); - if ((Storage.GetCacheStatus("game", 0) == Storage.CacheStatus.NotPresent) || (forceRefresh == true)) + if (Storage.GetCacheStatus("game", 0) == Storage.CacheStatus.NotPresent) { returnValue = new Game { @@ -55,6 +56,11 @@ namespace gaseous_server.Classes.Metadata return RetVal.Result; } + public static Game GetGame(DataRow dataRow) + { + return Storage.BuildCacheObject(new Game(), dataRow); + } + private static async Task _GetGame(SearchUsing searchUsing, object searchValue, bool followSubGames = false, bool forceRefresh = false) { // check database first @@ -229,6 +235,9 @@ namespace gaseous_server.Classes.Metadata searchBody += "fields id,name,slug,platforms,summary; "; switch (searchType) { + case SearchType.searchNoPlatform: + searchBody += "search \"" + SearchString + "\"; "; + break; case SearchType.search: searchBody += "search \"" + SearchString + "\"; "; searchBody += "where platforms = (" + PlatformId + ");"; @@ -252,7 +261,8 @@ namespace gaseous_server.Classes.Metadata { where = 0, wherefuzzy = 1, - search = 2 + search = 2, + searchNoPlatform = 3 } } } \ No newline at end of file diff --git a/gaseous-server/Classes/Metadata/Storage.cs b/gaseous-server/Classes/Metadata/Storage.cs index 8d543cc..d2fb9be 100644 --- a/gaseous-server/Classes/Metadata/Storage.cs +++ b/gaseous-server/Classes/Metadata/Storage.cs @@ -26,6 +26,26 @@ namespace gaseous_server.Classes.Metadata return _GetCacheStatus(Endpoint, "id", Id); } + public static CacheStatus GetCacheStatus(DataRow Row) + { + if (Row.Table.Columns.Contains("lastUpdated")) + { + DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168); + if ((DateTime)Row["lastUpdated"] < CacheExpiryTime) + { + return CacheStatus.Expired; + } + else + { + return CacheStatus.Current; + } + } + else + { + throw new Exception("No lastUpdated column!"); + } + } + private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue) { Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); @@ -161,189 +181,194 @@ namespace gaseous_server.Classes.Metadata else { DataRow dataRow = dt.Rows[0]; - foreach (PropertyInfo property in EndpointType.GetType().GetProperties()) - { - if (dataRow.Table.Columns.Contains(property.Name)) - { - if (dataRow[property.Name] != DBNull.Value) - { - string objectTypeName = property.PropertyType.Name.ToLower().Split("`")[0]; - string subObjectTypeName = ""; - object? objectToStore = null; - if (objectTypeName == "nullable") - { - objectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[System.", "").Replace("]", "").ToLower(); - } - try - { - switch (objectTypeName) - { - case "datetimeoffset": - DateTimeOffset? storedDate = (DateTime?)dataRow[property.Name]; - property.SetValue(EndpointType, storedDate); - break; - //case "nullable": - // Console.WriteLine("Nullable: " + property.PropertyType.UnderlyingSystemType); - // break; - case "identityorvalue": - subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower(); - - switch (subObjectTypeName) - { - case "collection": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - case "cover": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - case "franchise": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - case "game": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - case "platformfamily": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - case "platformlogo": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - case "platformversioncompany": - objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); - break; - } - - if (objectToStore != null) - { - property.SetValue(EndpointType, objectToStore); - } - - break; - case "identitiesorvalues": - subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower(); - - long[] fromJsonObject = Newtonsoft.Json.JsonConvert.DeserializeObject((string)dataRow[property.Name]); - - switch (subObjectTypeName) - { - case "agerating": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "alternativename": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "artwork": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "ageratingcontentdescription": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "game": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "externalgame": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "franchise": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "gameengine": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "gamemode": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "gamevideo": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "genre": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "involvedcompany": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "multiplayermode": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "platform": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "platformversion": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "platformwebsite": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "platformversioncompany": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "platformversionreleasedate": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "playerperspective": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "releasedate": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "screenshot": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "theme": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - case "website": - objectToStore = new IdentitiesOrValues(ids: fromJsonObject); - break; - } - - if (objectToStore != null) - { - property.SetValue(EndpointType, objectToStore); - } - - break; - case "int32[]": - Int32[] fromJsonObject_int32Array = Newtonsoft.Json.JsonConvert.DeserializeObject((string)dataRow[property.Name]); - if (fromJsonObject_int32Array != null) - { - property.SetValue(EndpointType, fromJsonObject_int32Array); - } - break; - case "[igdb.models.category": - property.SetValue(EndpointType, (Category)dataRow[property.Name]); - break; - case "[igdb.models.gamestatus": - property.SetValue(EndpointType, (GameStatus)dataRow[property.Name]); - break; - case "[igdb.models.ageratingcategory": - property.SetValue(EndpointType, (AgeRatingCategory)dataRow[property.Name]); - break; - case "[igdb.models.ageratingcontentdescriptioncategory": - property.SetValue(EndpointType, (AgeRatingContentDescriptionCategory)dataRow[property.Name]); - break; - case "[igdb.models.ageratingtitle": - property.SetValue(EndpointType, (AgeRatingTitle)dataRow[property.Name]); - break; - case "[igdb.models.externalcategory": - property.SetValue(EndpointType, (ExternalCategory)dataRow[property.Name]); - break; - default: - property.SetValue(EndpointType, dataRow[property.Name]); - break; - } - } - catch (Exception ex) - { - Console.WriteLine("Error occurred in column " + property.Name); - Console.WriteLine(ex.ToString()); - } - } - } - } - - return EndpointType; + return BuildCacheObject(EndpointType, dataRow); } + } + + public static T BuildCacheObject(T EndpointType, DataRow dataRow) + { + foreach (PropertyInfo property in EndpointType.GetType().GetProperties()) + { + if (dataRow.Table.Columns.Contains(property.Name)) + { + if (dataRow[property.Name] != DBNull.Value) + { + string objectTypeName = property.PropertyType.Name.ToLower().Split("`")[0]; + string subObjectTypeName = ""; + object? objectToStore = null; + if (objectTypeName == "nullable") + { + objectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[System.", "").Replace("]", "").ToLower(); + } + try + { + switch (objectTypeName) + { + case "datetimeoffset": + DateTimeOffset? storedDate = (DateTime?)dataRow[property.Name]; + property.SetValue(EndpointType, storedDate); + break; + //case "nullable": + // Console.WriteLine("Nullable: " + property.PropertyType.UnderlyingSystemType); + // break; + case "identityorvalue": + subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower(); + + switch (subObjectTypeName) + { + case "collection": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + case "cover": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + case "franchise": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + case "game": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + case "platformfamily": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + case "platformlogo": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + case "platformversioncompany": + objectToStore = new IdentityOrValue(id: (long)dataRow[property.Name]); + break; + } + + if (objectToStore != null) + { + property.SetValue(EndpointType, objectToStore); + } + + break; + case "identitiesorvalues": + subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower(); + + long[] fromJsonObject = Newtonsoft.Json.JsonConvert.DeserializeObject((string)dataRow[property.Name]); + + switch (subObjectTypeName) + { + case "agerating": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "alternativename": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "artwork": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "ageratingcontentdescription": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "game": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "externalgame": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "franchise": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "gameengine": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "gamemode": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "gamevideo": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "genre": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "involvedcompany": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "multiplayermode": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "platform": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "platformversion": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "platformwebsite": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "platformversioncompany": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "platformversionreleasedate": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "playerperspective": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "releasedate": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "screenshot": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "theme": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + case "website": + objectToStore = new IdentitiesOrValues(ids: fromJsonObject); + break; + } + + if (objectToStore != null) + { + property.SetValue(EndpointType, objectToStore); + } + + break; + case "int32[]": + Int32[] fromJsonObject_int32Array = Newtonsoft.Json.JsonConvert.DeserializeObject((string)dataRow[property.Name]); + if (fromJsonObject_int32Array != null) + { + property.SetValue(EndpointType, fromJsonObject_int32Array); + } + break; + case "[igdb.models.category": + property.SetValue(EndpointType, (Category)dataRow[property.Name]); + break; + case "[igdb.models.gamestatus": + property.SetValue(EndpointType, (GameStatus)dataRow[property.Name]); + break; + case "[igdb.models.ageratingcategory": + property.SetValue(EndpointType, (AgeRatingCategory)dataRow[property.Name]); + break; + case "[igdb.models.ageratingcontentdescriptioncategory": + property.SetValue(EndpointType, (AgeRatingContentDescriptionCategory)dataRow[property.Name]); + break; + case "[igdb.models.ageratingtitle": + property.SetValue(EndpointType, (AgeRatingTitle)dataRow[property.Name]); + break; + case "[igdb.models.externalcategory": + property.SetValue(EndpointType, (ExternalCategory)dataRow[property.Name]); + break; + default: + property.SetValue(EndpointType, dataRow[property.Name]); + break; + } + } + catch (Exception ex) + { + Console.WriteLine("Error occurred in column " + property.Name); + Console.WriteLine(ex.ToString()); + } + } + } + } + + return EndpointType; } } } diff --git a/gaseous-server/Controllers/FilterController.cs b/gaseous-server/Controllers/FilterController.cs index 8247192..865510a 100644 --- a/gaseous-server/Controllers/FilterController.cs +++ b/gaseous-server/Controllers/FilterController.cs @@ -16,6 +16,7 @@ namespace gaseous_server.Controllers { [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] + [ResponseCache(CacheProfileName = "5Minute")] public Dictionary Filter() { Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); diff --git a/gaseous-server/Controllers/GamesController.cs b/gaseous-server/Controllers/GamesController.cs index 0fd0ac9..47cfbad 100644 --- a/gaseous-server/Controllers/GamesController.cs +++ b/gaseous-server/Controllers/GamesController.cs @@ -111,14 +111,15 @@ namespace gaseous_server.Controllers } Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT DISTINCT games_roms.gameid AS ROMGameId, game.ageratings, game.aggregatedrating, game.aggregatedratingcount, game.alternativenames, game.artworks, game.bundles, game.category, game.collection, game.cover, game.dlcs, game.expansions, game.externalgames, game.firstreleasedate, game.`follows`, game.franchise, game.franchises, game.gameengines, game.gamemodes, game.genres, game.hypes, game.involvedcompanies, game.keywords, game.multiplayermodes, game.`name`, game.parentgame, game.platforms, game.playerperspectives, game.rating, game.ratingcount, game.releasedates, game.screenshots, game.similargames, game.slug, game.standaloneexpansions, game.`status`, game.storyline, game.summary, game.tags, game.themes, game.totalrating, game.totalratingcount, game.versionparent, game.versiontitle, game.videos, game.websites FROM gaseous.games_roms LEFT JOIN game ON game.id = games_roms.gameid " + whereClause + " " + havingClause + " " + orderByClause; + string sql = "SELECT DISTINCT games_roms.gameid AS ROMGameId, game.id, game.ageratings, game.aggregatedrating, game.aggregatedratingcount, game.alternativenames, game.artworks, game.bundles, game.category, game.collection, game.cover, game.dlcs, game.expansions, game.externalgames, game.firstreleasedate, game.`follows`, game.franchise, game.franchises, game.gameengines, game.gamemodes, game.genres, game.hypes, game.involvedcompanies, game.keywords, game.multiplayermodes, game.`name`, game.parentgame, game.platforms, game.playerperspectives, game.rating, game.ratingcount, game.releasedates, game.screenshots, game.similargames, game.slug, game.standaloneexpansions, game.`status`, game.storyline, game.summary, game.tags, game.themes, game.totalrating, game.totalratingcount, game.versionparent, game.versiontitle, game.videos, game.websites FROM gaseous.games_roms LEFT JOIN game ON game.id = games_roms.gameid " + whereClause + " " + havingClause + " " + orderByClause; List RetVal = new List(); DataTable dbResponse = db.ExecuteCMD(sql, whereParams); foreach (DataRow dr in dbResponse.Rows) { - RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["ROMGameId"], false, false)); + //RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["ROMGameId"], false, false)); + RetVal.Add(Classes.Metadata.Games.GetGame(dr)); } return Ok(RetVal); @@ -128,6 +129,7 @@ namespace gaseous_server.Controllers [Route("{GameId}")] [ProducesResponseType(typeof(Game), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "5Minute")] public ActionResult Game(long GameId, bool forceRefresh = false) { try @@ -153,6 +155,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/alternativename")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameAlternativeNames(long GameId) { try @@ -183,6 +186,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/agerating")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameAgeClassification(long GameId) { try @@ -275,6 +279,7 @@ namespace gaseous_server.Controllers }; Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Cache-Control", "public, max-age=604800"); return File(filedata, contentType); } @@ -291,6 +296,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/artwork")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameArtwork(long GameId) { try @@ -319,6 +325,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/artwork/{ArtworkId}")] [ProducesResponseType(typeof(Artwork), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameArtwork(long GameId, long ArtworkId) { try @@ -377,6 +384,7 @@ namespace gaseous_server.Controllers }; Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Cache-Control", "public, max-age=604800"); return File(filedata, contentType); } @@ -405,6 +413,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/cover")] [ProducesResponseType(typeof(Cover), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameCover(long GameId) { try @@ -457,6 +466,7 @@ namespace gaseous_server.Controllers }; Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Cache-Control", "public, max-age=604800"); return File(filedata, contentType); } @@ -475,6 +485,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/roms")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "5Minute")] public ActionResult GameRom(long GameId) { try @@ -494,7 +505,8 @@ namespace gaseous_server.Controllers [HttpGet] [Route("{GameId}/roms/{RomId}")] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "5Minute")] public ActionResult GameRom(long GameId, long RomId) { try @@ -602,6 +614,7 @@ namespace gaseous_server.Controllers }; Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Cache-Control", "public, max-age=604800"); return File(filedata, contentType); } @@ -620,6 +633,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/screenshots")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameScreenshot(long GameId) { try @@ -648,6 +662,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/screenshots/{ScreenshotId}")] [ProducesResponseType(typeof(Screenshot), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameScreenshot(long GameId, long ScreenshotId) { try @@ -702,6 +717,7 @@ namespace gaseous_server.Controllers }; Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Cache-Control", "public, max-age=604800"); return File(filedata, contentType); } @@ -720,6 +736,7 @@ namespace gaseous_server.Controllers [Route("{GameId}/videos")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] public ActionResult GameVideo(long GameId) { try diff --git a/gaseous-server/Program.cs b/gaseous-server/Program.cs index 49342f4..7ae0da9 100644 --- a/gaseous-server/Program.cs +++ b/gaseous-server/Program.cs @@ -1,6 +1,7 @@ using System.Text.Json.Serialization; using gaseous_server; using gaseous_tools; +using Microsoft.AspNetCore.Mvc; Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server"); @@ -32,6 +33,27 @@ builder.Services.AddControllers().AddJsonOptions(x => // suppress nulls x.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; }); +builder.Services.AddResponseCaching(); +builder.Services.AddControllers(options => +{ + options.CacheProfiles.Add("Default30", + new CacheProfile() + { + Duration = 30 + }); + options.CacheProfiles.Add("5Minute", + new CacheProfile() + { + Duration = 300, + Location = ResponseCacheLocation.Any + }); + options.CacheProfiles.Add("7Days", + new CacheProfile() + { + Duration = 604800, + Location = ResponseCacheLocation.Any + }); +}); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); @@ -49,6 +71,8 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); +app.UseResponseCaching(); + app.UseAuthorization(); app.UseDefaultFiles(); diff --git a/gaseous-server/gaseous-server.csproj b/gaseous-server/gaseous-server.csproj index aa3be2e..ac0ffa6 100644 --- a/gaseous-server/gaseous-server.csproj +++ b/gaseous-server/gaseous-server.csproj @@ -103,6 +103,7 @@ + @@ -123,6 +124,7 @@ + diff --git a/gaseous-server/wwwroot/fonts/Commodore Pixelized v1.2.ttf b/gaseous-server/wwwroot/fonts/Commodore Pixelized v1.2.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6bf582a19f44f6b635581e31befd73a56ee0d83b GIT binary patch literal 20324 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 diff --git a/gaseous-server/wwwroot/index.html b/gaseous-server/wwwroot/index.html index b1b9d2f..29c8c95 100644 --- a/gaseous-server/wwwroot/index.html +++ b/gaseous-server/wwwroot/index.html @@ -11,14 +11,14 @@ - Gaseous + Gaseous Games
diff --git a/gaseous-server/wwwroot/pages/game.html b/gaseous-server/wwwroot/pages/game.html index 9bbe21c..962cdf3 100644 --- a/gaseous-server/wwwroot/pages/game.html +++ b/gaseous-server/wwwroot/pages/game.html @@ -139,17 +139,19 @@ if (result.videos) { imageIndex = result.videos.ids.length; } - for (var i = 0; i < result.screenshots.ids.length; i++) { - var screenshotItem = document.createElement('div'); - screenshotItem.id = 'gamescreenshots_gallery_' + imageIndex; - screenshotItem.setAttribute('name', 'gamescreenshots_gallery_item'); - screenshotItem.setAttribute('style', 'background-image: url("/api/v1/Games/' + gameId + '/screenshots/' + result.screenshots.ids[i] + '/image"); background-position: center; background-repeat: no-repeat; background-size: contain;)'); - screenshotItem.setAttribute('imageid', imageIndex); - screenshotItem.setAttribute('imagetype', 0); - screenshotItem.className = 'gamescreenshosts_gallery_item'; - screenshotItem.setAttribute('onclick', 'selectScreenshot(' + imageIndex + ');'); - gameScreenshots_Gallery.appendChild(screenshotItem); - imageIndex += 1; + if (result.screenshots) { + for (var i = 0; i < result.screenshots.ids.length; i++) { + var screenshotItem = document.createElement('div'); + screenshotItem.id = 'gamescreenshots_gallery_' + imageIndex; + screenshotItem.setAttribute('name', 'gamescreenshots_gallery_item'); + screenshotItem.setAttribute('style', 'background-image: url("/api/v1/Games/' + gameId + '/screenshots/' + result.screenshots.ids[i] + '/image"); background-position: center; background-repeat: no-repeat; background-size: contain;)'); + screenshotItem.setAttribute('imageid', imageIndex); + screenshotItem.setAttribute('imagetype', 0); + screenshotItem.className = 'gamescreenshots_gallery_item'; + screenshotItem.setAttribute('onclick', 'selectScreenshot(' + imageIndex + ');'); + gameScreenshots_Gallery.appendChild(screenshotItem); + imageIndex += 1; + } } // load videos @@ -164,8 +166,14 @@ vScreenshotItem.setAttribute('imageid', i); vScreenshotItem.setAttribute('imagetype', 1); vScreenshotItem.setAttribute('imageref', result[i].videoId); - vScreenshotItem.className = 'gamescreenshosts_gallery_item'; + vScreenshotItem.className = 'gamescreenshots_gallery_item'; vScreenshotItem.setAttribute('onclick', 'selectScreenshot(' + i + ');'); + + var youtubeIcon = document.createElement('img'); + youtubeIcon.src = '/images/YouTube.svg'; + youtubeIcon.className = 'gamescreenshosts_gallery_item_youtube'; + vScreenshotItem.appendChild(youtubeIcon); + gameScreenshots_vGallery.insertBefore(vScreenshotItem, gameScreenshots_vGallery.firstChild); } @@ -206,7 +214,7 @@ var newTable = document.createElement('table'); newTable.className = 'romtable'; newTable.setAttribute('cellspacing', 0); - newTable.appendChild(createTableRow(true, ['Name', 'Size', 'Media', ''])); + newTable.appendChild(createTableRow(true, ['Name', 'Size', 'Media', '', ''])); var lastPlatform = ''; for (var i = 0; i < result.length; i++) { @@ -224,7 +232,8 @@ '' + result[i].name + '', formatBytes(result[i].size, 2), result[i].romTypeMedia, - result[i].mediaLabel + result[i].mediaLabel, + '...' ]; newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell')); } diff --git a/gaseous-server/wwwroot/styles/style.css b/gaseous-server/wwwroot/styles/style.css index 6927b06..1305b25 100644 --- a/gaseous-server/wwwroot/styles/style.css +++ b/gaseous-server/wwwroot/styles/style.css @@ -8,6 +8,11 @@ font-size: 13px; } +@font-face { + font-family: Commodore64; + src: url('/fonts/Commodore Pixelized v1.2.ttf'); +} + h3 { border-bottom-style: solid; /*border-bottom-color: #916b01;*/ @@ -48,11 +53,13 @@ h3 { } #banner_header_label { + font-family: Commodore64; display: inline; padding: 10px; - font-size: 18pt; - font-weight: 700; - color: #edeffa; + font-size: 16pt; + vertical-align: top; + /*color: #edeffa;*/ + color: #7c70da; } #content { @@ -193,7 +200,7 @@ input[id='filter_panel_search'] { right: 0; margin: auto; width: 90%; - min-width: 800px; + min-width: 911px; max-width: 1122px; padding-top: 1px; @@ -270,7 +277,7 @@ iframe { white-space: nowrap; } -.gamescreenshosts_gallery_item { +.gamescreenshots_gallery_item { display: inline-block; height: 50px; width: 70px; @@ -282,6 +289,14 @@ iframe { cursor: pointer; } +.gamescreenshosts_gallery_item_youtube { + max-height: 20px; + max-width: 20px; + float: right; + margin-top: 30px; + margin-right: 5px; +} + .gamescreenshots_arrows { margin-top: 140px; height: 50px; @@ -290,7 +305,6 @@ iframe { background-color: #383838; color: black; text-align: center; - user-select: none; /* standard syntax */ -webkit-user-select: none; /* webkit (safari, chrome) browsers */ -moz-user-select: none; /* mozilla browsers */ diff --git a/gaseous-tools/Database/MySQL/gaseous-1000.sql b/gaseous-tools/Database/MySQL/gaseous-1000.sql index 3e0143a..b44033b 100644 --- a/gaseous-tools/Database/MySQL/gaseous-1000.sql +++ b/gaseous-tools/Database/MySQL/gaseous-1000.sql @@ -302,7 +302,9 @@ CREATE TABLE `games_roms` ( `path` longtext, `metadatasource` int DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `id_UNIQUE` (`id`) + UNIQUE KEY `id_UNIQUE` (`id`), + INDEX `gameid` (`gameid` ASC) VISIBLE, + INDEX `id_gameid` (`gameid` ASC, `id` ASC) VISIBLE ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */;