diff --git a/gaseous-server/Classes/Filters.cs b/gaseous-server/Classes/Filters.cs index 8152272..7bdaf7b 100644 --- a/gaseous-server/Classes/Filters.cs +++ b/gaseous-server/Classes/Filters.cs @@ -16,15 +16,15 @@ namespace gaseous_server.Classes // platforms List platforms = new List(); - string ageRestriction_Platform = "Game.AgeGroupId <= " + (int)MaximumAgeRestriction; + string ageRestriction_Platform = "AgeGroup.AgeGroupId <= " + (int)MaximumAgeRestriction; string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction; if (IncludeUnrated == true) { - ageRestriction_Platform += " OR Game.AgeGroupId IS NULL"; + ageRestriction_Platform += " OR AgeGroup.AgeGroupId IS NULL"; ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL"; } - string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(view_Games.Id) AS GameCount FROM view_Games JOIN Relation_Game_Platforms ON Relation_Game_Platforms.GameId = view_Games.Id AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) JOIN Platform ON Platform.Id = Relation_Game_Platforms.PlatformsId WHERE (" + ageRestriction_Generic + ") GROUP BY Platform.`Name` ORDER BY Platform.`Name`;"; + string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, Games_Roms.PlatformId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id , Games_Roms.PlatformId HAVING RomCount > 0) Game JOIN Platform ON Game.PlatformId = Platform.Id GROUP BY Platform.`Name`;"; DataTable dbResponse = db.ExecuteCMD(sql); @@ -38,7 +38,7 @@ namespace gaseous_server.Classes // genres List genres = new List(); - dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic); + dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Platform); foreach (DataRow dr in dbResponse.Rows) { @@ -49,7 +49,7 @@ namespace gaseous_server.Classes // game modes List gameModes = new List(); - dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic); + dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Platform); foreach (DataRow dr in dbResponse.Rows) { @@ -60,7 +60,7 @@ namespace gaseous_server.Classes // player perspectives List playerPerspectives = new List(); - dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic); + dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Platform); foreach (DataRow dr in dbResponse.Rows) { @@ -71,7 +71,7 @@ namespace gaseous_server.Classes // themes List themes = new List(); - dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic); + dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Platform); foreach (DataRow dr in dbResponse.Rows) { @@ -82,7 +82,7 @@ namespace gaseous_server.Classes // age groups List agegroupings = new List(); - sql = "SELECT view_Games.Id, view_Games.AgeGroupId, COUNT(view_Games.Id) AS GameCount FROM view_Games WHERE (" + ageRestriction_Generic + ") GROUP BY view_Games.AgeGroupId ORDER BY view_Games.AgeGroupId DESC;"; + sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC"; dbResponse = db.ExecuteCMD(sql); foreach (DataRow dr in dbResponse.Rows) @@ -108,9 +108,11 @@ namespace gaseous_server.Classes return FilterSet; } - private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction_Generic) + private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction) { - string sql = "SELECT DISTINCT .Id, .`Name`, COUNT(view_Games.Id) AS GameCount FROM LEFT JOIN Relation_Game_s ON Relation_Game_s.sId = .Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY .Id HAVING GameCount > 0 ORDER BY .`Name`;"; + //string sql = "SELECT DISTINCT .Id, .`Name`, COUNT(view_Games.Id) AS GameCount FROM LEFT JOIN Relation_Game_s ON Relation_Game_s.sId = .Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY .Id HAVING GameCount > 0 ORDER BY .`Name`;"; + + string sql = "SELECT .Id, .`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + AgeRestriction + ") GROUP BY Game.Id HAVING RomCount > 0) Game JOIN Relation_Game_s ON Game.Id = Relation_Game_s.GameId JOIN ON Relation_Game_s.sId = .Id GROUP BY .`Name` ORDER BY .`Name`;"; sql = sql.Replace("", Name); DataTable dbResponse = db.ExecuteCMD(sql); diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs index 3b90faa..d61543d 100644 --- a/gaseous-server/Classes/Metadata/Games.cs +++ b/gaseous-server/Classes/Metadata/Games.cs @@ -331,6 +331,37 @@ namespace gaseous_server.Classes.Metadata ids: platformIds.ToArray() ); + // get cover art from parent if this has no cover + if (result.Cover == null) + { + if (result.ParentGame != null) + { + if (result.ParentGame.Id != null) + { + Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no cover art, fetching cover art from parent game"); + Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false); + result.Cover = parentGame.Cover; + } + } + } + + // get missing metadata from parent if this is a port + if (result.Category == Category.Port) + { + if (result.Summary == null) + { + if (result.ParentGame != null) + { + if (result.ParentGame.Id != null) + { + Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no summary, fetching summary from parent game"); + Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false); + result.Summary = parentGame.Summary; + } + } + } + } + return result; } @@ -348,37 +379,112 @@ namespace gaseous_server.Classes.Metadata } } + private static bool AllowNoPlatformSearch = false; + public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType) { - Task games = _SearchForGame(SearchString, PlatformId, searchType); + // search local first + Logging.Log(Logging.LogType.Information, "Game Search", "Attempting local search of type '" + searchType.ToString() + "' for " + SearchString); + Task games = _SearchForGameDatabase(SearchString, PlatformId, searchType); + if (games.Result.Length == 0) + { + // fall back to online search + Logging.Log(Logging.LogType.Information, "Game Search", "Falling back to remote search of type '" + searchType.ToString() + "' for " + SearchString); + games = _SearchForGameRemote(SearchString, PlatformId, searchType); + } return games.Result; } - private static async Task _SearchForGame(string SearchString, long PlatformId, SearchType searchType) + private static async Task _SearchForGameDatabase(string SearchString, long PlatformId, SearchType searchType) { - string searchBody = ""; - string searchFields = "fields id,name,slug,platforms,summary; "; + string whereClause = ""; + Dictionary dbDict = new Dictionary(); + + bool allowSearch = true; switch (searchType) { case SearchType.searchNoPlatform: - searchBody += "search \"" + SearchString + "\"; "; + whereClause = "MATCH(`Name`) AGAINST (@gamename)"; + dbDict.Add("platformid", PlatformId); + dbDict.Add("gamename", SearchString); + + allowSearch = AllowNoPlatformSearch; break; case SearchType.search: - searchBody += "search \"" + SearchString + "\"; "; - searchBody += "where platforms = (" + PlatformId + ");"; + whereClause = "PlatformsId = @platformid AND MATCH(`Name`) AGAINST (@gamename)"; + dbDict.Add("platformid", PlatformId); + dbDict.Add("gamename", SearchString); break; case SearchType.wherefuzzy: - searchBody += "where platforms = (" + PlatformId + ") & name ~ *\"" + SearchString + "\"*;"; + whereClause = "PlatformsId = @platformid AND `Name` LIKE @gamename"; + dbDict.Add("platformid", PlatformId); + dbDict.Add("gamename", "%" + SearchString + "%"); break; case SearchType.where: - searchBody += "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";"; + whereClause = "PlatformsId = @platformid AND `Name` = @gamename"; + dbDict.Add("platformid", PlatformId); + dbDict.Add("gamename", SearchString); + break; + } + + string sql = "SELECT Game.Id, Game.`Name`, Game.Slug, Relation_Game_Platforms.PlatformsId AS PlatformsId, Game.Summary FROM gaseous.Game JOIN Relation_Game_Platforms ON Game.Id = Relation_Game_Platforms.GameId WHERE " + whereClause + ";"; + + + // get Game metadata + Game[]? results = new Game[0]; + if (allowSearch == true) + { + List searchResults = new List(); + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + DataTable data = db.ExecuteCMD(sql, dbDict); + foreach (DataRow row in data.Rows) + { + Game game = new Game{ + Id = (long)row["Id"], + Name = (string)row["Name"], + Slug = (string)row["Slug"], + Summary = (string)row["Summary"] + }; + searchResults.Add(game); + } + + results = searchResults.ToArray(); + } + + return results; + } + + private static async Task _SearchForGameRemote(string SearchString, long PlatformId, SearchType searchType) + { + string searchBody = ""; + string searchFields = "fields id,name,slug,platforms,summary; "; + bool allowSearch = true; + switch (searchType) + { + case SearchType.searchNoPlatform: + searchBody = "search \"" + SearchString + "\"; "; + + allowSearch = AllowNoPlatformSearch; + break; + case SearchType.search: + searchBody = "search \"" + SearchString + "\"; where platforms = (" + PlatformId + ");"; + break; + case SearchType.wherefuzzy: + searchBody = "where platforms = (" + PlatformId + ") & name ~ *\"" + SearchString + "\"*;"; + break; + case SearchType.where: + searchBody = "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";"; break; } // get Game metadata Communications comms = new Communications(); - var results = await comms.APIComm(IGDBClient.Endpoints.Games, searchFields, searchBody); + Game[]? results = new Game[0]; + if (allowSearch == true) + { + results = await comms.APIComm(IGDBClient.Endpoints.Games, searchFields, searchBody); + } return results; } @@ -401,6 +507,7 @@ namespace gaseous_server.Classes.Metadata this.TotalRatingCount = gameObject.TotalRatingCount; this.Cover = gameObject.Cover; this.Artworks = gameObject.Artworks; + this.FirstReleaseDate = gameObject.FirstReleaseDate; // compile age ratings this.AgeRatings = new List(); @@ -421,6 +528,7 @@ namespace gaseous_server.Classes.Metadata public string Name { get; set; } public double? TotalRating { get; set; } public int? TotalRatingCount { get; set; } + public DateTimeOffset? FirstReleaseDate { get; set; } public IGDB.IdentityOrValue Cover { get; set; } public IGDB.IdentitiesOrValues Artworks { get; set; } public List AgeRatings { get; set; } diff --git a/gaseous-server/Controllers/V1.1/GamesController.cs b/gaseous-server/Controllers/V1.1/GamesController.cs index c4c3a3a..fdc7b13 100644 --- a/gaseous-server/Controllers/V1.1/GamesController.cs +++ b/gaseous-server/Controllers/V1.1/GamesController.cs @@ -192,11 +192,14 @@ namespace gaseous_server.Controllers.v1_1 string tempVal = ""; + string nameWhereClause = ""; if (model.Name.Length > 0) { - tempVal = "`Name` LIKE @Name"; - whereParams.Add("@Name", "%" + model.Name + "%"); - havingClauses.Add(tempVal); + // tempVal = "`Name` LIKE @Name"; + // whereParams.Add("@Name", "%" + model.Name + "%"); + // havingClauses.Add(tempVal); + nameWhereClause = "WHERE (MATCH(Game.`Name`) AGAINST (@Name IN BOOLEAN MODE) OR MATCH(AlternativeName.`Name`) AGAINST (@Name IN BOOLEAN MODE))"; + whereParams.Add("@Name", "(*" + model.Name + "*) (" + model.Name + ") "); } if (model.GameRating != null) @@ -263,9 +266,10 @@ namespace gaseous_server.Controllers.v1_1 } } + string platformWhereClause = ""; if (model.Platform.Count > 0) { - tempVal = "Relation_Game_Platforms.PlatformsId IN ("; + tempVal = " AND Games_Roms.PlatformId IN ("; for (int i = 0; i < model.Platform.Count; i++) { if (i > 0) @@ -277,7 +281,8 @@ namespace gaseous_server.Controllers.v1_1 whereParams.Add(platformLabel, model.Platform[i]); } tempVal += ")"; - whereClauses.Add(tempVal); + //whereClauses.Add(tempVal); + platformWhereClause = tempVal; } if (model.Genre.Count > 0) @@ -352,7 +357,7 @@ namespace gaseous_server.Controllers.v1_1 { if (model.GameAgeRating.AgeGroupings.Count > 0) { - tempVal = "(AgeGroupId IN ("; + tempVal = "(Game.AgeGroupId IN ("; for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++) { if (i > 0) @@ -367,7 +372,7 @@ namespace gaseous_server.Controllers.v1_1 if (model.GameAgeRating.IncludeUnrated == true) { - tempVal += " OR AgeGroupId IS NULL"; + tempVal += " OR Game.AgeGroupId IS NULL"; } tempVal += ")"; @@ -439,7 +444,9 @@ namespace gaseous_server.Controllers.v1_1 string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder; Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause; + //string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause; + + string sql = "SELECT DISTINCT Game.Id, Game.`Name`, Game.NameThe, Game.PlatformId, Game.TotalRating, Game.TotalRatingCount, Game.Cover, Game.Artworks, Game.FirstReleaseDate, Game.Category, Game.ParentGame, Game.AgeRatings, Game.AgeGroupId, Game.RomCount FROM (SELECT DISTINCT Game.*, CASE WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The') ELSE Game.`Name` END AS NameThe, Games_Roms.PlatformId, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + " LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + " GROUP BY Game.Id HAVING RomCount > 0) Game LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause; List RetVal = new List(); @@ -457,7 +464,8 @@ namespace gaseous_server.Controllers.v1_1 break; } - RetVal.Add(Classes.Metadata.Games.GetGame(dbResponse.Rows[i])); + Game retGame = Storage.BuildCacheObject(new Game() , dbResponse.Rows[i]); + RetVal.Add(retGame); } GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal); diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1011.sql b/gaseous-server/Support/Database/MySQL/gaseous-1011.sql index defe3d0..27c9de5 100644 --- a/gaseous-server/Support/Database/MySQL/gaseous-1011.sql +++ b/gaseous-server/Support/Database/MySQL/gaseous-1011.sql @@ -6,6 +6,9 @@ CREATE TABLE `AgeGroup` ( `lastUpdated` DATETIME NULL DEFAULT NULL, PRIMARY KEY (`Id`)); +ALTER TABLE `AgeGroup` +ADD INDEX `id_GameId` (`GameId` ASC) VISIBLE; + ALTER TABLE `Game` CHANGE COLUMN `Slug` `Slug` VARCHAR(255) NULL DEFAULT NULL; @@ -15,4 +18,10 @@ SELECT FROM view_GamesWithRoms a LEFT JOIN AgeGroup b ON b.GameId = a.Id -ORDER BY NameThe; \ No newline at end of file +ORDER BY NameThe; + +ALTER TABLE `Game` +ADD FULLTEXT INDEX `ft_Name` (`Name`) VISIBLE; + +ALTER TABLE `AlternativeName` +ADD FULLTEXT INDEX `ft_Name` (`Name`) VISIBLE; \ No newline at end of file diff --git a/gaseous-server/wwwroot/pages/dialogs/rominfo.html b/gaseous-server/wwwroot/pages/dialogs/rominfo.html index 440966d..ad4212b 100644 --- a/gaseous-server/wwwroot/pages/dialogs/rominfo.html +++ b/gaseous-server/wwwroot/pages/dialogs/rominfo.html @@ -120,9 +120,10 @@ ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) { romData = result; + console.log(romData); document.getElementById('modal-heading').innerHTML = result.name; document.getElementById('rominfo_library').innerHTML = result.library.name; - document.getElementById('rominfo_platform').innerHTML = result.platform.name; + document.getElementById('rominfo_platform').innerHTML = result.platform; document.getElementById('rominfo_size').innerHTML = formatBytes(result.size, 2); document.getElementById('rominfo_type').innerHTML = getRomType(result.romType); document.getElementById('rominfo_mediatype').innerHTML = result.romTypeMedia; @@ -132,7 +133,7 @@ document.getElementById('rominfo_signaturematch').innerHTML = result.source; document.getElementById('rominfo_signaturetitle').innerHTML = result.signatureSourceGameTitle; - document.getElementById('properties_fixplatform').innerHTML = ""; + document.getElementById('properties_fixplatform').innerHTML = ""; document.getElementById('properties_fixgame').innerHTML = ""; if (result.library.isDefaultLibrary == false) { @@ -233,7 +234,8 @@ arr.push({ id: data[i].id, text: data[i].name, - cover: data[i].cover + cover: data[i].cover, + releaseDate: data[i].firstReleaseDate }); } diff --git a/gaseous-server/wwwroot/pages/game.html b/gaseous-server/wwwroot/pages/game.html index 78cb017..0db35ba 100644 --- a/gaseous-server/wwwroot/pages/game.html +++ b/gaseous-server/wwwroot/pages/game.html @@ -889,7 +889,8 @@ arr.push({ id: data[i].id, text: data[i].name, - cover: data[i].cover + cover: data[i].cover, + releaseDate: data[i].firstReleaseDate }); } diff --git a/gaseous-server/wwwroot/scripts/gamesformating.js b/gaseous-server/wwwroot/scripts/gamesformating.js index 89dd539..d4a0e7d 100644 --- a/gaseous-server/wwwroot/scripts/gamesformating.js +++ b/gaseous-server/wwwroot/scripts/gamesformating.js @@ -12,6 +12,8 @@ function formatGamesPanel(targetElement, result, pageNumber, pageSize) { console.log("Displaying page: " + pageNumber); console.log("Page size: " + pageSize); + window.scrollTo(0, 0); + var pageMode = GetPreference('LibraryPagination', 'paged'); if (pageNumber == 1 || pageMode == 'paged') { diff --git a/gaseous-server/wwwroot/scripts/main.js b/gaseous-server/wwwroot/scripts/main.js index c9a8000..05b2788 100644 --- a/gaseous-server/wwwroot/scripts/main.js +++ b/gaseous-server/wwwroot/scripts/main.js @@ -253,13 +253,18 @@ function DropDownRenderGameOption(state) { var response; + var releaseDate; + if (state.releaseDate) { + releaseDate = moment(state.releaseDate).format('yyyy'); + } + if (state.cover) { response = $( - '' + '' ); } else { response = $( - '' + '' ); } return response; diff --git a/gaseous-server/wwwroot/styles/style.css b/gaseous-server/wwwroot/styles/style.css index d35f9c7..b8da098 100644 --- a/gaseous-server/wwwroot/styles/style.css +++ b/gaseous-server/wwwroot/styles/style.css @@ -895,6 +895,18 @@ div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover text-align: left; } +.dropdown-title { + display: block; + font-weight: bold; + margin-right: 5px; +} + +.dropdown-releasedate { + display: block; + font-weight: normal; + color: lightgray; +} + button { background-color: #555; color: white;