Compare commits

..

23 Commits

Author SHA1 Message Date
Michael Green
ea0a5a6a71 Add missing indexes (#238)
* Add missing index

* Check of indexes
2023-12-21 20:39:37 +11:00
Michael Green
d014a176ce Many performance updates (#237) 2023-12-21 16:59:17 +11:00
Michael Green
b9d9b0ea16 Filter ROMs by platform (#236)
* Added paging to the ROM display on game pages

* Added basic ROM filtering
2023-12-20 13:31:45 +11:00
Michael Green
7e3e4991dc Now pre-compiles age group metadata instead of generating it on every query (#235)
* Server will now quit on start up if schema updates fail (closes Abort start up if an error occurs during database upgrade #221)

* Moved AgeGroups to it's own class

* Improved query performance by defining the AgeGroupId as a metadata item. A metadata refresh is required to generate this data
2023-12-19 23:03:02 +11:00
Michael Green
57248cd467 Overhaul of SQL queries to (hopefully) improve performance with large libraries (#233)
* Latest round of performance updates

* Improved first set up logging

* Updated logging display
2023-12-17 00:51:46 +11:00
Michael Green
722c153e40 Many queries re-written to improve performance (#232) 2023-12-14 22:06:16 +11:00
Michael Green
b691eab0ee Resolve a critical error in new logging code (#231) 2023-12-14 01:09:19 +11:00
Michael Green
907b3e42c4 Added log correlation and revised background tasks layout (#230) 2023-12-13 23:18:21 +11:00
Michael Green
128bbcc1df Filter controller performance improvements (#229)
* Removed perfomance impacting code from filter generation

* Minor pagination fixes
2023-12-13 13:36:10 +11:00
Michael Green
789ec7fc17 Improve background task progress feedback (#228)
* Include a last run duration field for background tasks

* Improved background task progress feedback
2023-12-12 17:42:40 +11:00
Michael Green
32051493a8 Implement API rate limit handling support (#227)
* Merged all IGDB API communications into one class

* Added code to handle IGDB's rate limiter

* Revised IGDB rate limit avoidance and recovery times
2023-12-12 12:00:24 +11:00
Michael Green
7b241ee13e Miscellaneous bug fixes (#220) 2023-12-09 15:50:39 +11:00
Michael Green
84017639eb Logs now have filtering options (#219)
* Added logging configuration options

* Add support for filtering logs
2023-12-09 14:07:08 +11:00
Michael Green
9e346910f4 Set paging default to "paged" (#218) 2023-12-07 13:57:40 +11:00
Michael Green
e7239c428b Library filtering and display enhancements (#214)
* Implement infinite scrolling and paging (selected via preference) (closes #202)
* Display game counts on more filter types (closes #194)
* Make game counts larger (closes #194)
* Include age groups in filtering (closes #200)
* Add sorting options (closes #145)
2023-12-07 13:20:53 +11:00
Michael Green
9288eb8f12 Enhanced firmware availability page (#213) 2023-12-01 17:01:16 +11:00
Michael Green
e32e7ad36f Improved handling of password user feedback (#212) 2023-12-01 15:51:00 +11:00
Michael Green
3de551be95 Fix spelling error (#211) 2023-12-01 14:40:32 +11:00
Michael Green
8e3fa4f8d5 Added release notes configuration (#210) 2023-12-01 14:27:20 +11:00
Michael Green
b564edb158 Background task execution intervals are now user configurable (#209)
* Store background task intervals in database

* Background task intervals are now user customisable
2023-12-01 13:28:41 +11:00
Michael Green
0bf2ba5d96 Store background task intervals in database (#207) 2023-11-30 09:35:05 +11:00
Michael Green
688af162f5 Make library scan run once a day rather than every hour (#206) 2023-11-30 09:19:00 +11:00
Michael Green
7e4fccb0c1 Apply indexes to improve signature search (#205) 2023-11-30 07:50:32 +11:00
87 changed files with 4354 additions and 1266 deletions

15
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
changelog:
categories:
- title: What's New
labels:
- '*'
exclude:
labels:
- bug
- dependencies
- title: Bug Fixes
labels:
- bug
- title: Dependencies
labels:
- dependencies

View File

@@ -1,6 +1,6 @@
# Gaseous Server # Gaseous Server
This is the server for the Gaseous system. It offers ROM and title management, as well as some basic in browser emulation of those ROM's. This is the server for the Gaseous system. It offers ROM and title management, as well as some basic in browser emulation of those ROMs.
## Warning ## Warning

View File

@@ -11,5 +11,6 @@ namespace Authentication
public class ApplicationUser : IdentityUser public class ApplicationUser : IdentityUser
{ {
public SecurityProfileViewModel SecurityProfile { get; set; } public SecurityProfileViewModel SecurityProfile { get; set; }
public List<UserPreferenceViewModel> UserPreferences { get; set; }
} }
} }

View File

@@ -99,6 +99,7 @@ namespace Authentication
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user); user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
} }
return user; return user;
@@ -135,6 +136,7 @@ namespace Authentication
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user); user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
users.Add(user); users.Add(user);
} }
@@ -166,6 +168,7 @@ namespace Authentication
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user); user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
users.Add(user); users.Add(user);
} }
@@ -273,6 +276,9 @@ namespace Authentication
// set default security profile // set default security profile
SetSecurityProfile(user, new SecurityProfileViewModel()); SetSecurityProfile(user, new SecurityProfileViewModel());
// set default preferences
SetPreferences(user, new List<UserPreferenceViewModel>());
return _database.ExecuteCMD(commandText, parameters).Rows.Count; return _database.ExecuteCMD(commandText, parameters).Rows.Count;
} }
@@ -283,7 +289,7 @@ namespace Authentication
/// <returns></returns> /// <returns></returns>
private int Delete(string userId) private int Delete(string userId)
{ {
string commandText = "Delete from Users where Id = @userId"; string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;";
Dictionary<string, object> parameters = new Dictionary<string, object>(); Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId); parameters.Add("@userId", userId);
@@ -328,6 +334,9 @@ namespace Authentication
// set the security profile // set the security profile
SetSecurityProfile(user, user.SecurityProfile); SetSecurityProfile(user, user.SecurityProfile);
// set preferences
SetPreferences(user, user.UserPreferences);
return _database.ExecuteCMD(commandText, parameters).Rows.Count; return _database.ExecuteCMD(commandText, parameters).Rows.Count;
} }
@@ -367,5 +376,66 @@ namespace Authentication
return _database.ExecuteCMD(commandText, parameters).Rows.Count; return _database.ExecuteCMD(commandText, parameters).Rows.Count;
} }
public List<UserPreferenceViewModel> GetPreferences(TUser user)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT `Setting`, `Value` FROM User_Settings WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", user.Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
List<UserPreferenceViewModel> userPrefs = new List<UserPreferenceViewModel>();
foreach (DataRow row in data.Rows)
{
UserPreferenceViewModel userPref = new UserPreferenceViewModel();
userPref.Setting = (string)row["Setting"];
userPref.Value = (string)row["Value"];
userPrefs.Add(userPref);
}
return userPrefs;
}
public int SetPreferences(TUser user, List<UserPreferenceViewModel> model)
{
if (model != null)
{
List<UserPreferenceViewModel> userPreferences = GetPreferences(user);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
foreach (UserPreferenceViewModel modelItem in model)
{
bool prefItemFound = false;
foreach (UserPreferenceViewModel existing in userPreferences)
{
if (existing.Setting.ToLower() == modelItem.Setting.ToLower())
{
prefItemFound = true;
break;
}
}
string sql = "INSERT INTO User_Settings (`Id`, `Setting`, `Value`) VALUES (@id, @setting, @value);";
if (prefItemFound == true)
{
sql = "UPDATE User_Settings SET `Value`=@value WHERE `Id`=@id AND `Setting`=@setting";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", user.Id);
dbDict.Add("setting", modelItem.Setting);
dbDict.Add("value", modelItem.Value);
db.ExecuteNonQuery(sql, dbDict);
}
return model.Count;
}
else
{
return 0;
}
}
} }
} }

View File

@@ -7,6 +7,7 @@ namespace Authentication
public string EmailAddress { get; set; } public string EmailAddress { get; set; }
public List<String> Roles { get; set; } public List<String> Roles { get; set; }
public SecurityProfileViewModel SecurityProfile { get; set; } public SecurityProfileViewModel SecurityProfile { get; set; }
public List<UserPreferenceViewModel> UserPreferences { get; set; }
public string HighestRole { public string HighestRole {
get get
{ {

View File

@@ -5,13 +5,13 @@ namespace Authentication
public class SecurityProfileViewModel public class SecurityProfileViewModel
{ {
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{ public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{
MaximumAgeRestriction = "Adult", MaximumAgeRestriction = gaseous_server.Classes.Metadata.AgeGroups.AgeRestrictionGroupings.Adult,
IncludeUnrated = true IncludeUnrated = true
}; };
public class AgeRestrictionItem public class AgeRestrictionItem
{ {
public string MaximumAgeRestriction { get; set; } public gaseous_server.Classes.Metadata.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction { get; set; }
public bool IncludeUnrated { get; set; } public bool IncludeUnrated { get; set; }
} }
} }

View File

@@ -0,0 +1,8 @@
namespace Authentication;
public class UserPreferenceViewModel
{
public string Setting { get; set; }
public string Value { get; set; }
}

View File

@@ -4,20 +4,29 @@ using System.IO.Compression;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using Authentication;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers; using gaseous_server.Controllers;
using gaseous_server.Models; using gaseous_server.Models;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class Collections public class Collections
{ {
public Collections() private readonly UserManager<ApplicationUser> _userManager;
{ private readonly SignInManager<ApplicationUser> _signInManager;
} public Collections(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public static List<CollectionItem> GetCollections() { public static List<CollectionItem> GetCollections() {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -211,8 +220,11 @@ namespace gaseous_server.Classes
} }
} else { } else {
// get all platforms to pull from // get all platforms to pull from
FilterController filterController = new FilterController(); Dictionary<string, List<Filters.FilterItem>> FilterDict = Filters.Filter(AgeGroups.AgeRestrictionGroupings.Adult, true);
platforms.AddRange((List<FilterController.FilterPlatform>)filterController.Filter()["platforms"]); List<Classes.Filters.FilterItem> filteredPlatforms = (List<Classes.Filters.FilterItem>)FilterDict["platforms"];
foreach (Filters.FilterItem filterItem in filteredPlatforms) {
platforms.Add(Platforms.GetPlatform(filterItem.Id));
}
} }
// build collection // build collection
@@ -487,7 +499,7 @@ namespace gaseous_server.Classes
if (File.Exists(gameRomItem.Path)) if (File.Exists(gameRomItem.Path))
{ {
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name); Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name);
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name)); File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name), true);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
using System; using System.Collections.Concurrent;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace gaseous_server.Classes namespace gaseous_server.Classes
@@ -111,5 +111,29 @@ namespace gaseous_server.Classes
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
} }
} }
}
/// <summary>
/// Provides a way to set contextual data that flows with the call and
/// async context of a test or invocation.
/// </summary>
public static class CallContext
{
static ConcurrentDictionary<string, AsyncLocal<object>> state = new ConcurrentDictionary<string, AsyncLocal<object>>();
/// <summary>
/// Stores a given object and associates it with the specified name.
/// </summary>
/// <param name="name">The name with which to associate the new item in the call context.</param>
/// <param name="data">The object to store in the call context.</param>
public static void SetData(string name, object data) =>
state.GetOrAdd(name, _ => new AsyncLocal<object>()).Value = data;
/// <summary>
/// Retrieves an object with the specified name from the <see cref="CallContext"/>.
/// </summary>
/// <param name="name">The name of the item in the call context.</param>
/// <returns>The object in the call context associated with the specified name, or <see langword="null"/> if not found.</returns>
public static object GetData(string name) =>
state.TryGetValue(name, out AsyncLocal<object> data) ? data.Value : null;
}
}

View File

@@ -2,6 +2,7 @@
using System.Data; using System.Data;
using Newtonsoft.Json; using Newtonsoft.Json;
using IGDB.Models; using IGDB.Models;
using gaseous_server.Classes.Metadata;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
@@ -57,6 +58,14 @@ namespace gaseous_server.Classes
} }
} }
public static ConfigFile.MetadataAPI MetadataConfiguration
{
get
{
return _config.MetadataConfiguration;
}
}
public static ConfigFile.IGDB IGDB public static ConfigFile.IGDB IGDB
{ {
get get
@@ -247,6 +256,8 @@ namespace gaseous_server.Classes
[JsonIgnore] [JsonIgnore]
public Library LibraryConfiguration = new Library(); public Library LibraryConfiguration = new Library();
public MetadataAPI MetadataConfiguration = new MetadataAPI();
public IGDB IGDBConfiguration = new IGDB(); public IGDB IGDBConfiguration = new IGDB();
public Logging LoggingConfiguration = new Logging(); public Logging LoggingConfiguration = new Logging();
@@ -460,6 +471,26 @@ namespace gaseous_server.Classes
} }
} }
public class MetadataAPI
{
private static Communications.MetadataSources _Source
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
{
return (Communications.MetadataSources)Enum.Parse(typeof(Communications.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
}
else
{
return Communications.MetadataSources.IGDB;
}
}
}
public Communications.MetadataSources Source = _Source;
}
public class IGDB public class IGDB
{ {
private static string _DefaultIGDBClientId private static string _DefaultIGDBClientId
@@ -503,11 +534,7 @@ namespace gaseous_server.Classes
// log retention in days // log retention in days
public int LogRetention = 7; public int LogRetention = 7;
public enum LoggingFormat public bool AlwaysLogToDisk = false;
{
Json,
Text
}
} }
} }
} }

View File

@@ -107,20 +107,28 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer); Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer);
if (SchemaVer < i) if (SchemaVer < i)
{ {
// run pre-upgrade code try
DatabaseMigration.PreUpgradeScript(i, _ConnectorType); {
// run pre-upgrade code
// apply schema! DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
ExecuteCMD(dbScript, dbDict); // apply schema!
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
ExecuteCMD(dbScript, dbDict, 180);
sql = "UPDATE schema_version SET schema_version=@schemaver"; sql = "UPDATE schema_version SET schema_version=@schemaver";
dbDict = new Dictionary<string, object>(); dbDict = new Dictionary<string, object>();
dbDict.Add("schemaver", i); dbDict.Add("schemaver", i);
ExecuteCMD(sql, dbDict); ExecuteCMD(sql, dbDict);
// run post-upgrade code // run post-upgrade code
DatabaseMigration.PostUpgradeScript(i, _ConnectorType); DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Database", "Schema upgrade failed! Unable to continue.", ex);
System.Environment.Exit(1);
}
} }
} }
} }

View File

@@ -0,0 +1,143 @@
using System.Data;
using System.Reflection.Metadata.Ecma335;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
namespace gaseous_server.Classes
{
public class Filters
{
public static Dictionary<string, List<FilterItem>> Filter(Metadata.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, List<FilterItem>> FilterSet = new Dictionary<string, List<FilterItem>>();
// platforms
List<FilterItem> platforms = new List<FilterItem>();
string ageRestriction_Platform = "AgeGroup.AgeGroupId <= " + (int)MaximumAgeRestriction;
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
if (IncludeUnrated == true)
{
ageRestriction_Platform += " OR AgeGroup.AgeGroupId IS NULL";
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
}
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);
foreach (DataRow dr in dbResponse.Rows)
{
FilterItem platformItem = new FilterItem(dr);
platforms.Add(platformItem);
}
FilterSet.Add("platforms", platforms);
// genres
List<FilterItem> genres = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterItem genreItem = new FilterItem(dr);
genres.Add(genreItem);
}
FilterSet.Add("genres", genres);
// game modes
List<FilterItem> gameModes = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterItem gameModeItem = new FilterItem(dr);
gameModes.Add(gameModeItem);
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<FilterItem> playerPerspectives = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterItem playerPerspectiveItem = new FilterItem(dr);
playerPerspectives.Add(playerPerspectiveItem);
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<FilterItem> themes = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterItem themeItem = new FilterItem(dr);
themes.Add(themeItem);
}
FilterSet.Add("themes", themes);
// age groups
List<FilterItem> agegroupings = new List<FilterItem>();
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)
{
FilterItem filterAgeGrouping = new FilterItem();
if (dr["AgeGroupId"] == DBNull.Value)
{
filterAgeGrouping.Id = (int)(long)AgeGroups.AgeRestrictionGroupings.Unclassified;
filterAgeGrouping.Name = AgeGroups.AgeRestrictionGroupings.Unclassified.ToString();
}
else
{
int ageGroupLong = (int)dr["AgeGroupId"];
AgeGroups.AgeRestrictionGroupings ageGroup = (AgeGroups.AgeRestrictionGroupings)ageGroupLong;
filterAgeGrouping.Id = ageGroupLong;
filterAgeGrouping.Name = ageGroup.ToString();
}
filterAgeGrouping.GameCount = (int)(long)dr["GameCount"];
agegroupings.Add(filterAgeGrouping);
}
FilterSet.Add("agegroupings", agegroupings);
return FilterSet;
}
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction)
{
//string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`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_<ITEMNAME>s ON Game.Id = Relation_Game_<ITEMNAME>s.GameId JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
sql = sql.Replace("<ITEMNAME>", Name);
DataTable dbResponse = db.ExecuteCMD(sql);
return dbResponse;
}
public class FilterItem
{
public FilterItem()
{
}
public FilterItem(DataRow dr)
{
this.Id = (long)dr["Id"];
this.Name = (string)dr["Name"];
this.GameCount = (int)(long)dr["GameCount"];
}
public long Id { get; set; }
public string Name { get; set; }
public int GameCount { get; set; }
}
}
}

View File

@@ -45,6 +45,11 @@ namespace gaseous_server
DataRow row = data.Rows[0]; DataRow row = data.Rows[0];
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"])); LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
if (!Directory.Exists(library.Path))
{
Directory.CreateDirectory(library.Path);
}
return library; return library;
} }
} }
@@ -61,6 +66,15 @@ namespace gaseous_server
{ {
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"])); LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
libraryItems.Add(library); libraryItems.Add(library);
if (library.IsDefaultLibrary == true)
{
// check directory exists
if (!Directory.Exists(library.Path))
{
Directory.CreateDirectory(library.Path);
}
}
} }
return libraryItems; return libraryItems;
@@ -143,6 +157,11 @@ namespace gaseous_server
_Path = Path; _Path = Path;
_DefaultPlatformId = DefaultPlatformId; _DefaultPlatformId = DefaultPlatformId;
_IsDefaultLibrary = IsDefaultLibrary; _IsDefaultLibrary = IsDefaultLibrary;
if (!Directory.Exists(Path))
{
Directory.CreateDirectory(Path);
}
} }
int _Id = 0; int _Id = 0;

View File

@@ -1,17 +1,19 @@
using System; using System;
using System.Data; using System.Data;
using System.IO.Compression; using System.IO.Compression;
using System.Security.Authentication;
using System.Security.Policy; using System.Security.Policy;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using IGDB.Models; using IGDB.Models;
using NuGet.Common; using NuGet.Common;
using NuGet.LibraryModel;
using static gaseous_server.Classes.Metadata.Games; using static gaseous_server.Classes.Metadata.Games;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class ImportGames public class ImportGames : QueueItemStatus
{ {
public ImportGames(string ImportPath) public ImportGames(string ImportPath)
{ {
@@ -21,9 +23,15 @@ namespace gaseous_server.Classes
string[] importContents_Directories = Directory.GetDirectories(ImportPath); string[] importContents_Directories = Directory.GetDirectories(ImportPath);
// import files first // import files first
int importCount = 1;
foreach (string importContent in importContents_Files) { foreach (string importContent in importContents_Files) {
SetStatus(importCount, importContents_Files.Length, "Importing file: " + importContent);
ImportGame.ImportGameFile(importContent, null); ImportGame.ImportGameFile(importContent, null);
importCount += 1;
} }
ClearStatus();
// import sub directories // import sub directories
foreach (string importDir in importContents_Directories) { foreach (string importDir in importContents_Directories) {
@@ -40,7 +48,7 @@ namespace gaseous_server.Classes
} }
public class ImportGame public class ImportGame : QueueItemStatus
{ {
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform) public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{ {
@@ -600,7 +608,7 @@ namespace gaseous_server.Classes
} }
} }
public static void LibraryScan() public void LibraryScan()
{ {
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries) foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{ {
@@ -645,8 +653,10 @@ namespace gaseous_server.Classes
// search for files in the library that aren't in the database // search for files in the library that aren't in the database
Logging.Log(Logging.LogType.Information, "Library Scan", "Looking for orphaned library files to add"); Logging.Log(Logging.LogType.Information, "Library Scan", "Looking for orphaned library files to add");
string[] LibraryFiles = Directory.GetFiles(library.Path, "*.*", SearchOption.AllDirectories); string[] LibraryFiles = Directory.GetFiles(library.Path, "*.*", SearchOption.AllDirectories);
int StatusCount = 0;
foreach (string LibraryFile in LibraryFiles) foreach (string LibraryFile in LibraryFiles)
{ {
SetStatus(StatusCount, LibraryFiles.Length, "Processing file " + LibraryFile);
if (!Common.SkippableFiles.Contains<string>(Path.GetFileName(LibraryFile), StringComparer.OrdinalIgnoreCase)) if (!Common.SkippableFiles.Contains<string>(Path.GetFileName(LibraryFile), StringComparer.OrdinalIgnoreCase))
{ {
Common.hashObject LibraryFileHash = new Common.hashObject(LibraryFile); Common.hashObject LibraryFileHash = new Common.hashObject(LibraryFile);
@@ -680,34 +690,44 @@ namespace gaseous_server.Classes
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId); IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
IGDB.Models.Game determinedGame = new Game(); IGDB.Models.Game determinedGame = new Game();
if (determinedPlatform == null) try
{ {
if (library.DefaultPlatformId == 0) if (determinedPlatform == null)
{ {
determinedPlatform = new IGDB.Models.Platform(); if (library.DefaultPlatformId == 0)
determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId); {
determinedPlatform = new IGDB.Models.Platform();
determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
}
else
{
determinedPlatform = Platforms.GetPlatform(library.DefaultPlatformId);
determinedGame = SearchForGame(sig.Game.Name, library.DefaultPlatformId);
}
} }
else else
{ {
determinedPlatform = Platforms.GetPlatform(library.DefaultPlatformId); determinedGame = SearchForGame(sig.Game.Name, (long)determinedPlatform.Id);
determinedGame = SearchForGame(sig.Game.Name, library.DefaultPlatformId);
} }
}
else
{
determinedGame = SearchForGame(sig.Game.Name, (long)determinedPlatform.Id);
}
StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile); StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Library Scan", " An error occurred while matching orphaned file: " + LibraryFile + ". Skipping.", ex);
}
} }
} }
StatusCount += 1;
} }
ClearStatus();
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`"; sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict); dtRoms = db.ExecuteCMD(sql, dbDict);
// check all roms to see if their local file still exists // check all roms to see if their local file still exists
Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk"); Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk");
StatusCount = 0;
if (dtRoms.Rows.Count > 0) if (dtRoms.Rows.Count > 0)
{ {
for (var i = 0; i < dtRoms.Rows.Count; i++) for (var i = 0; i < dtRoms.Rows.Count; i++)
@@ -716,6 +736,7 @@ namespace gaseous_server.Classes
string romPath = (string)dtRoms.Rows[i]["Path"]; string romPath = (string)dtRoms.Rows[i]["Path"];
gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType romMetadataSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(int)dtRoms.Rows[i]["MetadataSource"]; gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType romMetadataSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(int)dtRoms.Rows[i]["MetadataSource"];
SetStatus(StatusCount, dtRoms.Rows.Count, "Processing file " + romPath);
Logging.Log(Logging.LogType.Information, "Library Scan", " Processing ROM at path " + romPath); Logging.Log(Logging.LogType.Information, "Library Scan", " Processing ROM at path " + romPath);
if (File.Exists(romPath)) if (File.Exists(romPath))
@@ -739,6 +760,8 @@ namespace gaseous_server.Classes
deleteDict.Add("libraryid", library.Id); deleteDict.Add("libraryid", library.Id);
db.ExecuteCMD(deleteSql, deleteDict); db.ExecuteCMD(deleteSql, deleteDict);
} }
StatusCount += 1;
} }
} }
@@ -746,7 +769,7 @@ namespace gaseous_server.Classes
} }
} }
public static void Rematcher(bool ForceExecute = false) public void Rematcher(bool ForceExecute = false)
{ {
// rescan all titles with an unknown platform or title and see if we can get a match // rescan all titles with an unknown platform or title and see if we can get a match
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting"); Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting");
@@ -755,17 +778,20 @@ namespace gaseous_server.Classes
string sql = ""; string sql = "";
if (ForceExecute == false) if (ForceExecute == false)
{ {
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 OR GameId = 0 OR MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;"; sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;";
} }
else else
{ {
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 OR GameId = 0 OR MetadataSource = 0);"; sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);";
} }
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7)); dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
DataTable data = db.ExecuteCMD(sql, dbDict); DataTable data = db.ExecuteCMD(sql, dbDict);
int StatusCount = -0;
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
SetStatus(StatusCount, data.Rows.Count, "Running rematcher");
// get library // get library
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]); GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
@@ -800,9 +826,13 @@ namespace gaseous_server.Classes
dbLastAttemptDict.Add("id", romId); dbLastAttemptDict.Add("id", romId);
dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow); dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow);
db.ExecuteCMD(attemptSql, dbLastAttemptDict); db.ExecuteCMD(attemptSql, dbLastAttemptDict);
StatusCount += 1;
} }
ClearStatus();
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed"); Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed");
ClearStatus();
} }
} }
} }

View File

@@ -7,6 +7,7 @@ namespace gaseous_server.Classes
{ {
public class Logging public class Logging
{ {
private static DateTime lastDiskRetentionSweep = DateTime.UtcNow;
public static bool WriteToDiskOnly { get; set; } = false; public static bool WriteToDiskOnly { get; set; } = false;
static public void Log(LogType EventType, string ServerProcess, string Message, Exception? ExceptionValue = null, bool LogToDiskOnly = false) static public void Log(LogType EventType, string ServerProcess, string Message, Exception? ExceptionValue = null, bool LogToDiskOnly = false)
@@ -69,8 +70,64 @@ namespace gaseous_server.Classes
if (LogToDiskOnly == false) if (LogToDiskOnly == false)
{ {
if (Config.LoggingConfiguration.AlwaysLogToDisk == true)
{
LogToDisk(logItem, TraceOutput, null);
}
string correlationId;
try
{
if (CallContext.GetData("CorrelationId").ToString() == null)
{
correlationId = "";
}
else
{
correlationId = CallContext.GetData("CorrelationId").ToString();
}
}
catch
{
correlationId = "";
}
string callingProcess;
try
{
if (CallContext.GetData("CallingProcess").ToString() == null)
{
callingProcess = "";
}
else
{
callingProcess = CallContext.GetData("CallingProcess").ToString();
}
}
catch
{
callingProcess = "";
}
string callingUser;
try
{
if (CallContext.GetData("CallingUser").ToString() == null)
{
callingUser = "";
}
else
{
callingUser = CallContext.GetData("CallingUser").ToString();
}
}
catch
{
callingUser = "";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception) VALUES (@EventTime, @EventType, @Process, @Message, @Exception);"; string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1)); dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
dbDict.Add("EventTime", logItem.EventTime); dbDict.Add("EventTime", logItem.EventTime);
@@ -78,6 +135,9 @@ namespace gaseous_server.Classes
dbDict.Add("Process", logItem.Process); dbDict.Add("Process", logItem.Process);
dbDict.Add("Message", logItem.Message); dbDict.Add("Message", logItem.Message);
dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString()); dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString());
dbDict.Add("correlationid", correlationId);
dbDict.Add("callingprocess", callingProcess);
dbDict.Add("callinguser", callingUser);
try try
{ {
@@ -93,6 +153,22 @@ namespace gaseous_server.Classes
LogToDisk(logItem, TraceOutput, null); LogToDisk(logItem, TraceOutput, null);
} }
} }
if (lastDiskRetentionSweep.AddMinutes(60) < DateTime.UtcNow)
{
// time to delete any old logs
lastDiskRetentionSweep = DateTime.UtcNow;
string[] files = Directory.GetFiles(Config.LogPath);
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
if (fi.LastAccessTime < DateTime.Now.AddDays(Config.LoggingConfiguration.LogRetention * -1))
{
fi.Delete();
}
}
}
} }
static void LogToDisk(LogItem logItem, string TraceOutput, Exception? exception) static void LogToDisk(LogItem logItem, string TraceOutput, Exception? exception)
@@ -110,22 +186,111 @@ namespace gaseous_server.Classes
File.AppendAllText(Config.LogFilePath, TraceOutput); File.AppendAllText(Config.LogFilePath, TraceOutput);
} }
static public List<LogItem> GetLogs(long? StartIndex, int PageNumber = 1, int PageSize = 100) static public List<LogItem> GetLogs(LogsViewModel model)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", model.StartIndex);
dbDict.Add("PageNumber", (model.PageNumber - 1) * model.PageSize);
dbDict.Add("PageSize", model.PageSize);
string sql = ""; string sql = "";
if (StartIndex == null)
List<string> whereClauses = new List<string>();
// handle status criteria
if (model.Status != null)
{ {
sql = "SELECT * FROM ServerLogs ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;"; if (model.Status.Count > 0)
{
List<string> statusWhere = new List<string>();
for (int i = 0; i < model.Status.Count; i++)
{
string valueName = "@eventtype" + i;
statusWhere.Add(valueName);
dbDict.Add(valueName, (int)model.Status[i]);
}
whereClauses.Add("EventType IN (" + string.Join(",", statusWhere) + ")");
}
}
// handle start date criteria
if (model.StartDateTime != null)
{
dbDict.Add("startdate", model.StartDateTime);
whereClauses.Add("EventTime >= @startdate");
}
// handle end date criteria
if (model.EndDateTime != null)
{
dbDict.Add("enddate", model.EndDateTime);
whereClauses.Add("EventTime <= @enddate");
}
// handle search text criteria
if (model.SearchText != null)
{
if (model.SearchText.Length > 0)
{
dbDict.Add("messageSearch", model.SearchText);
whereClauses.Add("MATCH(Message) AGAINST (@messageSearch)");
}
}
if (model.CorrelationId != null)
{
if (model.CorrelationId.Length > 0)
{
dbDict.Add("correlationId", model.CorrelationId);
whereClauses.Add("CorrelationId = @correlationId");
}
}
if (model.CallingProcess != null)
{
if (model.CallingProcess.Length > 0)
{
dbDict.Add("callingProcess", model.CallingProcess);
whereClauses.Add("CallingProcess = @callingProcess");
}
}
if (model.CallingUser != null)
{
if (model.CallingUser.Length > 0)
{
dbDict.Add("callingUser", model.CallingUser);
whereClauses.Add("CallingUser = @callingUser");
}
}
// compile WHERE clause
string whereClause = "";
if (whereClauses.Count > 0)
{
whereClause = "(" + String.Join(" AND ", whereClauses) + ")";
}
// execute query
if (model.StartIndex == null)
{
if (whereClause.Length > 0)
{
whereClause = "WHERE " + whereClause;
}
sql = "SELECT ServerLogs.Id, ServerLogs.EventTime, ServerLogs.EventType, ServerLogs.`Process`, ServerLogs.Message, ServerLogs.Exception, ServerLogs.CorrelationId, ServerLogs.CallingProcess, Users.Email FROM ServerLogs LEFT JOIN Users ON ServerLogs.CallingUser = Users.Id " + whereClause + " ORDER BY ServerLogs.Id DESC LIMIT @PageSize OFFSET @PageNumber;";
} }
else else
{ {
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;"; if (whereClause.Length > 0)
{
whereClause = "AND " + whereClause;
}
sql = "SELECT ServerLogs.Id, ServerLogs.EventTime, ServerLogs.EventType, ServerLogs.`Process`, ServerLogs.Message, ServerLogs.Exception, ServerLogs.CorrelationId, ServerLogs.CallingProcess, Users.Email FROM ServerLogs LEFT JOIN Users ON ServerLogs.CallingUser = Users.Id WHERE ServerLogs.Id < @StartIndex " + whereClause + " ORDER BY ServerLogs.Id DESC LIMIT @PageSize OFFSET @PageNumber;";
} }
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", StartIndex);
dbDict.Add("PageNumber", (PageNumber - 1) * PageSize);
dbDict.Add("PageSize", PageSize);
DataTable dataTable = db.ExecuteCMD(sql, dbDict); DataTable dataTable = db.ExecuteCMD(sql, dbDict);
List<LogItem> logs = new List<LogItem>(); List<LogItem> logs = new List<LogItem>();
@@ -138,7 +303,10 @@ namespace gaseous_server.Classes
EventType = (LogType)row["EventType"], EventType = (LogType)row["EventType"],
Process = (string)row["Process"], Process = (string)row["Process"],
Message = (string)row["Message"], Message = (string)row["Message"],
ExceptionValue = (string)row["Exception"] ExceptionValue = (string)row["Exception"],
CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""),
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], ""),
CallingUser = (string)Common.ReturnValueIfNull(row["Email"], "")
}; };
logs.Add(log); logs.Add(log);
@@ -161,6 +329,9 @@ namespace gaseous_server.Classes
public DateTime EventTime { get; set; } public DateTime EventTime { get; set; }
public LogType? EventType { get; set; } public LogType? EventType { get; set; }
public string Process { get; set; } = ""; public string Process { get; set; } = "";
public string CorrelationId { get; set; } = "";
public string? CallingProcess { get; set; } = "";
public string? CallingUser { get; set; } = "";
private string _Message = ""; private string _Message = "";
public string Message public string Message
{ {
@@ -175,6 +346,20 @@ namespace gaseous_server.Classes
} }
public string? ExceptionValue { get; set; } public string? ExceptionValue { get; set; }
} }
public class LogsViewModel
{
public long? StartIndex { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 100;
public List<LogType> Status { get; set; } = new List<LogType>();
public DateTime? StartDateTime { get; set; }
public DateTime? EndDateTime { get; set; }
public string? SearchText { get; set; }
public string? CorrelationId { get; set; }
public string? CallingProcess { get; set; }
public string? CallingUser { get; set; }
}
} }
} }

View File

@@ -5,11 +5,11 @@ using Microsoft.VisualStudio.Web.CodeGeneration;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class Maintenance public class Maintenance : QueueItemStatus
{ {
const int MaxFileAge = 30; const int MaxFileAge = 30;
public static void RunMaintenance() public void RunMaintenance()
{ {
// delete files and directories older than 7 days in PathsToClean // delete files and directories older than 7 days in PathsToClean
List<string> PathsToClean = new List<string>(); List<string> PathsToClean = new List<string>();
@@ -49,8 +49,11 @@ namespace gaseous_server.Classes
string sql = "SHOW TABLES;"; string sql = "SHOW TABLES;";
DataTable tables = db.ExecuteCMD(sql); DataTable tables = db.ExecuteCMD(sql);
int StatusCounter = 1;
foreach (DataRow row in tables.Rows) foreach (DataRow row in tables.Rows)
{ {
SetStatus(StatusCounter, tables.Rows.Count, "Optimising table " + row[0].ToString());
sql = "OPTIMIZE TABLE " + row[0].ToString(); sql = "OPTIMIZE TABLE " + row[0].ToString();
DataTable response = db.ExecuteCMD(sql); DataTable response = db.ExecuteCMD(sql);
foreach (DataRow responseRow in response.Rows) foreach (DataRow responseRow in response.Rows)
@@ -60,9 +63,12 @@ namespace gaseous_server.Classes
{ {
retVal += responseRow.ItemArray[i] + "; "; retVal += responseRow.ItemArray[i] + "; ";
} }
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimizse table " + row[0].ToString() + ": " + retVal); Logging.Log(Logging.LogType.Information, "Maintenance", "(" + StatusCounter + "/" + tables.Rows.Count + "): Optimise table " + row[0].ToString() + ": " + retVal);
} }
StatusCounter += 1;
} }
ClearStatus();
} }
} }
} }

View File

@@ -0,0 +1,304 @@
using System;
using System.Reflection;
using System.Text.Json.Serialization;
using IGDB;
using IGDB.Models;
using Microsoft.CodeAnalysis.Classification;
namespace gaseous_server.Classes.Metadata
{
public class AgeGroups
{
public AgeGroups()
{
}
public static AgeGroup? GetAgeGroup(Game? game)
{
if (game == null)
{
return null;
}
else
{
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
cacheStatus = Storage.GetCacheStatus("AgeGroup", (long)game.Id);
AgeGroup? RetVal = new AgeGroup();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
RetVal = _GetAgeGroup(game);
Storage.NewCacheValue(RetVal, false);
break;
case Storage.CacheStatus.Expired:
RetVal = _GetAgeGroup(game);
Storage.NewCacheValue(RetVal, true);
break;
case Storage.CacheStatus.Current:
RetVal = Storage.GetCacheValue<AgeGroup>(RetVal, "Id", game.Id);
break;
default:
throw new Exception("How did you get here?");
}
return RetVal;
}
}
public static AgeGroup? _GetAgeGroup(Game game)
{
// compile the maximum age group for the given game
if (game != null)
{
if (game.AgeRatings != null)
{
if (game.AgeRatings.Ids != null)
{
// collect ratings values from metadata
List<AgeRating> ageRatings = new List<AgeRating>();
foreach (long ratingId in game.AgeRatings.Ids)
{
AgeRating? rating = AgeRatings.GetAgeRatings(ratingId);
if (rating != null)
{
ageRatings.Add(rating);
}
}
// compile the ratings values into the ratings groups
AgeRestrictionGroupings highestAgeGroup = AgeRestrictionGroupings.Unclassified;
foreach (AgeRating ageRating in ageRatings)
{
foreach (KeyValuePair<AgeRestrictionGroupings, AgeGroupItem> ageGroupItem in AgeGroupingsFlat)
{
PropertyInfo[] groupProps = typeof(AgeGroupItem).GetProperties();
foreach (PropertyInfo property in groupProps)
{
if (RatingsBoards.Contains(property.Name))
{
List<AgeRatingTitle> ratingBoard = (List<AgeRatingTitle>)property.GetValue(ageGroupItem.Value);
foreach (AgeRatingTitle ratingTitle in ratingBoard)
{
if (ageRating.Rating == ratingTitle)
{
if (highestAgeGroup < ageGroupItem.Key)
{
highestAgeGroup = ageGroupItem.Key;
}
}
}
}
}
}
}
// return the compiled ratings group
AgeGroup ageGroup = new AgeGroup();
ageGroup.Id = game.Id;
ageGroup.GameId = game.Id;
if (highestAgeGroup == 0)
{
ageGroup.AgeGroupId = null;
}
else
{
ageGroup.AgeGroupId = highestAgeGroup;
}
return ageGroup;
}
else
{
AgeGroup ageGroup = new AgeGroup();
ageGroup.Id = game.Id;
ageGroup.GameId = game.Id;
ageGroup.AgeGroupId = null;
return ageGroup;
}
}
else
{
AgeGroup ageGroup = new AgeGroup();
ageGroup.Id = game.Id;
ageGroup.GameId = game.Id;
ageGroup.AgeGroupId = null;
return ageGroup;
}
}
return null;
}
public class AgeGroup
{
public long? Id { get; set; }
public long? GameId { get; set; }
public AgeRestrictionGroupings? AgeGroupId { get; set; }
}
public static Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>> AgeGroupings
{
get
{
return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
{
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
}
};
}
}
public static Dictionary<AgeRestrictionGroupings, AgeGroupItem> AgeGroupingsFlat
{
get
{
return new Dictionary<AgeRestrictionGroupings, AgeGroupItem>{
{
AgeRestrictionGroupings.Adult, Adult_Item
},
{
AgeRestrictionGroupings.Mature, Mature_Item
},
{
AgeRestrictionGroupings.Teen, Teen_Item
},
{
AgeRestrictionGroupings.Child, Child_Item
}
};
}
}
public enum AgeRestrictionGroupings
{
Adult = 4,
Mature = 3,
Teen = 2,
Child = 1,
Unclassified = 0
}
public static List<string> RatingsBoards
{
get
{
List<string> boards = new List<string>{
"ACB", "CERO", "CLASS_IND", "ESRB", "GRAC", "PEGI", "USK"
};
return boards;
}
}
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_Z },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Eighteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.RP, AgeRatingTitle.AO },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Eighteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Eighteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_18}
};
readonly static AgeGroupItem Mature_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Sixteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.M },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Fifteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Sixteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_16}
};
readonly static AgeGroupItem Teen_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_PG },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_B },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.T },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Twelve },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Twelve},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_12}
};
readonly static AgeGroupItem Child_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_G },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_A },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.E, AgeRatingTitle.E10 },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_All },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Three, AgeRatingTitle.Seven},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_0, AgeRatingTitle.USK_6}
};
public class AgeGroupItem
{
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
[JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public List<long> AgeGroupItemValues
{
get
{
List<long> values = new List<long>();
{
foreach (AgeRatingTitle ageRatingTitle in ACB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CERO)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in ESRB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in GRAC)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in PEGI)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in USK)
{
values.Add((long)ageRatingTitle);
}
}
return values;
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.CodeAnalysis.Classification;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -14,12 +15,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static AgeRating? GetAgeRatings(long? Id) public static AgeRating? GetAgeRatings(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -116,7 +111,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<AgeRating> GetObjectFromServer(string WhereClause) private static async Task<AgeRating> GetObjectFromServer(string WhereClause)
{ {
// get AgeRatings metadata // get AgeRatings metadata
var results = await igdb.QueryAsync<AgeRating>(IGDBClient.Endpoints.AgeRating, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<AgeRating>(IGDBClient.Endpoints.AgeRating, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;
@@ -153,200 +149,42 @@ namespace gaseous_server.Classes.Metadata
public string[] Descriptions { get; set; } public string[] Descriptions { get; set; }
} }
public class AgeGroups public static void PopulateAgeMap()
{ {
public AgeGroups() Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
{ string sql = "DELETE FROM ClassificationMap;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
db.ExecuteNonQuery(sql);
} // loop all age groups
foreach(KeyValuePair<AgeGroups.AgeRestrictionGroupings, AgeGroups.AgeGroupItem> ageGrouping in AgeGroups.AgeGroupingsFlat)
public static Dictionary<string, List<AgeGroupItem>> AgeGroupings
{ {
get AgeGroups.AgeGroupItem ageGroupItem = ageGrouping.Value;
var properties = ageGroupItem.GetType().GetProperties();
foreach (var prop in properties)
{ {
return new Dictionary<string, List<AgeGroupItem>>{ if (prop.GetGetMethod() != null)
{
"Adult", new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
},
{
"Mature", new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
},
{
"Teen", new List<AgeGroupItem>{ Teen_Item, Child_Item }
},
{
"Child", new List<AgeGroupItem>{ Child_Item }
}
};
}
}
public static Dictionary<string, AgeGroupItem> AgeGroupingsFlat
{
get
{
return new Dictionary<string, AgeGroupItem>{
{
"Adult", Adult_Item
},
{
"Mature", Mature_Item
},
{
"Teen", Teen_Item
},
{
"Child", Child_Item
}
};
}
}
public static List<ClassificationBoardItem> ClassificationBoards
{
get
{
ClassificationBoardItem boardItem = new ClassificationBoardItem{
Board = AgeRatingCategory.ACB,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.ACB_G, AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15, AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC
}
};
return new List<ClassificationBoardItem>{
new ClassificationBoardItem{
Board = AgeRatingCategory.ACB,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.ACB_G,
AgeRatingTitle.ACB_M,
AgeRatingTitle.ACB_MA15,
AgeRatingTitle.ACB_R18,
AgeRatingTitle.ACB_RC
}
},
new ClassificationBoardItem{
Board = AgeRatingCategory.CERO,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.CERO_A,
AgeRatingTitle.CERO_B,
AgeRatingTitle.CERO_C,
AgeRatingTitle.CERO_D,
AgeRatingTitle.CERO_Z
}
},
new ClassificationBoardItem{
Board = AgeRatingCategory.CLASS_IND,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.CLASS_IND_L,
AgeRatingTitle.CLASS_IND_Ten,
AgeRatingTitle.CLASS_IND_Twelve,
AgeRatingTitle.CLASS_IND_Fourteen,
AgeRatingTitle.CLASS_IND_Sixteen,
AgeRatingTitle.CLASS_IND_Eighteen
}
}
};
}
}
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_Z },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Eighteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.RP, AgeRatingTitle.AO },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Eighteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Eighteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_18}
};
readonly static AgeGroupItem Mature_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Sixteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.M },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Fifteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Sixteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_16}
};
readonly static AgeGroupItem Teen_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_PG },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_B },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.T },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Twelve },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Twelve},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_12}
};
readonly static AgeGroupItem Child_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_G },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_A },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.E, AgeRatingTitle.E10 },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_All },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Three, AgeRatingTitle.Seven},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_0, AgeRatingTitle.USK_6}
};
public class AgeGroupItem
{
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
[JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public List<long> AgeGroupItemValues
{
get
{ {
List<long> values = new List<long>(); List<string> AgeRatingCategories = new List<string>(Enum.GetNames(typeof(AgeRatingCategory)));
if (AgeRatingCategories.Contains(prop.Name))
{ {
foreach (AgeRatingTitle ageRatingTitle in ACB) AgeRatingCategory ageRatingCategory = (AgeRatingCategory)Enum.Parse(typeof(AgeRatingCategory), prop.Name);
List<AgeRatingTitle> ageRatingTitles = (List<AgeRatingTitle>)prop.GetValue(ageGroupItem);
foreach (AgeRatingTitle ageRatingTitle in ageRatingTitles)
{ {
values.Add((long)ageRatingTitle); dbDict.Clear();
} dbDict.Add("AgeGroupId", ageGrouping.Key);
foreach (AgeRatingTitle ageRatingTitle in CERO) dbDict.Add("ClassificationBoardId", ageRatingCategory);
{ dbDict.Add("RatingId", ageRatingTitle);
values.Add((long)ageRatingTitle);
} sql = "INSERT INTO ClassificationMap (AgeGroupId, ClassificationBoardId, RatingId) VALUES (@AgeGroupId, @ClassificationBoardId, @RatingId);";
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND) db.ExecuteCMD(sql, dbDict);
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in ESRB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in GRAC)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in PEGI)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in USK)
{
values.Add((long)ageRatingTitle);
} }
} }
return values;
} }
} }
} }
public class ClassificationBoardItem
{
public IGDB.Models.AgeRatingCategory Board { get; set; }
public List<IGDB.Models.AgeRatingTitle> Classifications { get; set; }
}
} }
} }
} }

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static AgeRatingContentDescription? GetAgeRatingContentDescriptions(long? Id) public static AgeRatingContentDescription? GetAgeRatingContentDescriptions(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<AgeRatingContentDescription> GetObjectFromServer(string WhereClause) private static async Task<AgeRatingContentDescription> GetObjectFromServer(string WhereClause)
{ {
// get AgeRatingContentDescriptionContentDescriptions metadata // get AgeRatingContentDescriptionContentDescriptions metadata
var results = await igdb.QueryAsync<AgeRatingContentDescription>(IGDBClient.Endpoints.AgeRatingContentDescriptions, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<AgeRatingContentDescription>(IGDBClient.Endpoints.AgeRatingContentDescriptions, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static AlternativeName? GetAlternativeNames(long? Id) public static AlternativeName? GetAlternativeNames(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<AlternativeName> GetObjectFromServer(string WhereClause) private static async Task<AlternativeName> GetObjectFromServer(string WhereClause)
{ {
// get AlternativeNames metadata // get AlternativeNames metadata
var results = await igdb.QueryAsync<AlternativeName>(IGDBClient.Endpoints.AlternativeNames, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<AlternativeName>(IGDBClient.Endpoints.AlternativeNames, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Artwork? GetArtwork(long? Id, string LogoPath) public static Artwork? GetArtwork(long? Id, string LogoPath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -113,7 +107,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<Artwork> GetObjectFromServer(string WhereClause, string LogoPath)
{ {
// get Artwork metadata // get Artwork metadata
var results = await igdb.QueryAsync<Artwork>(IGDBClient.Endpoints.Artworks, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Artwork>(IGDBClient.Endpoints.Artworks, fieldList, WhereClause);
var result = results.First(); var result = results.First();
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId); //GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Collection? GetCollections(long? Id) public static Collection? GetCollections(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Collection> GetObjectFromServer(string WhereClause) private static async Task<Collection> GetObjectFromServer(string WhereClause)
{ {
// get Collections metadata // get Collections metadata
var results = await igdb.QueryAsync<Collection>(IGDBClient.Endpoints.Collections, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Collection>(IGDBClient.Endpoints.Collections, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -0,0 +1,238 @@
using System.Net;
using IGDB;
using RestEase;
namespace gaseous_server.Classes.Metadata
{
/// <summary>
/// Handles all metadata API communications
/// </summary>
public class Communications
{
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
/// <summary>
/// Configure metadata API communications
/// </summary>
public static MetadataSources MetadataSource
{
get
{
return _MetadataSource;
}
set
{
_MetadataSource = value;
switch (value)
{
case MetadataSources.IGDB:
// set rate limiter avoidance values
RateLimitAvoidanceWait = 1500;
RateLimitAvoidanceThreshold = 3;
RateLimitAvoidancePeriod = 1;
// set rate limiter recovery values
RateLimitRecoveryWaitTime = 10000;
break;
default:
// leave all values at default
break;
}
}
}
private static MetadataSources _MetadataSource = MetadataSources.None;
// rate limit avoidance - what can we do to ensure that rate limiting is avoided?
// these values affect all communications
/// <summary>
/// How long to wait to avoid hitting an API rate limiter
/// </summary>
private static int RateLimitAvoidanceWait = 2000;
/// <summary>
/// How many API calls in the period are allowed before we start introducing a wait
/// </summary>
private static int RateLimitAvoidanceThreshold = 80;
/// <summary>
/// A counter of API calls since the beginning of the period
/// </summary>
private static int RateLimitAvoidanceCallCount = 0;
/// <summary>
/// How large the period (in seconds) to measure API call counts against
/// </summary>
private static int RateLimitAvoidancePeriod = 60;
/// <summary>
/// The start of the rate limit avoidance period
/// </summary>
private static DateTime RateLimitAvoidanceStartTime = DateTime.UtcNow;
/// <summary>
/// Used to determine if we're already in rate limit avoidance mode - always query "InRateLimitAvoidanceMode"
/// for up to date mode status.
/// This bool is used to track status changes and should not be relied upon for current status.
/// </summary>
private static bool InRateLimitAvoidanceModeStatus = false;
/// <summary>
/// Determine if we're in rate limit avoidance mode.
/// </summary>
private static bool InRateLimitAvoidanceMode
{
get
{
if (RateLimitAvoidanceStartTime.AddSeconds(RateLimitAvoidancePeriod) <= DateTime.UtcNow)
{
// avoidance period has expired - reset
RateLimitAvoidanceCallCount = 0;
RateLimitAvoidanceStartTime = DateTime.UtcNow;
return false;
}
else
{
// we're in the avoidance period
if (RateLimitAvoidanceCallCount > RateLimitAvoidanceThreshold)
{
// the number of call counts indicates we should throttle things a bit
if (InRateLimitAvoidanceModeStatus == false)
{
Logging.Log(Logging.LogType.Information, "API Connection", "Entered rate limit avoidance period, API calls will be throttled by " + RateLimitAvoidanceWait + " milliseconds.");
InRateLimitAvoidanceModeStatus = true;
}
return true;
}
else
{
// still in full speed mode - no throttle required
if (InRateLimitAvoidanceModeStatus == true)
{
Logging.Log(Logging.LogType.Information, "API Connection", "Exited rate limit avoidance period, API call rate is returned to full speed.");
InRateLimitAvoidanceModeStatus = false;
}
return false;
}
}
}
}
// rate limit handling - how long to wait to allow the server to recover and try again
// these values affect ALL communications if a 429 response code is received
/// <summary>
/// How long to wait (in milliseconds) if a 429 status code is received before trying again
/// </summary>
private static int RateLimitRecoveryWaitTime = 10000;
/// <summary>
/// The time when normal communications can attempt to be resumed
/// </summary>
private static DateTime RateLimitResumeTime = DateTime.UtcNow.AddMinutes(5 * -1);
// rate limit retry - how many times to retry before aborting
private int RetryAttempts = 0;
private int RetryAttemptsMax = 3;
/// <summary>
/// Supported metadata sources
/// </summary>
public enum MetadataSources
{
/// <summary>
/// None - always returns null for metadata requests - should not really be using this source
/// </summary>
None,
/// <summary>
/// IGDB - queries the IGDB service for metadata
/// </summary>
IGDB
}
/// <summary>
/// Request data from the metadata API
/// </summary>
/// <typeparam name="T">Type of object to return</typeparam>
/// <param name="Endpoint">API endpoint segment to use</param>
/// <param name="Fields">Fields to request from the API</param>
/// <param name="Query">Selection criteria for data to request</param>
/// <returns></returns>
public async Task<T[]?> APIComm<T>(string Endpoint, string Fields, string Query)
{
switch (_MetadataSource)
{
case MetadataSources.None:
return null;
case MetadataSources.IGDB:
return await IGDBAPI<T>(Endpoint, Fields, Query);
default:
return null;
}
}
private async Task<T[]> IGDBAPI<T>(string Endpoint, string Fields, string Query)
{
Logging.Log(Logging.LogType.Debug, "API Connection", "Accessing API for endpoint: " + Endpoint);
if (RateLimitResumeTime > DateTime.UtcNow)
{
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB rate limit hit. Pausing API communications until " + RateLimitResumeTime.ToString() + ". Attempt " + RetryAttempts + " of " + RetryAttemptsMax + " retries.");
Thread.Sleep(RateLimitRecoveryWaitTime);
}
try
{
if (InRateLimitAvoidanceMode == true)
{
// sleep for a moment to help avoid hitting the rate limiter
Thread.Sleep(RateLimitAvoidanceWait);
}
// perform the actual API call
var results = await igdb.QueryAsync<T>(Endpoint, query: Fields + " " + Query + ";");
// increment rate limiter avoidance call count
RateLimitAvoidanceCallCount += 1;
return results;
}
catch (ApiException apiEx)
{
switch (apiEx.StatusCode)
{
case HttpStatusCode.TooManyRequests:
if (RetryAttempts >= RetryAttemptsMax)
{
Logging.Log(Logging.LogType.Warning, "API Connection", "IGDB rate limiter attempts expired. Aborting.", apiEx);
throw;
}
else
{
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB API rate limit hit while accessing endpoint " + Endpoint, apiEx);
RetryAttempts += 1;
return await IGDBAPI<T>(Endpoint, Fields, Query);
}
default:
Logging.Log(Logging.LogType.Warning, "API Connection", "Exception when accessing endpoint " + Endpoint, apiEx);
throw;
}
}
catch(Exception ex)
{
Logging.Log(Logging.LogType.Warning, "API Connection", "Exception when accessing endpoint " + Endpoint, ex);
throw;
}
}
}
}

View File

@@ -12,12 +12,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Company? GetCompanies(long? Id) public static Company? GetCompanies(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -111,7 +105,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Company> GetObjectFromServer(string WhereClause) private static async Task<Company> GetObjectFromServer(string WhereClause)
{ {
// get Companies metadata // get Companies metadata
var results = await igdb.QueryAsync<Company>(IGDBClient.Endpoints.Companies, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Company>(IGDBClient.Endpoints.Companies, fieldList, WhereClause);
if (results.Length > 0) if (results.Length > 0)
{ {
var result = results.First(); var result = results.First();

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath) public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -118,7 +112,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<CompanyLogo?> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<CompanyLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
{ {
// get CompanyLogo metadata // get CompanyLogo metadata
var results = await igdb.QueryAsync<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause);
if (results.Length > 0) if (results.Length > 0)
{ {
var result = results.First(); var result = results.First();

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Cover? GetCover(long? Id, string LogoPath) public static Cover? GetCover(long? Id, string LogoPath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -113,7 +107,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Cover> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<Cover> GetObjectFromServer(string WhereClause, string LogoPath)
{ {
// get Cover metadata // get Cover metadata
var results = await igdb.QueryAsync<Cover>(IGDBClient.Endpoints.Covers, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Cover>(IGDBClient.Endpoints.Covers, fieldList, WhereClause);
var result = results.First(); var result = results.First();
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId); //GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static ExternalGame? GetExternalGames(long? Id) public static ExternalGame? GetExternalGames(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -106,7 +100,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<ExternalGame?> GetObjectFromServer(string WhereClause) private static async Task<ExternalGame?> GetObjectFromServer(string WhereClause)
{ {
// get ExternalGames metadata // get ExternalGames metadata
var results = await igdb.QueryAsync<ExternalGame>(IGDBClient.Endpoints.ExternalGames, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<ExternalGame>(IGDBClient.Endpoints.ExternalGames, fieldList, WhereClause);
if (results.Length > 0) if (results.Length > 0)
{ {
var result = results.First(); var result = results.First();

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Franchise? GetFranchises(long? Id) public static Franchise? GetFranchises(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Franchise> GetObjectFromServer(string WhereClause) private static async Task<Franchise> GetObjectFromServer(string WhereClause)
{ {
// get FranchiseContentDescriptions metadata // get FranchiseContentDescriptions metadata
var results = await igdb.QueryAsync<Franchise>(IGDBClient.Endpoints.Franchies, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Franchise>(IGDBClient.Endpoints.Franchies, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static GameMode? GetGame_Modes(long? Id) public static GameMode? GetGame_Modes(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<GameMode> GetObjectFromServer(string WhereClause) private static async Task<GameMode> GetObjectFromServer(string WhereClause)
{ {
// get Game_Modes metadata // get Game_Modes metadata
var results = await igdb.QueryAsync<GameMode>(IGDBClient.Endpoints.GameModes, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<GameMode>(IGDBClient.Endpoints.GameModes, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static GameVideo? GetGame_Videos(long? Id) public static GameVideo? GetGame_Videos(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<GameVideo> GetObjectFromServer(string WhereClause) private static async Task<GameVideo> GetObjectFromServer(string WhereClause)
{ {
// get Game_Videos metadata // get Game_Videos metadata
var results = await igdb.QueryAsync<GameVideo>(IGDBClient.Endpoints.GameVideos, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<GameVideo>(IGDBClient.Endpoints.GameVideos, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -20,12 +20,6 @@ namespace gaseous_server.Classes.Metadata
{} {}
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh) public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh)
{ {
if (Id == 0) if (Id == 0)
@@ -120,7 +114,9 @@ namespace gaseous_server.Classes.Metadata
} }
return returnValue; return returnValue;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
UpdateSubClasses(returnValue, false, false);
return returnValue;
default: default:
throw new Exception("How did you get here?"); throw new Exception("How did you get here?");
} }
@@ -131,7 +127,14 @@ namespace gaseous_server.Classes.Metadata
// required metadata // required metadata
if (Game.Cover != null) if (Game.Cover != null)
{ {
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game)); try
{
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
}
} }
if (Game.Genres != null) if (Game.Genres != null)
@@ -174,17 +177,26 @@ namespace gaseous_server.Classes.Metadata
} }
} }
if (Game.AgeRatings != null)
{
foreach (long AgeRatingId in Game.AgeRatings.Ids)
{
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
AgeGroups.GetAgeGroup(Game);
if (Game.ReleaseDates != null)
{
foreach (long ReleaseDateId in Game.ReleaseDates.Ids)
{
ReleaseDate GameReleaseDate = ReleaseDates.GetReleaseDates(ReleaseDateId);
}
}
// optional metadata - usually downloaded as needed // optional metadata - usually downloaded as needed
if (getAllMetadata == true) if (getAllMetadata == true)
{ {
if (Game.AgeRatings != null)
{
foreach (long AgeRatingId in Game.AgeRatings.Ids)
{
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
if (Game.AlternativeNames != null) if (Game.AlternativeNames != null)
{ {
foreach (long AlternativeNameId in Game.AlternativeNames.Ids) foreach (long AlternativeNameId in Game.AlternativeNames.Ids)
@@ -197,7 +209,14 @@ namespace gaseous_server.Classes.Metadata
{ {
foreach (long ArtworkId in Game.Artworks.Ids) foreach (long ArtworkId in Game.Artworks.Ids)
{ {
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game)); try
{
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch artwork id: " + ArtworkId, ex);
}
} }
} }
@@ -264,7 +283,14 @@ namespace gaseous_server.Classes.Metadata
{ {
foreach (long ScreenshotId in Game.Screenshots.Ids) foreach (long ScreenshotId in Game.Screenshots.Ids)
{ {
try
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game)); Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch screenshot id: " + ScreenshotId, ex);
}
} }
} }
@@ -287,42 +313,178 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Game> GetObjectFromServer(string WhereClause) private static async Task<Game> GetObjectFromServer(string WhereClause)
{ {
// get Game metadata // get Game metadata
var results = await igdb.QueryAsync<Game>(IGDBClient.Endpoints.Games, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, fieldList, WhereClause);
var result = results.First(); var result = results.First();
// add artificial unknown platform mapping
List<long> platformIds = new List<long>();
platformIds.Add(0);
if (result.Platforms != null)
{
if (result.Platforms.Ids != null)
{
platformIds.AddRange(result.Platforms.Ids.ToList());
}
}
result.Platforms = new IdentitiesOrValues<Platform>(
ids: platformIds.ToArray<long>()
);
// 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; return result;
} }
public static void AssignAllGamesToPlatformIdZero()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Game;";
DataTable gamesTable = db.ExecuteCMD(sql);
foreach (DataRow gameRow in gamesTable.Rows)
{
sql = "DELETE FROM Relation_Game_Platforms WHERE PlatformsId = 0 AND GameId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (@Id, 0);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", (long)gameRow["Id"]);
db.ExecuteCMD(sql, dbDict);
}
}
private static bool AllowNoPlatformSearch = false;
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType) public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType)
{ {
Task<Game[]> 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<Game[]> 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; return games.Result;
} }
private static async Task<Game[]> _SearchForGame(string SearchString, long PlatformId, SearchType searchType) private static async Task<Game[]> _SearchForGameDatabase(string SearchString, long PlatformId, SearchType searchType)
{ {
string searchBody = ""; string whereClause = "";
searchBody += "fields id,name,slug,platforms,summary; "; Dictionary<string, object> dbDict = new Dictionary<string, object>();
bool allowSearch = true;
switch (searchType) switch (searchType)
{ {
case SearchType.searchNoPlatform: case SearchType.searchNoPlatform:
searchBody += "search \"" + SearchString + "\"; "; whereClause = "MATCH(`Name`) AGAINST (@gamename)";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
allowSearch = AllowNoPlatformSearch;
break; break;
case SearchType.search: case SearchType.search:
searchBody += "search \"" + SearchString + "\"; "; whereClause = "PlatformsId = @platformid AND MATCH(`Name`) AGAINST (@gamename)";
searchBody += "where platforms = (" + PlatformId + ");"; dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
break; break;
case SearchType.wherefuzzy: 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; break;
case SearchType.where: 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<Game> searchResults = new List<Game>();
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<Game[]> _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; break;
} }
// get Game metadata // get Game metadata
var results = await igdb.QueryAsync<Game>(IGDBClient.Endpoints.Games, query: searchBody); Communications comms = new Communications();
Game[]? results = new Game[0];
if (allowSearch == true)
{
results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
}
return results; return results;
} }
@@ -334,5 +496,42 @@ namespace gaseous_server.Classes.Metadata
search = 2, search = 2,
searchNoPlatform = 3 searchNoPlatform = 3
} }
public class MinimalGameItem
{
public MinimalGameItem(Game gameObject)
{
this.Id = gameObject.Id;
this.Name = gameObject.Name;
this.TotalRating = gameObject.TotalRating;
this.TotalRatingCount = gameObject.TotalRatingCount;
this.Cover = gameObject.Cover;
this.Artworks = gameObject.Artworks;
this.FirstReleaseDate = gameObject.FirstReleaseDate;
// compile age ratings
this.AgeRatings = new List<AgeRating>();
if (gameObject.AgeRatings != null)
{
foreach (long ageRatingId in gameObject.AgeRatings.Ids)
{
AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRatings(ageRatingId);
if (rating != null)
{
this.AgeRatings.Add(rating);
}
}
}
}
public long? Id { get; set; }
public string Name { get; set; }
public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; }
public DateTimeOffset? FirstReleaseDate { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
public List<IGDB.Models.AgeRating> AgeRatings { get; set; }
}
} }
} }

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Genre? GetGenres(long? Id) public static Genre? GetGenres(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Genre> GetObjectFromServer(string WhereClause) private static async Task<Genre> GetObjectFromServer(string WhereClause)
{ {
// get Genres metadata // get Genres metadata
var results = await igdb.QueryAsync<Genre>(IGDBClient.Endpoints.Genres, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Genre>(IGDBClient.Endpoints.Genres, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -12,12 +12,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static InvolvedCompany? GetInvolvedCompanies(long? Id) public static InvolvedCompany? GetInvolvedCompanies(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -113,7 +107,8 @@ namespace gaseous_server.Classes.Metadata
// get InvolvedCompanies metadata // get InvolvedCompanies metadata
try try
{ {
var results = await igdb.QueryAsync<InvolvedCompany>(IGDBClient.Endpoints.InvolvedCompanies, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<InvolvedCompany>(IGDBClient.Endpoints.InvolvedCompanies, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static MultiplayerMode? GetGame_MultiplayerModes(long? Id) public static MultiplayerMode? GetGame_MultiplayerModes(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<MultiplayerMode> GetObjectFromServer(string WhereClause) private static async Task<MultiplayerMode> GetObjectFromServer(string WhereClause)
{ {
// get Game_MultiplayerModes metadata // get Game_MultiplayerModes metadata
var results = await igdb.QueryAsync<MultiplayerMode>(IGDBClient.Endpoints.MultiplayerModes, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<MultiplayerMode>(IGDBClient.Endpoints.MultiplayerModes, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath) public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -118,7 +112,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<PlatformLogo?> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<PlatformLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
{ {
// get PlatformLogo metadata // get PlatformLogo metadata
var results = await igdb.QueryAsync<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause);
if (results.Length > 0) if (results.Length > 0)
{ {
var result = results.First(); var result = results.First();

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform) public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform)
{ {
if (Id == 0) if (Id == 0)
@@ -113,7 +107,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<PlatformVersion?> GetObjectFromServer(string WhereClause) private static async Task<PlatformVersion?> GetObjectFromServer(string WhereClause)
{ {
// get PlatformVersion metadata // get PlatformVersion metadata
var results = await igdb.QueryAsync<PlatformVersion>(IGDBClient.Endpoints.PlatformVersions, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<PlatformVersion>(IGDBClient.Endpoints.PlatformVersions, fieldList, WhereClause);
if (results.Length > 0) if (results.Length > 0)
{ {
var result = results.First(); var result = results.First();

View File

@@ -15,12 +15,6 @@ namespace gaseous_server.Classes.Metadata
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Platform? GetPlatform(long Id, bool forceRefresh = false) public static Platform? GetPlatform(long Id, bool forceRefresh = false)
{ {
if (Id == 0) if (Id == 0)
@@ -168,11 +162,26 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Platform> GetObjectFromServer(string WhereClause) private static async Task<Platform> GetObjectFromServer(string WhereClause)
{ {
// get platform metadata // get platform metadata
var results = await igdb.QueryAsync<Platform>(IGDBClient.Endpoints.Platforms, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;
} }
public static void AssignAllPlatformsToGameIdZero()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Platform;";
DataTable platformsTable = db.ExecuteCMD(sql);
foreach (DataRow platformRow in platformsTable.Rows)
{
sql = "DELETE FROM Relation_Game_Platforms WHERE GameId = 0 AND PlatformsId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (0, @Id);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", (long)platformRow["Id"]);
db.ExecuteCMD(sql, dbDict);
}
}
} }
} }

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static PlayerPerspective? GetGame_PlayerPerspectives(long? Id) public static PlayerPerspective? GetGame_PlayerPerspectives(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -105,7 +99,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<PlayerPerspective> GetObjectFromServer(string WhereClause) private static async Task<PlayerPerspective> GetObjectFromServer(string WhereClause)
{ {
// get Game_PlayerPerspectives metadata // get Game_PlayerPerspectives metadata
var results = await igdb.QueryAsync<PlayerPerspective>(IGDBClient.Endpoints.PlayerPerspectives, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<PlayerPerspective>(IGDBClient.Endpoints.PlayerPerspectives, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -0,0 +1,108 @@
using System;
using IGDB;
using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class ReleaseDates
{
const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
public ReleaseDates()
{
}
public static ReleaseDate? GetReleaseDates(long? Id)
{
if ((Id == 0) || (Id == null))
{
return null;
}
else
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.id, Id);
return RetVal.Result;
}
}
public static ReleaseDate GetReleaseDates(string Slug)
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<ReleaseDate> _GetReleaseDates(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
ReleaseDate returnValue = new ReleaseDate();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<ReleaseDate> GetObjectFromServer(string WhereClause)
{
// get ReleaseDates metadata
Communications comms = new Communications();
var results = await comms.APIComm<ReleaseDate>(IGDBClient.Endpoints.ReleaseDates, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Screenshot? GetScreenshot(long? Id, string LogoPath) public static Screenshot? GetScreenshot(long? Id, string LogoPath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -114,7 +108,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string LogoPath)
{ {
// get Screenshot metadata // get Screenshot metadata
var results = await igdb.QueryAsync<Screenshot>(IGDBClient.Endpoints.Screenshots, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause);
var result = results.First(); var result = results.First();
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId); //GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);

View File

@@ -16,32 +16,14 @@ namespace gaseous_server.Classes.Metadata
Expired Expired
} }
private static Dictionary<string, MemoryCacheObject> ObjectCache = new Dictionary<string, MemoryCacheObject>();
public static CacheStatus GetCacheStatus(string Endpoint, string Slug) public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
{ {
CacheClean(); return _GetCacheStatus(Endpoint, "slug", Slug);
if (ObjectCache.ContainsKey(Endpoint + Slug))
{
return CacheStatus.Current;
}
else
{
return _GetCacheStatus(Endpoint, "slug", Slug);
}
} }
public static CacheStatus GetCacheStatus(string Endpoint, long Id) public static CacheStatus GetCacheStatus(string Endpoint, long Id)
{ {
CacheClean(); return _GetCacheStatus(Endpoint, "id", Id);
if (ObjectCache.ContainsKey(Endpoint + Id))
{
return CacheStatus.Current;
}
else
{
return _GetCacheStatus(Endpoint, "id", Id);
}
} }
public static CacheStatus GetCacheStatus(DataRow Row) public static CacheStatus GetCacheStatus(DataRow Row)
@@ -185,21 +167,6 @@ namespace gaseous_server.Classes.Metadata
{ {
string Endpoint = EndpointType.GetType().Name; string Endpoint = EndpointType.GetType().Name;
if (ObjectCache.ContainsKey(Endpoint + SearchValue))
{
MemoryCacheObject cacheObject = ObjectCache[Endpoint + SearchValue];
if (cacheObject.ExpiryTime < DateTime.UtcNow)
{
// object has expired, remove it
ObjectCache.Remove(Endpoint + SearchValue);
}
else
{
// object is valid, return it
return (T)cacheObject.Object;
}
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField; string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
@@ -218,9 +185,7 @@ namespace gaseous_server.Classes.Metadata
{ {
DataRow dataRow = dt.Rows[0]; DataRow dataRow = dt.Rows[0];
object returnObject = BuildCacheObject<T>(EndpointType, dataRow); object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{
Object = returnObject
});
return (T)returnObject; return (T)returnObject;
} }
} }
@@ -404,6 +369,15 @@ namespace gaseous_server.Classes.Metadata
case "[igdb.models.startdatecategory": case "[igdb.models.startdatecategory":
property.SetValue(EndpointType, (StartDateCategory)dataRow[property.Name]); property.SetValue(EndpointType, (StartDateCategory)dataRow[property.Name]);
break; break;
case "[igdb.models.releasedateregion":
property.SetValue(EndpointType, (ReleaseDateRegion)dataRow[property.Name]);
break;
case "[igdb.models.releasedatecategory":
property.SetValue(EndpointType, (ReleaseDateCategory)dataRow[property.Name]);
break;
case "[gaseous_server.classes.metadata.agegroups+agerestrictiongroupings":
property.SetValue(EndpointType, (AgeGroups.AgeRestrictionGroupings)dataRow[property.Name]);
break;
default: default:
property.SetValue(EndpointType, dataRow[property.Name]); property.SetValue(EndpointType, dataRow[property.Name]);
break; break;
@@ -454,29 +428,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
private static void CacheClean()
{
try
{
if (ObjectCache == null)
{
ObjectCache = new Dictionary<string, MemoryCacheObject>();
}
Dictionary<string, MemoryCacheObject> workCache = ObjectCache;
foreach (KeyValuePair<string, MemoryCacheObject> objectCache in workCache)
{
if (objectCache.Value.ExpiryTime < DateTime.UtcNow)
{
ObjectCache.Remove(objectCache.Key);
}
}
}
catch
{
ObjectCache = new Dictionary<string, MemoryCacheObject>();
}
}
private class MemoryCacheObject private class MemoryCacheObject
{ {
public object Object { get; set; } public object Object { get; set; }

View File

@@ -13,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Theme? GetGame_Themes(long? Id) public static Theme? GetGame_Themes(long? Id)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
@@ -105,7 +99,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Theme> GetObjectFromServer(string WhereClause) private static async Task<Theme> GetObjectFromServer(string WhereClause)
{ {
// get Game_Themes metadata // get Game_Themes metadata
var results = await igdb.QueryAsync<Theme>(IGDBClient.Endpoints.Themes, query: fieldList + " " + WhereClause + ";"); Communications comms = new Communications();
var results = await comms.APIComm<Theme>(IGDBClient.Endpoints.Themes, fieldList, WhereClause);
var result = results.First(); var result = results.First();
return result; return result;

View File

@@ -4,47 +4,71 @@ using gaseous_server.Models;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class MetadataManagement public class MetadataManagement : QueueItemStatus
{ {
public static void RefreshMetadata(bool forceRefresh = false) public void RefreshMetadata(bool forceRefresh = false)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
DataTable dt = new DataTable(); DataTable dt = new DataTable();
// disabling forceRefresh
forceRefresh = false;
// update platforms // update platforms
sql = "SELECT Id, `Name` FROM Platform;"; sql = "SELECT Id, `Name` FROM Platform;";
dt = db.ExecuteCMD(sql); dt = db.ExecuteCMD(sql);
int StatusCounter = 1;
foreach (DataRow dr in dt.Rows) foreach (DataRow dr in dt.Rows)
{ {
SetStatus(StatusCounter, dt.Rows.Count, "Refreshing metadata for platform " + dr["name"]);
try try
{ {
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")"); Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Platforms.GetPlatform((long)dr["id"], true); Metadata.Platforms.GetPlatform((long)dr["id"], true);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex); Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex);
} }
StatusCounter += 1;
} }
ClearStatus();
// update games // update games
sql = "SELECT Id, `Name` FROM Game;"; if (forceRefresh == true)
{
// when forced, only update games with ROMs for
sql = "SELECT Id, `Name` FROM view_GamesWithRoms;";
}
else
{
// when run normally, update all games (since this will honour cache timeouts)
sql = "SELECT Id, `Name` FROM Game;";
}
dt = db.ExecuteCMD(sql); dt = db.ExecuteCMD(sql);
StatusCounter = 1;
foreach (DataRow dr in dt.Rows) foreach (DataRow dr in dt.Rows)
{ {
SetStatus(StatusCounter, dt.Rows.Count, "Refreshing metadata for game " + dr["name"]);
try try
{ {
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")"); Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Games.GetGame((long)dr["id"], true, true, forceRefresh); Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex); Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex);
} }
StatusCounter += 1;
} }
ClearStatus();
} }
} }
} }

View File

@@ -0,0 +1,41 @@
namespace gaseous_server.Classes
{
public class QueueItemStatus
{
internal ProcessQueue.QueueItem? CallingQueueItem = null;
private int _CurrentItemNumber = 0;
private int _MaxItemsNumber = 0;
private string _StatusText = "";
public int CurrentItemNumber => _CurrentItemNumber;
public int MaxItemsNumber => _MaxItemsNumber;
public string StatusText => _StatusText;
public void SetStatus(int CurrentItemNumber, int MaxItemsNumber, string StatusText)
{
this._CurrentItemNumber = CurrentItemNumber;
this._MaxItemsNumber = MaxItemsNumber;
this._StatusText = StatusText;
if (CallingQueueItem != null)
{
CallingQueueItem.CurrentState = _CurrentItemNumber + " of " + _MaxItemsNumber + ": " + _StatusText;
CallingQueueItem.CurrentStateProgress = _CurrentItemNumber + " of " + _MaxItemsNumber;
}
}
public void ClearStatus()
{
this._CurrentItemNumber = 0;
this._MaxItemsNumber = 0;
this._StatusText = "";
if (CallingQueueItem != null)
{
CallingQueueItem.CurrentState = "";
CallingQueueItem.CurrentStateProgress = "";
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Data;
using gaseous_signature_parser.models.RomSignatureObject; using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup; using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using IGDB.Models;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
@@ -14,7 +15,7 @@ namespace gaseous_server.Classes
{} {}
} }
public static GameRomObject GetRoms(long GameId, long PlatformId = -1) public static GameRomObject GetRoms(long GameId, long PlatformId = -1, int pageNumber = 0, int pageSize = 0)
{ {
GameRomObject GameRoms = new GameRomObject(); GameRomObject GameRoms = new GameRomObject();
@@ -24,23 +25,43 @@ namespace gaseous_server.Classes
dbDict.Add("id", GameId); dbDict.Add("id", GameId);
if (PlatformId == -1) { if (PlatformId == -1) {
sql = "SELECT * FROM Games_Roms WHERE GameId = @id ORDER BY `Name`"; sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id ORDER BY Platform.`Name`, Games_Roms.`Name`";
} else { } else {
sql = "SELECT * FROM Games_Roms WHERE GameId = @id AND PlatformId = @platformid ORDER BY `Name`"; sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid ORDER BY Platform.`Name`, Games_Roms.`Name`";
dbDict.Add("platformid", PlatformId); dbDict.Add("platformid", PlatformId);
} }
DataTable romDT = db.ExecuteCMD(sql, dbDict); DataTable romDT = db.ExecuteCMD(sql, dbDict);
if (romDT.Rows.Count > 0) if (romDT.Rows.Count > 0)
{ {
foreach (DataRow romDR in romDT.Rows) // set count of roms
GameRoms.Count = romDT.Rows.Count;
// setup platforms list
Dictionary<long, string> platformDict = new Dictionary<long, string>();
int pageOffset = pageSize * (pageNumber - 1);
for (int i = 0; i < romDT.Rows.Count; i++)
{ {
GameRoms.GameRomItems.Add(BuildRom(romDR)); GameRomItem gameRomItem = BuildRom(romDT.Rows[i]);
if ((i >= pageOffset && i < pageOffset + pageSize) || pageSize == 0)
{
GameRoms.GameRomItems.Add(gameRomItem);
}
if (!platformDict.ContainsKey(gameRomItem.PlatformId))
{
platformDict.Add(gameRomItem.PlatformId, gameRomItem.Platform);
}
} }
// get rom media groups // get rom media groups
GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId); GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId);
// sort the platforms
GameRoms.Platforms = platformDict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value).ToList<KeyValuePair<long, string>>();
return GameRoms; return GameRoms;
} }
else else
@@ -52,7 +73,7 @@ namespace gaseous_server.Classes
public static GameRomItem GetRom(long RomId) public static GameRomItem GetRom(long RomId)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE Id = @id"; string sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
DataTable romDT = db.ExecuteCMD(sql, dbDict); DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -114,7 +135,7 @@ namespace gaseous_server.Classes
{ {
Id = (long)romDR["id"], Id = (long)romDR["id"],
PlatformId = (long)romDR["platformid"], PlatformId = (long)romDR["platformid"],
Platform = Classes.Metadata.Platforms.GetPlatform((long)romDR["platformid"]), Platform = (string)romDR["platformname"],
GameId = (long)romDR["gameid"], GameId = (long)romDR["gameid"],
Name = (string)romDR["name"], Name = (string)romDR["name"],
Size = (long)romDR["size"], Size = (long)romDR["size"],
@@ -151,13 +172,15 @@ namespace gaseous_server.Classes
{ {
public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>(); public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>();
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>(); public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>();
public int Count { get; set; }
public List<KeyValuePair<long, string>> Platforms { get; set; }
} }
public class GameRomItem public class GameRomItem
{ {
public long Id { get; set; } public long Id { get; set; }
public long PlatformId { get; set; } public long PlatformId { get; set; }
public IGDB.Models.Platform Platform { get; set; } public string Platform { get; set; }
//public Dictionary<string, object>? Emulator { get; set; } //public Dictionary<string, object>? Emulator { get; set; }
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; } public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public long GameId { get; set; } public long GameId { get; set; }

View File

@@ -6,7 +6,7 @@ using System.Data;
namespace gaseous_server.SignatureIngestors.XML namespace gaseous_server.SignatureIngestors.XML
{ {
public class XMLIngestor public class XMLIngestor : QueueItemStatus
{ {
public void Import(string SearchPath, gaseous_signature_parser.parser.SignatureParser XMLType) public void Import(string SearchPath, gaseous_signature_parser.parser.SignatureParser XMLType)
{ {
@@ -31,222 +31,232 @@ namespace gaseous_server.SignatureIngestors.XML
{ {
string XMLFile = PathContents[i]; string XMLFile = PathContents[i];
// check xml file md5 SetStatus(i + 1, PathContents.Length, "Processing signature file: " + XMLFile);
Common.hashObject hashObject = new Common.hashObject(XMLFile);
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
dbDict = new Dictionary<string, object>();
dbDict.Add("sourcemd5", hashObject.md5hash);
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0) if (Common.SkippableFiles.Contains(Path.GetFileName(XMLFile), StringComparer.OrdinalIgnoreCase))
{ {
try Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Skipping file: " + XMLFile);
}
else
{
// check xml file md5
Common.hashObject hashObject = new Common.hashObject(XMLFile);
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
dbDict = new Dictionary<string, object>();
dbDict.Add("sourcemd5", hashObject.md5hash);
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{ {
Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing file: " + XMLFile); try
// start parsing file
gaseous_signature_parser.parser Parser = new gaseous_signature_parser.parser();
RomSignatureObject Object = Parser.ParseSignatureDAT(XMLFile, XMLType);
// store in database
string[] flipNameAndDescription = {
"MAMEArcade",
"MAMEMess"
};
// store source object
bool processGames = false;
if (Object.SourceMd5 != null)
{ {
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5"; Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing file: " + XMLFile);
dbDict = new Dictionary<string, object>();
string sourceUriStr = ""; // start parsing file
if (Object.Url != null) gaseous_signature_parser.parser Parser = new gaseous_signature_parser.parser();
RomSignatureObject Object = Parser.ParseSignatureDAT(XMLFile, XMLType);
// store in database
string[] flipNameAndDescription = {
"MAMEArcade",
"MAMEMess"
};
// store source object
bool processGames = false;
if (Object.SourceMd5 != null)
{ {
sourceUriStr = Object.Url.ToString(); sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
} dbDict = new Dictionary<string, object>();
dbDict.Add("name", Common.ReturnValueIfNull(Object.Name, "")); string sourceUriStr = "";
dbDict.Add("description", Common.ReturnValueIfNull(Object.Description, "")); if (Object.Url != null)
dbDict.Add("category", Common.ReturnValueIfNull(Object.Category, ""));
dbDict.Add("version", Common.ReturnValueIfNull(Object.Version, ""));
dbDict.Add("author", Common.ReturnValueIfNull(Object.Author, ""));
dbDict.Add("email", Common.ReturnValueIfNull(Object.Email, ""));
dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, ""));
dbDict.Add("uri", sourceUriStr);
dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
dbDict.Add("sourcemd5", Object.SourceMd5);
dbDict.Add("sourcesha1", Object.SourceSHA1);
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{
// entry not present, insert it
sql = "INSERT INTO Signatures_Sources (Name, Description, Category, Version, Author, Email, Homepage, Url, SourceType, SourceMD5, SourceSHA1) VALUES (@name, @description, @category, @version, @author, @email, @homepage, @uri, @sourcetype, @sourcemd5, @sourcesha1)";
db.ExecuteCMD(sql, dbDict);
processGames = true;
}
if (processGames == true)
{
for (int x = 0; x < Object.Games.Count; ++x)
{ {
RomSignatureObject.Game gameObject = Object.Games[x]; sourceUriStr = Object.Url.ToString();
}
dbDict.Add("name", Common.ReturnValueIfNull(Object.Name, ""));
dbDict.Add("description", Common.ReturnValueIfNull(Object.Description, ""));
dbDict.Add("category", Common.ReturnValueIfNull(Object.Category, ""));
dbDict.Add("version", Common.ReturnValueIfNull(Object.Version, ""));
dbDict.Add("author", Common.ReturnValueIfNull(Object.Author, ""));
dbDict.Add("email", Common.ReturnValueIfNull(Object.Email, ""));
dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, ""));
dbDict.Add("uri", sourceUriStr);
dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
dbDict.Add("sourcemd5", Object.SourceMd5);
dbDict.Add("sourcesha1", Object.SourceSHA1);
// set up game dictionary sigDB = db.ExecuteCMD(sql, dbDict);
dbDict = new Dictionary<string, object>(); if (sigDB.Rows.Count == 0)
if (flipNameAndDescription.Contains(Object.SourceType)) {
{ // entry not present, insert it
dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Description, "")); sql = "INSERT INTO Signatures_Sources (Name, Description, Category, Version, Author, Email, Homepage, Url, SourceType, SourceMD5, SourceSHA1) VALUES (@name, @description, @category, @version, @author, @email, @homepage, @uri, @sourcetype, @sourcemd5, @sourcesha1)";
dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Name, ""));
}
else
{
dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Name, ""));
dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Description, ""));
}
dbDict.Add("year", Common.ReturnValueIfNull(gameObject.Year, ""));
dbDict.Add("publisher", Common.ReturnValueIfNull(gameObject.Publisher, ""));
dbDict.Add("demo", (int)gameObject.Demo);
dbDict.Add("system", Common.ReturnValueIfNull(gameObject.System, ""));
dbDict.Add("platform", Common.ReturnValueIfNull(gameObject.System, ""));
dbDict.Add("systemvariant", Common.ReturnValueIfNull(gameObject.SystemVariant, ""));
dbDict.Add("video", Common.ReturnValueIfNull(gameObject.Video, ""));
dbDict.Add("country", Common.ReturnValueIfNull(gameObject.Country, ""));
dbDict.Add("language", Common.ReturnValueIfNull(gameObject.Language, ""));
dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
// store platform db.ExecuteCMD(sql, dbDict);
int gameSystem = 0;
if (gameObject.System != null)
{
sql = "SELECT Id FROM Signatures_Platforms WHERE Platform=@platform";
sigDB = db.ExecuteCMD(sql, dbDict); processGames = true;
if (sigDB.Rows.Count == 0) }
if (processGames == true)
{
for (int x = 0; x < Object.Games.Count; ++x)
{
RomSignatureObject.Game gameObject = Object.Games[x];
// set up game dictionary
dbDict = new Dictionary<string, object>();
if (flipNameAndDescription.Contains(Object.SourceType))
{ {
// entry not present, insert it dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Description, ""));
sql = "INSERT INTO Signatures_Platforms (Platform) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Name, ""));
sigDB = db.ExecuteCMD(sql, dbDict);
gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
} }
else else
{ {
gameSystem = (int)sigDB.Rows[0][0]; dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Name, ""));
dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Description, ""));
} }
} dbDict.Add("year", Common.ReturnValueIfNull(gameObject.Year, ""));
dbDict.Add("systemid", gameSystem); dbDict.Add("publisher", Common.ReturnValueIfNull(gameObject.Publisher, ""));
dbDict.Add("demo", (int)gameObject.Demo);
dbDict.Add("system", Common.ReturnValueIfNull(gameObject.System, ""));
dbDict.Add("platform", Common.ReturnValueIfNull(gameObject.System, ""));
dbDict.Add("systemvariant", Common.ReturnValueIfNull(gameObject.SystemVariant, ""));
dbDict.Add("video", Common.ReturnValueIfNull(gameObject.Video, ""));
dbDict.Add("country", Common.ReturnValueIfNull(gameObject.Country, ""));
dbDict.Add("language", Common.ReturnValueIfNull(gameObject.Language, ""));
dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
// store publisher // store platform
int gamePublisher = 0; int gameSystem = 0;
if (gameObject.Publisher != null) if (gameObject.System != null)
{
sql = "SELECT * FROM Signatures_Publishers WHERE Publisher=@publisher";
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{ {
// entry not present, insert it sql = "SELECT Id FROM Signatures_Platforms WHERE Platform=@platform";
sql = "INSERT INTO Signatures_Publishers (Publisher) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]);
}
else
{
gamePublisher = (int)sigDB.Rows[0][0];
}
}
dbDict.Add("publisherid", gamePublisher);
// store game
int gameId = 0;
sql = "SELECT * FROM Signatures_Games WHERE Name=@name AND Year=@year AND Publisherid=@publisher AND Systemid=@systemid AND Country=@country AND Language=@language";
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{
// entry not present, insert it
sql = "INSERT INTO Signatures_Games " +
"(Name, Description, Year, PublisherId, Demo, SystemId, SystemVariant, Video, Country, Language, Copyright) VALUES " +
"(@name, @description, @year, @publisherid, @demo, @systemid, @systemvariant, @video, @country, @language, @copyright); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
gameId = Convert.ToInt32(sigDB.Rows[0][0]);
}
else
{
gameId = (int)sigDB.Rows[0][0];
}
// store rom
foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms)
{
if (romObject.Md5 != null || romObject.Sha1 != null)
{
int romId = 0;
sql = "SELECT * FROM Signatures_Roms WHERE GameId=@gameid AND MD5=@md5";
dbDict = new Dictionary<string, object>();
dbDict.Add("gameid", gameId);
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, ""));
dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower());
dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "").ToString().ToLower());
dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "").ToString().ToLower());
dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, ""));
if (romObject.Attributes != null)
{
if (romObject.Attributes.Count > 0)
{
dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(romObject.Attributes));
}
else
{
dbDict.Add("attributes", "[ ]");
}
}
else
{
dbDict.Add("attributes", "[ ]");
}
dbDict.Add("romtype", (int)romObject.RomType);
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, ""));
dbDict.Add("metadatasource", romObject.SignatureSource);
dbDict.Add("ingestorversion", 2);
sigDB = db.ExecuteCMD(sql, dbDict); sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0) if (sigDB.Rows.Count == 0)
{ {
// entry not present, insert it // entry not present, insert it
sql = "INSERT INTO Signatures_Roms (GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, MetadataSource, IngestorVersion) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource, @ingestorversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; sql = "INSERT INTO Signatures_Platforms (Platform) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict); sigDB = db.ExecuteCMD(sql, dbDict);
gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
romId = Convert.ToInt32(sigDB.Rows[0][0]);
} }
else else
{ {
romId = (int)sigDB.Rows[0][0]; gameSystem = (int)sigDB.Rows[0][0];
}
}
dbDict.Add("systemid", gameSystem);
// store publisher
int gamePublisher = 0;
if (gameObject.Publisher != null)
{
sql = "SELECT * FROM Signatures_Publishers WHERE Publisher=@publisher";
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{
// entry not present, insert it
sql = "INSERT INTO Signatures_Publishers (Publisher) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]);
}
else
{
gamePublisher = (int)sigDB.Rows[0][0];
}
}
dbDict.Add("publisherid", gamePublisher);
// store game
int gameId = 0;
sql = "SELECT * FROM Signatures_Games WHERE Name=@name AND Year=@year AND Publisherid=@publisher AND Systemid=@systemid AND Country=@country AND Language=@language";
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{
// entry not present, insert it
sql = "INSERT INTO Signatures_Games " +
"(Name, Description, Year, PublisherId, Demo, SystemId, SystemVariant, Video, Country, Language, Copyright) VALUES " +
"(@name, @description, @year, @publisherid, @demo, @systemid, @systemvariant, @video, @country, @language, @copyright); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
gameId = Convert.ToInt32(sigDB.Rows[0][0]);
}
else
{
gameId = (int)sigDB.Rows[0][0];
}
// store rom
foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms)
{
if (romObject.Md5 != null || romObject.Sha1 != null)
{
int romId = 0;
sql = "SELECT * FROM Signatures_Roms WHERE GameId=@gameid AND MD5=@md5";
dbDict = new Dictionary<string, object>();
dbDict.Add("gameid", gameId);
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, ""));
dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower());
dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "").ToString().ToLower());
dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "").ToString().ToLower());
dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, ""));
if (romObject.Attributes != null)
{
if (romObject.Attributes.Count > 0)
{
dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(romObject.Attributes));
}
else
{
dbDict.Add("attributes", "[ ]");
}
}
else
{
dbDict.Add("attributes", "[ ]");
}
dbDict.Add("romtype", (int)romObject.RomType);
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, ""));
dbDict.Add("metadatasource", romObject.SignatureSource);
dbDict.Add("ingestorversion", 2);
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{
// entry not present, insert it
sql = "INSERT INTO Signatures_Roms (GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, MetadataSource, IngestorVersion) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource, @ingestorversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
romId = Convert.ToInt32(sigDB.Rows[0][0]);
}
else
{
romId = (int)sigDB.Rows[0][0];
}
} }
} }
} }
} }
} }
} }
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Signature Ingestor - XML", "Invalid import file: " + XMLFile, ex);
}
} }
catch (Exception ex) else
{ {
Logging.Log(Logging.LogType.Warning, "Signature Ingestor - XML", "Invalid import file: " + XMLFile, ex); Logging.Log(Logging.LogType.Debug, "Signature Ingestor - XML", "Rejecting already imported file: " + XMLFile);
} }
} }
else
{
Logging.Log(Logging.LogType.Debug, "Signature Ingestor - XML", "Rejecting already imported file: " + XMLFile);
}
} }
ClearStatus();
} }
} }
} }

View File

@@ -1,3 +1,4 @@
using System.Data;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using Authentication; using Authentication;
@@ -95,6 +96,7 @@ namespace gaseous_server.Controllers
profile.EmailAddress = await _userManager.GetEmailAsync(user); profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user)); profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile; profile.SecurityProfile = user.SecurityProfile;
profile.UserPreferences = user.UserPreferences;
profile.Roles.Sort(); profile.Roles.Sort();
return Ok(profile); return Ok(profile);
@@ -115,6 +117,7 @@ namespace gaseous_server.Controllers
profile.EmailAddress = await _userManager.GetEmailAsync(user); profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user)); profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile; profile.SecurityProfile = user.SecurityProfile;
profile.UserPreferences = user.UserPreferences;
profile.Roles.Sort(); profile.Roles.Sort();
string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";"; string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";";
@@ -375,7 +378,7 @@ namespace gaseous_server.Controllers
IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, resetToken, model.NewPassword); IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, resetToken, model.NewPassword);
if (passwordChangeResult.Succeeded == true) if (passwordChangeResult.Succeeded == true)
{ {
return Ok(); return Ok(passwordChangeResult);
} }
else else
{ {
@@ -392,5 +395,23 @@ namespace gaseous_server.Controllers
return NotFound(); return NotFound();
} }
} }
[HttpPost]
[Route("Preferences")]
public async Task<IActionResult> SetPreferences(List<UserPreferenceViewModel> model)
{
ApplicationUser? user = await _userManager.GetUserAsync(User);
if (user == null)
{
return Unauthorized();
}
else
{
user.UserPreferences = model;
await _userManager.UpdateAsync(user);
return Ok();
}
}
} }
} }

View File

@@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes; using gaseous_server.Classes;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -16,98 +18,29 @@ namespace gaseous_server.Controllers
[ApiVersion("1.1")] [ApiVersion("1.1")]
[Authorize] [Authorize]
[ApiController] [ApiController]
public class FilterController : ControllerBase public class FilterController : Controller
{ {
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public FilterController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
//[ResponseCache(CacheProfileName = "5Minute")] //[ResponseCache(CacheProfileName = "5Minute")]
public Dictionary<string, object> Filter() public async Task<IActionResult> FilterAsync()
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); var user = await _userManager.GetUserAsync(User);
Dictionary<string, object> FilterSet = new Dictionary<string, object>(); return Ok(Filters.Filter(user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction, user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated));
// platforms
List<FilterPlatform> platforms = new List<FilterPlatform>();
//string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`";
string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id) AS GameCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`";
DataTable dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"]));
platformItem.RomCount = (int)(long)dr["RomCount"];
platformItem.GameCount = (int)(long)dr["GameCount"];
platforms.Add(platformItem);
}
FilterSet.Add("platforms", platforms);
// genres
List<Genre> genres = new List<Genre>();
sql = "SELECT DISTINCT Relation_Game_Genres.GenresId AS id, Genre.`Name` FROM Relation_Game_Genres JOIN Genre ON Relation_Game_Genres.GenresId = Genre.Id WHERE Relation_Game_Genres.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
genres.Add(Classes.Metadata.Genres.GetGenres((long)dr["id"]));
}
FilterSet.Add("genres", genres);
// game modes
List<GameMode> gameModes = new List<GameMode>();
sql = "SELECT DISTINCT Relation_Game_GameModes.GameModesId AS id, GameMode.`Name` FROM Relation_Game_GameModes JOIN GameMode ON Relation_Game_GameModes.GameModesId = GameMode.Id WHERE Relation_Game_GameModes.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
gameModes.Add(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"]));
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<PlayerPerspective> playerPerspectives = new List<PlayerPerspective>();
sql = "SELECT DISTINCT Relation_Game_PlayerPerspectives.PlayerPerspectivesId AS id, PlayerPerspective.`Name` FROM Relation_Game_PlayerPerspectives JOIN PlayerPerspective ON Relation_Game_PlayerPerspectives.PlayerPerspectivesId = PlayerPerspective.Id WHERE Relation_Game_PlayerPerspectives.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
playerPerspectives.Add(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"]));
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<Theme> themes = new List<Theme>();
sql = "SELECT DISTINCT Relation_Game_Themes.ThemesId AS id, Theme.`Name` FROM Relation_Game_Themes JOIN Theme ON Relation_Game_Themes.ThemesId = Theme.Id WHERE Relation_Game_Themes.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
themes.Add(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"]));
}
FilterSet.Add("themes", themes);
return FilterSet;
}
public class FilterPlatform : IGDB.Models.Platform
{
public FilterPlatform(Platform platform)
{
var properties = platform.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(platform));
}
}
}
public int RomCount { get; set; }
public int GameCount { get; set; }
} }
} }
} }

View File

@@ -22,7 +22,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.1")] [ApiVersion("1.1")]
[Authorize] [Authorize]
[ApiController] [ApiController]
public class GamesController : ControllerBase public class GamesController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[HttpGet] [HttpGet]
@@ -181,9 +181,10 @@ namespace gaseous_server.Controllers
List<long> AgeClassificationsList = new List<long>(); List<long> AgeClassificationsList = new List<long>();
foreach (string ratingGroup in ratinggroup.Split(',')) foreach (string ratingGroup in ratinggroup.Split(','))
{ {
if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup)) AgeGroups.AgeRestrictionGroupings ageRestriction = (AgeGroups.AgeRestrictionGroupings)Enum.Parse(typeof(AgeGroups.AgeRestrictionGroupings), ratingGroup);
if (AgeGroups.AgeGroupings.ContainsKey(ageRestriction))
{ {
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ratingGroup]; List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ageRestriction];
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups) foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{ {
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues); AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
@@ -824,6 +825,44 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/releasedates")]
[ProducesResponseType(typeof(List<ReleaseDate>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ResponseCache(CacheProfileName = "7Days")]
public ActionResult GameReleaseDates(long GameId)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
{
List<ReleaseDate> rdObjects = new List<ReleaseDate>();
if (gameObject.ReleaseDates != null)
{
foreach (long icId in gameObject.ReleaseDates.Ids)
{
ReleaseDate releaseDate = Classes.Metadata.ReleaseDates.GetReleaseDates(icId);
rdObjects.Add(releaseDate);
}
}
return Ok(rdObjects);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
@@ -831,13 +870,13 @@ namespace gaseous_server.Controllers
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
//[ResponseCache(CacheProfileName = "5Minute")] //[ResponseCache(CacheProfileName = "5Minute")]
public ActionResult GameRom(long GameId) public ActionResult GameRom(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1)
{ {
try try
{ {
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
return Ok(Classes.Roms.GetRoms(GameId)); return Ok(Classes.Roms.GetRoms(GameId, PlatformId, pageNumber, pageSize));
} }
catch catch
{ {

View File

@@ -17,11 +17,11 @@ namespace gaseous_server.Controllers
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100) public List<Logging.LogItem> Logs(Logging.LogsViewModel model)
{ {
return Logging.GetLogs(StartIndex, PageNumber, PageSize); return Logging.GetLogs(model);
} }
} }
} }

View File

@@ -22,7 +22,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.1")] [ApiVersion("1.1")]
[Authorize] [Authorize]
[ApiController] [ApiController]
public class RomsController : ControllerBase public class RomsController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@@ -19,12 +20,6 @@ namespace gaseous_server.Controllers
[Authorize] [Authorize]
public class SearchController : Controller public class SearchController : Controller
{ {
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
@@ -39,11 +34,12 @@ namespace gaseous_server.Controllers
private static async Task<List<Platform>> _SearchForPlatform(string SearchString) private static async Task<List<Platform>> _SearchForPlatform(string SearchString)
{ {
string searchBody = ""; string searchBody = "";
searchBody += "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; "; string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
searchBody += "where name ~ *\"" + SearchString + "\"*;"; searchBody += "where name ~ *\"" + SearchString + "\"*;";
// get Platform metadata // get Platform metadata
var results = await igdb.QueryAsync<Platform>(IGDBClient.Endpoints.Platforms, query: searchBody); Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
return results.ToList(); return results.ToList();
} }
@@ -62,12 +58,13 @@ namespace gaseous_server.Controllers
private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString) private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString)
{ {
string searchBody = ""; string searchBody = "";
searchBody += "fields cover.*,first_release_date,name,platforms,slug; "; string searchFields = "fields cover.*,first_release_date,name,platforms,slug; ";
searchBody += "search \"" + SearchString + "\";"; searchBody += "search \"" + SearchString + "\";";
searchBody += "where platforms = (" + PlatformId + ");"; searchBody += "where platforms = (" + PlatformId + ");";
// get Platform metadata // get Platform metadata
var results = await igdb.QueryAsync<Game>(IGDBClient.Endpoints.Games, query: searchBody); Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
return results.ToList(); return results.ToList();
} }

View File

@@ -18,7 +18,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")] [ApiVersion("1.1")]
[Authorize] [Authorize]
public class SignaturesController : ControllerBase public class SignaturesController : Controller
{ {
/// <summary> /// <summary>
/// Get the current signature counts from the database /// Get the current signature counts from the database

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -10,6 +11,7 @@ using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Razor.Hosting;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
@@ -80,6 +82,12 @@ namespace gaseous_server.Controllers
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings dictionary // get age ratings dictionary
Dictionary<int, string> ClassificationBoardsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)) )
{
ClassificationBoardsStrings.Add((int)ageRatingCategory, ageRatingCategory.ToString());
}
Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>(); Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) ) foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) )
{ {
@@ -89,16 +97,122 @@ namespace gaseous_server.Controllers
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine + string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine + "var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
"var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine + "var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine +
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{ "var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
WriteIndented = true WriteIndented = true
}) + ";" + Environment.NewLine + }) + ";" + Environment.NewLine +
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeRatings.AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{ "var AgeRatingGroups = " + JsonSerializer.Serialize(AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
WriteIndented = true WriteIndented = true
}) + ";"; }) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(ver); byte[] bytes = Encoding.UTF8.GetBytes(ver);
return File(bytes, "text/javascript"); return File(bytes, "text/javascript");
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Settings/BackgroundTasks/Intervals")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetBackgroundTasks()
{
Dictionary<string, BackgroundTaskItem> Intervals = new Dictionary<string, BackgroundTaskItem>();
Intervals.Add(ProcessQueue.QueueItemType.SignatureIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.SignatureIngestor));
Intervals.Add(ProcessQueue.QueueItemType.TitleIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.TitleIngestor));
Intervals.Add(ProcessQueue.QueueItemType.MetadataRefresh.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.MetadataRefresh));
Intervals.Add(ProcessQueue.QueueItemType.OrganiseLibrary.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.OrganiseLibrary));
Intervals.Add(ProcessQueue.QueueItemType.LibraryScan.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.LibraryScan));
Intervals.Add(ProcessQueue.QueueItemType.Rematcher.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Rematcher));
Intervals.Add(ProcessQueue.QueueItemType.Maintainer.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Maintainer));
return Ok(Intervals);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Settings/BackgroundTasks/Intervals")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult SetBackgroundTasks(Dictionary<string, int> Intervals)
{
foreach (KeyValuePair<string, int> Interval in Intervals)
{
if (Enum.IsDefined(typeof(ProcessQueue.QueueItemType), Interval.Key))
{
try
{
BackgroundTaskItem taskItem = new BackgroundTaskItem(
(ProcessQueue.QueueItemType)Enum.Parse(typeof(ProcessQueue.QueueItemType), Interval.Key)
);
if (Interval.Value >= taskItem.MinimumAllowedValue)
{
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + Interval.Key + " with new interval " + Interval.Value);
Config.SetSetting("Interval_" + Interval.Key, Interval.Value.ToString());
// update existing process
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
{
if (item.ItemType.ToString().ToLower() == Interval.Key.ToLower())
{
item.Interval = Interval.Value;
}
}
}
else
{
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Interval " + Interval.Value + " for task " + Interval.Key + " is below the minimum allowed value of " + taskItem.MinimumAllowedValue + ". Skipping.");
}
}
catch
{
// task name not defined
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Task " + Interval.Key + " is not user definable. Skipping.");
}
}
}
return Ok();
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Settings/System")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetSystemSettings()
{
SystemSettingsModel systemSettingsModel = new SystemSettingsModel{
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention
};
return Ok(systemSettingsModel);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Settings/System")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult SetSystemSettings(SystemSettingsModel model)
{
if (ModelState.IsValid)
{
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
Config.UpdateConfig();
}
return Ok(model);
}
private SystemInfo.PathItem GetDisk(string Path) private SystemInfo.PathItem GetDisk(string Path)
{ {
SystemInfo.PathItem pathItem = new SystemInfo.PathItem { SystemInfo.PathItem pathItem = new SystemInfo.PathItem {
@@ -139,4 +253,69 @@ namespace gaseous_server.Controllers
} }
} }
} }
public class BackgroundTaskItem
{
public BackgroundTaskItem(ProcessQueue.QueueItemType TaskName)
{
this.Task = TaskName.ToString();
switch (TaskName)
{
case ProcessQueue.QueueItemType.SignatureIngestor:
this.DefaultInterval = 60;
this.MinimumAllowedValue = 20;
break;
case ProcessQueue.QueueItemType.TitleIngestor:
this.DefaultInterval = 1;
this.MinimumAllowedValue = 1;
break;
case ProcessQueue.QueueItemType.MetadataRefresh:
this.DefaultInterval = 360;
this.MinimumAllowedValue = 360;
break;
case ProcessQueue.QueueItemType.OrganiseLibrary:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 120;
break;
case ProcessQueue.QueueItemType.LibraryScan:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 120;
break;
case ProcessQueue.QueueItemType.Rematcher:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 360;
break;
case ProcessQueue.QueueItemType.Maintainer:
this.DefaultInterval = 10080;
this.MinimumAllowedValue = 10080;
break;
default:
throw new Exception("Invalid task");
}
}
public string Task { get; set; }
public int Interval {
get
{
return int.Parse(Config.ReadSetting("Interval_" + Task, DefaultInterval.ToString()));
}
}
public int DefaultInterval { get; set; }
public int MinimumAllowedValue { get; set; }
}
public class SystemSettingsModel
{
public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; }
}
} }

View File

@@ -52,18 +52,29 @@ namespace gaseous_server.Controllers
NormalizedEmail = model.Email.ToUpper(), NormalizedEmail = model.Email.ToUpper(),
SecurityProfile = new SecurityProfileViewModel() SecurityProfile = new SecurityProfileViewModel()
}; };
Logging.Log(Logging.LogType.Information, "First Run", "Creating new account " + model.Email);
var result = await _userManager.CreateAsync(user, model.Password); var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded) if (result.Succeeded)
{ {
Logging.Log(Logging.LogType.Information, "First Run", "Creation of " + model.Email + " successful.");
Logging.Log(Logging.LogType.Information, "First Run", "Adding Player role to " + model.Email);
await _userManager.AddToRoleAsync(user, "Player"); await _userManager.AddToRoleAsync(user, "Player");
Logging.Log(Logging.LogType.Information, "First Run", "Adding Gamer role to " + model.Email);
await _userManager.AddToRoleAsync(user, "Gamer"); await _userManager.AddToRoleAsync(user, "Gamer");
Logging.Log(Logging.LogType.Information, "First Run", "Adding Admin role to " + model.Email);
await _userManager.AddToRoleAsync(user, "Admin"); await _userManager.AddToRoleAsync(user, "Admin");
Logging.Log(Logging.LogType.Information, "First Run", "Signing in as " + model.Email);
await _signInManager.SignInAsync(user, isPersistent: true); await _signInManager.SignInAsync(user, isPersistent: true);
Logging.Log(Logging.LogType.Information, "First Run", "Setting first run state to 1");
Config.SetSetting("FirstRunStatus", "1"); Config.SetSetting("FirstRunStatus", "1");
return Ok(); return Ok(result);
}
else
{
return Ok(result);
} }
} }

View File

@@ -39,33 +39,41 @@ namespace gaseous_server.Controllers.v1_1
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> Game_v1_1(GameSearchModel model) public async Task<IActionResult> Game_v1_1(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
{ {
var user = await _userManager.GetUserAsync(User); var user = await _userManager.GetUserAsync(User);
if (user != null) if (user != null)
{ {
// apply security profile filtering // apply security profile filtering
List<string> RemoveAgeGroups = new List<string>(); if (model.GameAgeRating.AgeGroupings.Count == 0)
switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction.ToLower())
{ {
case "adult": model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Adult);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Mature);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Teen);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Child);
model.GameAgeRating.IncludeUnrated = true;
}
List<AgeGroups.AgeRestrictionGroupings> RemoveAgeGroups = new List<AgeGroups.AgeRestrictionGroupings>();
switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction)
{
case AgeGroups.AgeRestrictionGroupings.Adult:
break; break;
case "mature": case AgeGroups.AgeRestrictionGroupings.Mature:
RemoveAgeGroups.Add("Adult"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
break; break;
case "teen": case AgeGroups.AgeRestrictionGroupings.Teen:
RemoveAgeGroups.Add("Adult"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
RemoveAgeGroups.Add("Mature"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature);
break; break;
case "child": case AgeGroups.AgeRestrictionGroupings.Child:
RemoveAgeGroups.Add("Adult"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
RemoveAgeGroups.Add("Mature"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature);
RemoveAgeGroups.Add("Teen"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Teen);
break; break;
} }
foreach (string RemoveAgeGroup in RemoveAgeGroups) foreach (AgeGroups.AgeRestrictionGroupings RemoveAgeGroup in RemoveAgeGroups)
{ {
if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup)) if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup))
{ {
@@ -77,7 +85,47 @@ namespace gaseous_server.Controllers.v1_1
model.GameAgeRating.IncludeUnrated = false; model.GameAgeRating.IncludeUnrated = false;
} }
return Ok(GetGames(model)); return Ok(GetGames(model, pageNumber, pageSize));
}
else
{
return Unauthorized();
}
}
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/Related")]
[ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> GameRelated(long GameId)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
string IncludeUnrated = "";
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == true) {
IncludeUnrated = " OR view_Games.AgeGroupId IS NULL";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Games.Id, view_Games.AgeGroupId, Relation_Game_SimilarGames.SimilarGamesId FROM view_Games JOIN Relation_Game_SimilarGames ON view_Games.Id = Relation_Game_SimilarGames.GameId AND Relation_Game_SimilarGames.SimilarGamesId IN (SELECT Id FROM view_Games) WHERE view_Games.Id = @id AND (view_Games.AgeGroupId <= @agegroupid" + IncludeUnrated + ")";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId);
dbDict.Add("agegroupid", (int)user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction);
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, dbDict);
foreach (DataRow dr in dbResponse.Rows)
{
RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["SimilarGamesId"], false, false, false));
}
GameReturnPackage gameReturn = new GameReturnPackage(RetVal.Count, RetVal);
return Ok(gameReturn);
} }
else else
{ {
@@ -109,7 +157,12 @@ namespace gaseous_server.Controllers.v1_1
public class GameAgeRatingItem public class GameAgeRatingItem
{ {
public List<string> AgeGroupings { get; set; } = new List<string>{ "Child", "Teen", "Mature", "Adult" }; public List<AgeGroups.AgeRestrictionGroupings> AgeGroupings { get; set; } = new List<AgeGroups.AgeRestrictionGroupings>{
AgeGroups.AgeRestrictionGroupings.Child,
AgeGroups.AgeRestrictionGroupings.Teen,
AgeGroups.AgeRestrictionGroupings.Mature,
AgeGroups.AgeRestrictionGroupings.Adult
};
public bool IncludeUnrated { get; set; } = true; public bool IncludeUnrated { get; set; } = true;
} }
@@ -128,7 +181,7 @@ namespace gaseous_server.Controllers.v1_1
} }
} }
public static List<Game> GetGames(GameSearchModel model) public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
{ {
string whereClause = ""; string whereClause = "";
string havingClause = ""; string havingClause = "";
@@ -139,11 +192,14 @@ namespace gaseous_server.Controllers.v1_1
string tempVal = ""; string tempVal = "";
string nameWhereClause = "";
if (model.Name.Length > 0) if (model.Name.Length > 0)
{ {
tempVal = "`Name` LIKE @Name"; // tempVal = "`Name` LIKE @Name";
whereParams.Add("@Name", "%" + model.Name + "%"); // whereParams.Add("@Name", "%" + model.Name + "%");
havingClauses.Add(tempVal); // 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) if (model.GameRating != null)
@@ -191,7 +247,7 @@ namespace gaseous_server.Controllers.v1_1
} }
} }
string unratedClause = "totalRating IS NOT NULL"; string unratedClause = "";
if (model.GameRating.IncludeUnrated == true) if (model.GameRating.IncludeUnrated == true)
{ {
unratedClause = "totalRating IS NULL"; unratedClause = "totalRating IS NULL";
@@ -199,13 +255,21 @@ namespace gaseous_server.Controllers.v1_1
if (ratingClauseValue.Length > 0) if (ratingClauseValue.Length > 0)
{ {
havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")"); if (unratedClause.Length > 0)
{
havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")");
}
else
{
havingClauses.Add("(" + ratingClauseValue + ")");
}
} }
} }
string platformWhereClause = "";
if (model.Platform.Count > 0) if (model.Platform.Count > 0)
{ {
tempVal = "Games_Roms.PlatformId IN ("; tempVal = " AND Games_Roms.PlatformId IN (";
for (int i = 0; i < model.Platform.Count; i++) for (int i = 0; i < model.Platform.Count; i++)
{ {
if (i > 0) if (i > 0)
@@ -217,7 +281,8 @@ namespace gaseous_server.Controllers.v1_1
whereParams.Add(platformLabel, model.Platform[i]); whereParams.Add(platformLabel, model.Platform[i]);
} }
tempVal += ")"; tempVal += ")";
whereClauses.Add(tempVal); //whereClauses.Add(tempVal);
platformWhereClause = tempVal;
} }
if (model.Genre.Count > 0) if (model.Genre.Count > 0)
@@ -292,43 +357,26 @@ namespace gaseous_server.Controllers.v1_1
{ {
if (model.GameAgeRating.AgeGroupings.Count > 0) if (model.GameAgeRating.AgeGroupings.Count > 0)
{ {
List<long> AgeClassificationsList = new List<long>(); tempVal = "(Game.AgeGroupId IN (";
foreach (string ratingGroup in model.GameAgeRating.AgeGroupings) for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++)
{ {
if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup)) if (i > 0)
{ {
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ratingGroup]; tempVal += ", ";
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
}
} }
string themeLabel = "@Rating" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, model.GameAgeRating.AgeGroupings[i]);
} }
tempVal += ")";
if (AgeClassificationsList.Count > 0) if (model.GameAgeRating.IncludeUnrated == true)
{ {
AgeClassificationsList = new HashSet<long>(AgeClassificationsList).ToList(); tempVal += " OR Game.AgeGroupId IS NULL";
tempVal = "(view_AgeRatings.Rating IN (";
for (int i = 0; i < AgeClassificationsList.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string themeLabel = "@Rating" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, AgeClassificationsList[i]);
}
tempVal += ")";
if (model.GameAgeRating.IncludeUnrated == true)
{
tempVal += " OR view_AgeRatings.Rating IS NULL";
}
tempVal += ")";
whereClauses.Add(tempVal);
} }
tempVal += ")";
whereClauses.Add(tempVal);
} }
} }
@@ -396,18 +444,57 @@ namespace gaseous_server.Controllers.v1_1
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder; string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.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 LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.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<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>(); List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, whereParams); DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
foreach (DataRow dr in dbResponse.Rows)
// get count
int RecordCount = dbResponse.Rows.Count;
// compile data for return
int pageOffset = pageSize * (pageNumber - 1);
for (int i = pageOffset; i < dbResponse.Rows.Count; i++)
{ {
//RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["ROMGameId"], false, false)); if (i >= (pageOffset + pageSize))
RetVal.Add(Classes.Metadata.Games.GetGame(dr)); {
break;
}
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
RetVal.Add(retGame);
} }
return RetVal; GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);
return gameReturn;
}
public class GameReturnPackage
{
public GameReturnPackage()
{
}
public GameReturnPackage(int Count, List<Game> Games)
{
this.Count = Count;
List<Games.MinimalGameItem> minimalGames = new List<Games.MinimalGameItem>();
foreach (Game game in Games)
{
minimalGames.Add(new Classes.Metadata.Games.MinimalGameItem(game));
}
this.Games = minimalGames;
}
public int Count { get; set; }
public List<Games.MinimalGameItem> Games { get; set; } = new List<Games.MinimalGameItem>();
} }
} }
} }

View File

@@ -157,18 +157,18 @@ namespace gaseous_server.Models
if (Update == false) if (Update == false)
{ {
// insert // insert
sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core, AvailableWebEmulators) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core, @AvailableWebEmulators)"; sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core, AvailableWebEmulators) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core, @AvailableWebEmulators);";
} }
else else
{ {
// update // update
if (AllowAvailableEmulatorOverwrite == true) if (AllowAvailableEmulatorOverwrite == true)
{ {
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core, AvailableWebEmulators=@AvailableWebEmulators WHERE Id = @Id"; sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core, AvailableWebEmulators=@AvailableWebEmulators WHERE Id = @Id; ";
} }
else else
{ {
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core WHERE Id = @Id"; sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core WHERE Id = @Id;";
} }
} }
dbDict.Add("Id", item.IGDBId); dbDict.Add("Id", item.IGDBId);

View File

@@ -1,4 +1,6 @@
using System; using System;
using System.ComponentModel.Design.Serialization;
using System.Data;
using gaseous_server.Classes; using gaseous_server.Classes;
namespace gaseous_server namespace gaseous_server
@@ -13,7 +15,7 @@ namespace gaseous_server
{ {
_ItemType = ItemType; _ItemType = ItemType;
_ItemState = QueueItemState.NeverStarted; _ItemState = QueueItemState.NeverStarted;
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))); _LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))).AddMinutes(-5);
_Interval = ExecutionInterval; _Interval = ExecutionInterval;
_AllowManualStart = AllowManualStart; _AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped; _RemoveWhenStopped = RemoveWhenStopped;
@@ -23,7 +25,7 @@ namespace gaseous_server
{ {
_ItemType = ItemType; _ItemType = ItemType;
_ItemState = QueueItemState.NeverStarted; _ItemState = QueueItemState.NeverStarted;
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))); _LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))).AddMinutes(-5);
_Interval = ExecutionInterval; _Interval = ExecutionInterval;
_AllowManualStart = AllowManualStart; _AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped; _RemoveWhenStopped = RemoveWhenStopped;
@@ -33,6 +35,7 @@ namespace gaseous_server
private QueueItemType _ItemType = QueueItemType.NotConfigured; private QueueItemType _ItemType = QueueItemType.NotConfigured;
private QueueItemState _ItemState = QueueItemState.NeverStarted; private QueueItemState _ItemState = QueueItemState.NeverStarted;
private DateTime _LastRunTime = DateTime.UtcNow; private DateTime _LastRunTime = DateTime.UtcNow;
private double _LastRunDuration = 0;
private DateTime _LastFinishTime private DateTime _LastFinishTime
{ {
get get
@@ -55,19 +58,32 @@ namespace gaseous_server
private bool _AllowManualStart = true; private bool _AllowManualStart = true;
private bool _RemoveWhenStopped = false; private bool _RemoveWhenStopped = false;
private bool _IsBlocked = false; private bool _IsBlocked = false;
private string _CorrelationId = "";
private List<QueueItemType> _Blocks = new List<QueueItemType>(); private List<QueueItemType> _Blocks = new List<QueueItemType>();
public QueueItemType ItemType => _ItemType; public QueueItemType ItemType => _ItemType;
public QueueItemState ItemState => _ItemState; public QueueItemState ItemState => _ItemState;
public DateTime LastRunTime => _LastRunTime; public DateTime LastRunTime => _LastRunTime;
public DateTime LastFinishTime => _LastFinishTime; public DateTime LastFinishTime => _LastFinishTime;
public DateTime NextRunTime { public double LastRunDuration => _LastRunDuration;
public DateTime NextRunTime
{
get get
{ {
return LastRunTime.AddMinutes(Interval); return LastRunTime.AddMinutes(Interval);
} }
} }
public int Interval => _Interval; public int Interval
{
get
{
return _Interval;
}
set
{
_Interval = value;
}
}
public string LastResult => _LastResult; public string LastResult => _LastResult;
public string? LastError => _LastError; public string? LastError => _LastError;
public bool Force => _ForceExecute; public bool Force => _ForceExecute;
@@ -75,6 +91,9 @@ namespace gaseous_server
public bool RemoveWhenStopped => _RemoveWhenStopped; public bool RemoveWhenStopped => _RemoveWhenStopped;
public bool IsBlocked => _IsBlocked; public bool IsBlocked => _IsBlocked;
public object? Options { get; set; } = null; public object? Options { get; set; } = null;
public string CurrentState { get; set; } = "";
public string CurrentStateProgress { get; set; } = "";
public string CorrelationId => _CorrelationId;
public List<QueueItemType> Blocks => _Blocks; public List<QueueItemType> Blocks => _Blocks;
public void Execute() public void Execute()
@@ -89,7 +108,15 @@ namespace gaseous_server
_LastResult = ""; _LastResult = "";
_LastError = null; _LastError = null;
Logging.Log(Logging.LogType.Debug, "Timered Event", "Executing " + _ItemType); // set the correlation id
Guid correlationId = Guid.NewGuid();
_CorrelationId = correlationId.ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", _ItemType.ToString());
CallContext.SetData("CallingUser", "System");
// log the start
Logging.Log(Logging.LogType.Debug, "Timered Event", "Executing " + _ItemType + " with correlation id " + _CorrelationId);
try try
{ {
@@ -97,8 +124,11 @@ namespace gaseous_server
{ {
case QueueItemType.SignatureIngestor: case QueueItemType.SignatureIngestor:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Signature Ingestor"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Signature Ingestor");
SignatureIngestors.XML.XMLIngestor tIngest = new SignatureIngestors.XML.XMLIngestor(); SignatureIngestors.XML.XMLIngestor tIngest = new SignatureIngestors.XML.XMLIngestor
{
CallingQueueItem = this
};
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files"); Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC); tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC);
@@ -114,8 +144,12 @@ namespace gaseous_server
case QueueItemType.TitleIngestor: case QueueItemType.TitleIngestor:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory); Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory)
{
CallingQueueItem = this
};
// clean up
Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory); Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory);
_SaveLastRunTime = true; _SaveLastRunTime = true;
@@ -124,7 +158,11 @@ namespace gaseous_server
case QueueItemType.MetadataRefresh: case QueueItemType.MetadataRefresh:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher");
Classes.MetadataManagement.RefreshMetadata(); Classes.MetadataManagement metadataManagement = new MetadataManagement
{
CallingQueueItem = this
};
metadataManagement.RefreshMetadata(_ForceExecute);
_SaveLastRunTime = true; _SaveLastRunTime = true;
@@ -140,7 +178,11 @@ namespace gaseous_server
case QueueItemType.LibraryScan: case QueueItemType.LibraryScan:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner");
Classes.ImportGame.LibraryScan(); Classes.ImportGame import = new ImportGame
{
CallingQueueItem = this
};
import.LibraryScan();
_SaveLastRunTime = true; _SaveLastRunTime = true;
@@ -148,7 +190,11 @@ namespace gaseous_server
case QueueItemType.Rematcher: case QueueItemType.Rematcher:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Rematch"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Rematch");
Classes.ImportGame.Rematcher(_ForceExecute); Classes.ImportGame importRematch = new ImportGame
{
CallingQueueItem = this
};
importRematch.Rematcher(_ForceExecute);
_SaveLastRunTime = true; _SaveLastRunTime = true;
@@ -171,7 +217,10 @@ namespace gaseous_server
case QueueItemType.Maintainer: case QueueItemType.Maintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Maintenance"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Maintenance");
Classes.Maintenance.RunMaintenance(); Classes.Maintenance maintenance = new Maintenance{
CallingQueueItem = this
};
maintenance.RunMaintenance();
break; break;
} }
@@ -186,8 +235,9 @@ namespace gaseous_server
_ForceExecute = false; _ForceExecute = false;
_ItemState = QueueItemState.Stopped; _ItemState = QueueItemState.Stopped;
_LastFinishTime = DateTime.UtcNow; _LastFinishTime = DateTime.UtcNow;
_LastRunDuration = Math.Round((DateTime.UtcNow - _LastRunTime).TotalSeconds, 2);
Logging.Log(Logging.LogType.Information, "Timered Event", "Total " + _ItemType + " run time = " + (DateTime.UtcNow - _LastRunTime).TotalSeconds); Logging.Log(Logging.LogType.Information, "Timered Event", "Total " + _ItemType + " run time = " + _LastRunDuration);
} }
} }
} }
@@ -201,6 +251,66 @@ namespace gaseous_server
{ {
_IsBlocked = BlockState; _IsBlocked = BlockState;
} }
public HasErrorsItem HasErrors
{
get
{
return new HasErrorsItem(_CorrelationId);
}
}
public class HasErrorsItem
{
public HasErrorsItem(string? CorrelationId)
{
if (CorrelationId != null)
{
if (CorrelationId.Length > 0)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT EventType, COUNT(EventType) AS EventTypes FROM gaseous.ServerLogs WHERE CorrelationId = @correlationid GROUP BY EventType ORDER BY EventType DESC LIMIT 1;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("correlationid", CorrelationId);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count == 0)
{
ErrorType = null;
ErrorCount = 0;
}
else
{
Logging.LogType errorType = (Logging.LogType)data.Rows[0]["EventType"];
if (errorType != Logging.LogType.Information)
{
ErrorType = errorType;
ErrorCount = (int)(long)data.Rows[0]["EventTypes"];
}
else
{
ErrorType = null;
ErrorCount = 0;
}
}
}
else
{
ErrorType = null;
ErrorCount = 0;
}
}
else
{
ErrorType = null;
ErrorCount = 0;
}
}
public Logging.LogType? ErrorType { get; set; }
public int ErrorCount { get; set; }
}
} }
public enum QueueItemType public enum QueueItemType

View File

@@ -13,6 +13,8 @@ using Microsoft.OpenApi.Models;
using Authentication; using Authentication;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Identity.UI.Services;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
Logging.WriteToDiskOnly = true; Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version); Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
@@ -39,11 +41,17 @@ db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.Conn
// set up db // set up db
db.InitDB(); db.InitDB();
// populate db with static data for lookups
AgeRatings.PopulateAgeMap();
// load app settings // load app settings
Config.InitSettings(); Config.InitSettings();
// write updated settings back to the config file // write updated settings back to the config file
Config.UpdateConfig(); Config.UpdateConfig();
// set api metadata source from config
Communications.MetadataSource = Config.MetadataConfiguration.Source;
// set initial values // set initial values
Guid APIKey = Guid.NewGuid(); Guid APIKey = Guid.NewGuid();
if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key") if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
@@ -287,27 +295,6 @@ using (var scope = app.Services.CreateScope())
await roleManager.CreateAsync(applicationRole, CancellationToken.None); await roleManager.CreateAsync(applicationRole, CancellationToken.None);
} }
} }
// // set up administrator account
// var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
// if (await userManager.FindByNameAsync("admin@localhost", CancellationToken.None) == null)
// {
// ApplicationUser adminUser = new ApplicationUser{
// Id = Guid.NewGuid().ToString(),
// Email = "admin@localhost",
// NormalizedEmail = "ADMIN@LOCALHOST",
// EmailConfirmed = true,
// UserName = "administrator",
// NormalizedUserName = "ADMINISTRATOR"
// };
// //set user password
// PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
// adminUser.PasswordHash = ph.HashPassword(adminUser, "letmein");
// await userManager.CreateAsync(adminUser, CancellationToken.None);
// await userManager.AddToRoleAsync(adminUser, "Admin", CancellationToken.None);
// }
} }
app.UseAuthorization(); app.UseAuthorization();
@@ -321,6 +308,28 @@ app.UseStaticFiles(new StaticFileOptions
app.MapControllers(); app.MapControllers();
app.Use(async (context, next) =>
{
// set the correlation id
string correlationId = Guid.NewGuid().ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", context.Request.Method + ": " + context.Request.Path);
string userIdentity;
try
{
userIdentity = context.User.Claims.Where(x=>x.Type==System.Security.Claims.ClaimTypes.NameIdentifier).FirstOrDefault().Value;
}
catch
{
userIdentity = "";
}
CallContext.SetData("CallingUser", userIdentity);
context.Response.Headers.Add("x-correlation-id", correlationId.ToString());
await next();
});
// emergency password recovery if environment variable is set // emergency password recovery if environment variable is set
// process: // process:
// - set the environment variable "recoveraccount" to the email address of the account to be recovered // - set the environment variable "recoveraccount" to the email address of the account to be recovered
@@ -392,20 +401,25 @@ Config.LibraryConfiguration.InitLibrary();
// insert unknown platform and game if not present // insert unknown platform and game if not present
gaseous_server.Classes.Metadata.Games.GetGame(0, false, false, false); gaseous_server.Classes.Metadata.Games.GetGame(0, false, false, false);
gaseous_server.Classes.Metadata.Games.AssignAllGamesToPlatformIdZero();
gaseous_server.Classes.Metadata.Platforms.GetPlatform(0); gaseous_server.Classes.Metadata.Platforms.GetPlatform(0);
gaseous_server.Classes.Metadata.Platforms.AssignAllPlatformsToGameIdZero();
// extract platform map if not present // extract platform map if not present
PlatformMapping.ExtractPlatformMap(); PlatformMapping.ExtractPlatformMap();
// force load platform map into cache
var platformMap = PlatformMapping.PlatformMap;
// add background tasks // add background tasks
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.SignatureIngestor, ProcessQueue.QueueItemType.SignatureIngestor,
60 int.Parse(Config.ReadSetting("Interval_SignatureIngestor", "60"))
) )
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.TitleIngestor, ProcessQueue.QueueItemType.TitleIngestor,
1, int.Parse(Config.ReadSetting("Interval_TitleIngestor", "1")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
@@ -414,12 +428,12 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.MetadataRefresh, ProcessQueue.QueueItemType.MetadataRefresh,
360 int.Parse(Config.ReadSetting("Interval_MetadataRefresh", "360"))
) )
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
1440, int.Parse(Config.ReadSetting("Interval_OrganiseLibrary", "1440")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.LibraryScan, ProcessQueue.QueueItemType.LibraryScan,
@@ -429,7 +443,7 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScan, ProcessQueue.QueueItemType.LibraryScan,
60, int.Parse(Config.ReadSetting("Interval_LibraryScan", "1440")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
@@ -438,7 +452,7 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Rematcher, ProcessQueue.QueueItemType.Rematcher,
1440, int.Parse(Config.ReadSetting("Interval_Rematcher", "1440")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
@@ -447,7 +461,7 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Maintainer, ProcessQueue.QueueItemType.Maintainer,
10080, int.Parse(Config.ReadSetting("Interval_Maintainer", "10080")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.All ProcessQueue.QueueItemType.All

View File

@@ -0,0 +1,2 @@
ALTER TABLE `Signatures_Roms`
ADD INDEX `name_Idx` USING BTREE (`Name`) VISIBLE;

View File

@@ -0,0 +1,76 @@
CREATE TABLE `ClassificationMap` (
`AgeGroupId` INT NOT NULL,
`ClassificationBoardId` INT NOT NULL,
`RatingId` INT NOT NULL,
PRIMARY KEY (`AgeGroupId`, `ClassificationBoardId`, `RatingId`));
CREATE OR REPLACE VIEW `view_GamesWithRoms` AS
SELECT DISTINCT
Games_Roms.GameId AS ROMGameId,
Game.*,
CASE
WHEN
Game.`Name` LIKE 'The %'
THEN
CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)),
', The')
ELSE Game.`Name`
END AS NameThe
FROM
Games_Roms
LEFT JOIN
Game ON Game.Id = Games_Roms.GameId;
CREATE OR REPLACE VIEW `view_Games` AS
SELECT
*
FROM
(SELECT DISTINCT
row_number() over (partition by Id order by AgeGroupId desc) as seqnum, view_GamesWithRoms.*,
(SELECT
AgeGroupId
FROM
ClassificationMap
WHERE
RatingId = AgeRating.Rating
ORDER BY AgeGroupId DESC) AgeGroupId
FROM
view_GamesWithRoms
LEFT JOIN Relation_Game_AgeRatings ON view_GamesWithRoms.Id = Relation_Game_AgeRatings.GameId
LEFT JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id
) g
WHERE g.seqnum = 1;
CREATE TABLE `ReleaseDate` (
`Id` BIGINT NOT NULL,
`Category` INT(11) NULL DEFAULT NULL,
`Checksum` VARCHAR(45) NULL DEFAULT NULL,
`CreatedAt` DATETIME NULL DEFAULT NULL,
`Date` DATETIME NULL,
`Game` BIGINT NULL,
`Human` VARCHAR(100) NULL,
`m` INT NULL,
`Platform` BIGINT NULL,
`Region` INT NULL,
`Status` BIGINT NULL,
`UpdatedAt` DATETIME NULL DEFAULT NULL,
`y` INT NULL,
`dateAdded` DATETIME NULL DEFAULT NULL,
`lastUpdated` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`Id`));
CREATE TABLE `User_Settings` (
`Id` VARCHAR(128) NOT NULL,
`Setting` VARCHAR(45) NOT NULL,
`Value` LONGTEXT NULL DEFAULT NULL,
PRIMARY KEY (`Id`, `Setting`));
ALTER TABLE `ServerLogs`
ADD FULLTEXT INDEX `ft_message` (`Message`) VISIBLE;
CREATE TABLE `Relation_Game_Platforms` (
`GameId` BIGINT NOT NULL,
`PlatformsId` BIGINT NOT NULL,
PRIMARY KEY (`GameId`, `PlatformsId`),
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
);

View File

@@ -0,0 +1,26 @@
ALTER TABLE `ClassificationMap`
ADD INDEX `idx_RatingId` (`RatingId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_AgeRatings`
ADD INDEX `idx_SecondaryColumn` (`AgeRatingsId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_GameModes`
ADD INDEX `idx_SecondaryColumn` (`GameModesId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_Genres`
ADD INDEX `idx_SecondaryColumn` (`GenresId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_Platforms`
ADD INDEX `idx_SecondaryColumn` (`PlatformsId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_PlayerPerspectives`
ADD INDEX `idx_SecondaryColumn` (`PlayerPerspectivesId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_Themes`
ADD INDEX `idx_SecondaryColumn` (`ThemesId` ASC) VISIBLE;
ALTER TABLE `ServerLogs`
ADD COLUMN `CorrelationId` VARCHAR(45) NULL AFTER `Exception`,
ADD COLUMN `CallingProcess` VARCHAR(255) NULL AFTER `CorrelationId`,
ADD INDEX `idx_CorrelationId` (`CorrelationId` ASC) VISIBLE,
ADD INDEX `idx_CallingProcess` (`CallingProcess` ASC) VISIBLE;

View File

@@ -0,0 +1,4 @@
ALTER TABLE `Games_Roms`
ADD INDEX `id_LibraryId` (`LibraryId` ASC) VISIBLE,
ADD INDEX `id_MD5` USING BTREE (`MD5`) VISIBLE;

View File

@@ -0,0 +1,23 @@
CREATE OR REPLACE VIEW `view_Games` AS
SELECT
a.*, b.AgeGroupId
FROM
view_GamesWithRoms a
INNER JOIN
(SELECT
view_GamesWithRoms.Id,
MAX((SELECT
AgeGroupId
FROM
ClassificationMap
WHERE
RatingId = AgeRating.Rating)) AgeGroupId
FROM
view_GamesWithRoms
LEFT JOIN Relation_Game_AgeRatings ON view_GamesWithRoms.Id = Relation_Game_AgeRatings.GameId
LEFT JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id
GROUP BY Id) b ON a.Id = b.Id
ORDER BY NameThe;
ALTER TABLE `ServerLogs`
ADD COLUMN `CallingUser` VARCHAR(255) NULL AFTER `CallingProcess`;

View File

@@ -0,0 +1,29 @@
CREATE TABLE `AgeGroup` (
`Id` BIGINT NOT NULL,
`GameId` BIGINT NULL,
`AgeGroupId` INT NULL,
`dateAdded` DATETIME NULL DEFAULT NULL,
`lastUpdated` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`Id`));
ALTER TABLE `AgeGroup`
ADD INDEX `id_GameId` (`GameId` ASC) VISIBLE,
ADD INDEX `id_AgeGroupId` (`AgeGroupId` ASC) VISIBLE;
ALTER TABLE `Game`
CHANGE COLUMN `Slug` `Slug` VARCHAR(255) NULL DEFAULT NULL;
CREATE OR REPLACE VIEW `view_Games` AS
SELECT
a.*, b.AgeGroupId
FROM
view_GamesWithRoms a
LEFT JOIN AgeGroup b ON b.GameId = a.Id
ORDER BY NameThe;
ALTER TABLE `Game`
ADD FULLTEXT INDEX `ft_Name` (`Name`) VISIBLE;
ALTER TABLE `AlternativeName`
ADD FULLTEXT INDEX `ft_Name` (`Name`) VISIBLE,
ADD INDEX `id_GameId` (`Game` ASC) VISIBLE;

View File

@@ -46,6 +46,12 @@
<None Remove="Support\Database\MySQL\gaseous-1003.sql" /> <None Remove="Support\Database\MySQL\gaseous-1003.sql" />
<None Remove="Support\Database\MySQL\gaseous-1004.sql" /> <None Remove="Support\Database\MySQL\gaseous-1004.sql" />
<None Remove="Support\Database\MySQL\gaseous-1005.sql" /> <None Remove="Support\Database\MySQL\gaseous-1005.sql" />
<None Remove="Support\Database\MySQL\gaseous-1006.sql" />
<None Remove="Support\Database\MySQL\gaseous-1007.sql" />
<None Remove="Support\Database\MySQL\gaseous-1008.sql" />
<None Remove="Support\Database\MySQL\gaseous-1009.sql" />
<None Remove="Support\Database\MySQL\gaseous-1010.sql" />
<None Remove="Support\Database\MySQL\gaseous-1011.sql" />
<None Remove="Classes\Metadata\" /> <None Remove="Classes\Metadata\" />
<None Remove="Assets\" /> <None Remove="Assets\" />
<None Remove="Assets\Ratings\" /> <None Remove="Assets\Ratings\" />
@@ -174,5 +180,11 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1006.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1008.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1009.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1010.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1011.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

View File

@@ -3,9 +3,9 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<script src="/api/v1.1/System/VersionFile"></script> <script src="/api/v1.1/System/VersionFile"></script>
<link type="text/css" rel="stylesheet" dat-href="/styles/style.css" /> <link type="text/css" rel="stylesheet" href="/styles/style.css" dat-href="/styles/style.css" />
<script src="/scripts/jquery-3.6.0.min.js"></script> <script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script> <script src="/scripts/moment-with-locales.min.js"></script>
<link href="/styles/select2.min.css" rel="stylesheet" /> <link href="/styles/select2.min.css" rel="stylesheet" />
<link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" /> <link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" />
<script src="/scripts/jquery.lazy.min.js"></script> <script src="/scripts/jquery.lazy.min.js"></script>

View File

@@ -71,11 +71,11 @@
</td> </td>
<td> <td>
<span id="collection_directorylayout_gaseous_label" name="collection_directorylayout_label"> <span id="collection_directorylayout_gaseous_label" name="collection_directorylayout_label">
<p>Standard layout: /&lt;IGDB Platform Slug&gt;/&lt;IGDB Game Slug&gt;/Game ROM's</p> <p>Standard layout: /&lt;IGDB Platform Slug&gt;/&lt;IGDB Game Slug&gt;/Game ROMs</p>
<p>Example: /genesis-slash-megadrive/sonic-the-hedgehog/Sonic the Hedgehog.smd</p> <p>Example: /genesis-slash-megadrive/sonic-the-hedgehog/Sonic the Hedgehog.smd</p>
</span> </span>
<span id="collection_directorylayout_retropie_label" style="display: none;" name="collection_directorylayout_label"> <span id="collection_directorylayout_retropie_label" style="display: none;" name="collection_directorylayout_label">
<p>RetroPie layout: /roms/&lt;RetroPie Platform Label&gt;/Game ROM's</p> <p>RetroPie layout: /roms/&lt;RetroPie Platform Label&gt;/Game ROMs</p>
<p>Example: /roms/megadrive/Sonic the Hedgehog.smd</p> <p>Example: /roms/megadrive/Sonic the Hedgehog.smd</p>
</span> </span>
</td> </td>

View File

@@ -120,9 +120,10 @@
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) { ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) {
romData = result; romData = result;
console.log(romData);
document.getElementById('modal-heading').innerHTML = result.name; document.getElementById('modal-heading').innerHTML = result.name;
document.getElementById('rominfo_library').innerHTML = result.library.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_size').innerHTML = formatBytes(result.size, 2);
document.getElementById('rominfo_type').innerHTML = getRomType(result.romType); document.getElementById('rominfo_type').innerHTML = getRomType(result.romType);
document.getElementById('rominfo_mediatype').innerHTML = result.romTypeMedia; document.getElementById('rominfo_mediatype').innerHTML = result.romTypeMedia;
@@ -132,7 +133,7 @@
document.getElementById('rominfo_signaturematch').innerHTML = result.source; document.getElementById('rominfo_signaturematch').innerHTML = result.source;
document.getElementById('rominfo_signaturetitle').innerHTML = result.signatureSourceGameTitle; document.getElementById('rominfo_signaturetitle').innerHTML = result.signatureSourceGameTitle;
document.getElementById('properties_fixplatform').innerHTML = "<option value='" + result.platform.id + "' selected='selected'>" + result.platform.name + "</option>"; document.getElementById('properties_fixplatform').innerHTML = "<option value='" + result.platformId + "' selected='selected'>" + result.platform + "</option>";
document.getElementById('properties_fixgame').innerHTML = "<option value='" + gameData.id + "' selected='selected'>" + gameData.name + "</option>"; document.getElementById('properties_fixgame').innerHTML = "<option value='" + gameData.id + "' selected='selected'>" + gameData.name + "</option>";
if (result.library.isDefaultLibrary == false) { if (result.library.isDefaultLibrary == false) {
@@ -196,40 +197,57 @@
} }
}); });
$('#properties_fixgame').select2({ $('#properties_fixplatform').on('select2:select', function (e) {
minimumInputLength: 3, var platformData = e.params.data;
templateResult: DropDownRenderGameOption, console.log(platformData);
ajax: {
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#properties_fixplatform').select2('data');
var query = { var gameValue = $('#properties_fixgame').select2('data');
PlatformId: fixplatform[0].id, if (gameValue) {
SearchString: params.term console.log(gameValue[0]);
}
// Query parameters will be ?SearchString=[term] setFixGameDropDown();
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover
});
}
return {
results: arr
};
}
} }
}); });
function setFixGameDropDown() {
$('#properties_fixgame').empty().select2({
minimumInputLength: 3,
placeholder: 'Game',
templateResult: DropDownRenderGameOption,
ajax: {
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#properties_fixplatform').select2('data');
var query = {
PlatformId: fixplatform[0].id,
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover,
releaseDate: data[i].firstReleaseDate
});
}
return {
results: arr
};
}
}
});
}
setFixGameDropDown();
function SaveFixedGame() { function SaveFixedGame() {
var fixplatform = $('#properties_fixplatform').select2('data'); var fixplatform = $('#properties_fixplatform').select2('data');
var fixgame = $('#properties_fixgame').select2('data'); var fixgame = $('#properties_fixgame').select2('data');

View File

@@ -27,6 +27,9 @@
<tr> <tr>
<td colspan="2" id="settings_users_edit_label"></td> <td colspan="2" id="settings_users_edit_label"></td>
</tr> </tr>
<tr>
<td colspan="2" id="settings_users_edit_errorlabel" style="color: red;"></td>
</tr>
</table> </table>
</div> </div>
@@ -47,7 +50,7 @@
</th> </th>
</tr> </tr>
<tr> <tr>
<th colspan="4"><h3>Games and ROM's</h3></td> <th colspan="4"><h3>Games and ROMs</h3></td>
</tr> </tr>
<tr class="romrow"> <tr class="romrow">
<td class="romcell">Play games</td> <td class="romcell">Play games</td>
@@ -339,6 +342,7 @@
var newPassword = document.getElementById('settings_users_edit_password').value; var newPassword = document.getElementById('settings_users_edit_password').value;
if (newPassword.length > 0) { if (newPassword.length > 0) {
console.log("New password value is long enough to commit to database");
var model = { var model = {
"newPassword": newPassword, "newPassword": newPassword,
"confirmPassword": newPassword "confirmPassword": newPassword
@@ -348,12 +352,10 @@
'/api/v1.1/Account/Users/' + modalVariables + '/Password', '/api/v1.1/Account/Users/' + modalVariables + '/Password',
'POST', 'POST',
function(result) { function(result) {
console.log(JSON.stringify(result)); savePasswordsCallback(result);
savePropertiesCallback();
}, },
function(error) { function(error) {
console.log(JSON.stringify(error)); savePasswordsCallback(error);
savePropertiesCallback();
}, },
JSON.stringify(model) JSON.stringify(model)
); );
@@ -366,6 +368,21 @@
} }
} }
function savePasswordsCallback(result) {
console.log(result);
if (result.succeeded == true) {
savePropertiesCallback();
} else {
var errorBox = document.getElementById('settings_users_edit_errorlabel');
errorBox.innerHTML = '';
for (var i = 0; i < result.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.errors[i].description;
errorBox.appendChild(errorMessage);
}
}
}
function savePropertiesCallback() { function savePropertiesCallback() {
GetUsers(); GetUsers();
closeDialog(); closeDialog();

View File

@@ -28,6 +28,9 @@
<tr> <tr>
<td colspan="2" id="settings_users_new_label"></td> <td colspan="2" id="settings_users_new_label"></td>
</tr> </tr>
<tr>
<td colspan="2" id="settings_users_new_errors" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="text-align: right; padding-top: 10px;"> <td colspan="2" style="text-align: right; padding-top: 10px;">
<button value="OK" id="settings_users_new_okbutton" disabled="disabled" onclick="createUser();">OK</button><button value="Cancel" onclick="closeSubDialog();">Cancel</button> <button value="OK" id="settings_users_new_okbutton" disabled="disabled" onclick="createUser();">OK</button><button value="Cancel" onclick="closeSubDialog();">Cancel</button>
@@ -75,14 +78,27 @@
'/api/v1.1/Account/Users', '/api/v1.1/Account/Users',
'POST', 'POST',
function(result) { function(result) {
GetUsers(); createUserCallback(result);
closeSubDialog();
}, },
function(error) { function(error) {
GetUsers(); createUserCallback(result);
closeSubDialog();
}, },
JSON.stringify(model) JSON.stringify(model)
); );
} }
function createUserCallback(result) {
if (result.succeeded == true) {
GetUsers();
closeSubDialog();
} else {
var errorBox = document.getElementById('settings_users_new_errors');
errorBox.innerHTML = '';
for (var i = 0; i < result.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.errors[i].description;
errorBox.appendChild(errorMessage);
}
}
}
</script> </script>

View File

@@ -1,8 +1,79 @@
<div id="properties_toc"> <div id="properties_toc">
<div id="properties_profile_toc_general" name="properties_profile_toc_item" onclick="ProfileSelectTab('general');">Account</div> <div id="properties_profile_toc_general" name="properties_profile_toc_item" onclick="ProfileSelectTab('general');">Preferences</div>
<div id="properties_profile_toc_account" name="properties_profile_toc_item" onclick="ProfileSelectTab('account');">Account</div>
</div> </div>
<div id="properties_bodypanel"> <div id="properties_bodypanel">
<div id="properties_bodypanel_general" name="properties_profile_tab" style="display: none;"> <div id="properties_bodypanel_general" name="properties_profile_tab" style="display: none;">
<h3>Game Library</h3>
<table style="width: 100%;">
<tr>
<th>
Library
</th>
</tr>
<tr>
<td>
Pagination mode:
</td>
</tr>
<tr>
<td>
<select id="profile_pref-LibraryPagination" data-pref="LibraryPagination" onchange="SavePrefValue_Value(this);">
<option value="paged">Paged</option>
<option value="infinite">Infinite scrolling</option>
</select>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<th>
Game Icons
</th>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameTitle" data-pref="LibraryShowGameTitle" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameTitle"> Show title</label>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameRating" data-pref="LibraryShowGameRating" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameRating"> Show rating</label>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameClassification" data-pref="LibraryShowGameClassification" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameClassification"> Show age classification badges</label>
</td>
</tr>
<tr>
<td>
<table id="profile_pref_LibraryClassificationBadgeSelect">
<tr>
<td>Use classification badges from:</td>
</tr>
<tr>
<td>
<select id="profile_pref_LibraryPrimaryClassificationBadge" data-primary="primary" onchange="SavePrefValue_ClassBadge(this);">
</select>
</td>
</tr>
<tr>
<td>Fallback to classification badges from:</td>
</tr>
<tr>
<td>
<select id="profile_pref_LibraryFallbackClassificationBadge" onchange="SavePrefValue_ClassBadge(this);">
</select>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div id="properties_bodypanel_account" name="properties_profile_tab" style="display: none;">
<h3>Reset Password</h3> <h3>Reset Password</h3>
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tr>
@@ -20,6 +91,9 @@
<tr> <tr>
<td colspan="2" id="profile_passwordnotice"></td> <td colspan="2" id="profile_passwordnotice"></td>
</tr> </tr>
<tr>
<td colspan="2" id="profile_passworderrors" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="text-align: right;"> <td colspan="2" style="text-align: right;">
<button id="profile_resetpassword" value="Reset Password" disabled="disabled" onclick="ResetPassword();">Reset Password</button> <button id="profile_resetpassword" value="Reset Password" disabled="disabled" onclick="ResetPassword();">Reset Password</button>
@@ -52,6 +126,125 @@
} }
} }
function GetPrefInitialValues() {
var paginationMode = document.getElementById('profile_pref-LibraryPagination');
paginationMode.value = GetPreference('LibraryPagination', 'paged');
ConfigurePrefInitialValue_Checkbox("LibraryShowGameTitle", GetPreference("LibraryShowGameTitle", true));
ConfigurePrefInitialValue_Checkbox("LibraryShowGameRating", GetPreference("LibraryShowGameRating", true));
ConfigurePrefInitialValue_Checkbox("LibraryShowGameClassification", GetPreference("LibraryShowGameClassification", true));
var primary = document.getElementById('profile_pref_LibraryPrimaryClassificationBadge');
var secondary = document.getElementById('profile_pref_LibraryFallbackClassificationBadge');
PopulateClassificationMenus(primary);
PopulateClassificationMenus(secondary, true);
var classificationOrder = JSON.parse(GetPreference("LibraryGameClassificationDisplayOrder", JSON.stringify([ "ESRB" ])));
primary.value = classificationOrder[0];
if (classificationOrder[1]) {
secondary.value = classificationOrder[1];
}
for (var i = 0; i < secondary.childNodes.length; i++) {
if (secondary.childNodes[i].value == primary.value) {
secondary.childNodes[i].setAttribute('disabled', 'disabled');
} else {
secondary.childNodes[i].removeAttribute('disabled');
}
}
}
function PopulateClassificationMenus(targetSelector, IsSecondary) {
targetSelector.innerHTML = '';
if (IsSecondary == true) {
var defaultOpt = document.createElement('option');
defaultOpt.value = '-';
defaultOpt.innerHTML = 'None';
targetSelector.appendChild(defaultOpt);
}
for (const [key, value] of Object.entries(ClassificationBoards)) {
var opt = document.createElement('option');
opt.value = key;
opt.innerHTML = value;
targetSelector.appendChild(opt);
}
}
function ConfigurePrefInitialValue_Checkbox(ValueName, ValueSetting) {
var valueCheckbox = document.getElementById("profile_pref_" + ValueName);
if (ValueSetting == "true" || ValueSetting == true) {
valueCheckbox.checked = true;
updateDisplay(ValueName, true);
} else {
valueCheckbox.checked = false;
updateDisplay(ValueName, false);
}
}
function SavePrefValue_Checkbox(e) {
var ValueName = e.getAttribute("data-pref");
SetPreference(ValueName, e.checked);
updateDisplay(ValueName, e.checked);
executeFilter1_1(1);
}
function SavePrefValue_Value(e) {
var ValueName = e.getAttribute("data-pref");
SetPreference(ValueName, e.value);
executeFilter1_1(1);
}
function updateDisplay(ValueName, ValueSetting) {
switch(ValueName) {
case "LibraryShowGameClassification":
var badgeSelector = document.getElementById("profile_pref_LibraryClassificationBadgeSelect");
if (ValueSetting == true || ValueSetting == "true") {
badgeSelector.style.display = '';
} else {
badgeSelector.style.display = 'none';
}
break;
}
}
function SavePrefValue_ClassBadge(e) {
var primary = document.getElementById('profile_pref_LibraryPrimaryClassificationBadge');
var secondary = document.getElementById('profile_pref_LibraryFallbackClassificationBadge');
if (e.getAttribute('data-primary') == 'primary') {
// reset secondary to "none" if the same board is selected in both
if (primary.value == secondary.value) {
secondary.value = '-';
}
// disable in secondary board selected in primary
for (var i = 0; i < secondary.childNodes.length; i++) {
if (secondary.childNodes[i].value == primary.value) {
secondary.childNodes[i].setAttribute('disabled', 'disabled');
} else {
secondary.childNodes[i].removeAttribute('disabled');
}
}
}
// save values
var model = [];
if (secondary.value == '-') {
model = [ primary.value ];
} else {
model = [ primary.value, secondary.value ];
}
SetPreference('LibraryGameClassificationDisplayOrder', JSON.stringify(model));
executeFilter1_1(1);
}
function checkPasswordsMatch() { function checkPasswordsMatch() {
var oldPassword = document.getElementById('profile_oldpassword').value; var oldPassword = document.getElementById('profile_oldpassword').value;
var newPassword = document.getElementById('profile_newpassword').value; var newPassword = document.getElementById('profile_newpassword').value;
@@ -106,9 +299,16 @@
function ResetPasswordCallback(result) { function ResetPasswordCallback(result) {
var errorLabel = document.getElementById('profile_passwordnotice'); var errorLabel = document.getElementById('profile_passwordnotice');
var errorBox = document.getElementById('profile_passworderrors');
errorBox.innerHTML = '';
if (result.succeeded == false) { console.log(result);
errorLabel.innerHTML = result.errors.description; if (result.responseJSON.succeeded == false) {
for (var i = 0; i < result.responseJSON.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.responseJSON.errors[i].description;
errorBox.appendChild(errorMessage);
}
} else { } else {
document.getElementById('profile_oldpassword').value = ''; document.getElementById('profile_oldpassword').value = '';
document.getElementById('profile_newpassword').value = ''; document.getElementById('profile_newpassword').value = '';
@@ -119,4 +319,5 @@
} }
ProfileSelectTab('general'); ProfileSelectTab('general');
GetPrefInitialValues();
</script> </script>

View File

@@ -63,7 +63,7 @@
<table style="width: 100%; margin-top: 20px;" cellpadding="5px"> <table style="width: 100%; margin-top: 20px;" cellpadding="5px">
<tr> <tr>
<td colspan="2" style="font-size: 18px;">Create your account.</td> <td colspan="2" style="font-size: 18px;">Create your administrator account.</td>
</tr> </tr>
<tr> <tr>
<th>Email</th> <th>Email</th>
@@ -80,6 +80,9 @@
<tr> <tr>
<td colspan="2" id="login_passwordnotice">&nbsp;</td> <td colspan="2" id="login_passwordnotice">&nbsp;</td>
</tr> </tr>
<tr>
<td colspan="2" id="login_passworderrors" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="padding-top: 20px;"> <td colspan="2" style="padding-top: 20px;">
<button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Create Account</button> <button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Create Account</button>
@@ -154,13 +157,19 @@
} }
function loginCallback(result) { function loginCallback(result) {
switch(result.status) { var errorLabel = document.getElementById('login_passwordnotice');
case 200: var errorBox = document.getElementById('login_passworderrors');
window.location.replace('/index.html'); errorBox.innerHTML = '';
break;
default: console.log(result);
// login failed if (result.succeeded == false) {
break; for (var i = 0; i < result.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.errors[i].description;
errorBox.appendChild(errorMessage);
}
} else {
window.location.replace('/index.html');
} }
} }
</script> </script>

View File

@@ -35,6 +35,9 @@
<div id="gamesummary"> <div id="gamesummary">
<div id="gamesummary_cover"></div> <div id="gamesummary_cover"></div>
<div id="gamesummary_firstrelease">
<h3>First Released</h3>
</div>
<div id="gamesumarry_genres"> <div id="gamesumarry_genres">
<h3>Genres</h3> <h3>Genres</h3>
</div> </div>
@@ -71,7 +74,7 @@
</div> </div>
<div id="gamesummaryroms"> <div id="gamesummaryroms">
<span id="rom_edit" class="romlink" onclick="DisplayROMCheckboxes(true);">Edit</span> <span id="rom_edit" class="romlink" onclick="DisplayROMCheckboxes(true);">Edit</span>
<h3>ROM's/Images</h3> <h3>ROMs/Images</h3>
<div id="rom_edit_panel" style="display: none;"> <div id="rom_edit_panel" style="display: none;">
<div id="rom_edit_panel_center"> <div id="rom_edit_panel_center">
<button id="rom_edit_delete" class="redbutton" onclick="deleteGameRoms();">Delete</button> <button id="rom_edit_delete" class="redbutton" onclick="deleteGameRoms();">Delete</button>
@@ -83,6 +86,10 @@
</div> </div>
</div> </div>
</div> </div>
<div id="gamesummarysimilar" style="display: none;">
<h3>Similar Games</h3>
<div id="gamesummarysimilarcontent"></div>
</div>
</div> </div>
</div> </div>
@@ -234,6 +241,16 @@
} }
gameSummaryCover.appendChild(gameImage); gameSummaryCover.appendChild(gameImage);
// load release date
var gameSummaryRelease = document.getElementById('gamesummary_firstrelease');
if (result.firstReleaseDate) {
var firstRelease = document.createElement('p');
firstRelease.innerHTML = '<p>' + moment(result.firstReleaseDate).format('LL') + ' (' + moment(result.firstReleaseDate).fromNow() + ')</p>';
gameSummaryRelease.appendChild(firstRelease);
} else {
gameSummaryRelease.setAttribute('style', 'display: none;');
}
// load ratings // load ratings
var gameSummaryRatings = document.getElementById('gamesummary_ratings'); var gameSummaryRatings = document.getElementById('gamesummary_ratings');
if (result.ageRatings) { if (result.ageRatings) {
@@ -344,29 +361,117 @@
gamescreenshots.setAttribute('style', 'display: none;'); gamescreenshots.setAttribute('style', 'display: none;');
} }
// load similar
var gameSummarySimilar = document.getElementById('gamesummarysimilar');
ajaxCall('/api/v1.1/Games/' + gameId + '/Related', 'GET', function (result) {
if (result.games.length > 0) {
gameSummarySimilar.removeAttribute('style');
var gameSummarySimilarContent = document.getElementById('gamesummarysimilar');
for (var i = 0; i < result.games.length; i++) {
var similarObject = renderGameIcon(result.games[i], false, false, false, null, true);
gameSummarySimilarContent.appendChild(similarObject);
}
$('.lazy').Lazy({
scrollDirection: 'vertical',
effect: 'fadeIn',
visibleOnly: true
});
} else {
gameSummarySimilar.setAttribute('style', 'display: none;');
}
});
// load roms // load roms
loadRoms(); loadRoms(false);
}); });
function loadRoms(displayCheckboxes) { function loadRoms(displayCheckboxes, pageNumber, selectedPlatform) {
if (!pageNumber) {
pageNumber = 1;
}
if (selectedPlatform == undefined) {
selectedPlatform = -1;
}
console.log(selectedPlatform);
var filterControlBlock = document.getElementById('games_library_controls');
if (filterControlBlock) {
filterControlBlock.remove();
}
var existingTable = document.getElementById('romtable'); var existingTable = document.getElementById('romtable');
if (existingTable) { if (existingTable) {
existingTable.remove(); existingTable.remove();
} }
var romPager = document.getElementById('romPaginator');
if (romPager) {
romPager.remove();
}
var existingMgTable = document.getElementById('mediagrouptable'); var existingMgTable = document.getElementById('mediagrouptable');
if (existingMgTable) { if (existingMgTable) {
existingMgTable.remove(); existingMgTable.remove();
} }
if (displayCheckboxes == undefined) {
if (document.getElementById('rom_edit_panel').style.display == 'none') {
displayCheckboxes = false;
} else {
displayCheckboxes = true;
}
}
var gameRoms = document.getElementById('gamesummaryroms'); var gameRoms = document.getElementById('gamesummaryroms');
ajaxCall('/api/v1.1/Games/' + gameId + '/roms', 'GET', function (result) { var pageSize = 20;
ajaxCall('/api/v1.1/Games/' + gameId + '/roms?pageNumber=' + pageNumber + '&pageSize=' + pageSize + '&platformId=' + selectedPlatform, 'GET', function (result) {
// display filter tools
var filterControls = document.createElement('div');
filterControls.id = "games_library_controls";
var platformFilterBlock = document.createElement('div');
platformFilterBlock.className = 'games_library_controlblock';
var platformFilterOpt = document.createElement('select');
platformFilterOpt.id = "platform_filter";
platformFilterOpt.setAttribute('onchange', 'loadRoms(' + undefined + ', ' + 1 + ', Number(document.getElementById("platform_filter").value));');
var platformFilterOptDefault = document.createElement('option');
platformFilterOptDefault.value = '-1';
platformFilterOptDefault.innerHTML = 'All Platforms';
if (selectedPlatform == -1) {
platformFilterOptDefault.selected = 'selected';
}
platformFilterOpt.appendChild(platformFilterOptDefault);
for (var i = 0; i < result.platforms.length; i++) {
var platformFilterOptValue = document.createElement('option');
platformFilterOptValue.value = result.platforms[i].key;
platformFilterOptValue.innerHTML = result.platforms[i].value;
if (selectedPlatform == Number(result.platforms[i].key)) {
platformFilterOptValue.selected = 'selected';
}
platformFilterOpt.appendChild(platformFilterOptValue);
}
platformFilterBlock.appendChild(platformFilterOpt);
filterControls.appendChild(platformFilterBlock);
var romCounter = document.createElement('div');
romCounter.className = 'games_library_controlblock';
romCounter.innerHTML = result.count + ' ROMs';
filterControls.appendChild(romCounter);
gameRoms.appendChild(filterControls);
if (result.gameRomItems) { if (result.gameRomItems) {
var gameRomItems = result.gameRomItems; var gameRomItems = result.gameRomItems;
var mediaGroups = result.mediaGroups; var mediaGroups = result.mediaGroups;
gameRomItems.sort((a, b) => a.platform.name.charCodeAt(0) - b.platform.name.charCodeAt(0)); // display roms
var newTable = document.createElement('table'); var newTable = document.createElement('table');
newTable.id = 'romtable'; newTable.id = 'romtable';
newTable.className = 'romtable'; newTable.className = 'romtable';
@@ -375,23 +480,21 @@
var lastPlatform = ''; var lastPlatform = '';
for (var i = 0; i < gameRomItems.length; i++) { for (var i = 0; i < gameRomItems.length; i++) {
if (gameRomItems[i].platform.name != lastPlatform) { if (gameRomItems[i].platform != lastPlatform) {
lastPlatform = gameRomItems[i].platform.name; lastPlatform = gameRomItems[i].platform;
var platformRow = document.createElement('tr'); var platformRow = document.createElement('tr');
var platformHeader = document.createElement('th'); var platformHeader = document.createElement('th');
platformHeader.setAttribute('colspan', 6); platformHeader.setAttribute('colspan', 6);
platformHeader.innerHTML = '<a href="#" onclick="ShowPlatformMappingDialog(' + gameRomItems[i].platform.id + ');" style="float: right; text-decoration: none;" class="romlink"><img src="/images/map.svg" class="banner_button_image banner_button_image_smaller" alt="Edit platform mapping" title="Edit platform mapping" /></a><a href="#" onclick="ShowCollectionDialog(' + gameRomItems[i].platform.id + ');" style="float: right; text-decoration: none;" class="romlink"><img src="/images/collections.svg" class="banner_button_image banner_button_image_smaller" alt="Add to collection" title="Add to collection" /></a>' + gameRomItems[i].platform.name; platformHeader.innerHTML = '<a href="#" onclick="ShowPlatformMappingDialog(' + gameRomItems[i].platformId + ');" style="float: right; text-decoration: none;" class="romlink"><img src="/images/map.svg" class="banner_button_image banner_button_image_smaller" alt="Edit platform mapping" title="Edit platform mapping" /></a><a href="#" onclick="ShowCollectionDialog(' + gameRomItems[i].platformId + ');" style="float: right; text-decoration: none;" class="romlink"><img src="/images/collections.svg" class="banner_button_image banner_button_image_smaller" alt="Add to collection" title="Add to collection" /></a>' + gameRomItems[i].platform;
platformRow.appendChild(platformHeader); platformRow.appendChild(platformHeader);
newTable.appendChild(platformRow); newTable.appendChild(platformRow);
} }
var launchButton = ''; var launchButton = '';
if (result.gameRomItems[i].emulator) { if (result.gameRomItems[i].emulator) {
if (gameRomItems[i].emulator.type) { if (gameRomItems[i].emulator.type) {
if (gameRomItems[i].emulator.type.length > 0) { if (gameRomItems[i].emulator.type.length > 0) {
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItems[i].emulator.type + '&core=' + gameRomItems[i].emulator.core + '&platformid=' + gameRomItems[i].platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name)) + '" class="romstart">Launch</a>'; launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItems[i].emulator.type + '&core=' + gameRomItems[i].emulator.core + '&platformid=' + gameRomItems[i].platformId + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name)) + '" class="romstart">Launch</a>';
} }
} }
} }
@@ -413,7 +516,52 @@
if (displayCheckboxes == true) { if (displayCheckboxes == true) {
DisplayROMCheckboxes(true); DisplayROMCheckboxes(true);
} }
if (result.count > pageSize) {
// draw pagination
var numOfPages = Math.ceil(result.count / pageSize);
var romPaginator = document.createElement('div');
romPaginator.id = 'romPaginator';
romPaginator.className = 'rom_pager';
// draw previous page button
var prevPage = document.createElement('span');
prevPage.className = 'rom_pager_number_disabled';
prevPage.innerHTML = '&lt;';
if (pageNumber != 1) {
prevPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (pageNumber - 1) + ', ' + selectedPlatform + ');');
prevPage.className = 'rom_pager_number';
}
romPaginator.appendChild(prevPage);
// draw page numbers
for (var i = 0; i < numOfPages; i++) {
var romPaginatorPage = document.createElement('span');
romPaginatorPage.className = 'rom_pager_number_disabled';
romPaginatorPage.innerHTML = (i + 1);
if ((i + 1) != pageNumber) {
romPaginatorPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (i + 1) + ', ' + selectedPlatform + ');');
romPaginatorPage.className = 'rom_pager_number';
}
romPaginator.appendChild(romPaginatorPage);
}
// draw next page button
var nextPage = document.createElement('span');
nextPage.className = 'rom_pager_number_disabled';
nextPage.innerHTML = '&gt;';
if (pageNumber != numOfPages) {
nextPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (pageNumber + 1) + ', ' + selectedPlatform + ');');
nextPage.className = 'rom_pager_number';
}
romPaginator.appendChild(nextPage);
gameRoms.appendChild(romPaginator);
}
// display media groups
var mediaGroupDiv = document.getElementById('gamesummarymediagroups'); var mediaGroupDiv = document.getElementById('gamesummarymediagroups');
if (mediaGroups.length == 0) { if (mediaGroups.length == 0) {
mediaGroupDiv.style.display = 'none'; mediaGroupDiv.style.display = 'none';
@@ -704,41 +852,57 @@
} }
}); });
$('#rom_edit_fixgame').select2({ $('#rom_edit_fixplatform').on('select2:select', function (e) {
minimumInputLength: 3, var platformData = e.params.data;
templateResult: DropDownRenderGameOption, console.log(platformData);
placeholder: "Game",
ajax: {
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#rom_edit_fixplatform').select2('data');
var query = { var gameValue = $('#rom_edit_fixgame').select2('data');
PlatformId: fixplatform[0].id, if (gameValue) {
SearchString: params.term console.log(gameValue[0]);
}
// Query parameters will be ?SearchString=[term] setRomFixGameDropDown();
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover
});
}
return {
results: arr
};
}
} }
}); });
function setRomFixGameDropDown() {
$('#rom_edit_fixgame').empty().select2({
minimumInputLength: 3,
templateResult: DropDownRenderGameOption,
placeholder: "Game",
ajax: {
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#rom_edit_fixplatform').select2('data');
var query = {
PlatformId: fixplatform[0].id,
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover,
releaseDate: data[i].firstReleaseDate
});
}
return {
results: arr
};
}
}
});
}
setRomFixGameDropDown();
var remapCallCounter = 0; var remapCallCounter = 0;
var remapCallCounterMax = 0; var remapCallCounterMax = 0;

View File

@@ -2,9 +2,34 @@
<div id="bgImage_Opacity"></div> <div id="bgImage_Opacity"></div>
</div> </div>
<div id="games_home"> <div id="games_filter_scroller">
<div id="games_filter"></div> <div id="games_filter"></div>
<div id="games_library"></div> </div>
<div id="games_home">
<div id="games_home_box">
<div id="games_library_controls">
<div id="games_library_orderby" class="games_library_controlblock">
<span>Order by: </span>
<select id="games_library_orderby_select" onchange="executeFilter1_1();">
<option selected="selected" value="NameThe">Name, The</option>
<option value="Name">Name</option>
<option value="Rating">User Rating</option>
<option value="RatingCount">User Rating Count</option>
</select>
<select id="games_library_orderby_direction_select" onchange="executeFilter1_1();">
<option selected="selected" value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
</select>
</div>
<div id="games_library_recordcount" class="games_library_controlblock"></div>
</div>
<div id="games_library"></div>
<div id="games_library_pagerstore" style="display: none;">0</div>
</div>
</div>
<div id="games_pager" style="display: none;">
&lt; 1 2 3 4 5 &gt;
</div> </div>
<script type="text/javascript"> <script type="text/javascript">

View File

@@ -66,6 +66,9 @@
<input type="checkbox" id="login_rememberme"> <label for="login_rememberme">Remember Me</label> <input type="checkbox" id="login_rememberme"> <label for="login_rememberme">Remember Me</label>
</td> </td>
</tr> </tr>
<tr>
<td colspan="2" id="login_errorlabel" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="padding-top: 20px;"> <td colspan="2" style="padding-top: 20px;">
<button type="button" value="Sign In" onclick="UserLogin();" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Sign In</button> <button type="button" value="Sign In" onclick="UserLogin();" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Sign In</button>
@@ -120,6 +123,7 @@
break; break;
default: default:
// login failed // login failed
document.getElementById('login_errorlabel').innerHTML = 'Incorrect password';
break; break;
} }
} }

View File

@@ -47,6 +47,10 @@
SelectTab(selectedTab); SelectTab(selectedTab);
function SelectTab(TabName) { function SelectTab(TabName) {
if (selectedTab != TabName) {
window.location.href = '/index.html?page=settings&sub=' + TabName;
}
var tocs = document.getElementsByName('properties_toc_item'); var tocs = document.getElementsByName('properties_toc_item');
for (var i = 0; i < tocs.length; i++) { for (var i = 0; i < tocs.length; i++) {
if ((tocs[i].id) == ("properties_toc_" + TabName)) { if ((tocs[i].id) == ("properties_toc_" + TabName)) {

View File

@@ -2,60 +2,121 @@
<h1 id="gametitle_label">Firmware</h1> <h1 id="gametitle_label">Firmware</h1>
</div> </div>
<h3>Firmware Availablility</h3> <h3>Firmware Availablility <span id="firmware_totalcount" style="float: right;"></span></h3>
<p>
Display:
<ul style="list-style-type:none;">
<li><input type="checkbox" id="firmware_showavailable" checked="checked" onclick="displayFirmwareList();"/><label for="firmware_showavailable"> Available</label></li>
<li><input type="checkbox" id="firmware_showunavailable" checked="checked" onclick="displayFirmwareList();"/><label for="firmware_showunavailable"> Unavailable</label></li>
</ul>
</p>
<table id="table_firmware" class="romtable" cellspacing="0"> <table id="table_firmware" class="romtable" cellspacing="0">
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
var biosDict = {};
ajaxCall('/api/v1.1/Bios', 'GET', function (result) { ajaxCall('/api/v1.1/Bios', 'GET', function (result) {
result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0)); result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0));
// sort into a dictionary
for (var i = 0; i < result.length; i++) {
var tempArray = [];
if (biosDict.hasOwnProperty(result[i].platformname)) {
tempArray = biosDict[result[i].platformname];
tempArray.push(result[i]);
} else {
tempArray.push(result[i]);
biosDict[result[i].platformname] = tempArray;
}
biosDict[result[i].platformname] = tempArray;
}
console.log(biosDict);
displayFirmwareList();
});
function displayFirmwareList() {
var lastPlatform = ''; var lastPlatform = '';
var newTable = document.getElementById('table_firmware'); var newTable = document.getElementById('table_firmware');
newTable.innerHTML = '';
newTable.appendChild(createTableRow(true, ['Description', 'File name', 'MD5 Hash', 'Available'])); newTable.appendChild(createTableRow(true, ['Description', 'File name', 'MD5 Hash', 'Available']));
for (var i = 0; i < result.length; i++) { var totalAvailable = 0;
if (result[i].platformname != lastPlatform) { var totalCount = 0;
lastPlatform = result[i].platformname;
var platformRow = document.createElement('tr'); for (const [key, value] of Object.entries(biosDict)) {
var platformHeader = document.createElement('th'); // new platform - show a header
platformHeader.setAttribute('colspan', 4); var platformRow = document.createElement('tr');
platformHeader.innerHTML = result[i].platformname; var platformHeader = document.createElement('th');
platformRow.appendChild(platformHeader); platformHeader.setAttribute('colspan', 4);
newTable.appendChild(platformRow);
var platformHeaderValue = document.createElement('span');
platformHeaderValue.innerHTML = key;
platformHeader.appendChild(platformHeaderValue);
var platformHeaderCounter = document.createElement('span');
platformHeaderCounter.style.float = 'right';
platformHeader.appendChild(platformHeaderCounter);
platformRow.appendChild(platformHeader);
newTable.appendChild(platformRow);
var totalPlatformAvailable = 0;
var showAvailable = document.getElementById('firmware_showavailable').checked;
var showUnavailable = document.getElementById('firmware_showunavailable').checked;
for (var i = 0; i < value.length; i++) {
// update counters
if (value[i].available == true) {
totalAvailable += 1;
totalPlatformAvailable += 1;
}
if (
(value[i].available == true && showAvailable == true) ||
(value[i].available == false && showUnavailable == true)
) {
var biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.1/Bios/' + value[i].platformid + '/' + value[i].filename;
biosFilename.innerHTML = value[i].filename;
biosFilename.className = 'romlink';
var availableText = document.createElement('span');
if (value[i].available == true) {
availableText.innerHTML = 'Available';
availableText.className = 'greentext';
biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.1/Bios/' + value[i].platformid + '/' + value[i].filename;
biosFilename.innerHTML = value[i].filename;
biosFilename.className = 'romlink';
} else {
availableText.innerHTML = 'Unavailable';
availableText.className = 'redtext';
biosFilename = document.createElement('span');
biosFilename.innerHTML = value[i].filename;
}
var newRow = [
value[i].description,
biosFilename,
value[i].hash,
availableText
];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
}
totalCount += 1;
} }
var biosFilename = document.createElement('a'); platformHeaderCounter.innerHTML = totalPlatformAvailable + ' / ' + value.length + ' available';
biosFilename.href = '/api/v1.1/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.innerHTML = result[i].filename;
biosFilename.className = 'romlink';
var availableText = document.createElement('span');
if (result[i].available == true) {
availableText.innerHTML = 'Available';
availableText.className = 'greentext';
biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.1/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.innerHTML = result[i].filename;
biosFilename.className = 'romlink';
} else {
availableText.innerHTML = 'Unavailable';
availableText.className = 'redtext';
biosFilename = document.createElement('span');
biosFilename.innerHTML = result[i].filename;
}
var newRow = [
result[i].description,
biosFilename,
result[i].hash,
availableText
];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
} }
});
document.getElementById('firmware_totalcount').innerHTML = totalAvailable + ' / ' + totalCount + ' available';
}
</script> </script>

View File

@@ -2,7 +2,38 @@
<h1 id="gametitle_label">Logs</h1> <h1 id="gametitle_label">Logs</h1>
</div> </div>
<a href="#" class="romlink" onclick="loadLogs();" style="float: right;"><img src="/images/refresh.svg" alt="Refresh" title="Refresh" class="banner_button_image" /></a> <table style="width: 960px; max-width: 960px;" cellspacing="0">
<tr>
<td>
<input type="datetime-local" id="logs_startdate" style="width: 30%;" /> <input type="datetime-local" id="logs_enddate" style="width: 30%;" />
</td>
<td>
<input type="checkbox" id="logs_type_info"><label for="logs_type_info">Information</label>
</td>
<td>
<input type="checkbox" id="logs_type_warning"><label for="logs_type_warning">Warning</label>
</td>
<td>
<input type="checkbox" id="logs_type_critical"><label for="logs_type_critical">Critical</label>
</td>
</tr>
<tr>
<td colspan="1">
<input type="text" id="logs_textsearch" placeholder="Search" style="width: 75%;" />
</td>
<td colspan="3">
<input type="text" id="logs_correlationid" placeholder="Correlation Id" style="width: 75%;" />
</td>
</tr>
<tr>
<td colspan="4" style="text-align: right;">
<button onclick="loadLogs();">Search</button>
<button onclick="resetFilters();">Reset</button>
</td>
</tr>
</table>
<!-- <a href="#" class="romlink" onclick="loadLogs();" style="float: right;"><img src="/images/refresh.svg" alt="Refresh" title="Refresh" class="banner_button_image" /></a> -->
<table id="settings_events_table" style="width: 960px; max-width: 960px;" cellspacing="0"> <table id="settings_events_table" style="width: 960px; max-width: 960px;" cellspacing="0">
</table> </table>
@@ -14,20 +45,76 @@
<script type="text/javascript"> <script type="text/javascript">
var lastStartIndex = 0; var lastStartIndex = 0;
var currentPage = 1; var currentPage = 1;
var searchModel = {};
var correlationIdParam = getQueryString('correlationid', 'string');
if (correlationIdParam) {
if (correlationIdParam.length > 0) {
document.getElementById('logs_correlationid').value = correlationIdParam;
}
}
function resetFilters() {
document.getElementById('logs_startdate').value = '';
document.getElementById('logs_enddate').value = '';
document.getElementById('logs_type_info').checked = false;
document.getElementById('logs_type_warning').checked = false;
document.getElementById('logs_type_critical').checked = false;
document.getElementById('logs_textsearch').value = '';
document.getElementById('logs_correlationid').value = '';
loadLogs();
}
function loadLogs(StartIndex, PageNumber) { function loadLogs(StartIndex, PageNumber) {
var apiQuery = ''; var model = {}
if (StartIndex && PageNumber) { if (StartIndex && PageNumber) {
currentPage += 1; currentPage += 1;
apiQuery = '?StartIndex=' + StartIndex + '&PageNumber=' + PageNumber;
// get saved search model
model = searchModel;
model.StartIndex = StartIndex;
model.PageNumber = PageNumber;
} else { } else {
currentPage = 1; currentPage = 1;
// create search model
var statusList = [];
if (document.getElementById('logs_type_info').checked == true) { statusList.push(0); }
if (document.getElementById('logs_type_warning').checked == true) { statusList.push(2); }
if (document.getElementById('logs_type_critical').checked == true) { statusList.push(3); }
var startDate = null;
var startDateObj = document.getElementById('logs_startdate');
if (startDateObj.value != null) { startDate = new Date(startDateObj.value); }
var endDate = null;
var endDateObj = document.getElementById('logs_enddate');
if (endDateObj.value != null) { endDate = new Date(endDateObj.value); }
var searchText = null;
var searchTextObj = document.getElementById('logs_textsearch');
if (searchTextObj.value != null) { searchText = searchTextObj.value; }
var correlationId = null;
var correlationIdTextObj = document.getElementById('logs_correlationid');
if (correlationIdTextObj.value != null) { correlationId = correlationIdTextObj.value; }
model = {
"StartIndex": StartIndex,
"PageNumber": PageNumber,
"PageSize": 100,
"Status": statusList,
"StartDateTime": startDate,
"EndDateTime": endDate,
"SearchText": searchText,
"CorrelationId": correlationId
}
searchModel = model;
} }
console.log(model);
ajaxCall( ajaxCall(
'/api/v1.1/Logs' + apiQuery, '/api/v1.1/Logs',
'GET', 'POST',
function (result) { function (result) {
var newTable = document.getElementById('settings_events_table'); var newTable = document.getElementById('settings_events_table');
if (currentPage == 1) { if (currentPage == 1) {
@@ -52,18 +139,25 @@
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
lastStartIndex = result[i].id; lastStartIndex = result[i].id;
console.log(result[i]);
var surroundingRow = document.createElement('tbody');
surroundingRow.setAttribute('colspan', 4);
surroundingRow.className = 'logs_table_row_' + result[i].eventType;
var newRow = [ var newRow = [
//result[i].id, moment(result[i].eventTime).format("YYYY-MM-DD h:mm:ss a"),
moment(result[i].eventTime).format("YYYY-MM-DD H:mm:ss"),
result[i].eventType, result[i].eventType,
result[i].process, result[i].process,
result[i].message result[i].message
]; ];
newTable.appendChild(createTableRow(false, newRow, 'romrow logs_table_row_' + result[i].eventType, 'romcell logs_table_cell')); surroundingRow.appendChild(createTableRow(false, newRow, '', 'romcell logs_table_cell'));
// exception
var exceptionString = '';
if (result[i].exceptionValue) { if (result[i].exceptionValue) {
var exceptionString = "<h3>Exception</h3><pre class='logs_table_exception' style='width: 795px; word-wrap: break-word; overflow-wrap: break-word; overflow-y: scroll;'>" + syntaxHighlight(JSON.stringify(result[i].exceptionValue, null, 2)).replace(/\\n/g, "<br /> ") + "</pre>"; exceptionString = "<strong>Exception</strong><pre class='logs_table_exception' style='width: 795px; word-wrap: break-word; overflow-wrap: break-word; overflow-y: scroll;'>" + syntaxHighlight(JSON.stringify(result[i].exceptionValue, null, 2)).replace(/\\n/g, "<br /> ") + "</pre>";
var exRow = document.createElement('tr'); var exRow = document.createElement('tr');
var leadCell = document.createElement('td'); var leadCell = document.createElement('td');
exRow.appendChild(leadCell); exRow.appendChild(leadCell);
@@ -71,10 +165,65 @@
exCell.colSpan = '3'; exCell.colSpan = '3';
exCell.innerHTML = exceptionString; exCell.innerHTML = exceptionString;
exRow.appendChild(exCell); exRow.appendChild(exCell);
newTable.appendChild(exRow); surroundingRow.appendChild(exRow);
} }
// calling process
var infoRow = document.createElement('tr');
var infoRowEmptyCell = document.createElement('td');
infoRowEmptyCell.className = 'romcell';
var infoRowDataCell = document.createElement('td');
infoRowDataCell.className = 'romcell';
infoRowDataCell.setAttribute('colspan', 3);
infoRowDataCell.innerHTML = '<strong>Calling process:</strong> ' + result[i].callingProcess;
infoRow.appendChild(infoRowEmptyCell);
infoRow.appendChild(infoRowDataCell);
surroundingRow.appendChild(infoRow);
// initiated by user
if (result[i].callingUser) {
if (result[i].callingUser.length > 0) {
var infoRow3 = document.createElement('tr');
var infoRowEmptyCell3 = document.createElement('td');
infoRowEmptyCell3.className = 'romcell';
var infoRowDataCell3 = document.createElement('td');
infoRowDataCell3.className = 'romcell';
infoRowDataCell3.setAttribute('colspan', 3);
infoRowDataCell3.innerHTML = '<strong>User:</strong> ' + result[i].callingUser + "</a>";
infoRow3.appendChild(infoRowEmptyCell3);
infoRow3.appendChild(infoRowDataCell3);
surroundingRow.appendChild(infoRow3);
}
}
// correlation id
var infoRow2 = document.createElement('tr');
var infoRowEmptyCell2 = document.createElement('td');
infoRowEmptyCell2.className = 'romcell';
var infoRowDataCell2 = document.createElement('td');
infoRowDataCell2.className = 'romcell';
infoRowDataCell2.setAttribute('colspan', 3);
infoRowDataCell2.innerHTML = '<strong>Correlation Id:</strong> <a class="romlink" href="/index.html?page=settings&sub=logs&correlationid=' + result[i].correlationId + '">' + result[i].correlationId + "</a>";
infoRow2.appendChild(infoRowEmptyCell2);
infoRow2.appendChild(infoRowDataCell2);
surroundingRow.appendChild(infoRow2);
newTable.appendChild(surroundingRow);
} }
} },
function (error) {
},
JSON.stringify(model)
); );
} }

View File

@@ -8,6 +8,49 @@
</table> </table>
<div style="text-align: right;"><button id="settings_newlibrary" onclick="showSubDialog('librarynew');">New Library</button></div> <div style="text-align: right;"><button id="settings_newlibrary" onclick="showSubDialog('librarynew');">New Library</button></div>
<h2>Advanced Settings</h2>
<p><strong>Warning</strong> Do not modify the below settings unless you know what you're doing.</p>
<h3>Background Task Timers</h3>
<p>All intervals are in minutes.</p>
<table id="settings_tasktimers" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_tasktimers_default" onclick="defaultTaskTimers();">Reset to Default</button><button id="settings_tasktimers_new" onclick="saveTaskTimers();">Save</button></div>
<h3>Logging</h3>
<table cellspacing="0" style="width: 100%;">
<tr>
<th>
Write logs
</th>
<td>
<input type="radio" name="settings_logs_write" id="settings_logs_write_db" value="false" checked="checked"><label for="settings_logs_write_db"> To database only (default)</label>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="radio" name="settings_logs_write" id="settings_logs_write_fs" value="true"><label for="settings_logs_write_fs"> To database and disk</label>
</td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<th>
Minimum log retention (days):
</th>
<td>
<input type="number" min="1" id="settings_logs_retention" />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<button id="settings_tasktimers_new" onclick="setLoggingSettings();">Save</button>
</td>
</tr>
</table>
<script type="text/javascript"> <script type="text/javascript">
function drawLibrary() { function drawLibrary() {
ajaxCall( ajaxCall(
@@ -59,5 +102,116 @@
); );
} }
function getBackgroundTaskTimers() {
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Intervals',
'GET',
function(result) {
var targetTable = document.getElementById('settings_tasktimers');
targetTable.innerHTML = '';
targetTable.appendChild(
createTableRow(true, ['Background Task', 'Timer Interval', 'Default Interval', 'Minimum Allowed Interval'])
);
for (const [key, value] of Object.entries(result)) {
var newTableRow = createTableRow(
false,
[
GetTaskFriendlyName(value.task),
'<input id="settings_tasktimers_' + value.task + '" name="settings_tasktimers_values" data-name="' + value.task + '" data-default="' + value.defaultInterval + '" type="number" placeholder="0" min="' + value.minimumAllowedValue + '" value="' + value.interval + '" />',
value.defaultInterval,
value.minimumAllowedValue
],
'romrow',
'romcell'
);
targetTable.appendChild(newTableRow);
}
}
);
}
function saveTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_values');
var model = {};
for (var i = 0; i < timerValues.length; i++) {
model[timerValues[i].getAttribute('data-name')] = timerValues[i].value;
}
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Intervals',
'POST',
function(result) {
getBackgroundTaskTimers();
},
function(error) {
getBackgroundTaskTimers();
},
JSON.stringify(model)
);
}
function defaultTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_values');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].value = timerValues[i].getAttribute('data-default');
}
saveTaskTimers();
}
function getLoggingSettings() {
ajaxCall(
'/api/v1/System/Settings/System',
'GET',
function(result) {
var optionToSelect = 'settings_logs_write_db';
if (result.alwaysLogToDisk == true) {
optionToSelect = 'settings_logs_write_fs';
}
document.getElementById(optionToSelect).checked = true;
document.getElementById('settings_logs_retention').value = result.minimumLogRetentionPeriod;
}
);
}
function setLoggingSettings() {
var alwaysLogToDisk = false;
if ($("input[type='radio'][name='settings_logs_write']:checked").val() == "true") {
alwaysLogToDisk = true;
}
var retention = document.getElementById('settings_logs_retention');
var retentionValue = 0;
if (retention.value) {
retentionValue = retention.value;
} else {
retentionValue = 7;
}
var model = {
"alwaysLogToDisk": alwaysLogToDisk,
"minimumLogRetentionPeriod": retentionValue
};
ajaxCall(
'/api/v1/System/Settings/System',
'POST',
function(result) {
getLoggingSettings();
},
function(error) {
getLoggingSettings();
},
JSON.stringify(model)
);
}
drawLibrary(); drawLibrary();
getBackgroundTaskTimers();
getLoggingSettings();
</script> </script>

View File

@@ -25,92 +25,90 @@
<h3>Signatures</h3> <h3>Signatures</h3>
<div id="system_signatures"></div> <div id="system_signatures"></div>
<script type="text/javascript">function SystemLoadStatus() { <script type="text/javascript">
function SystemLoadStatus() {
ajaxCall('/api/v1.1/BackgroundTasks', 'GET', function (result) { ajaxCall('/api/v1.1/BackgroundTasks', 'GET', function (result) {
var newTable = document.createElement('table'); var newTable = document.createElement('table');
newTable.className = 'romtable'; newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0); newTable.setAttribute('cellspacing', 0);
newTable.appendChild(createTableRow(true, ['Task', 'Status', 'Interval', 'Last Run Time', 'Next Run Time', ''])); newTable.appendChild(createTableRow(true, ['Task', 'Status', 'Interval<br/>(minutes)', 'Last Run Duration<br />(hh:mm:ss)', '', 'Last Run Start', 'Next Run Start', '']));
if (result) { if (result) {
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
var itemTypeName; if (result[i].itemState != "Disabled") {
switch (result[i].itemType) { var itemTypeName = GetTaskFriendlyName(result[i].itemType, result[i].options);
case 'SignatureIngestor':
itemTypeName = "Signature import"; var itemStateName;
break; var itemLastStart;
case 'TitleIngestor':
itemTypeName = "Title import"; var hasError = "";
break; if (result[i].hasErrors) {
case 'MetadataRefresh': if (result[i].hasErrors.errorType != null) {
itemTypeName = "Metadata refresh" hasError = " (" + result[i].hasErrors.errorType + ")";
break; }
case 'OrganiseLibrary':
itemTypeName = "Organise library";
break;
case 'LibraryScan':
itemTypeName = "Library scan";
break;
case 'CollectionCompiler':
itemTypeName = "Compress collection id: " + result[i].options;
break;
case 'BackgroundDatabaseUpgrade':
itemTypeName = "Background database upgrade";
break;
default:
itemTypeName = result[i].itemType;
break;
}
var itemStateName;
var itemLastStart;
if (result[i].isBlocked == false) {
switch (result[i].itemState) {
case 'NeverStarted':
itemStateName = "Never started";
itemLastStart = '-';
break;
case 'Stopped':
itemStateName = "Stopped";
itemLastStart = moment(result[i].lastRunTime).fromNow();
break;
case 'Running':
itemStateName = "Running";
itemLastStart = moment(result[i].lastRunTime).fromNow();
break;
default:
itemStateName = "Unknown status";
itemLastStart = moment(result[i].lastRunTime).fromNow();
break;
} }
} else {
itemStateName = "Blocked";
itemLastStart = moment(result[i].lastRunTime).fromNow();
}
var itemInterval = result[i].interval; if (result[i].isBlocked == false) {
var nextRunTime = moment(result[i].nextRunTime).fromNow(); switch (result[i].itemState) {
var startButton = ''; case 'NeverStarted':
if (userProfile.roles.includes("Admin")) { itemStateName = "Never started";
if (result[i].allowManualStart == true && result[i].itemState != "Running") { itemLastStart = '-';
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>"; break;
case 'Stopped':
itemStateName = "Stopped";
itemLastStart = moment(result[i].lastRunTime).format("YYYY-MM-DD h:mm:ss a");
break;
case 'Running':
var progressPercent = "";
if (result[i].currentStateProgress) {
progressPercent = " (" + result[i].currentStateProgress + ")";
}
itemStateName = "Running" + progressPercent;
itemLastStart = moment(result[i].lastRunTime).format("YYYY-MM-DD h:mm:ss a");
break;
default:
itemStateName = "Unknown status";
itemLastStart = moment(result[i].lastRunTime).format("YYYY-MM-DD h:mm:ss a");
break;
}
} else {
itemStateName = "Blocked";
itemLastStart = moment(result[i].lastRunTime).format("YYYY-MM-DD h:mm:ss a");
} }
}
if (result[i].allowManualStart == false && result[i].removeWhenStopped == true) { itemStateName += hasError;
itemInterval = '';
nextRunTime = '';
}
var newRow = [ var itemInterval = result[i].interval;
itemTypeName, var nextRunTime = moment(result[i].nextRunTime).format("YYYY-MM-DD h:mm:ss a");
itemStateName, var startButton = '';
itemInterval, if (userProfile.roles.includes("Admin")) {
itemLastStart, if (result[i].allowManualStart == true && result[i].itemState != "Running") {
nextRunTime, startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
startButton }
]; }
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
if (result[i].allowManualStart == false && result[i].removeWhenStopped == true) {
itemInterval = '';
nextRunTime = '';
}
var logLink = '';
if (result[i].correlationId) {
logLink = '<a href="/index.html?page=settings&sub=logs&correlationid=' + result[i].correlationId + '" class="romlink">View Log</a>';
}
var newRow = [
itemTypeName,
itemStateName,
itemInterval,
new Date(result[i].lastRunDuration * 1000).toISOString().slice(11, 19),
logLink,
itemLastStart,
nextRunTime,
startButton
];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
}
} }
} }
@@ -202,6 +200,9 @@
} }
function BuildLibraryStatisticsBar(TargetObject, TargetObjectLegend, LibraryStatistics, LibrarySize) { function BuildLibraryStatisticsBar(TargetObject, TargetObjectLegend, LibraryStatistics, LibrarySize) {
TargetObject.innerHTML = '';
TargetObjectLegend.innerHTML = '';
var newTable = document.createElement('table'); var newTable = document.createElement('table');
newTable.setAttribute('cellspacing', 0); newTable.setAttribute('cellspacing', 0);
newTable.setAttribute('style', 'width: 100%; height: 10px;'); newTable.setAttribute('style', 'width: 100%; height: 10px;');
@@ -265,8 +266,9 @@
} }
SystemLoadStatus(); SystemLoadStatus();
setInterval(SystemLoadStatus, 30000); setInterval(SystemLoadStatus, 3000);
SystemLoadSystemStatus(); SystemLoadSystemStatus();
setInterval(SystemLoadStatus, 60000); setInterval(SystemLoadSystemStatus, 60000);
SystemSignaturesStatus(); SystemSignaturesStatus();
setInterval(SystemSignaturesStatus, 300000);</script> setInterval(SystemSignaturesStatus, 300000);
</script>

View File

@@ -19,9 +19,25 @@
panel.appendChild(containerPanelSearch); panel.appendChild(containerPanelSearch);
panel.appendChild(buildFilterPanelHeader('userrating', 'User Rating')); panel.appendChild(buildFilterPanelHeader('userrating', 'User Rating', true, false));
var containerPanelUserRating = document.createElement('div'); var containerPanelUserRating = document.createElement('div');
containerPanelUserRating.id = 'filter_panel_box_userrating';
containerPanelUserRating.className = 'filter_panel_box'; containerPanelUserRating.className = 'filter_panel_box';
var containerPanelUserRatingCheckBox = document.createElement('input');
containerPanelUserRatingCheckBox.id = 'filter_panel_userrating_enabled';
containerPanelUserRatingCheckBox.type = 'checkbox';
containerPanelUserRatingCheckBox.setAttribute('oninput', 'executeFilterDelayed();');
var ratingEnabledCookie = getCookie('filter_panel_userrating_enabled');
if (ratingEnabledCookie) {
if (ratingEnabledCookie == "true") {
containerPanelUserRatingCheckBox.checked = true;
} else {
containerPanelUserRatingCheckBox.checked = false;
}
}
containerPanelUserRating.appendChild(containerPanelUserRatingCheckBox);
var containerPanelUserRatingMinField = document.createElement('input'); var containerPanelUserRatingMinField = document.createElement('input');
var minRatingCookie = getCookie('filter_panel_userrating_min'); var minRatingCookie = getCookie('filter_panel_userrating_min');
if (minRatingCookie) { if (minRatingCookie) {
@@ -72,7 +88,23 @@
buildFilterPanel(panel, 'theme', 'Themes', result.themes, true, false); buildFilterPanel(panel, 'theme', 'Themes', result.themes, true, false);
} }
if (result.agegroupings) {
if (result.agegroupings.length > 1) {
buildFilterPanel(panel, 'agegroupings', 'Age Groups', result.agegroupings, true, false);
}
}
targetElement.appendChild(panel); targetElement.appendChild(panel);
// set order by values
var orderByCookie = getCookie('games_library_orderby_select');
if (orderByCookie) {
document.getElementById('games_library_orderby_select').value = orderByCookie;
}
var orderByDirectionCookie = getCookie('games_library_orderby_direction_select');
if (orderByDirectionCookie) {
document.getElementById('games_library_orderby_direction_select').value = orderByDirectionCookie;
}
} }
function buildFilterPanel(targetElement, headerString, friendlyHeaderString, valueList, showToggle, initialDisplay) { function buildFilterPanel(targetElement, headerString, friendlyHeaderString, valueList, showToggle, initialDisplay) {
@@ -80,7 +112,6 @@ function buildFilterPanel(targetElement, headerString, friendlyHeaderString, val
var displayCookie = getCookie('filter_panel_box_' + headerString); var displayCookie = getCookie('filter_panel_box_' + headerString);
if (displayCookie) { if (displayCookie) {
initialDisplay = (displayCookie === 'true'); initialDisplay = (displayCookie === 'true');
console.log(displayCookie);
} }
targetElement.appendChild(buildFilterPanelHeader(headerString, friendlyHeaderString, showToggle, initialDisplay)); targetElement.appendChild(buildFilterPanelHeader(headerString, friendlyHeaderString, showToggle, initialDisplay));
@@ -92,14 +123,13 @@ function buildFilterPanel(targetElement, headerString, friendlyHeaderString, val
} }
for (var i = 0; i < valueList.length; i++) { for (var i = 0; i < valueList.length; i++) {
var tags; var tags;
switch(headerString) {
case 'platform': if (valueList[i].gameCount) {
tags = [ tags = [
{ {
'label': valueList[i].gameCount 'label': valueList[i].gameCount
} }
]; ];
break;
} }
containerPanel.appendChild(buildFilterPanelItem(headerString, valueList[i].id, valueList[i].name, tags)); containerPanel.appendChild(buildFilterPanelItem(headerString, valueList[i].id, valueList[i].name, tags));
} }
@@ -201,85 +231,6 @@ function executeFilterDelayed() {
filterExecutor = setTimeout(executeFilter1_1, 1000); filterExecutor = setTimeout(executeFilter1_1, 1000);
} }
function executeFilter() {
// build filter lists
var queries = [];
var platforms = '';
var genres = '';
var searchString = document.getElementById('filter_panel_search');
if (searchString.value.length > 0) {
queries.push('name=' + searchString.value);
}
setCookie(searchString.id, searchString.value);
var minUserRating = 0;
var minUserRatingInput = document.getElementById('filter_panel_userrating_min');
if (minUserRatingInput.value) {
minUserRating = minUserRatingInput.value;
queries.push('minrating=' + minUserRating);
}
setCookie(minUserRatingInput.id, minUserRatingInput.value);
var maxUserRating = 100;
var maxUserRatingInput = document.getElementById('filter_panel_userrating_max');
if (maxUserRatingInput.value) {
maxUserRating = maxUserRatingInput.value;
queries.push('maxrating=' + maxUserRating);
}
setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
queries.push(GetFilterQuery('platform'));
queries.push(GetFilterQuery('genre'));
queries.push(GetFilterQuery('gamemode'));
queries.push(GetFilterQuery('playerperspective'));
queries.push(GetFilterQuery('theme'));
var queryString = '';
for (var i = 0; i < queries.length; i++) {
if (queries[i].length > 0) {
if (queryString.length == 0) {
queryString = '?';
} else {
queryString += '&';
}
queryString += queries[i];
}
}
console.log('Query string = ' + queryString);
ajaxCall('/api/v1.0/Games' + queryString, 'GET', function (result) {
var gameElement = document.getElementById('games_library');
formatGamesPanel(gameElement, result);
});
}
function GetFilterQuery(filterName) {
var Filters = document.getElementsByName('filter_' + filterName);
var queryString = '';
for (var i = 0; i < Filters.length; i++) {
if (Filters[i].checked) {
setCookie(Filters[i].id, true);
if (queryString.length > 0) {
queryString += ',';
}
queryString += Filters[i].getAttribute('filter_id');
} else {
setCookie(Filters[i].id, false);
}
}
if (queryString.length > 0) {
queryString = filterName + '=' + queryString;
}
return queryString;
}
function buildFilterTag(tags) { function buildFilterTag(tags) {
// accepts an array of numbers + classes for styling (optional) // accepts an array of numbers + classes for styling (optional)
// example [ { label: "G: 13", class: "tag_Green" }, { label: "R: 17", class: "tag_Orange" } ] // example [ { label: "G: 13", class: "tag_Green" }, { label: "R: 17", class: "tag_Orange" } ]
@@ -301,12 +252,23 @@ function buildFilterTag(tags) {
return boundingDiv; return boundingDiv;
} }
function executeFilter1_1() { function executeFilter1_1(pageNumber, pageSize) {
console.log("Execute filter 1.1"); if (!pageNumber) {
pageNumber = 1;
}
if (!pageSize) {
pageSize = 30;
}
// user ratings
var userRatingEnabled = document.getElementById('filter_panel_userrating_enabled');
var minUserRating = -1; var minUserRating = -1;
var minUserRatingInput = document.getElementById('filter_panel_userrating_min'); var minUserRatingInput = document.getElementById('filter_panel_userrating_min');
if (minUserRatingInput.value) { if (minUserRatingInput.value) {
minUserRating = minUserRatingInput.value; minUserRating = minUserRatingInput.value;
userRatingEnabled.checked = true;
} }
setCookie(minUserRatingInput.id, minUserRatingInput.value); setCookie(minUserRatingInput.id, minUserRatingInput.value);
@@ -314,15 +276,51 @@ function executeFilter1_1() {
var maxUserRatingInput = document.getElementById('filter_panel_userrating_max'); var maxUserRatingInput = document.getElementById('filter_panel_userrating_max');
if (maxUserRatingInput.value) { if (maxUserRatingInput.value) {
maxUserRating = maxUserRatingInput.value; maxUserRating = maxUserRatingInput.value;
userRatingEnabled.checked = true;
} }
setCookie(maxUserRatingInput.id, maxUserRatingInput.value); setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
if (minUserRating == -1 && maxUserRating == -1) {
userRatingEnabled.checked = false;
}
if (userRatingEnabled.checked == false) {
setCookie("filter_panel_userrating_enabled", false);
minUserRating = -1;
minUserRatingInput.value = "";
setCookie(minUserRatingInput.id, minUserRatingInput.value);
maxUserRating = -1;
maxUserRatingInput.value = "";
setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
} else {
setCookie("filter_panel_userrating_enabled", true);
}
// get order by
var orderBy = document.getElementById('games_library_orderby_select').value;
setCookie('games_library_orderby_select', orderBy);
var orderByDirection = true;
var orderByDirectionSelect = document.getElementById('games_library_orderby_direction_select').value;
if (orderByDirectionSelect == "Ascending") {
orderByDirection = true;
} else {
orderByDirection = false;
}
setCookie('games_library_orderby_direction_select', orderByDirectionSelect);
// build filter model // build filter model
var ratingAgeGroups = GetFilterQuery1_1('agegroupings');
var ratingIncludeUnrated = false;
if (ratingAgeGroups.includes("0")) {
ratingIncludeUnrated = true;
}
var model = { var model = {
"Name": document.getElementById('filter_panel_search').value, "Name": document.getElementById('filter_panel_search').value,
"Platform": GetFilterQuery1_1('platform'), "Platform": GetFilterQuery1_1('platform'),
"Genre": GetFilterQuery1_1('genre'), "Genre": GetFilterQuery1_1('genre'),
"GameMode": GetFilterQuery1_1('gamemmode'), "GameMode": GetFilterQuery1_1('gamemode'),
"PlayerPerspective": GetFilterQuery1_1('playerperspective'), "PlayerPerspective": GetFilterQuery1_1('playerperspective'),
"Theme": GetFilterQuery1_1('theme'), "Theme": GetFilterQuery1_1('theme'),
"GameRating": { "GameRating": {
@@ -330,31 +328,24 @@ function executeFilter1_1() {
"MinimumRatingCount": -1, "MinimumRatingCount": -1,
"MaximumRating": maxUserRating, "MaximumRating": maxUserRating,
"MaximumRatingCount": -1, "MaximumRatingCount": -1,
"IncludeUnrated": true "IncludeUnrated": !userRatingEnabled
}, },
"GameAgeRating": { "GameAgeRating": {
"AgeGroupings": [ "AgeGroupings": ratingAgeGroups,
"Child", "IncludeUnrated": ratingIncludeUnrated
"Teen",
"Mature",
"Adult"
],
"IncludeUnrated": true
}, },
"Sorting": { "Sorting": {
"SortBy": "NameThe", "SortBy": orderBy,
"SortAscenting": true "SortAscending": orderByDirection
} }
}; };
console.log('Search model = ' + JSON.stringify(model));
ajaxCall( ajaxCall(
'/api/v1.1/Games', '/api/v1.1/Games?pageNumber=' + pageNumber + '&pageSize=' + pageSize,
'POST', 'POST',
function (result) { function (result) {
var gameElement = document.getElementById('games_library'); var gameElement = document.getElementById('games_library');
formatGamesPanel(gameElement, result); formatGamesPanel(gameElement, result, pageNumber, pageSize);
}, },
function (error) { function (error) {
console.log('An error occurred: ' + JSON.stringify(error)); console.log('An error occurred: ' + JSON.stringify(error));

View File

@@ -1,31 +1,251 @@
function formatGamesPanel(targetElement, result) { var ClassificationBoards = {
targetElement.innerHTML = ''; "ESRB": "Entertainment Software Rating Board (ESRB)",
for (var i = 0; i < result.length; i++) { "PEGI": "Pan European Game Information (PEGI)",
var game = renderGameIcon(result[i], true, false); "CERO": "Computer Entertainment Rating Organisation (CERO)",
targetElement.appendChild(game); "USK": "Unterhaltungssoftware Selbstkontrolle (USK)",
"GRAC": "Game Rating and Administration Committee (GRAC)",
"CLASS_IND": "Brazilian advisory rating system",
"ACB": "Australian Classification Board (ACB)"
};
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') {
targetElement.innerHTML = '';
} }
$('.lazy').Lazy({ var pagerCheck = document.getElementById('games_library_pagerstore');
scrollDirection: 'vertical', if (pageNumber == 1) {
effect: 'fadeIn', pagerCheck.innerHTML = "0";
visibleOnly: true }
});
if (pageNumber > Number(pagerCheck.innerHTML) || pageMode == 'paged') {
pagerCheck.innerHTML = pageNumber;
document.getElementById('games_library_recordcount').innerHTML = result.count + ' games';
var existingLoadPageButton = document.getElementById('games_library_loadmore');
if (existingLoadPageButton) {
existingLoadPageButton.parentNode.removeChild(existingLoadPageButton);
}
// setup preferences
var showTitle = GetPreference("LibraryShowGameTitle", true);
var showRatings = GetPreference("LibraryShowGameRating", true);
var showClassification = GetPreference("LibraryShowGameClassification", true);
var classificationDisplayOrderString = GetPreference("LibraryGameClassificationDisplayOrder", JSON.stringify([ "ESRB" ]));
var classificationDisplayOrder = JSON.parse(classificationDisplayOrderString);
if (showTitle == "true") { showTitle = true; } else { showTitle = false; }
if (showRatings == "true") { showRatings = true; } else { showRatings = false; }
if (showClassification == "true") { showClassification = true; } else { showClassification = false; }
for (var i = 0; i < result.games.length; i++) {
var game = renderGameIcon(result.games[i], showTitle, showRatings, showClassification, classificationDisplayOrder, false);
targetElement.appendChild(game);
}
$('.lazy').Lazy({
scrollDirection: 'vertical',
effect: 'fadeIn',
visibleOnly: true
});
var pager = document.getElementById('games_pager');
pager.style.display = 'none';
switch(pageMode) {
case 'infinite':
if (result.games.length == pageSize) {
var loadPageButton = document.createElement("div");
loadPageButton.id = 'games_library_loadmore';
loadPageButton.innerHTML = 'Load More';
loadPageButton.setAttribute('onclick', 'executeFilter1_1(' + (pageNumber + 1) + ', ' + pageSize + ');');
loadPageButton.setAttribute('data-pagenumber', Number(pageNumber + 1));
loadPageButton.setAttribute('data-pagesize', pageSize);
targetElement.appendChild(loadPageButton);
}
break;
case 'paged':
if (result.count > pageSize) {
// add some padding to the bottom of the games list
var loadPageButton = document.createElement("div");
loadPageButton.id = 'games_library_padding';
targetElement.appendChild(loadPageButton);
var pageCount = Math.ceil(result.count / pageSize);
// add previous page button
var prevPage = document.createElement('span');
prevPage.innerHTML = '&lt;';
if (pageNumber == 1) {
prevPage.className = 'games_pager_number_disabled';
} else {
prevPage.className = 'games_pager_number';
prevPage.setAttribute('onclick', 'executeFilter1_1(' + (pageNumber - 1) + ');');
}
// add page numbers
var pageEitherSide = 4;
var currentPage = Number(pagerCheck.innerHTML);
var pageNumbers = document.createElement('span');
for (var i = 1; i <= pageCount; i++) {
if (
(
(i >= currentPage - pageEitherSide) &&
(i <= currentPage + pageEitherSide)
) ||
(
(
i <= (pageEitherSide * 2 + 1) &&
currentPage <= (pageEitherSide)
) ||
(
i >= (pageCount - (pageEitherSide * 2)) &&
currentPage >= (pageCount - (pageEitherSide))
)
)
) {
var pageNum = document.createElement('span');
if (Number(pagerCheck.innerHTML) == i) {
pageNum.className = 'games_pager_number_disabled';
} else {
pageNum.className = 'games_pager_number';
pageNum.setAttribute('onclick', 'executeFilter1_1(' + i + ');');
}
pageNum.innerHTML = i;
pageNumbers.appendChild(pageNum);
}
}
// add next page button
var nextPage = document.createElement('span');
nextPage.innerHTML = '&gt;';
if (pageNumber == pageCount) {
nextPage.className = 'games_pager_number_disabled';
} else {
nextPage.className = 'games_pager_number';
nextPage.setAttribute('onclick', 'executeFilter1_1(' + (pageNumber + 1) + ');');
}
pager.innerHTML = '';
pager.appendChild(prevPage);
pager.appendChild(pageNumbers);
pager.appendChild(nextPage);
pager.style.display = '';
}
break;
}
}
} }
function renderGameIcon(gameObject, showTitle, showRatings) { function isScrolledIntoView(elem) {
if (elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
}
function IsInView() {
var loadElement = document.getElementById('games_library_loadmore');
if (loadElement) {
if (isScrolledIntoView(loadElement)) {
var pageNumber = Number(document.getElementById('games_library_loadmore').getAttribute('data-pagenumber'));
var pageSize = document.getElementById('games_library_loadmore').getAttribute('data-pagesize');
executeFilter1_1(pageNumber, pageSize);
}
}
}
$(window).scroll(IsInView);
function renderGameIcon(gameObject, showTitle, showRatings, showClassification, classificationDisplayOrder, useSmallCover) {
var gameBox = document.createElement('div'); var gameBox = document.createElement('div');
gameBox.className = 'game_tile'; gameBox.id = "game_tile_" + gameObject.id;
if (useSmallCover == true) {
gameBox.className = 'game_tile game_tile_small';
} else {
gameBox.className = 'game_tile';
}
gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";'); gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";');
var gameImageBox = document.createElement('div');
gameImageBox.className = 'game_tile_box';
var gameImage = document.createElement('img'); var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image lazy'; if (useSmallCover == true) {
gameImage.className = 'game_tile_image game_tile_image_small lazy';
} else {
gameImage.className = 'game_tile_image lazy';
}
gameImage.src = '/images/unknowngame.png';
if (gameObject.cover) { if (gameObject.cover) {
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.id + '/cover/image'); gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.id + '/cover/image');
} else { } else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image unknown'; gameImage.className = 'game_tile_image unknown';
} }
gameBox.appendChild(gameImage); gameImageBox.appendChild(gameImage);
var classificationPath = '';
var displayClassification = false;
var shownClassificationBoard = '';
if (showClassification == true) {
for (var b = 0; b < classificationDisplayOrder.length; b++) {
if (shownClassificationBoard == '') {
for (var c = 0; c < gameObject.ageRatings.length; c++) {
if (gameObject.ageRatings[c].category == classificationDisplayOrder[b]) {
shownClassificationBoard = classificationDisplayOrder[b];
displayClassification = true;
classificationPath = '/api/v1.1/Ratings/Images/' + classificationDisplayOrder[b] + '/' + getKeyByValue(AgeRatingStrings, gameObject.ageRatings[c].rating) + '/image.svg';
}
}
} else {
break;
}
}
}
if (gameObject.totalRating || displayClassification == true) {
var gameImageRatingBanner = document.createElement('div');
gameImageRatingBanner.className = 'game_tile_box_ratingbanner';
if (showRatings == true || displayClassification == true) {
if (showRatings == true) {
if (gameObject.totalRating) {
var gameImageRatingBannerLogo = document.createElement('img');
gameImageRatingBannerLogo.src = '/images/IGDB_logo.svg';
gameImageRatingBannerLogo.setAttribute('style', 'filter: invert(100%); height: 10px; margin-right: 5px; padding-top: 4px;');
gameImageRatingBanner.appendChild(gameImageRatingBannerLogo);
var gameImageRatingBannerValue = document.createElement('span');
gameImageRatingBannerValue.innerHTML = Math.floor(gameObject.totalRating) + '% / ' + gameObject.totalRatingCount;
gameImageRatingBanner.appendChild(gameImageRatingBannerValue);
}
}
gameImageBox.appendChild(gameImageRatingBanner);
if (displayClassification == true) {
var gameImageClassificationLogo = document.createElement('img');
gameImageClassificationLogo.src = classificationPath;
gameImageClassificationLogo.className = 'rating_image_overlay';
gameImageBox.appendChild(gameImageClassificationLogo);
}
}
}
gameBox.appendChild(gameImageBox);
if (showTitle == true) { if (showTitle == true) {
var gameBoxTitle = document.createElement('div'); var gameBoxTitle = document.createElement('div');
@@ -34,19 +254,5 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
gameBox.appendChild(gameBoxTitle); gameBox.appendChild(gameBoxTitle);
} }
if (showRatings == true) {
if (gameObject.ageRatings) {
var ratingsSection = document.createElement('div');
ratingsSection.id = 'ratings_section';
for (var i = 0; i < gameObject.ageRatings.ids.length; i++) {
var ratingImage = document.createElement('img');
ratingImage.src = '/api/v1.1/Games/' + gameObject.id + '/agerating/' + gameObject.ageRatings.ids[i] + '/image';
ratingImage.className = 'rating_image_mini';
ratingsSection.appendChild(ratingImage);
}
gameBox.appendChild(ratingsSection);
}
}
return gameBox; return gameBox;
} }

View File

@@ -1,4 +1,8 @@
function ajaxCall(endpoint, method, successFunction, errorFunction, body) { var locale = window.navigator.userLanguage || window.navigator.language;
console.log(locale);
moment.locale(locale);
function ajaxCall(endpoint, method, successFunction, errorFunction, body) {
$.ajax({ $.ajax({
// Our sample url to make request // Our sample url to make request
@@ -249,13 +253,18 @@ function DropDownRenderGameOption(state) {
var response; var response;
var releaseDate;
if (state.releaseDate) {
releaseDate = moment(state.releaseDate).format('yyyy');
}
if (state.cover) { if (state.cover) {
response = $( response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="https://images.igdb.com/igdb/image/upload/t_cover_small/' + state.cover.value.imageId + '.jpg" /></td><td class="dropdown-label"><span>' + state.text + '</span></td></tr></table>' '<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="https://images.igdb.com/igdb/image/upload/t_cover_small/' + state.cover.value.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
); );
} else { } else {
response = $( response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/images/unknowngame.png" /></td><td class="dropdown-label"><span>' + state.text + '</span></td></tr></table>' '<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/images/unknowngame.png" style="max-width: 90px;" /></td><td class="dropdown-label"><span>' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
); );
} }
return response; return response;
@@ -388,4 +397,81 @@ function CreateBadge(BadgeText, ColourOverride) {
badgeItem.style.borderColor = ColourOverride; badgeItem.style.borderColor = ColourOverride;
} }
return badgeItem; return badgeItem;
}
function GetTaskFriendlyName(TaskName, options) {
switch (TaskName) {
case 'SignatureIngestor':
return "Signature import";
case 'TitleIngestor':
return "Title import";
case 'MetadataRefresh':
return "Metadata refresh";
case 'OrganiseLibrary':
return "Organise library";
case 'LibraryScan':
return "Library scan";
case 'CollectionCompiler':
return "Compress collection id: " + options;
case 'BackgroundDatabaseUpgrade':
return "Background database upgrade";
default:
return TaskName;
}
}
function getKeyByValue(object, value) {
return Object.keys(object).find(key => object[key] === value);
}
function GetPreference(Setting, DefaultValue) {
if (userProfile.userPreferences) {
for (var i = 0; i < userProfile.userPreferences.length; i++) {
if (userProfile.userPreferences[i].setting == Setting) {
return userProfile.userPreferences[i].value.toString();
}
}
}
SetPreference(Setting, DefaultValue);
return DefaultValue;
}
function SetPreference(Setting, Value) {
var model = [
{
"setting": Setting,
"value": Value.toString()
}
];
ajaxCall(
'/api/v1.1/Account/Preferences',
'POST',
function(result) {
SetPreference_Local(Setting, Value);
},
function(error) {
SetPreference_Local(Setting, Value);
},
JSON.stringify(model)
);
}
function SetPreference_Local(Setting, Value) {
if (userProfile.userPreferences) {
var prefFound = false;
for (var i = 0; i < userProfile.userPreferences.length; i++) {
if (userProfile.userPreferences[i].setting == Setting) {
userProfile.userPreferences[i].value = Value;
prefFound = true;
break;
}
}
if (prefFound == false) {
userProfile.userPreferences.push(model);
}
}
} }

File diff suppressed because one or more lines are too long

View File

@@ -187,12 +187,28 @@ h3 {
z-index: -100; z-index: -100;
} }
#games_filter_scroller {
position: fixed;
width: 210px;
overflow-y: hidden;
overflow-x: hidden;
overscroll-behavior: contain;
top: 60px;
bottom: 20px;
/* margin-right: 10px; */
}
#games_filter { #games_filter {
width: 200px; width: 200px;
border-style: solid; /* border-style: solid;
border-width: 1px; border-width: 1px;
border-color: #2b2b2b; border-color: #2b2b2b; */
margin-right: 10px; overflow: hidden;
}
#games_filter_scroller:hover {
overflow-y: auto;
} }
.filter_header { .filter_header {
@@ -207,7 +223,7 @@ h3 {
z-index: 1; z-index: 1;
} }
input[type='text'], input[type='number'], input[type="email"], input[type="password"] { input[type='text'], input[type='number'], input[type="email"], input[type="password"], input[type="datetime-local"] {
background-color: #2b2b2b; background-color: #2b2b2b;
color: white; color: white;
padding: 5px; padding: 5px;
@@ -266,8 +282,91 @@ input[id='filter_panel_userrating_max'] {
} }
#games_home { #games_home {
display: flex; display: absolute;
margin-left: 210px;
margin-top: 20px; margin-top: 20px;
right: 20px;
}
#games_home_box {
display: relative;
}
#games_pager {
position: fixed;
bottom: 50px;
left: 50%;
transform: translate(-50%, 0);
border-style: solid;
border-width: 1px;
border-radius: 7px;
border-color: rgba(0, 22, 56, 0.8);
padding-left: 20px;
padding-right: 20px;
padding-top: 15px;
padding-bottom: 15px;
font-size: 18px;
font-family: Commodore64;
background-color: rgba(0, 22, 56, 0.8);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
}
.games_pager_number {
display: inline-block;
padding: 5px;
width: 40px;
text-align: center;
}
.games_pager_number:hover {
background-color: blue;
cursor: pointer;
}
.games_pager_number_disabled {
display: inline-block;
padding: 5px;
width: 40px;
text-align: center;
color: grey;
}
.rom_pager {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
padding-left: 20px;
padding-right: 20px;
padding-top: 15px;
padding-bottom: 15px;
font-size: 16px;
font-family: Commodore64;
text-align: center;
width: 80%;
}
.rom_pager_number {
display: inline-block;
padding: 5px;
width: 40px;
text-align: center;
}
.rom_pager_number:hover {
background-color: blue;
cursor: pointer;
}
.rom_pager_number_disabled {
display: inline-block;
padding: 5px;
width: 40px;
text-align: center;
color: grey;
} }
.filter_panel_item { .filter_panel_item {
@@ -285,18 +384,52 @@ input[id='filter_panel_userrating_max'] {
margin-right: 15px; margin-right: 15px;
} }
#games_library { #games_library_controls {
width: 90%; display: absolute;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-color: #2b2b2b; border-color: #2b2b2b;
border-radius: 5px;
margin-bottom: 10px;
padding: 10px; padding: 10px;
text-align: right;
}
.games_library_controlblock {
margin-left: 20px;
display: inline-block;
}
#games_library {
display: block;
/* width: 90%; */
/* border-style: solid;
border-width: 1px;
border-color: #2b2b2b; */
/* padding: 10px; */
}
#games_library_loadmore {
display: block;
width: 100%;
text-align: center;
padding-top: 10px;
height: 50px;
}
#games_library_padding {
display: block;
width: 100%;
text-align: center;
padding-top: 10px;
height: 100px;
} }
.game_tile { .game_tile {
padding: 5px; padding: 5px;
display: inline-block; display: inline-block;
width: 220px; width: 220px;
min-height: 200px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
@@ -308,6 +441,12 @@ input[id='filter_panel_userrating_max'] {
border: 1px solid transparent; border: 1px solid transparent;
} }
.game_tile_small {
min-height: 50px;
min-width: 50px;
width: 105px;
}
.game_tile:hover { .game_tile:hover {
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
@@ -318,19 +457,57 @@ input[id='filter_panel_userrating_max'] {
border: 1px solid #2b2b2b; border: 1px solid #2b2b2b;
} }
.game_tile_image { .game_tile_small:hover {
cursor: pointer;
text-decoration: underline;
background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b;
}
.game_tile_box {
position: relative;
display: inline-block;
max-width: 200px; max-width: 200px;
max-height: 200px; max-height: 200px;
}
.game_tile_box_ratingbanner {
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
text-align: right;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 5px;
padding-right: 5px;
height: 19px;
background-color: rgba(0, 22, 56, 0.8);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.game_tile_image {
max-width: 200px;
min-width: 150px;
max-height: 200px;
min-height: 200px;
background-color: transparent;
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44); box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44); -webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44); -moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
} }
.game_tile_image, .unknown { .game_tile_image, .unknown {
background-color: white; background-color: transparent;
} }
.game_tile_image_small { .game_tile_image_small {
min-width: 50px;
min-height: 50px;
max-width: 100px; max-width: 100px;
max-height: 100px; max-height: 100px;
} }
@@ -381,6 +558,10 @@ input[id='filter_panel_userrating_max'] {
width: 250px; width: 250px;
} }
#gamesummaryroms {
position: relative;
}
.game_cover_image { .game_cover_image {
display: block; display: block;
max-width: 250px; max-width: 250px;
@@ -408,10 +589,18 @@ input[id='filter_panel_userrating_max'] {
} }
.rating_image_mini { .rating_image_mini {
display: inline-block;
max-width: 32px; max-width: 32px;
max-height: 32px; max-height: 32px;
margin-right: 10px; margin-right: 5px;
margin-bottom: 5px;
}
.rating_image_overlay {
position: absolute;
max-width: 32px;
max-height: 32px;
left: 5px;
bottom: 5px;
} }
#gamescreenshots { #gamescreenshots {
@@ -706,6 +895,18 @@ div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover
text-align: left; text-align: left;
} }
.dropdown-title {
display: block;
font-weight: bold;
margin-right: 5px;
}
.dropdown-releasedate {
display: block;
font-weight: normal;
color: lightgray;
}
button { button {
background-color: #555; background-color: #555;
color: white; color: white;
@@ -909,22 +1110,38 @@ button:disabled {
vertical-align: top; vertical-align: top;
} }
.logs_table_row_Information:hover { .logs_table_row_Information:nth-child(even) {
background: rgba(42, 41, 150, 0.3); background: rgba(42, 41, 150, 0.3);
} }
.logs_table_row_Warning:hover { .logs_table_row_Information:nth-child(odd) {
background: rgba(10, 9, 83, 0.3);
}
.logs_table_row_Warning:nth-child(even) {
background: rgba(139, 150, 41, 0.3); background: rgba(139, 150, 41, 0.3);
} }
.logs_table_row_Critical:hover { .logs_table_row_Warning:nth-child(odd) {
background: rgba(49, 53, 14, 0.3);
}
.logs_table_row_Critical:nth-child(even) {
background: rgba(150, 41, 41, 0.3); background: rgba(150, 41, 41, 0.3);
} }
.logs_table_row_Debug:hover { .logs_table_row_Critical:nth-child(odd) {
background: rgba(58, 16, 16, 0.3);
}
.logs_table_row_Debug:nth-child(even) {
background: rgba(150, 41, 135, 0.3); background: rgba(150, 41, 135, 0.3);
} }
.logs_table_row_Debug:nth-child(odd) {
background: rgba(68, 18, 61, 0.3);
}
.logs_table_exception { .logs_table_exception {
margin-right: 10px; margin-right: 10px;
padding: 5px; padding: 5px;
@@ -947,7 +1164,7 @@ button:disabled {
top: 0px; top: 0px;
right: 0px; right: 0px;
text-align: center; text-align: center;
font-size: 8px; font-size: 10px;
} }
.tagBoxItem { .tagBoxItem {