Migrate to new EJS CDN, and save state tweaks (#264)

* Saved game icon now displays on game cover art in library

* Fixed casing error on save state download icon

* Migrate EJS from submodule to 7z download during docker build

* Updated README and gitignore

* Resized library search buttons

* Export to JSON now triggers the download rather than display of a formatted platform map
This commit is contained in:
Michael Green
2024-01-20 16:12:21 +11:00
committed by GitHub
parent 127eab683b
commit 9b8874902a
23 changed files with 206 additions and 100 deletions

Binary file not shown.

View File

@@ -289,7 +289,7 @@ namespace Authentication
/// <returns></returns>
private int Delete(string userId)
{
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;";
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId; Delete from GameState where UserId = @userId;";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId);

View File

@@ -529,6 +529,11 @@ namespace gaseous_server.Classes.Metadata
public class MinimalGameItem
{
public MinimalGameItem()
{
}
public MinimalGameItem(Game gameObject)
{
this.Id = gameObject.Id;
@@ -558,6 +563,7 @@ namespace gaseous_server.Classes.Metadata
public string Name { get; set; }
public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; }
public bool HasSavedGame { get; set; } = false;
public DateTimeOffset? FirstReleaseDate { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }

View File

@@ -55,12 +55,15 @@ namespace gaseous_server.Classes
return GetMediaGroup(mgId);
}
public static GameRomMediaGroupItem GetMediaGroup(long Id)
public static GameRomMediaGroupItem GetMediaGroup(long Id, string userid = "")
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 WHERE RomMediaGroup.Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id);
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "id", Id },
{ "userid", userid }
};
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
@@ -75,12 +78,15 @@ namespace gaseous_server.Classes
}
}
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId)
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId, string userid = "")
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 WHERE RomMediaGroup.GameId=@gameid;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("gameid", GameId);
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.GameId=@gameid;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "gameid", GameId },
{ "userid", userid }
};
DataTable dataTable = db.ExecuteCMD(sql, dbDict);

View File

@@ -1034,13 +1034,15 @@ namespace gaseous_server.Controllers
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
//[ResponseCache(CacheProfileName = "5Minute")]
public ActionResult GameRomGroup(long GameId, long RomGroupId)
public async Task<ActionResult> GameRomGroupAsync(long GameId, long RomGroupId)
{
var user = await _userManager.GetUserAsync(User);
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId, user.Id);
if (rom.GameId == GameId)
{
return Ok(rom);
@@ -1063,15 +1065,17 @@ namespace gaseous_server.Controllers
[Route("{GameId}/romgroup")]
[ProducesResponseType(typeof(List<RomMediaGroup.GameRomMediaGroupItem>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetGameRomGroup(long GameId)
public async Task<ActionResult> GetGameRomGroupAsync(long GameId)
{
var user = await _userManager.GetUserAsync(User);
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
try
{
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId));
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id));
}
catch (Exception ex)
{
@@ -1121,13 +1125,15 @@ namespace gaseous_server.Controllers
[Route("{GameId}/romgroup/{RomId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameRomGroupMembers(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
public async Task<ActionResult> GameRomGroupMembersAsync(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
{
var user = await _userManager.GetUserAsync(User);
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId, user.Id);
if (rom.GameId == GameId)
{
rom = Classes.RomMediaGroup.EditMediaGroup(RomGroupId, RomIds);

View File

@@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.AspNetCore.Authorization;
using System.Text;
namespace gaseous_server.Controllers
{
@@ -37,6 +38,32 @@ namespace gaseous_server.Controllers
return Ok(PlatformMapping.PlatformMap);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("PlatformMap.json")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult DownloadPlatformMap()
{
string srcJson = Newtonsoft.Json.JsonConvert.SerializeObject(PlatformMapping.PlatformMap, Newtonsoft.Json.Formatting.Indented);
string filename = "PlatformMap.json";
byte[] bytes = Encoding.UTF8.GetBytes(srcJson);
string contentType = "application/json";
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
DispositionType = "attachment"
};
Response.Headers.Add("Content-Disposition", cd.ToString());
return File(bytes, contentType);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]

View File

@@ -85,7 +85,7 @@ namespace gaseous_server.Controllers.v1_1
model.GameAgeRating.IncludeUnrated = false;
}
return Ok(GetGames(model, pageNumber, pageSize));
return Ok(GetGames(model, user.Id, pageNumber, pageSize));
}
else
{
@@ -144,6 +144,7 @@ namespace gaseous_server.Controllers.v1_1
public GameRatingItem GameRating { get; set; } = new GameRatingItem();
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
public bool HasSavedGame { get; set; }
public class GameRatingItem
@@ -181,11 +182,12 @@ namespace gaseous_server.Controllers.v1_1
}
}
public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
public static GameReturnPackage GetGames(GameSearchModel model, string userid, int pageNumber = 0, int pageSize = 0)
{
string whereClause = "";
string havingClause = "";
Dictionary<string, object> whereParams = new Dictionary<string, object>();
whereParams.Add("userid", userid);
List<string> whereClauses = new List<string>();
List<string> havingClauses = new List<string>();
@@ -202,6 +204,12 @@ namespace gaseous_server.Controllers.v1_1
whereParams.Add("@Name", "(*" + model.Name + "*) (" + model.Name + ") ");
}
if (model.HasSavedGame == true)
{
string hasSavesTemp = "(RomSavedStates.RomSaveCount IS NOT NULL OR RomGroupSavedStates.MediaGroupSaveCount IS NOT NULL)";
whereClauses.Add(hasSavesTemp);
}
if (model.GameRating != null)
{
List<string> ratingClauses = new List<string>();
@@ -444,9 +452,71 @@ 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 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<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
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,
RomSavedStates.RomSaveCount,
RomGroupSavedStates.MediaGroupSaveCount
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
(SELECT
Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
FROM
GameState
JOIN Games_Roms ON GameState.RomId = Games_Roms.Id
WHERE
GameState.IsMediaGroup = 0
AND GameState.UserId = @userid
GROUP BY Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
LEFT JOIN
(SELECT
RomMediaGroup.GameId,
COUNT(RomMediaGroup.GameId) AS MediaGroupSaveCount
FROM
RomMediaGroup
JOIN GameState ON RomMediaGroup.Id = GameState.RomId
AND GameState.IsMediaGroup = 1
AND GameState.UserId = @userid
GROUP BY RomMediaGroup.GameId) RomGroupSavedStates ON Game.Id = RomGroupSavedStates.GameId
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<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>();
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
@@ -463,10 +533,24 @@ namespace gaseous_server.Controllers.v1_1
}
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
RetVal.Add(retGame);
Games.MinimalGameItem retMinGame = new Games.MinimalGameItem(retGame);
if (dbResponse.Rows[i]["RomSaveCount"] != DBNull.Value || dbResponse.Rows[i]["MediaGroupSaveCount"] != DBNull.Value)
{
retMinGame.HasSavedGame = true;
}
else
{
retMinGame.HasSavedGame = false;
}
RetVal.Add(retMinGame);
}
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);
GameReturnPackage gameReturn = new GameReturnPackage
{
Count = RecordCount,
Games = RetVal
};
return gameReturn;
}

View File

@@ -29,6 +29,8 @@ namespace gaseous_server.Models
}
}
public bool HasSavedGame { get; set; } = false;
public IGDB.Models.Cover? CoverItem
{
get
@@ -46,49 +48,5 @@ namespace gaseous_server.Models
return null;
}
}
// public List<IGDB.Models.Artwork>? ArtworksItem
// {
// get
// {
// if (this.Artworks != null)
// {
// if (this.Artworks.Ids != null)
// {
// List<IGDB.Models.Artwork> artworks = new List<IGDB.Models.Artwork>();
// foreach (long id in this.Artworks.Ids)
// {
// artworks.Add(gaseous_server.Classes.Metadata.Artworks.GetArtwork(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false));
// }
// return artworks;
// }
// }
// return null;
// }
// }
// public List<IGDB.Models.Screenshot>? ScreenshotsItem
// {
// get
// {
// if (this.Screenshots != null)
// {
// if (this.Screenshots.Ids != null)
// {
// List<IGDB.Models.Screenshot> screenshots = new List<IGDB.Models.Screenshot>();
// foreach (long id in this.Screenshots.Ids)
// {
// screenshots.Add(gaseous_server.Classes.Metadata.Screenshots.GetScreenshot(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false));
// }
// return screenshots;
// }
// }
// return null;
// }
// }
}
}

Binary file not shown.

View File

@@ -102,7 +102,7 @@
stateControlsDownload.id = 'stateControlsDownload_' + result[i].id;
stateControlsDownload.className = 'saved_state_buttonlink';
stateControlsDownload.href = '/api/v1.1/StateManager/' + modalVariables.romId + '/' + result[i].id + '/State/savestate.state?IsMediaGroup=' + modalVariables.IsMediaGroup;
stateControlsDownload.innerHTML = '<img src="/images/Download.svg" class="banner_button_image" alt="Download" title="Download" />';
stateControlsDownload.innerHTML = '<img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" />';
stateControls.appendChild(stateControlsDownload);
var stateControlsDelete = document.createElement('span');

View File

@@ -55,7 +55,7 @@
switch (getQueryString('engine', 'string')) {
case 'EmulatorJS':
$('#emulator').load('/pages/EmulatorJS.html?v=' + AppVersion);
$('#emulator').load('/emulators/EmulatorJS.html?v=' + AppVersion);
break;
}
});

View File

@@ -500,8 +500,7 @@
"mediagroup": 1,
"rompath": romPath
};
saveStatesButton = document.createElement('a');
saveStatesButton.href = '#';
saveStatesButton = document.createElement('div');
saveStatesButton.setAttribute('onclick', 'showDialog("emulatorloadstate", ' + JSON.stringify(modalVariables) + ');');
saveStatesButton.innerHTML = '<img src="/images/SaveStates.png" class="savedstateicon" />';
}
@@ -672,8 +671,7 @@
"mediagroup": 0,
"rompath": romPath
};
saveStatesButton = document.createElement('a');
saveStatesButton.href = '#';
saveStatesButton = document.createElement('div');
saveStatesButton.setAttribute('onclick', 'showDialog("emulatorloadstate", ' + JSON.stringify(modalVariables) + ');');
saveStatesButton.innerHTML = '<img src="/images/SaveStates.png" class="savedstateicon" />';
}

View File

@@ -72,7 +72,7 @@
}
function DownloadJSON() {
window.open('/api/v1.1/PlatformMaps', '_blank');
window.location = '/api/v1.1/PlatformMaps/PlatformMap.json';
}
document.getElementById('importjson').addEventListener('click', openDialog);

View File

@@ -21,6 +21,14 @@ function formatFilterPanel(containerElement, result) {
containerPanelSearchField.id = 'filter_panel_search';
containerPanelSearchField.type = 'text';
containerPanelSearchField.placeholder = 'Search';
containerPanelSearchField.addEventListener("keypress", function(event) {
if (event.key === "Enter") {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
executeFilter1_1();
}
});
containerPanelSearch.appendChild(containerPanelSearchField);
panel.appendChild(containerPanelSearch);
@@ -73,6 +81,8 @@ function formatFilterPanel(containerElement, result) {
panel.appendChild(containerPanelUserRating);
buildFilterPanel(panel, 'settings', 'Settings', [{ "id": "savestatesavailable", "name": "Game has save states avaialble", "gameCount": 0 }], true, true);
if (result.platforms) {
buildFilterPanel(panel, 'platform', 'Platforms', result.platforms, true, true);
}
@@ -350,6 +360,9 @@ function executeFilter1_1(pageNumber, pageSize) {
setCookie('games_library_orderby_direction_select', orderByDirectionSelect);
if (existingSearchModel == undefined || freshSearch == true) {
// search name
setCookie('filter_panel_search', document.getElementById('filter_panel_search').value);
// user ratings
var userRatingEnabled = document.getElementById('filter_panel_userrating_enabled');
@@ -386,6 +399,9 @@ function executeFilter1_1(pageNumber, pageSize) {
setCookie("filter_panel_userrating_enabled", true);
}
// save cookies for settings
GetFilterQuery1_1('settings');
// build filter model
var ratingAgeGroups = GetFilterQuery1_1('agegroupings');
var ratingIncludeUnrated = false;
@@ -395,6 +411,7 @@ function executeFilter1_1(pageNumber, pageSize) {
model = {
"Name": document.getElementById('filter_panel_search').value,
"HasSavedGame": document.getElementById('filter_panel_item_settings_checkbox_savestatesavailable').checked,
"Platform": GetFilterQuery1_1('platform'),
"Genre": GetFilterQuery1_1('genre'),
"GameMode": GetFilterQuery1_1('gamemode'),

View File

@@ -283,6 +283,14 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
}
}
// add save game icon
if (gameObject.hasSavedGame == true) {
var gameSaveIcon = document.createElement('img');
gameSaveIcon.src = '/images/SaveStates.png';
gameSaveIcon.className = 'game_tile_box_savedgame savedstateicon';
gameImageBox.appendChild(gameSaveIcon);
}
if (gameObject.totalRating || displayClassification == true) {
var gameImageRatingBanner = document.createElement('div');
gameImageRatingBanner.className = 'game_tile_box_ratingbanner';

View File

@@ -213,13 +213,13 @@ h3 {
position: -webkit-sticky;
bottom: 0;
width: 180px;
height: 18px;
height: 15px;
padding: 10px;
background-color: #352879;
color: white;
text-align: center;
font-family: Commodore64;
font-size: 16px;
font-size: 13px;
}
#games_library_searchbutton:hover {
@@ -232,13 +232,13 @@ h3 {
position: -webkit-sticky;
bottom: 0;
width: 180px;
height: 18px;
height: 15px;
padding: 10px;
background-color: #646464;
color: white;
text-align: center;
font-family: Commodore64;
font-size: 16px;
font-size: 13px;
}
#games_library_resetbutton:hover {
@@ -557,6 +557,12 @@ input[id='filter_panel_userrating_max'] {
-webkit-backdrop-filter: blur(8px);
}
.game_tile_box_savedgame {
position: absolute;
top: 1px;
right: 5px;
}
.game_tile_image {
max-width: 200px;
min-width: 150px;
@@ -804,6 +810,7 @@ table .romrow:nth-child(odd) {
height: 24px;
width: 24px;
margin-top: 4px;
cursor: pointer;
}
th {