Resolved many database errors (#377)
* Resolved missing table errors. * These were due to some dynamically created tables being queried before they were created. * These tables are now created at start up. * Resolved many "INSERT" errors that were polluting the logs: * These were due to a race condition where sometimes the database would return the data as not being in the database causing Gaseous to try to insert it - even though the data was already there.
This commit is contained in:
@@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Elfie.Model.Strings;
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Covers
|
||||
public class Covers
|
||||
{
|
||||
const string fieldList = "fields alpha_channel,animated,checksum,game,game_localization,height,image_id,url,width;";
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
@@ -135,6 +135,6 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,8 +5,8 @@ using IGDB.Models;
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Games
|
||||
{
|
||||
public class Games
|
||||
{
|
||||
const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collection,collections,cover,created_at,dlcs,expanded_games,expansions,external_games,first_release_date,follows,forks,franchise,franchises,game_engines,game_localizations,game_modes,genres,hypes,involved_companies,keywords,language_supports,multiplayer_modes,name,parent_game,platforms,player_perspectives,ports,rating,rating_count,release_dates,remakes,remasters,screenshots,similar_games,slug,standalone_expansions,status,storyline,summary,tags,themes,total_rating,total_rating_count,updated_at,url,version_parent,version_title,videos,websites;";
|
||||
|
||||
public Games()
|
||||
@@ -15,9 +15,9 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
public class InvalidGameId : Exception
|
||||
{
|
||||
{
|
||||
public InvalidGameId(long Id) : base("Unable to find Game by id " + Id)
|
||||
{}
|
||||
{ }
|
||||
}
|
||||
|
||||
public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh)
|
||||
@@ -125,17 +125,17 @@ namespace gaseous_server.Classes.Metadata
|
||||
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames, bool forceRefresh)
|
||||
{
|
||||
// required metadata
|
||||
if (Game.Cover != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
|
||||
}
|
||||
}
|
||||
// if (Game.Cover != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (Game.Genres != null)
|
||||
{
|
||||
@@ -285,7 +285,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
try
|
||||
{
|
||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -347,7 +347,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
// get missing metadata from parent if this is a port
|
||||
if (result.Category == Category.Port)
|
||||
{
|
||||
{
|
||||
if (result.Summary == null)
|
||||
{
|
||||
if (result.ParentGame != null)
|
||||
@@ -364,7 +364,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static void AssignAllGamesToPlatformIdZero()
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
@@ -428,7 +428,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
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];
|
||||
@@ -439,7 +439,8 @@ namespace gaseous_server.Classes.Metadata
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
Game game = new Game{
|
||||
Game game = new Game
|
||||
{
|
||||
Id = (long)row["Id"],
|
||||
Name = (string)Common.ReturnValueIfNull(row["Name"], ""),
|
||||
Slug = (string)Common.ReturnValueIfNull(row["Slug"], ""),
|
||||
@@ -476,12 +477,12 @@ namespace gaseous_server.Classes.Metadata
|
||||
searchBody = "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// check search cache
|
||||
Game[]? games = Communications.GetSearchCache<Game[]?>(searchFields, searchBody);
|
||||
|
||||
if (games == null)
|
||||
{
|
||||
{
|
||||
// cache miss
|
||||
// get Game metadata
|
||||
Communications comms = new Communications();
|
||||
@@ -513,7 +514,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
KeyValuePair<long, string> valuePair = new KeyValuePair<long, string>((long)row["PlatformId"], (string)row["Name"]);
|
||||
platforms.Add(valuePair);
|
||||
platforms.Add(valuePair);
|
||||
}
|
||||
|
||||
return platforms;
|
||||
@@ -533,7 +534,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public MinimalGameItem(Game gameObject)
|
||||
{
|
||||
this.Id = gameObject.Id;
|
||||
|
@@ -7,19 +7,19 @@ using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Storage
|
||||
{
|
||||
public enum CacheStatus
|
||||
{
|
||||
NotPresent,
|
||||
Current,
|
||||
Expired
|
||||
}
|
||||
public class Storage
|
||||
{
|
||||
public enum CacheStatus
|
||||
{
|
||||
NotPresent,
|
||||
Current,
|
||||
Expired
|
||||
}
|
||||
|
||||
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
|
||||
{
|
||||
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
|
||||
{
|
||||
return _GetCacheStatus(Endpoint, "slug", Slug);
|
||||
}
|
||||
}
|
||||
|
||||
public static CacheStatus GetCacheStatus(string Endpoint, long Id)
|
||||
{
|
||||
@@ -47,124 +47,124 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue)
|
||||
{
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
|
||||
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
|
||||
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("Endpoint", Endpoint);
|
||||
dbDict.Add(SearchField, SearchValue);
|
||||
|
||||
DataTable dt = db.ExecuteCMD(sql, dbDict);
|
||||
if (dt.Rows.Count == 0)
|
||||
{
|
||||
// no data stored for this item, or lastUpdated
|
||||
return CacheStatus.NotPresent;
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168);
|
||||
if ((DateTime)dt.Rows[0]["lastUpdated"] < CacheExpiryTime)
|
||||
{
|
||||
return CacheStatus.Expired;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CacheStatus.Current;
|
||||
}
|
||||
}
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("Endpoint", Endpoint);
|
||||
dbDict.Add(SearchField, SearchValue);
|
||||
|
||||
DataTable dt = db.ExecuteCMD(sql, dbDict);
|
||||
if (dt.Rows.Count == 0)
|
||||
{
|
||||
// no data stored for this item, or lastUpdated
|
||||
return CacheStatus.NotPresent;
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168);
|
||||
if ((DateTime)dt.Rows[0]["lastUpdated"] < CacheExpiryTime)
|
||||
{
|
||||
return CacheStatus.Expired;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CacheStatus.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
|
||||
{
|
||||
// get the object type name
|
||||
string ObjectTypeName = ObjectToCache.GetType().Name;
|
||||
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
|
||||
{
|
||||
// get the object type name
|
||||
string ObjectTypeName = ObjectToCache.GetType().Name;
|
||||
|
||||
// build dictionary
|
||||
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
|
||||
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
|
||||
// build dictionary
|
||||
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
|
||||
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
|
||||
objectDict.Add("dateAdded", DateTime.UtcNow);
|
||||
objectDict.Add("lastUpdated", DateTime.UtcNow);
|
||||
|
||||
// generate sql
|
||||
string fieldList = "";
|
||||
string valueList = "";
|
||||
string updateFieldValueList = "";
|
||||
foreach (KeyValuePair<string, object?> key in objectDict)
|
||||
{
|
||||
if (fieldList.Length > 0)
|
||||
{
|
||||
fieldList = fieldList + ", ";
|
||||
valueList = valueList + ", ";
|
||||
}
|
||||
fieldList = fieldList + key.Key;
|
||||
valueList = valueList + "@" + key.Key;
|
||||
if ((key.Key != "id") && (key.Key != "dateAdded"))
|
||||
// generate sql
|
||||
string fieldList = "";
|
||||
string valueList = "";
|
||||
string updateFieldValueList = "";
|
||||
foreach (KeyValuePair<string, object?> key in objectDict)
|
||||
{
|
||||
if (fieldList.Length > 0)
|
||||
{
|
||||
fieldList = fieldList + ", ";
|
||||
valueList = valueList + ", ";
|
||||
}
|
||||
fieldList = fieldList + key.Key;
|
||||
valueList = valueList + "@" + key.Key;
|
||||
if ((key.Key != "id") && (key.Key != "dateAdded"))
|
||||
{
|
||||
if (updateFieldValueList.Length > 0)
|
||||
{
|
||||
updateFieldValueList = updateFieldValueList + ", ";
|
||||
}
|
||||
updateFieldValueList += key.Key + " = @" + key.Key;
|
||||
}
|
||||
}
|
||||
|
||||
// check property type
|
||||
Type objectType = ObjectToCache.GetType();
|
||||
if (objectType != null)
|
||||
{
|
||||
PropertyInfo objectProperty = objectType.GetProperty(key.Key);
|
||||
if (objectProperty != null)
|
||||
{
|
||||
string compareName = objectProperty.PropertyType.Name.ToLower().Split("`")[0];
|
||||
var objectValue = objectProperty.GetValue(ObjectToCache);
|
||||
if (objectValue != null)
|
||||
{
|
||||
string newObjectValue;
|
||||
Dictionary<string, object> newDict;
|
||||
// check property type
|
||||
Type objectType = ObjectToCache.GetType();
|
||||
if (objectType != null)
|
||||
{
|
||||
PropertyInfo objectProperty = objectType.GetProperty(key.Key);
|
||||
if (objectProperty != null)
|
||||
{
|
||||
string compareName = objectProperty.PropertyType.Name.ToLower().Split("`")[0];
|
||||
var objectValue = objectProperty.GetValue(ObjectToCache);
|
||||
if (objectValue != null)
|
||||
{
|
||||
string newObjectValue;
|
||||
Dictionary<string, object> newDict;
|
||||
switch (compareName)
|
||||
{
|
||||
case "identityorvalue":
|
||||
{
|
||||
case "identityorvalue":
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
objectDict[key.Key] = newDict["Id"];
|
||||
break;
|
||||
case "identitiesorvalues":
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
|
||||
objectDict[key.Key] = newObjectValue;
|
||||
|
||||
StoreRelations(ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
|
||||
|
||||
break;
|
||||
case "int32[]":
|
||||
case "int32[]":
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||
objectDict[key.Key] = newObjectValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string sql = "";
|
||||
if (UpdateRecord == false)
|
||||
{
|
||||
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string sql = "";
|
||||
if (UpdateRecord == false)
|
||||
{
|
||||
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
|
||||
}
|
||||
|
||||
// execute sql
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
db.ExecuteCMD(sql, objectDict);
|
||||
db.ExecuteCMD(sql, objectDict);
|
||||
}
|
||||
|
||||
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
|
||||
{
|
||||
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
|
||||
{
|
||||
string Endpoint = EndpointType.GetType().Name;
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
@@ -178,20 +178,20 @@ namespace gaseous_server.Classes.Metadata
|
||||
DataTable dt = db.ExecuteCMD(sql, dbDict);
|
||||
if (dt.Rows.Count == 0)
|
||||
{
|
||||
// no data stored for this item
|
||||
throw new Exception("No record found that matches endpoint " + Endpoint + " with search value " + SearchValue);
|
||||
// no data stored for this item
|
||||
throw new Exception("No record found that matches endpoint " + Endpoint + " with search value " + SearchValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
DataRow dataRow = dt.Rows[0];
|
||||
DataRow dataRow = dt.Rows[0];
|
||||
object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
|
||||
|
||||
|
||||
return (T)returnObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
|
||||
{
|
||||
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
|
||||
{
|
||||
foreach (PropertyInfo property in EndpointType.GetType().GetProperties())
|
||||
{
|
||||
if (dataRow.Table.Columns.Contains(property.Name))
|
||||
@@ -428,11 +428,35 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateRelationsTables<T>()
|
||||
{
|
||||
string PrimaryTable = typeof(T).Name;
|
||||
foreach (PropertyInfo property in typeof(T).GetProperties())
|
||||
{
|
||||
string SecondaryTable = property.Name;
|
||||
|
||||
if (property.PropertyType.Name == "IdentitiesOrValues`1")
|
||||
{
|
||||
|
||||
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "' AND table_name = '" + TableName + "';";
|
||||
DataTable data = db.ExecuteCMD(sql);
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// table doesn't exist, create it
|
||||
sql = "CREATE TABLE `" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + "` (`" + PrimaryTable + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
|
||||
db.ExecuteCMD(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MemoryCacheObject
|
||||
{
|
||||
public object Object { get; set; }
|
||||
public DateTime CreationTime { get; } = DateTime.UtcNow;
|
||||
public DateTime ExpiryTime
|
||||
public DateTime ExpiryTime
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@@ -73,7 +73,8 @@ namespace gaseous_server.Controllers
|
||||
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
|
||||
{
|
||||
string searchBody = "";
|
||||
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
|
||||
// string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
|
||||
string searchFields = "fields *; ";
|
||||
searchBody += "search \"" + SearchString + "\";";
|
||||
searchBody += "where platforms = (" + PlatformId + ");";
|
||||
searchBody += "limit 100;";
|
||||
@@ -86,12 +87,12 @@ namespace gaseous_server.Controllers
|
||||
// get Game metadata from data source
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
||||
|
||||
|
||||
List<GaseousGame> games = new List<GaseousGame>();
|
||||
foreach (Game game in results.ToList())
|
||||
{
|
||||
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
|
||||
switch(cacheStatus)
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
Storage.NewCacheValue(game, false);
|
||||
|
@@ -16,7 +16,7 @@ namespace gaseous_server.Models
|
||||
{
|
||||
var targetType = this.GetType();
|
||||
var sourceType = game.GetType();
|
||||
foreach(var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public| BindingFlags.SetProperty))
|
||||
foreach (var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty))
|
||||
{
|
||||
// check whether source object has the the property
|
||||
var sp = sourceType.GetProperty(prop.Name);
|
||||
@@ -39,7 +39,11 @@ namespace gaseous_server.Models
|
||||
{
|
||||
if (this.Cover.Id != null)
|
||||
{
|
||||
IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
|
||||
// IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
|
||||
IGDB.Models.Cover cover = new IGDB.Models.Cover()
|
||||
{
|
||||
Id = this.Cover.Id
|
||||
};
|
||||
|
||||
return cover;
|
||||
}
|
||||
|
@@ -36,6 +36,9 @@ db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.Conn
|
||||
|
||||
// set up db
|
||||
db.InitDB();
|
||||
// create relation tables if they don't exist
|
||||
Storage.CreateRelationsTables<IGDB.Models.Game>();
|
||||
Storage.CreateRelationsTables<IGDB.Models.Platform>();
|
||||
|
||||
// populate db with static data for lookups
|
||||
AgeRatings.PopulateAgeMap();
|
||||
@@ -273,7 +276,7 @@ using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var roleManager = scope.ServiceProvider.GetRequiredService<RoleStore>();
|
||||
var roles = new[] { "Admin", "Gamer", "Player" };
|
||||
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (await roleManager.FindByNameAsync(role, CancellationToken.None) == null)
|
||||
@@ -303,11 +306,11 @@ app.Use(async (context, next) =>
|
||||
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;
|
||||
userIdentity = context.User.Claims.Where(x => x.Type == System.Security.Claims.ClaimTypes.NameIdentifier).FirstOrDefault().Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -329,7 +332,7 @@ app.Use(async (context, next) =>
|
||||
// - the server will not start while the RecoverAccount.txt file exists
|
||||
string PasswordRecoveryFile = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "RecoverAccount.txt");
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
|
||||
{
|
||||
{
|
||||
if (File.Exists(PasswordRecoveryFile))
|
||||
{
|
||||
// password has already been set - do nothing and just exit
|
||||
@@ -345,7 +348,7 @@ if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
|
||||
string password = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
|
||||
File.WriteAllText(PasswordRecoveryFile, password);
|
||||
|
||||
|
||||
// reset the password
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
|
@@ -39,7 +39,7 @@ function ajaxCall(endpoint, method, successFunction, errorFunction, body) {
|
||||
|
||||
function getQueryString(stringName, type) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
var myParam = urlParams.get(stringName);
|
||||
var myParam = urlParams.get(stringName);
|
||||
|
||||
switch (type) {
|
||||
case "int":
|
||||
@@ -63,9 +63,9 @@ function getQueryString(stringName, type) {
|
||||
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
||||
if (exdays) {
|
||||
let expires = "expires="+ d.toUTCString();
|
||||
let expires = "expires=" + d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||
} else {
|
||||
document.cookie = cname + "=" + cvalue + ";path=/";
|
||||
@@ -76,14 +76,14 @@ function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for(let i = 0; i <ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -207,7 +207,7 @@ function createTableRow(isHeader, row, rowClass, cellClass) {
|
||||
}
|
||||
|
||||
var newCell = document.createElement(cellType);
|
||||
if (typeof(row[i]) != "object") {
|
||||
if (typeof (row[i]) != "object") {
|
||||
newCell.innerHTML = row[i];
|
||||
newCell.className = cellClass;
|
||||
} else {
|
||||
@@ -258,7 +258,7 @@ function DropDownRenderGameOption(state) {
|
||||
|
||||
if (state.cover) {
|
||||
response = $(
|
||||
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.cover.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
|
||||
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.id + '.jpg" class="game_tile_small_search" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
|
||||
);
|
||||
} else {
|
||||
response = $(
|
||||
@@ -317,8 +317,8 @@ function CreateEditableTable(TableName, Headers) {
|
||||
var addButton = document.createElement('button');
|
||||
addButton.value = 'Add Row';
|
||||
addButton.innerHTML = 'Add Row';
|
||||
|
||||
$(addButton).click(function() {
|
||||
|
||||
$(addButton).click(function () {
|
||||
eTable.appendChild(AddEditableTableRow(Headers));
|
||||
});
|
||||
|
||||
@@ -463,10 +463,10 @@ function SetPreference(Setting, Value) {
|
||||
ajaxCall(
|
||||
'/api/v1.1/Account/Preferences',
|
||||
'POST',
|
||||
function(result) {
|
||||
function (result) {
|
||||
SetPreference_Local(Setting, Value);
|
||||
},
|
||||
function(error) {
|
||||
function (error) {
|
||||
SetPreference_Local(Setting, Value);
|
||||
},
|
||||
JSON.stringify(model)
|
||||
@@ -478,12 +478,12 @@ function SetPreference_Batch(model) {
|
||||
ajaxCall(
|
||||
'/api/v1.1/Account/Preferences',
|
||||
'POST',
|
||||
function(result) {
|
||||
function (result) {
|
||||
for (var i = 0; i < model.length; i++) {
|
||||
SetPreference_Local(model[i].setting, model[i].value.toString());
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
function (error) {
|
||||
for (var i = 0; i < model.length; i++) {
|
||||
SetPreference_Local(model[i].setting, model[i].value.toString());
|
||||
}
|
||||
@@ -509,11 +509,11 @@ function SetPreference_Local(Setting, Value) {
|
||||
}
|
||||
}
|
||||
|
||||
function Uint8ToString(u8a){
|
||||
function Uint8ToString(u8a) {
|
||||
var CHUNK_SZ = 0x8000;
|
||||
var c = [];
|
||||
for (var i=0; i < u8a.length; i+=CHUNK_SZ) {
|
||||
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ)));
|
||||
for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
|
||||
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
|
||||
}
|
||||
return c.join("");
|
||||
}
|
||||
|
@@ -23,21 +23,29 @@ h3 {
|
||||
border-bottom-width: 1px;
|
||||
/*border-image: linear-gradient(to right, blue 25%, yellow 25%, yellow 50%,red 50%, red 75%, teal 75%) 5;*/
|
||||
|
||||
border-image: linear-gradient(to right, rgba(255,0,0,1) 0%, rgba(251,255,0,1) 16%, rgba(0,255,250,1) 30%, rgba(0,16,255,1) 46%, rgba(250,0,255,1) 62%, rgba(255,0,0,1) 78%, rgba(255,237,0,1) 90%, rgba(20,255,0,1) 100%) 5;
|
||||
border-image: linear-gradient(to right, rgba(255, 0, 0, 1) 0%, rgba(251, 255, 0, 1) 16%, rgba(0, 255, 250, 1) 30%, rgba(0, 16, 255, 1) 46%, rgba(250, 0, 255, 1) 62%, rgba(255, 0, 0, 1) 78%, rgba(255, 237, 0, 1) 90%, rgba(20, 255, 0, 1) 100%) 5;
|
||||
}
|
||||
|
||||
/* The Modal (background) */
|
||||
.modal {
|
||||
display: none; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 100; /* Sit on top */
|
||||
display: none;
|
||||
/* Hidden by default */
|
||||
position: fixed;
|
||||
/* Stay in place */
|
||||
z-index: 100;
|
||||
/* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: none; /* Enable scroll if needed */
|
||||
background-color: rgb(0,0,0); /* Fallback color */
|
||||
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||
width: 100%;
|
||||
/* Full width */
|
||||
height: 100%;
|
||||
/* Full height */
|
||||
overflow: none;
|
||||
/* Enable scroll if needed */
|
||||
background-color: rgb(0, 0, 0);
|
||||
/* Fallback color */
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
/* Black w/ opacity */
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
filter: drop-shadow(5px 5px 10px #000);
|
||||
@@ -47,22 +55,28 @@ h3 {
|
||||
/* Modal Content/Box */
|
||||
.modal-content {
|
||||
background-color: #383838;
|
||||
margin: 10% auto; /* 15% from the top and centered */
|
||||
margin: 10% auto;
|
||||
/* 15% from the top and centered */
|
||||
padding: 10px;
|
||||
border: 1px solid #888;
|
||||
border-radius: 10px;
|
||||
width: 700px; /* Could be more or less, depending on screen size */
|
||||
width: 700px;
|
||||
/* Could be more or less, depending on screen size */
|
||||
min-height: 358px;
|
||||
}
|
||||
|
||||
.modal-content-sub {
|
||||
background-color: #383838;
|
||||
margin: 20% auto; /* 20% from the top and centered */
|
||||
margin: 20% auto;
|
||||
/* 20% from the top and centered */
|
||||
padding: 10px;
|
||||
border: 1px solid #888;
|
||||
border-radius: 10px;
|
||||
width: 300px; /* Could be more or less, depending on screen size */
|
||||
width: 300px;
|
||||
/* Could be more or less, depending on screen size */
|
||||
min-height: 110px;
|
||||
}
|
||||
|
||||
#modal-heading {
|
||||
margin-block: 5px;
|
||||
border-bottom-style: solid;
|
||||
@@ -70,8 +84,9 @@ h3 {
|
||||
border-bottom-width: 3px;
|
||||
/*border-image: linear-gradient(to right, blue 25%, yellow 25%, yellow 50%,red 50%, red 75%, teal 75%) 5;*/
|
||||
|
||||
border-image: linear-gradient(to right, rgba(255,0,0,1) 0%, rgba(251,255,0,1) 16%, rgba(0,255,250,1) 30%, rgba(0,16,255,1) 46%, rgba(250,0,255,1) 62%, rgba(255,0,0,1) 78%, rgba(255,237,0,1) 90%, rgba(20,255,0,1) 100%) 5;
|
||||
border-image: linear-gradient(to right, rgba(255, 0, 0, 1) 0%, rgba(251, 255, 0, 1) 16%, rgba(0, 255, 250, 1) 30%, rgba(0, 16, 255, 1) 46%, rgba(250, 0, 255, 1) 62%, rgba(255, 0, 0, 1) 78%, rgba(255, 237, 0, 1) 90%, rgba(20, 255, 0, 1) 100%) 5;
|
||||
}
|
||||
|
||||
#modal-content {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -268,7 +283,7 @@ h3 {
|
||||
}
|
||||
|
||||
#games_filter {
|
||||
|
||||
|
||||
width: 200px;
|
||||
/* border-style: solid;
|
||||
border-width: 1px;
|
||||
@@ -296,7 +311,11 @@ h3 {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
input[type='text'], input[type='number'], input[type="email"], input[type="password"], input[type="datetime-local"] {
|
||||
input[type='text'],
|
||||
input[type='number'],
|
||||
input[type="email"],
|
||||
input[type="password"],
|
||||
input[type="datetime-local"] {
|
||||
background-color: #2b2b2b;
|
||||
color: white;
|
||||
padding: 4px;
|
||||
@@ -313,7 +332,11 @@ input[type='text'], input[type='number'], input[type="email"], input[type="passw
|
||||
height: 21px;
|
||||
}
|
||||
|
||||
input[type='text']:hover, input[type='number']:hover, input[type="email"]:hover, input[type="password"]:hover, input[type="datetime-local"]:hover {
|
||||
input[type='text']:hover,
|
||||
input[type='number']:hover,
|
||||
input[type="email"]:hover,
|
||||
input[type="password"]:hover,
|
||||
input[type="datetime-local"]:hover {
|
||||
border-color: #939393;
|
||||
}
|
||||
|
||||
@@ -351,9 +374,7 @@ input[name='filter_panel_range_max'] {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.text_link {
|
||||
|
||||
}
|
||||
.text_link {}
|
||||
|
||||
.text_link:hover {
|
||||
cursor: pointer;
|
||||
@@ -390,9 +411,9 @@ input[name='filter_panel_range_max'] {
|
||||
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);
|
||||
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 {
|
||||
@@ -525,13 +546,13 @@ input[name='filter_panel_range_max'] {
|
||||
overflow-y: auto;
|
||||
/* display: flex; */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#games_library_alpha_pager {
|
||||
width: 50px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.games_library_alpha_pager_letter {
|
||||
@@ -604,6 +625,12 @@ input[name='filter_panel_range_max'] {
|
||||
border: 1px solid #2b2b2b;
|
||||
}
|
||||
|
||||
.game_tile_small_search {
|
||||
min-height: 50px;
|
||||
min-width: 50px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.game_tile_row {
|
||||
padding: 5px;
|
||||
display: block;
|
||||
@@ -706,9 +733,9 @@ input[name='filter_panel_range_max'] {
|
||||
}
|
||||
|
||||
.game_tile_image_shadow {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
.game_tile_image_row {
|
||||
@@ -720,11 +747,13 @@ input[name='filter_panel_range_max'] {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.game_tile_image, .unknown {
|
||||
.game_tile_image,
|
||||
.unknown {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.game_tile_image_row, .unknown {
|
||||
.game_tile_image_row,
|
||||
.unknown {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -790,9 +819,9 @@ input[name='filter_panel_range_max'] {
|
||||
max-width: 250px;
|
||||
max-height: 350px;
|
||||
width: 100%;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
.gamegenrelabel {
|
||||
@@ -842,9 +871,9 @@ input[name='filter_panel_range_max'] {
|
||||
padding: 10px;
|
||||
/*height: 350px;*/
|
||||
margin-bottom: 20px;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
#gamescreenshots_main {
|
||||
@@ -903,11 +932,16 @@ iframe {
|
||||
background-color: #383838;
|
||||
color: black;
|
||||
text-align: center;
|
||||
user-select: none; /* standard syntax */
|
||||
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
|
||||
-moz-user-select: none; /* mozilla browsers */
|
||||
-khtml-user-select: none; /* webkit (konqueror) browsers */
|
||||
-ms-user-select: none; /* IE10+ */
|
||||
user-select: none;
|
||||
/* standard syntax */
|
||||
-webkit-user-select: none;
|
||||
/* webkit (safari, chrome) browsers */
|
||||
-moz-user-select: none;
|
||||
/* mozilla browsers */
|
||||
-khtml-user-select: none;
|
||||
/* webkit (konqueror) browsers */
|
||||
-ms-user-select: none;
|
||||
/* IE10+ */
|
||||
}
|
||||
|
||||
.gamescreenshots_arrows:hover {
|
||||
@@ -981,9 +1015,7 @@ iframe {
|
||||
background-color: rgba(56, 56, 56, 0.9);
|
||||
}
|
||||
|
||||
#gamesummarytext_label {
|
||||
|
||||
}
|
||||
#gamesummarytext_label {}
|
||||
|
||||
.line-clamp-4 {
|
||||
overflow: hidden;
|
||||
@@ -1057,9 +1089,9 @@ th {
|
||||
-webkit-border-radius: 5px 5px 5px 5px;
|
||||
-moz-border-radius: 5px 5px 5px 5px;
|
||||
border: 1px solid #19d348;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
.romstart:hover {
|
||||
@@ -1084,9 +1116,9 @@ th {
|
||||
border-color: white;
|
||||
background-color: blue;
|
||||
outline-color: blue;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
.properties_button:hover {
|
||||
@@ -1115,14 +1147,18 @@ th {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div[name="properties_toc_item"],div[name="properties_user_toc_item"],div[name="properties_profile_toc_item"] {
|
||||
div[name="properties_toc_item"],
|
||||
div[name="properties_user_toc_item"],
|
||||
div[name="properties_profile_toc_item"] {
|
||||
padding: 10px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #2b2b2b;
|
||||
}
|
||||
|
||||
div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover,div[name="properties_profile_toc_item"]:hover {
|
||||
div[name="properties_toc_item"]:hover,
|
||||
div[name="properties_user_toc_item"]:hover,
|
||||
div[name="properties_profile_toc_item"]:hover {
|
||||
background-color: #2b2b2b;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1150,7 +1186,8 @@ div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.select2-container--default:hover, .select2-selection--multiple:hover {
|
||||
.select2-container--default:hover,
|
||||
.select2-selection--multiple:hover {
|
||||
border-color: #939393;
|
||||
}
|
||||
|
||||
@@ -1197,7 +1234,8 @@ div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.select2-selection--single:hover, .select2-selection__rendered:hover {
|
||||
.select2-selection--single:hover,
|
||||
.select2-selection__rendered:hover {
|
||||
border-color: #939393;
|
||||
}
|
||||
|
||||
@@ -1302,9 +1340,7 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#emulator {
|
||||
|
||||
}
|
||||
#emulator {}
|
||||
|
||||
.emulator_partscreen {
|
||||
margin: 0 auto;
|
||||
@@ -1377,15 +1413,14 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.rom_checkbox_box {
|
||||
|
||||
}
|
||||
.rom_checkbox_box {}
|
||||
|
||||
.rom_checkbox_box_hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#rom_edit, #rom_edit_delete {
|
||||
#rom_edit,
|
||||
#rom_edit_delete {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -1398,9 +1433,9 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
}
|
||||
|
||||
#game {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
#gametitle_criticrating {
|
||||
@@ -1440,7 +1475,7 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
width: 1000px;
|
||||
height: 90%;
|
||||
margin: 25px auto;
|
||||
/* overflow-x: scroll;*/
|
||||
/* overflow-x: scroll;*/
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -1468,7 +1503,8 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
}
|
||||
|
||||
.bgalt1 {
|
||||
background-color: transparent;;
|
||||
background-color: transparent;
|
||||
;
|
||||
}
|
||||
|
||||
.logs_table_cell_150px {
|
||||
@@ -1520,13 +1556,33 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
background-color: #383838;
|
||||
}
|
||||
|
||||
.string { color: lightblue; }
|
||||
.number { color: lightblue; }
|
||||
.boolean { color: lightblue; }
|
||||
.null { color: magenta; }
|
||||
.key { color: greenyellow; }
|
||||
.brace { color: #888; }
|
||||
.square { color: #fff000; }
|
||||
.string {
|
||||
color: lightblue;
|
||||
}
|
||||
|
||||
.number {
|
||||
color: lightblue;
|
||||
}
|
||||
|
||||
.boolean {
|
||||
color: lightblue;
|
||||
}
|
||||
|
||||
.null {
|
||||
color: magenta;
|
||||
}
|
||||
|
||||
.key {
|
||||
color: greenyellow;
|
||||
}
|
||||
|
||||
.brace {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.square {
|
||||
color: #fff000;
|
||||
}
|
||||
|
||||
.tagBox {
|
||||
position: absolute;
|
||||
@@ -1557,12 +1613,16 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
}
|
||||
|
||||
.loginwindow {
|
||||
position: fixed; /* Stay in place */
|
||||
position: fixed;
|
||||
/* Stay in place */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
width: 100%;
|
||||
/* Full width */
|
||||
height: 100%;
|
||||
/* Full height */
|
||||
overflow: auto;
|
||||
/* Enable scroll if needed */
|
||||
/*background-color: rgb(0,0,0); /* Fallback color */
|
||||
/*background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||
/*backdrop-filter: blur(8px);
|
||||
@@ -1575,11 +1635,13 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
.loginwindow-content {
|
||||
position: relative;
|
||||
background-color: #383838;
|
||||
margin: 15% auto; /* 15% from the top and centered */
|
||||
margin: 15% auto;
|
||||
/* 15% from the top and centered */
|
||||
padding: 10px;
|
||||
border: 1px solid #888;
|
||||
border-radius: 10px;
|
||||
width: 350px; /* Could be more or less, depending on screen size */
|
||||
width: 350px;
|
||||
/* Could be more or less, depending on screen size */
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
@@ -1615,7 +1677,8 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
.dropdown-content a, .dropdown-content span {
|
||||
.dropdown-content a,
|
||||
.dropdown-content span {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
@@ -1625,12 +1688,16 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
||||
.dropdown-content span {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
|
||||
/* Change color of dropdown links on hover */
|
||||
.dropdown-content a:hover {background-color: #ddd;}
|
||||
|
||||
.dropdown-content a:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
|
||||
.show {display:block;}
|
||||
.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdownroleitem {
|
||||
text-transform: capitalize;
|
||||
|
Reference in New Issue
Block a user