Compare commits

..

16 Commits

Author SHA1 Message Date
Michael Green
1ade1922df Cache objects in memory rather than requesting from the database (#154)
* Removed import block from LibraryScan (#150)

* Cache objects from database in memory to improve performance (#151)

* IGDB objects are now cached in memory

* Completed caching of PlatformMaps

* Check for null during cache clean (#153)
2023-10-11 16:06:59 +11:00
Michael Green
f9d6cc4bdc Added a check for the DB and a delay at start up if the database is not available (#148) 2023-10-09 16:34:59 +11:00
Michael Green
1934558595 Create libraries based on external unmanaged directories (#147)
* Library management is now complete

* Library functions complete

* Added default platform support
2023-10-09 12:19:59 +11:00
Michael Green
fc09db60ab Updated the about page (#137) 2023-09-27 21:58:43 +10:00
Michael Green
906456782a Logs page now has paging (#136) 2023-09-25 21:22:27 +10:00
Michael Green
d6d6a5d808 Resolved blocked service UI bug (#134) 2023-09-24 13:59:01 +10:00
Michael Green
586f2c69d8 Fixed incorrect docker command (#132) 2023-09-23 17:09:07 -07:00
Michael Green
45e4666c51 EmulatorJS version bump (#131) 2023-09-24 09:57:44 +10:00
Michael Green
d94c921815 Add a list of available cores for each platform to the platform map (#130) 2023-09-23 16:14:04 -07:00
Michael Green
fff22ea8d9 Other processes should now be blocked during database upgrade processes (#128) 2023-09-22 23:42:24 +10:00
Michael Green
9b930b2a51 Fail safe when the IGDB connector experiences an exception (#127)
* Updated readme with database limitations

* Wrapped all IGDB calls (except search) in try catch blocks
2023-09-22 20:24:09 +10:00
Michael Green
a0408a1d1d Added EmulatorJS core selection help link (#124) 2023-09-20 12:07:25 +10:00
Michael Green
f2c58bb172 All uses of hashes should now be lower case (#122) 2023-09-20 09:32:40 +10:00
Michael Green
7eb418d6a2 Signature ingestor database update is now a background task (#121)
* Updated background task code to support options and self clearing

* Moved background safe database upgrade code to a background task
2023-09-20 00:35:24 +10:00
Michael Green
60fab488a2 Add internet exposure warning to the README #118 2023-09-19 11:57:31 +10:00
Michael Green
5a5a2f94fb Remove ‘and’ from end of Game Boy BIOS hash (#116) 2023-09-19 08:53:08 +10:00
63 changed files with 3220 additions and 540 deletions

View File

@@ -2,15 +2,25 @@
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 ROM's.
## Warning
This project is currently not suitable for being exposed to the internet.
1. there is currently no authentication support, meaning anyone could trash your library
2. the server has not been hardened for exposure to the internet - so there maybe unknown vulnerabilities
If you expose the server to the internet, **you do so at your own risk**.
## Screenshots ## Screenshots
![Library](./screenshots/Library.png) ![Library](./screenshots/Library.png)
![Game](./screenshots/Game.png) ![Game](./screenshots/Game.png)
![Emulator](./screenshots/Emulator.png) ![Emulator](./screenshots/Emulator.png)
## Requirements ## Requirements
* MySQL Server 8+ * MySQL Server 8+*
* Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation * Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation
***Note**: MariaDB is currently not supported as Gaseous uses features present only in MySQL. This is being tracked in https://github.com/gaseous-project/gaseous-server/issues/93
## Third Party Projects ## Third Party Projects
The following projects are used by Gaseous The following projects are used by Gaseous
* https://dotnet.microsoft.com/en-us/apps/aspnet * https://dotnet.microsoft.com/en-us/apps/aspnet
@@ -76,12 +86,12 @@ Dockerfile and docker-compose-build.yml files have been provided to make deploym
2. Change into the gaseous-server directory 2. Change into the gaseous-server directory
3. Clone the submodules with the command ```git submodule update --init``` 3. Clone the submodules with the command ```git submodule update --init```
4. Open the docker-compose-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account 4. Open the docker-compose-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
5. Run the command "docker-compose up --file docker-compose-build.yml -d" 5. Run the command ```docker-compose --file docker-compose-build.yml up -d```
6. Connect to the host on port 5198 6. Connect to the host on port 5198
## Source ## Source
### Build and deploy ### Build and deploy
1. Install and configure a MySQL or MariaDB instance 1. Install and configure a MySQL instance
2. Install the dotnet 7.0 packages appropriate for your operating system 2. Install the dotnet 7.0 packages appropriate for your operating system
* See: https://learn.microsoft.com/en-us/dotnet/core/install/linux * See: https://learn.microsoft.com/en-us/dotnet/core/install/linux
3. Create a database user with permission to create a databse. Gaseous will create the new database and apply the database schema on it's first startup. 3. Create a database user with permission to create a databse. Gaseous will create the new database and apply the database schema on it's first startup.

View File

@@ -79,7 +79,7 @@ namespace gaseous_server.Classes
platformname = platform.Name, platformname = platform.Name,
description = emulatorBios.description, description = emulatorBios.description,
filename = emulatorBios.filename, filename = emulatorBios.filename,
hash = emulatorBios.hash hash = emulatorBios.hash.ToLower()
}; };
biosItems.Add(biosItem); biosItems.Add(biosItem);
} }

View File

@@ -171,13 +171,10 @@ namespace gaseous_server.Classes
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
// start background task // start background task
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 1, false, true);
{ queueItem.Options = Id;
if (qi.ItemType == ProcessQueue.QueueItemType.CollectionCompiler) { queueItem.ForceExecute();
qi.ForceExecute(); ProcessQueue.QueueItems.Add(queueItem);
break;
}
}
} }
} }
@@ -364,176 +361,173 @@ namespace gaseous_server.Classes
return collectionContents; return collectionContents;
} }
public static void CompileCollections() public static void CompileCollections(long CollectionId)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
List<CollectionItem> collectionItems = GetCollections(); CollectionItem collectionItem = GetCollection(CollectionId);
foreach (CollectionItem collectionItem in collectionItems) if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
{ {
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild) Logging.Log(Logging.LogType.Information, "Collections", "Beginning build of collection: " + collectionItem.Name);
// set starting
string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", collectionItem.Id);
dbDict.Add("bs", CollectionItem.CollectionBuildStatus.Building);
db.ExecuteCMD(sql, dbDict);
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = GetCollectionContent(collectionItem).Collection;
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + ".zip");
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString());
try
{ {
Logging.Log(Logging.LogType.Information, "Collections", "Beginning build of collection: " + collectionItem.Name);
// clean up if needed
// set starting if (File.Exists(ZipFilePath))
string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", collectionItem.Id);
dbDict.Add("bs", CollectionItem.CollectionBuildStatus.Building);
db.ExecuteCMD(sql, dbDict);
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = GetCollectionContent(collectionItem).Collection;
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + ".zip");
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString());
try
{ {
Logging.Log(Logging.LogType.Warning, "Collections", "Deleting existing build of collection: " + collectionItem.Name);
// clean up if needed File.Delete(ZipFilePath);
if (File.Exists(ZipFilePath)) }
{
Logging.Log(Logging.LogType.Warning, "Collections", "Deleting existing build of collection: " + collectionItem.Name);
File.Delete(ZipFilePath);
}
if (Directory.Exists(ZipFileTempPath)) if (Directory.Exists(ZipFileTempPath))
{ {
Directory.Delete(ZipFileTempPath, true); Directory.Delete(ZipFileTempPath, true);
} }
// gather collection files // gather collection files
Directory.CreateDirectory(ZipFileTempPath); Directory.CreateDirectory(ZipFileTempPath);
string ZipBiosPath = Path.Combine(ZipFileTempPath, "BIOS"); string ZipBiosPath = Path.Combine(ZipFileTempPath, "BIOS");
// get the games // get the games
foreach (CollectionContents.CollectionPlatformItem collectionPlatformItem in collectionPlatformItems) foreach (CollectionContents.CollectionPlatformItem collectionPlatformItem in collectionPlatformItems)
{
// get platform bios files if present
if (collectionItem.IncludeBIOSFiles == true)
{ {
// get platform bios files if present List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
if (collectionItem.IncludeBIOSFiles == true) if (!Directory.Exists(ZipBiosPath)) {
Directory.CreateDirectory(ZipBiosPath);
}
foreach (Bios.BiosItem biosItem in bios)
{ {
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true); if (File.Exists(biosItem.biosPath))
if (!Directory.Exists(ZipBiosPath)) {
Directory.CreateDirectory(ZipBiosPath);
}
foreach (Bios.BiosItem biosItem in bios)
{ {
if (File.Exists(biosItem.biosPath)) Logging.Log(Logging.LogType.Information, "Collections", "Copying BIOS file: " + biosItem.filename);
{ File.Copy(biosItem.biosPath, Path.Combine(ZipBiosPath, biosItem.filename));
Logging.Log(Logging.LogType.Information, "Collections", "Copying BIOS file: " + biosItem.filename);
File.Copy(biosItem.biosPath, Path.Combine(ZipBiosPath, biosItem.filename));
}
} }
} }
}
// create platform directory // create platform directory
string ZipPlatformPath = ""; string ZipPlatformPath = "";
switch (collectionItem.FolderStructure) switch (collectionItem.FolderStructure)
{ {
case CollectionItem.FolderStructures.Gaseous: case CollectionItem.FolderStructures.Gaseous:
ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug);
break;
case CollectionItem.FolderStructures.RetroPie:
try
{
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(collectionPlatformItem.Id);
ZipPlatformPath = Path.Combine(ZipFileTempPath, "roms", platformMapItem.RetroPieDirectoryName);
}
catch
{
ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug); ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug);
break; }
case CollectionItem.FolderStructures.RetroPie: break;
try
{
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(collectionPlatformItem.Id);
ZipPlatformPath = Path.Combine(ZipFileTempPath, "roms", platformMapItem.RetroPieDirectoryName);
}
catch
{
ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug);
}
break; }
if (!Directory.Exists(ZipPlatformPath))
{
Directory.CreateDirectory(ZipPlatformPath);
}
} foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem in collectionPlatformItem.Games)
if (!Directory.Exists(ZipPlatformPath)) {
bool includeGame = false;
if (collectionGameItem.InclusionStatus == null)
{ {
Directory.CreateDirectory(ZipPlatformPath); includeGame = true;
} }
else
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem in collectionPlatformItem.Games)
{ {
bool includeGame = false; if (collectionGameItem.InclusionStatus.InclusionState == CollectionItem.AlwaysIncludeStatus.AlwaysInclude)
if (collectionGameItem.InclusionStatus == null)
{ {
includeGame = true; includeGame = true;
} }
else }
{
if (collectionGameItem.InclusionStatus.InclusionState == CollectionItem.AlwaysIncludeStatus.AlwaysInclude)
{
includeGame = true;
}
}
if (includeGame == true) if (includeGame == true)
{
string ZipGamePath = "";
switch (collectionItem.FolderStructure)
{ {
string ZipGamePath = ""; case CollectionItem.FolderStructures.Gaseous:
switch (collectionItem.FolderStructure) // create game directory
{ ZipGamePath = Path.Combine(ZipPlatformPath, collectionGameItem.Slug);
case CollectionItem.FolderStructures.Gaseous: if (!Directory.Exists(ZipGamePath))
// create game directory
ZipGamePath = Path.Combine(ZipPlatformPath, collectionGameItem.Slug);
if (!Directory.Exists(ZipGamePath))
{
Directory.CreateDirectory(ZipGamePath);
}
break;
case CollectionItem.FolderStructures.RetroPie:
ZipGamePath = ZipPlatformPath;
break;
}
// copy in roms
foreach (Roms.GameRomItem gameRomItem in collectionGameItem.Roms)
{
if (File.Exists(gameRomItem.Path))
{ {
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name); Directory.CreateDirectory(ZipGamePath);
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name));
} }
break;
case CollectionItem.FolderStructures.RetroPie:
ZipGamePath = ZipPlatformPath;
break;
}
// copy in roms
foreach (Roms.GameRomItem gameRomItem in collectionGameItem.Roms)
{
if (File.Exists(gameRomItem.Path))
{
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name);
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name));
} }
} }
} }
} }
// compress to zip
Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection");
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
// clean up
if (Directory.Exists(ZipFileTempPath))
{
Logging.Log(Logging.LogType.Information, "Collections", "Cleaning up");
Directory.Delete(ZipFileTempPath, true);
}
// set completed
dbDict["bs"] = CollectionItem.CollectionBuildStatus.Completed;
db.ExecuteCMD(sql, dbDict);
} }
catch (Exception ex)
// compress to zip
Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection");
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
// clean up
if (Directory.Exists(ZipFileTempPath))
{ {
// clean up Logging.Log(Logging.LogType.Information, "Collections", "Cleaning up");
if (Directory.Exists(ZipFileTempPath)) Directory.Delete(ZipFileTempPath, true);
{
Directory.Delete(ZipFileTempPath, true);
}
if (File.Exists(ZipFilePath))
{
File.Delete(ZipFilePath);
}
// set failed
dbDict["bs"] = CollectionItem.CollectionBuildStatus.Failed;
db.ExecuteCMD(sql, dbDict);
Logging.Log(Logging.LogType.Critical, "Collection Builder", "Collection building has failed", ex);
} }
// set completed
dbDict["bs"] = CollectionItem.CollectionBuildStatus.Completed;
db.ExecuteCMD(sql, dbDict);
}
catch (Exception ex)
{
// clean up
if (Directory.Exists(ZipFileTempPath))
{
Directory.Delete(ZipFileTempPath, true);
}
if (File.Exists(ZipFilePath))
{
File.Delete(ZipFilePath);
}
// set failed
dbDict["bs"] = CollectionItem.CollectionBuildStatus.Failed;
db.ExecuteCMD(sql, dbDict);
Logging.Log(Logging.LogType.Critical, "Collection Builder", "Collection building has failed", ex);
} }
} }
} }

View File

@@ -0,0 +1,176 @@
using System;
using System.Data;
using gaseous_server.Classes.Metadata;
using gaseous_tools;
using IGDB.Models;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
namespace gaseous_server
{
public static class GameLibrary
{
// exceptions
public class PathExists : Exception
{
public PathExists(string path) : base("The library path " + path + " already exists.")
{}
}
public class PathNotFound : Exception
{
public PathNotFound(string path) : base("The path " + path + " does not exist.")
{}
}
public class LibraryNotFound : Exception
{
public LibraryNotFound(int LibraryId) : base("Library id " + LibraryId + " does not exist.")
{}
}
public class CannotDeleteDefaultLibrary : Exception
{
public CannotDeleteDefaultLibrary() : base("Unable to delete the default library.")
{}
}
// code
public static LibraryItem GetDefaultLibrary
{
get
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM GameLibraries WHERE DefaultLibrary=1 LIMIT 1";
DataTable data = db.ExecuteCMD(sql);
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"]));
return library;
}
}
public static List<LibraryItem> GetLibraries
{
get
{
List<LibraryItem> libraryItems = new List<LibraryItem>();
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM GameLibraries";
DataTable data = db.ExecuteCMD(sql);
foreach (DataRow row in data.Rows)
{
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
libraryItems.Add(library);
}
return libraryItems;
}
}
public static LibraryItem AddLibrary(string Name, string Path, long DefaultPlatformId)
{
string PathName = Common.NormalizePath(Path);
// check path isn't already in place
foreach (LibraryItem item in GetLibraries)
{
if (Common.NormalizePath(PathName) == Common.NormalizePath(item.Path))
{
// already existing path!
throw new PathExists(PathName);
}
}
if (!System.IO.Path.Exists(PathName))
{
throw new PathNotFound(PathName);
}
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO GameLibraries (Name, Path, DefaultPlatform, DefaultLibrary) VALUES (@name, @path, @defaultplatform, 0); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("name", Name);
dbDict.Add("path", PathName);
dbDict.Add("defaultplatform", DefaultPlatformId);
DataTable data = db.ExecuteCMD(sql, dbDict);
int newLibraryId = (int)(long)data.Rows[0][0];
return GetLibrary(newLibraryId);
}
public static void DeleteLibrary(int LibraryId)
{
if (GetLibrary(LibraryId).IsDefaultLibrary == false)
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", LibraryId);
db.ExecuteCMD(sql, dbDict);
}
else
{
throw new CannotDeleteDefaultLibrary();
}
}
public static LibraryItem GetLibrary(int LibraryId)
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM GameLibraries WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", LibraryId);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 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"]));
return library;
}
else
{
throw new LibraryNotFound(LibraryId);
}
}
public class LibraryItem
{
public LibraryItem(int Id, string Name, string Path, long DefaultPlatformId, bool IsDefaultLibrary)
{
_Id = Id;
_Name = Name;
_Path = Path;
_DefaultPlatformId = DefaultPlatformId;
_IsDefaultLibrary = IsDefaultLibrary;
}
int _Id = 0;
string _Name = "";
string _Path = "";
long _DefaultPlatformId = 0;
bool _IsDefaultLibrary = false;
public int Id => _Id;
public string Name => _Name;
public string Path => _Path;
public long DefaultPlatformId => _DefaultPlatformId;
public string? DefaultPlatformName
{
get
{
if (_DefaultPlatformId != 0)
{
Platform platform = Platforms.GetPlatform(_DefaultPlatformId);
return platform.Name;
}
else
{
return "";
}
}
}
public bool IsDefaultLibrary => _IsDefaultLibrary;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.IO.Compression;
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_tools; using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI; using MySqlX.XDevAPI;
@@ -98,7 +99,7 @@ namespace gaseous_server.Classes
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId); IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
// add to database // add to database
StoreROM(hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath); StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
} }
} }
else else
@@ -391,7 +392,7 @@ namespace gaseous_server.Classes
return SearchCandidates; return SearchCandidates;
} }
public static long StoreROM(Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0) public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -401,7 +402,7 @@ namespace gaseous_server.Classes
if (UpdateId == 0) if (UpdateId == 0)
{ {
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
} else } else
{ {
sql = "UPDATE Games_Roms SET PlatformId=platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;"; sql = "UPDATE Games_Roms SET PlatformId=platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;";
@@ -418,6 +419,7 @@ namespace gaseous_server.Classes
dbDict.Add("metadatasource", discoveredSignature.Rom.SignatureSource); dbDict.Add("metadatasource", discoveredSignature.Rom.SignatureSource);
dbDict.Add("metadatagamename", discoveredSignature.Game.Name); dbDict.Add("metadatagamename", discoveredSignature.Game.Name);
dbDict.Add("metadataversion", 2); dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id);
if (discoveredSignature.Rom.Attributes != null) if (discoveredSignature.Rom.Attributes != null)
{ {
@@ -450,7 +452,10 @@ namespace gaseous_server.Classes
} }
// move to destination // move to destination
MoveGameFile(romId); if (library.IsDefaultLibrary == true)
{
MoveGameFile(romId);
}
return romId; return romId;
} }
@@ -474,7 +479,7 @@ namespace gaseous_server.Classes
{ {
gameSlug = game.Slug; gameSlug = game.Slug;
} }
string DestinationPath = Path.Combine(Config.LibraryConfiguration.LibraryDataDirectory, gameSlug, platformSlug); string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug);
if (!Directory.Exists(DestinationPath)) if (!Directory.Exists(DestinationPath))
{ {
Directory.CreateDirectory(DestinationPath); Directory.CreateDirectory(DestinationPath);
@@ -532,12 +537,16 @@ namespace gaseous_server.Classes
public static void OrganiseLibrary() public static void OrganiseLibrary()
{ {
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting library organisation"); Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary;
// move rom files to their new location // move rom files to their new location
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms"; string sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid";
DataTable romDT = db.ExecuteCMD(sql); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("libraryid", library.Id);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
if (romDT.Rows.Count > 0) if (romDT.Rows.Count > 0)
{ {
@@ -550,9 +559,9 @@ namespace gaseous_server.Classes
} }
// clean up empty directories // clean up empty directories
DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryDataDirectory); DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed library organisation"); Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed default library organisation");
} }
private static void DeleteOrphanedDirectories(string startLocation) private static void DeleteOrphanedDirectories(string startLocation)
@@ -570,154 +579,179 @@ namespace gaseous_server.Classes
public static void LibraryScan() public static void LibraryScan()
{ {
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting library scan"); foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Logging.Log(Logging.LogType.Information, "Library Scan", "Looking for duplicate library files to clean up");
string duplicateSql = "DELETE r1 FROM Games_Roms r1 INNER JOIN Games_Roms r2 WHERE r1.Id > r2.Id AND r1.MD5 = r2.MD5;";
db.ExecuteCMD(duplicateSql);
string sql = "SELECT * FROM Games_Roms ORDER BY `name`";
DataTable dtRoms = db.ExecuteCMD(sql);
// clean out database entries in the import folder
if (dtRoms.Rows.Count > 0)
{ {
for (var i = 0; i < dtRoms.Rows.Count; i++) Logging.Log(Logging.LogType.Information, "Library Scan", "Starting library scan. Library " + library.Name);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Logging.Log(Logging.LogType.Information, "Library Scan", "Looking for duplicate library files to clean up");
string duplicateSql = "DELETE r1 FROM Games_Roms r1 INNER JOIN Games_Roms r2 WHERE r1.Id > r2.Id AND r1.MD5 = r2.MD5 AND r1.LibraryId=@libraryid AND r2.LibraryId=@libraryid;";
Dictionary<string, object> dupDict = new Dictionary<string, object>();
dupDict.Add("libraryid", library.Id);
db.ExecuteCMD(duplicateSql, dupDict);
string sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("libraryid", library.Id);
DataTable dtRoms = db.ExecuteCMD(sql, dbDict);
// clean out database entries in the import folder
if (dtRoms.Rows.Count > 0)
{ {
long romId = (long)dtRoms.Rows[i]["Id"];
string romPath = (string)dtRoms.Rows[i]["Path"];
if (!romPath.StartsWith(Config.LibraryConfiguration.LibraryDataDirectory))
{
Logging.Log(Logging.LogType.Information, "Library Scan", " Deleting database entry for files with incorrect directory " + romPath);
string deleteSql = "DELETE FROM Games_Roms WHERE Id=@id";
Dictionary<string, object> deleteDict = new Dictionary<string, object>();
deleteDict.Add("Id", romId);
db.ExecuteCMD(deleteSql, deleteDict);
}
}
}
sql = "SELECT * FROM Games_Roms ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql);
// 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");
string[] LibraryFiles = Directory.GetFiles(Config.LibraryConfiguration.LibraryDataDirectory, "*.*", SearchOption.AllDirectories);
foreach (string LibraryFile in LibraryFiles)
{
if (!Common.SkippableFiles.Contains<string>(Path.GetFileName(LibraryFile), StringComparer.OrdinalIgnoreCase))
{
Common.hashObject LibraryFileHash = new Common.hashObject(LibraryFile);
// check if file is in database
bool romFound = false;
for (var i = 0; i < dtRoms.Rows.Count; i++) for (var i = 0; i < dtRoms.Rows.Count; i++)
{ {
long romId = (long)dtRoms.Rows[i]["Id"]; long romId = (long)dtRoms.Rows[i]["Id"];
string romPath = (string)dtRoms.Rows[i]["Path"]; string romPath = (string)dtRoms.Rows[i]["Path"];
string romMd5 = (string)dtRoms.Rows[i]["MD5"];
if ((LibraryFile == romPath) || (LibraryFileHash.md5hash == romMd5)) if (!romPath.StartsWith(library.Path))
{ {
romFound = true; Logging.Log(Logging.LogType.Information, "Library Scan", " Deleting database entry for files with incorrect directory " + romPath);
break; string deleteSql = "DELETE FROM Games_Roms WHERE Id=@id AND LibraryId=@libraryid";
Dictionary<string, object> deleteDict = new Dictionary<string, object>();
deleteDict.Add("Id", romId);
deleteDict.Add("libraryid", library.Id);
db.ExecuteCMD(deleteSql, deleteDict);
} }
} }
if (romFound == false)
{
// file is not in database - process it
Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile);
Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile);
Logging.Log(Logging.LogType.Information, "Library Scan", " Orphaned file found in library: " + LibraryFile);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
StoreROM(hash, determinedGame, determinedPlatform, sig, LibraryFile);
}
} }
}
sql = "SELECT * FROM Games_Roms ORDER BY `name`"; sql = "SELECT * FROM Games_Roms ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql); dtRoms = db.ExecuteCMD(sql, dbDict);
// check all roms to see if their local file still exists // search for files in the library that aren't in the database
Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk"); Logging.Log(Logging.LogType.Information, "Library Scan", "Looking for orphaned library files to add");
if (dtRoms.Rows.Count > 0) string[] LibraryFiles = Directory.GetFiles(library.Path, "*.*", SearchOption.AllDirectories);
{ foreach (string LibraryFile in LibraryFiles)
for (var i = 0; i < dtRoms.Rows.Count; i++)
{ {
long romId = (long)dtRoms.Rows[i]["Id"]; if (!Common.SkippableFiles.Contains<string>(Path.GetFileName(LibraryFile), StringComparer.OrdinalIgnoreCase))
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"];
Logging.Log(Logging.LogType.Information, "Library Scan", " Processing ROM at path " + romPath);
if (File.Exists(romPath))
{ {
// file exists, so lets check to make sure the signature was matched, and update if a signature can be found Common.hashObject LibraryFileHash = new Common.hashObject(LibraryFile);
if (
romMetadataSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None || // check if file is in database
(int)dtRoms.Rows[i]["MetadataVersion"] == 1 bool romFound = false;
) for (var i = 0; i < dtRoms.Rows.Count; i++)
{ {
Common.hashObject hash = new Common.hashObject long romId = (long)dtRoms.Rows[i]["Id"];
string romPath = (string)dtRoms.Rows[i]["Path"];
string romMd5 = (string)dtRoms.Rows[i]["MD5"];
if ((LibraryFile == romPath) || (LibraryFileHash.md5hash == romMd5))
{ {
md5hash = (string)dtRoms.Rows[i]["MD5"], romFound = true;
sha1hash = (string)dtRoms.Rows[i]["SHA1"] break;
};
FileInfo fi = new FileInfo(romPath);
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath);
if (sig.Rom.SignatureSource != gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None)
{
Logging.Log(Logging.LogType.Information, "Library Scan", " Update signature found for " + romPath);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
StoreROM(hash, determinedGame, determinedPlatform, sig, romPath, romId);
} }
} }
if (romPath != ComputeROMPath(romId)) if (romFound == false)
{ {
MoveGameFile(romId); // file is not in database - process it
Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile);
Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile);
Logging.Log(Logging.LogType.Information, "Library Scan", " Orphaned file found in library: " + LibraryFile);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
IGDB.Models.Game determinedGame = new Game();
if (determinedPlatform == null)
{
if (library.DefaultPlatformId == 0)
{
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
{
determinedGame = SearchForGame(sig.Game.Name, (long)determinedPlatform.Id);
}
StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile);
} }
} }
else }
{
// file doesn't exist where it's supposed to be! delete it from the db
Logging.Log(Logging.LogType.Warning, "Library Scan", " Deleting orphaned database entry for " + romPath);
string deleteSql = "DELETE FROM Games_Roms WHERE Id = @id"; sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
Dictionary<string, object> deleteDict = new Dictionary<string, object>(); dtRoms = db.ExecuteCMD(sql, dbDict);
deleteDict.Add("id", romId);
db.ExecuteCMD(deleteSql, deleteDict); // check all roms to see if their local file still exists
Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk");
if (dtRoms.Rows.Count > 0)
{
for (var i = 0; i < dtRoms.Rows.Count; i++)
{
long romId = (long)dtRoms.Rows[i]["Id"];
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"];
Logging.Log(Logging.LogType.Information, "Library Scan", " Processing ROM at path " + romPath);
if (File.Exists(romPath))
{
// file exists, so lets check to make sure the signature was matched, and update if a signature can be found
if (
romMetadataSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None ||
(int)dtRoms.Rows[i]["MetadataVersion"] == 1
)
{
Common.hashObject hash = new Common.hashObject
{
md5hash = (string)dtRoms.Rows[i]["MD5"],
sha1hash = (string)dtRoms.Rows[i]["SHA1"]
};
FileInfo fi = new FileInfo(romPath);
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath);
if (sig.Rom.SignatureSource != gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None)
{
Logging.Log(Logging.LogType.Information, "Library Scan", " Update signature found for " + romPath);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
}
}
if (library.IsDefaultLibrary == true)
{
if (romPath != ComputeROMPath(romId))
{
MoveGameFile(romId);
}
}
}
else
{
// file doesn't exist where it's supposed to be! delete it from the db
Logging.Log(Logging.LogType.Warning, "Library Scan", " Deleting orphaned database entry for " + romPath);
string deleteSql = "DELETE FROM Games_Roms WHERE Id = @id AND LibraryId = @libraryid";
Dictionary<string, object> deleteDict = new Dictionary<string, object>();
deleteDict.Add("id", romId);
deleteDict.Add("libraryid", library.Id);
db.ExecuteCMD(deleteSql, deleteDict);
}
} }
} }
}
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed"); Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
}
} }
} }
} }

View File

@@ -77,10 +77,17 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue); UpdateSubClasses(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
UpdateSubClasses(returnValue); returnValue = await GetObjectFromServer(WhereClause);
break; Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
break; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
break; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -78,10 +78,17 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true; forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause, LogoPath);
break; Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
break; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -74,9 +74,16 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue); UpdateSubClasses(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
if (returnValue != null) { Storage.NewCacheValue(returnValue, true); } {
UpdateSubClasses(returnValue); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);

View File

@@ -80,12 +80,17 @@ namespace gaseous_server.Classes.Metadata
} }
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); try
if (returnValue != null)
{ {
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
forceImageDownload = true; forceImageDownload = true;
} }
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);

View File

@@ -77,10 +77,18 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true; forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause, LogoPath);
break; Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -78,12 +78,17 @@ namespace gaseous_server.Classes.Metadata
} }
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
if (returnValue != null)
{ {
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
} }
break; catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
break; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -68,18 +68,23 @@ namespace gaseous_server.Classes.Metadata
} }
GameMode returnValue = new GameMode(); GameMode returnValue = new GameMode();
bool forceImageDownload = false;
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);

View File

@@ -68,19 +68,24 @@ namespace gaseous_server.Classes.Metadata
} }
GameVideo returnValue = new GameVideo(); GameVideo returnValue = new GameVideo();
bool forceImageDownload = false;
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause);
break; Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -102,9 +102,16 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue, getAllMetadata, followSubGames); UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
return returnValue; return returnValue;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
UpdateSubClasses(returnValue, getAllMetadata, followSubGames); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
}
return returnValue; return returnValue;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue); return Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);

View File

@@ -68,19 +68,24 @@ namespace gaseous_server.Classes.Metadata
} }
Genre returnValue = new Genre(); Genre returnValue = new Genre();
bool forceImageDownload = false;
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause);
break; Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -74,9 +74,16 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue); UpdateSubClasses(returnValue);
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
UpdateSubClasses(returnValue); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);

View File

@@ -68,18 +68,23 @@ namespace gaseous_server.Classes.Metadata
} }
MultiplayerMode returnValue = new MultiplayerMode(); MultiplayerMode returnValue = new MultiplayerMode();
bool forceImageDownload = false;
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);

View File

@@ -80,12 +80,17 @@ namespace gaseous_server.Classes.Metadata
} }
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); try
if (returnValue != null)
{ {
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
forceImageDownload = true; forceImageDownload = true;
} }
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);

View File

@@ -78,12 +78,17 @@ namespace gaseous_server.Classes.Metadata
} }
return returnValue; return returnValue;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
if (returnValue != null)
{ {
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(ParentPlatform, returnValue); UpdateSubClasses(ParentPlatform, returnValue);
} }
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);
}
return returnValue; return returnValue;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
return Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue); return Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);

View File

@@ -99,11 +99,19 @@ namespace gaseous_server.Classes.Metadata
AddPlatformMapping(returnValue); AddPlatformMapping(returnValue);
return returnValue; return returnValue;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
UpdateSubClasses(returnValue); returnValue = await GetObjectFromServer(WhereClause);
AddPlatformMapping(returnValue); Storage.NewCacheValue(returnValue, true);
return returnValue; UpdateSubClasses(returnValue);
AddPlatformMapping(returnValue);
return returnValue;
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
}
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue); return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
default: default:
@@ -148,7 +156,7 @@ namespace gaseous_server.Classes.Metadata
IGDBSlug = platform.Slug, IGDBSlug = platform.Slug,
AlternateNames = new List<string>{ platform.AlternativeName } AlternateNames = new List<string>{ platform.AlternativeName }
}; };
Models.PlatformMapping.WritePlatformMap(item, false); Models.PlatformMapping.WritePlatformMap(item, false, true);
} }
} }

View File

@@ -77,9 +77,16 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true; forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);
}
break; break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);

View File

@@ -78,10 +78,18 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true; forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause, LogoPath);
break; Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -4,6 +4,7 @@ using System.Reflection;
using gaseous_tools; using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.Extensions.Caching.Memory;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -16,14 +17,32 @@ 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)
{ {
return _GetCacheStatus(Endpoint, "slug", Slug); CacheClean();
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)
{ {
return _GetCacheStatus(Endpoint, "id", Id); CacheClean();
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)
@@ -164,6 +183,21 @@ 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 gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField; string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
@@ -181,7 +215,11 @@ namespace gaseous_server.Classes.Metadata
else else
{ {
DataRow dataRow = dt.Rows[0]; DataRow dataRow = dt.Rows[0];
return BuildCacheObject<T>(EndpointType, dataRow); object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{
Object = returnObject
});
return (T)returnObject;
} }
} }
@@ -380,6 +418,35 @@ namespace gaseous_server.Classes.Metadata
return EndpointType; return EndpointType;
} }
private static void CacheClean()
{
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);
}
}
}
private class MemoryCacheObject
{
public object Object { get; set; }
public DateTime CreationTime { get; } = DateTime.UtcNow;
public DateTime ExpiryTime
{
get
{
return CreationTime.AddMinutes(60);
}
}
}
} }
} }

View File

@@ -77,10 +77,17 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true; forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause); try
Storage.NewCacheValue(returnValue, true); {
forceImageDownload = true; returnValue = await GetObjectFromServer(WhereClause);
break; Storage.NewCacheValue(returnValue, true);
return returnValue;
}
catch (Exception ex)
{
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
}
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
break; break;

View File

@@ -81,16 +81,19 @@ namespace gaseous_server.Classes
public static void DeleteRom(long RomId) public static void DeleteRom(long RomId)
{ {
GameRomItem rom = GetRom(RomId); GameRomItem rom = GetRom(RomId);
if (File.Exists(rom.Path)) if (rom.Library.IsDefaultLibrary == true)
{ {
File.Delete(rom.Path); if (File.Exists(rom.Path))
} {
File.Delete(rom.Path);
}
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM Games_Roms WHERE Id = @id"; string sql = "DELETE FROM Games_Roms WHERE 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);
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
}
} }
private static GameRomItem BuildRom(DataRow romDR) private static GameRomItem BuildRom(DataRow romDR)
@@ -103,9 +106,9 @@ namespace gaseous_server.Classes
GameId = (long)romDR["gameid"], GameId = (long)romDR["gameid"],
Name = (string)romDR["name"], Name = (string)romDR["name"],
Size = (long)romDR["size"], Size = (long)romDR["size"],
CRC = (string)romDR["crc"], CRC = ((string)romDR["crc"]).ToLower(),
MD5 = (string)romDR["md5"], MD5 = ((string)romDR["md5"]).ToLower(),
SHA1 = (string)romDR["sha1"], SHA1 = ((string)romDR["sha1"]).ToLower(),
DevelopmentStatus = (string)romDR["developmentstatus"], DevelopmentStatus = (string)romDR["developmentstatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")), Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
RomType = (int)romDR["romtype"], RomType = (int)romDR["romtype"],
@@ -113,7 +116,8 @@ namespace gaseous_server.Classes
MediaLabel = (string)romDR["medialabel"], MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"], Path = (string)romDR["path"],
Source = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)romDR["metadatasource"], Source = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)romDR["metadatasource"],
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], "") SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
}; };
// check for a web emulator and update the romItem // check for a web emulator and update the romItem
@@ -153,6 +157,7 @@ namespace gaseous_server.Classes
public string? Path { get; set; } public string? Path { get; set; }
public gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; } public gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; }
public string? SignatureSourceGameTitle { get; set;} public string? SignatureSourceGameTitle { get; set;}
public GameLibrary.LibraryItem Library { get; set; }
} }
} }
} }

View File

@@ -188,9 +188,9 @@ namespace gaseous_server.SignatureIngestors.XML
dbDict.Add("gameid", gameId); dbDict.Add("gameid", gameId);
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, "")); dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, "")); dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, ""));
dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "")); dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower());
dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "")); dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "").ToString().ToLower());
dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "")); dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "").ToString().ToLower());
dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, "")); dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, ""));
if (romObject.Attributes != null) if (romObject.Attributes != null)
@@ -212,12 +212,13 @@ namespace gaseous_server.SignatureIngestors.XML
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, "")); dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, "")); dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, ""));
dbDict.Add("metadatasource", romObject.SignatureSource); 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) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; 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); sigDB = db.ExecuteCMD(sql, dbDict);

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
public class LibraryController : Controller
{
[HttpGet]
[ProducesResponseType(typeof(List<GameLibrary.LibraryItem>), StatusCodes.Status200OK)]
public ActionResult GetLibraries()
{
return Ok(GameLibrary.GetLibraries);
}
[HttpGet("{LibraryId}")]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetLibrary(int LibraryId)
{
try
{
return Ok(GameLibrary.GetLibrary(LibraryId));
}
catch
{
return NotFound();
}
}
[HttpPost]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public ActionResult AddLibrary(string Name, string Path, long DefaultPlatformId)
{
try
{
return Ok(GameLibrary.AddLibrary(Name, Path, DefaultPlatformId));
}
catch (GameLibrary.PathExists exPE)
{
return Conflict("Path already used in another library");
}
catch (GameLibrary.PathNotFound exPNF)
{
return NotFound("Path not found");
}
}
[HttpDelete("{LibraryId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult DelLibrary(int LibraryId)
{
try
{
GameLibrary.DeleteLibrary(LibraryId);
return Ok();
}
catch (GameLibrary.CannotDeleteDefaultLibrary exCDDL)
{
return BadRequest(exCDDL.ToString());
}
catch (GameLibrary.LibraryNotFound exLNF)
{
return NotFound(exLNF.ToString());
}
}
}
}

View File

@@ -13,9 +13,9 @@ namespace gaseous_server.Controllers
{ {
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs() public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100)
{ {
return Logging.GetLogs(); return Logging.GetLogs(StartIndex, PageNumber, PageSize);
} }
} }
} }

View File

@@ -110,32 +110,32 @@ namespace gaseous_server.Controllers
return Ok(new { count = files.Count, size }); return Ok(new { count = files.Count, size });
} }
[HttpPost] // [HttpPost]
[Route("{PlatformId}")] // [Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] // [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] // [ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)] // [ProducesResponseType(StatusCodes.Status409Conflict)]
public ActionResult NewPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map) // public ActionResult NewPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
{ // {
try // try
{ // {
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId); // PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId);
if (platformMapItem != null) // if (platformMapItem != null)
{ // {
return Conflict(); // return Conflict();
} // }
else // else
{ // {
PlatformMapping.WritePlatformMap(Map, false); // PlatformMapping.WritePlatformMap(Map, false, false);
return Ok(PlatformMapping.GetPlatformMap(PlatformId)); // return Ok(PlatformMapping.GetPlatformMap(PlatformId));
} // }
} // }
catch // catch
{ // {
return NotFound(); // return NotFound();
} // }
} // }
[HttpPatch] [HttpPatch]
[Route("{PlatformId}")] [Route("{PlatformId}")]
@@ -149,7 +149,7 @@ namespace gaseous_server.Controllers
if (platformMapItem != null) if (platformMapItem != null)
{ {
PlatformMapping.WritePlatformMap(Map, true); PlatformMapping.WritePlatformMap(Map, true, false);
return Ok(PlatformMapping.GetPlatformMap(PlatformId)); return Ok(PlatformMapping.GetPlatformMap(PlatformId));
} }
else else

View File

@@ -88,8 +88,8 @@ namespace gaseous_server.Controllers
Name = (string)sigDbRow["romname"], Name = (string)sigDbRow["romname"],
Size = (Int64)sigDbRow["Size"], Size = (Int64)sigDbRow["Size"],
Crc = (string)sigDbRow["CRC"], Crc = (string)sigDbRow["CRC"],
Md5 = (string)sigDbRow["MD5"], Md5 = ((string)sigDbRow["MD5"]).ToLower(),
Sha1 = (string)sigDbRow["SHA1"], Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"], DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")), Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
RomType = (Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"], RomType = (Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"],

View File

@@ -24,8 +24,10 @@ namespace gaseous_server.Controllers
// disk size // disk size
List<SystemInfo.PathItem> Disks = new List<SystemInfo.PathItem>(); List<SystemInfo.PathItem> Disks = new List<SystemInfo.PathItem>();
//Disks.Add(GetDisk(gaseous_tools.Config.ConfigurationPath)); foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries)
Disks.Add(GetDisk(gaseous_tools.Config.LibraryConfiguration.LibraryRootDirectory)); {
Disks.Add(GetDisk(libraryItem.Path));
}
ReturnValue.Paths = Disks; ReturnValue.Paths = Disks;
// database size // database size

View File

@@ -15,6 +15,8 @@ namespace gaseous_server.Models
{ {
public class PlatformMapping public class PlatformMapping
{ {
private static Dictionary<string, PlatformMapItem> PlatformMapCache = new Dictionary<string, PlatformMapItem>();
/// <summary> /// <summary>
/// Updates the platform map from the embedded platform map resource /// Updates the platform map from the embedded platform map resource
/// </summary> /// </summary>
@@ -25,7 +27,10 @@ namespace gaseous_server.Models
{ {
string rawJson = reader.ReadToEnd(); string rawJson = reader.ReadToEnd();
List<PlatformMapItem> platforms = new List<PlatformMapItem>(); List<PlatformMapItem> platforms = new List<PlatformMapItem>();
platforms = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem>>(rawJson); Newtonsoft.Json.JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings{
MaxDepth = 64
};
platforms = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem>>(rawJson, jsonSerializerSettings);
foreach (PlatformMapItem mapItem in platforms) foreach (PlatformMapItem mapItem in platforms)
{ {
@@ -41,7 +46,7 @@ namespace gaseous_server.Models
} }
else else
{ {
WritePlatformMap(mapItem, true); WritePlatformMap(mapItem, true, true);
Logging.Log(Logging.LogType.Information, "Platform Map", "Overwriting " + mapItem.IGDBName + " with default values."); Logging.Log(Logging.LogType.Information, "Platform Map", "Overwriting " + mapItem.IGDBName + " with default values.");
} }
} }
@@ -49,7 +54,7 @@ namespace gaseous_server.Models
{ {
Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from predefined data."); Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from predefined data.");
// doesn't exist - add it // doesn't exist - add it
WritePlatformMap(mapItem, false); WritePlatformMap(mapItem, false, true);
} }
} }
} }
@@ -73,13 +78,13 @@ namespace gaseous_server.Models
// still here? we must have found the item we're looking for! overwrite it // still here? we must have found the item we're looking for! overwrite it
Logging.Log(Logging.LogType.Information, "Platform Map", "Replacing " + mapItem.IGDBName + " from external JSON file."); Logging.Log(Logging.LogType.Information, "Platform Map", "Replacing " + mapItem.IGDBName + " from external JSON file.");
WritePlatformMap(mapItem, true); WritePlatformMap(mapItem, true, true);
} }
catch catch
{ {
// we caught a not found error, insert a new record // we caught a not found error, insert a new record
Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from external JSON file."); Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from external JSON file.");
WritePlatformMap(mapItem, false); WritePlatformMap(mapItem, false, true);
} }
} }
} }
@@ -95,7 +100,15 @@ namespace gaseous_server.Models
List<PlatformMapItem> platformMaps = new List<PlatformMapItem>(); List<PlatformMapItem> platformMaps = new List<PlatformMapItem>();
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
platformMaps.Add(BuildPlatformMapItem(row)); long mapId = (long)row["Id"];
if (PlatformMapCache.ContainsKey(mapId.ToString()))
{
platformMaps.Add(PlatformMapCache[mapId.ToString()]);
}
else
{
platformMaps.Add(BuildPlatformMapItem(row));
}
} }
platformMaps.Sort((x, y) => x.IGDBName.CompareTo(y.IGDBName)); platformMaps.Sort((x, y) => x.IGDBName.CompareTo(y.IGDBName));
@@ -106,27 +119,34 @@ namespace gaseous_server.Models
public static PlatformMapItem GetPlatformMap(long Id) public static PlatformMapItem GetPlatformMap(long Id)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); if (PlatformMapCache.ContainsKey(Id.ToString()))
string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 0)
{ {
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]); return PlatformMapCache[Id.ToString()];
return platformMap;
} }
else else
{ {
Exception exception = new Exception("Platform Map Id " + Id + " does not exist."); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Logging.Log(Logging.LogType.Critical, "Platform Map", "Platform Map Id " + Id + " does not exist.", exception); string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
throw exception; Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 0)
{
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]);
return platformMap;
}
else
{
Exception exception = new Exception("Platform Map Id " + Id + " does not exist.");
Logging.Log(Logging.LogType.Critical, "Platform Map", "Platform Map Id " + Id + " does not exist.", exception);
throw exception;
}
} }
} }
public static void WritePlatformMap(PlatformMapItem item, bool Update) public static void WritePlatformMap(PlatformMapItem item, bool Update, bool AllowAvailableEmulatorOverwrite)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
@@ -134,12 +154,19 @@ namespace gaseous_server.Models
if (Update == false) if (Update == false)
{ {
// insert // insert
sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core)"; sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core, AvailableWebEmulators) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core, @AvailableWebEmulators)";
} }
else else
{ {
// update // update
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core WHERE Id = @Id"; if (AllowAvailableEmulatorOverwrite == true)
{
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core, AvailableWebEmulators=@AvailableWebEmulators WHERE Id = @Id";
}
else
{
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);
dbDict.Add("RetroPieDirectoryName", item.RetroPieDirectoryName); dbDict.Add("RetroPieDirectoryName", item.RetroPieDirectoryName);
@@ -147,11 +174,13 @@ namespace gaseous_server.Models
{ {
dbDict.Add("WebEmulator_Type", item.WebEmulator.Type); dbDict.Add("WebEmulator_Type", item.WebEmulator.Type);
dbDict.Add("WebEmulator_Core", item.WebEmulator.Core); dbDict.Add("WebEmulator_Core", item.WebEmulator.Core);
dbDict.Add("AvailableWebEmulators", Newtonsoft.Json.JsonConvert.SerializeObject(item.WebEmulator.AvailableWebEmulators));
} }
else else
{ {
dbDict.Add("WebEmulator_Type", ""); dbDict.Add("WebEmulator_Type", "");
dbDict.Add("WebEmulator_Core", ""); dbDict.Add("WebEmulator_Core", "");
dbDict.Add("AvailableWebEmulators", "");
} }
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
@@ -206,6 +235,11 @@ namespace gaseous_server.Models
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
} }
} }
if (PlatformMapCache.ContainsKey(item.IGDBId.ToString()))
{
PlatformMapCache.Remove(item.IGDBId.ToString());
}
} }
static PlatformMapItem BuildPlatformMapItem(DataRow row) static PlatformMapItem BuildPlatformMapItem(DataRow row)
@@ -286,7 +320,7 @@ namespace gaseous_server.Models
{ {
filename = (string)Common.ReturnValueIfNull(biosRow["Filename"], ""), filename = (string)Common.ReturnValueIfNull(biosRow["Filename"], ""),
description = (string)Common.ReturnValueIfNull(biosRow["Description"], ""), description = (string)Common.ReturnValueIfNull(biosRow["Description"], ""),
hash = (string)Common.ReturnValueIfNull(biosRow["Hash"], "") hash = ((string)Common.ReturnValueIfNull(biosRow["Hash"], "")).ToLower()
}; };
bioss.Add(bios); bioss.Add(bios);
} }
@@ -304,10 +338,20 @@ namespace gaseous_server.Models
mapItem.RetroPieDirectoryName = (string)Common.ReturnValueIfNull(row["RetroPieDirectoryName"], ""); mapItem.RetroPieDirectoryName = (string)Common.ReturnValueIfNull(row["RetroPieDirectoryName"], "");
mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem{ mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem{
Type = (string)Common.ReturnValueIfNull(row["WebEmulator_Type"], ""), Type = (string)Common.ReturnValueIfNull(row["WebEmulator_Type"], ""),
Core = (string)Common.ReturnValueIfNull(row["WebEmulator_Core"], "") Core = (string)Common.ReturnValueIfNull(row["WebEmulator_Core"], ""),
AvailableWebEmulators = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem.WebEmulatorItem.AvailableWebEmulatorItem>>((string)Common.ReturnValueIfNull(row["AvailableWebEmulators"], "[]"))
}; };
mapItem.Bios = bioss; mapItem.Bios = bioss;
if (PlatformMapCache.ContainsKey(IGDBId.ToString()))
{
PlatformMapCache[IGDBId.ToString()] = mapItem;
}
else
{
PlatformMapCache.Add(IGDBId.ToString(), mapItem);
}
return mapItem; return mapItem;
} }
@@ -378,6 +422,21 @@ namespace gaseous_server.Models
{ {
public string Type { get; set; } public string Type { get; set; }
public string Core { get; set; } public string Core { get; set; }
public List<AvailableWebEmulatorItem> AvailableWebEmulators { get; set; } = new List<AvailableWebEmulatorItem>();
public class AvailableWebEmulatorItem
{
public string EmulatorType { get; set; }
public List<AvailableWebEmulatorCoreItem> AvailableWebEmulatorCores { get; set; } = new List<AvailableWebEmulatorCoreItem>();
public class AvailableWebEmulatorCoreItem
{
public string Core { get; set; }
public string? AlternateCoreName { get; set; } = "";
public bool Default { get; set; } = false;
}
}
} }
public List<EmulatorBiosItem> Bios { get; set; } public List<EmulatorBiosItem> Bios { get; set; }

View File

@@ -9,34 +9,52 @@ namespace gaseous_server
public class QueueItem public class QueueItem
{ {
public QueueItem(QueueItemType ItemType, int ExecutionInterval, bool AllowManualStart = true) public QueueItem(QueueItemType ItemType, int ExecutionInterval, bool AllowManualStart = true, bool RemoveWhenStopped = false)
{ {
_ItemType = ItemType; _ItemType = ItemType;
_ItemState = QueueItemState.NeverStarted; _ItemState = QueueItemState.NeverStarted;
_LastRunTime = DateTime.UtcNow.AddMinutes(ExecutionInterval); _LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
_Interval = ExecutionInterval; _Interval = ExecutionInterval;
_AllowManualStart = AllowManualStart; _AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped;
} }
public QueueItem(QueueItemType ItemType, int ExecutionInterval, List<QueueItemType> Blocks, bool AllowManualStart = true) public QueueItem(QueueItemType ItemType, int ExecutionInterval, List<QueueItemType> Blocks, bool AllowManualStart = true, bool RemoveWhenStopped = false)
{ {
_ItemType = ItemType; _ItemType = ItemType;
_ItemState = QueueItemState.NeverStarted; _ItemState = QueueItemState.NeverStarted;
_LastRunTime = DateTime.UtcNow.AddMinutes(ExecutionInterval); _LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
_Interval = ExecutionInterval; _Interval = ExecutionInterval;
_AllowManualStart = AllowManualStart; _AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped;
_Blocks = Blocks; _Blocks = Blocks;
} }
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 DateTime _LastFinishTime = DateTime.UtcNow; private DateTime _LastFinishTime
{
get
{
return DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
}
set
{
if (_SaveLastRunTime == true)
{
Config.SetSetting("LastRun_" + _ItemType.ToString(), value.ToString("yyyy-MM-ddThh:mm:ssZ"));
}
}
}
private bool _SaveLastRunTime = false;
private int _Interval = 0; private int _Interval = 0;
private string _LastResult = ""; private string _LastResult = "";
private string? _LastError = null; private string? _LastError = null;
private bool _ForceExecute = false; private bool _ForceExecute = false;
private bool _AllowManualStart = true; private bool _AllowManualStart = true;
private bool _RemoveWhenStopped = false;
private bool _IsBlocked = false;
private List<QueueItemType> _Blocks = new List<QueueItemType>(); private List<QueueItemType> _Blocks = new List<QueueItemType>();
public QueueItemType ItemType => _ItemType; public QueueItemType ItemType => _ItemType;
@@ -54,6 +72,9 @@ namespace gaseous_server
public string? LastError => _LastError; public string? LastError => _LastError;
public bool Force => _ForceExecute; public bool Force => _ForceExecute;
public bool AllowManualStart => _AllowManualStart; public bool AllowManualStart => _AllowManualStart;
public bool RemoveWhenStopped => _RemoveWhenStopped;
public bool IsBlocked => _IsBlocked;
public object? Options { get; set; } = null;
public List<QueueItemType> Blocks => _Blocks; public List<QueueItemType> Blocks => _Blocks;
public void Execute() public void Execute()
@@ -87,32 +108,52 @@ namespace gaseous_server
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME MESS files"); Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME MESS files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME MESS"), gaseous_signature_parser.parser.SignatureParser.MAMEMess); tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME MESS"), gaseous_signature_parser.parser.SignatureParser.MAMEMess);
_SaveLastRunTime = true;
break; break;
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);
_SaveLastRunTime = true;
break; break;
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(true); Classes.MetadataManagement.RefreshMetadata(true);
_SaveLastRunTime = true;
break; break;
case QueueItemType.OrganiseLibrary: case QueueItemType.OrganiseLibrary:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser");
Classes.ImportGame.OrganiseLibrary(); Classes.ImportGame.OrganiseLibrary();
_SaveLastRunTime = true;
break; break;
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.LibraryScan();
_SaveLastRunTime = true;
break; break;
case QueueItemType.CollectionCompiler: case QueueItemType.CollectionCompiler:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler");
Classes.Collections.CompileCollections(); Classes.Collections.CompileCollections((long)Options);
break; break;
case QueueItemType.BackgroundDatabaseUpgrade:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Background Upgrade");
gaseous_tools.DatabaseMigration.UpgradeScriptBackgroundTasks();
break;
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -133,17 +174,59 @@ namespace gaseous_server
{ {
_ForceExecute = true; _ForceExecute = true;
} }
public void BlockedState(bool BlockState)
{
_IsBlocked = BlockState;
}
} }
public enum QueueItemType public enum QueueItemType
{ {
/// <summary>
/// Reserved for blocking all services - no actual background service is tied to this type
/// </summary>
All,
/// <summary>
/// Default type - no background service is tied to this type
/// </summary>
NotConfigured, NotConfigured,
/// <summary>
/// Ingests signature DAT files into the database
/// </summary>
SignatureIngestor, SignatureIngestor,
/// <summary>
/// Imports game files into the database and moves them to the required location on disk
/// </summary>
TitleIngestor, TitleIngestor,
/// <summary>
/// Forces stored metadata to be refreshed
/// </summary>
MetadataRefresh, MetadataRefresh,
/// <summary>
/// Ensures all managed files are where they are supposed to be
/// </summary>
OrganiseLibrary, OrganiseLibrary,
/// <summary>
/// Looks for orphaned files in the library and re-adds them to the database
/// </summary>
LibraryScan, LibraryScan,
CollectionCompiler
/// <summary>
/// Builds collections - set the options attribute to the id of the collection to build
/// </summary>
CollectionCompiler,
/// <summary>
/// Performs and post database upgrade scripts that can be processed as a background task
/// </summary>
BackgroundDatabaseUpgrade
} }
public enum QueueItemState public enum QueueItemState

View File

@@ -9,10 +9,27 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
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);
// set up db
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// check db availability
bool dbOnline = false;
do
{
Logging.Log(Logging.LogType.Information, "Startup", "Waiting for database...");
if (db.TestConnection() == true)
{
dbOnline = true;
}
else
{
Thread.Sleep(30000);
}
} while (dbOnline == true);
// set up db
db.InitDB(); db.InitDB();
// load app settings // load app settings
@@ -29,6 +46,23 @@ if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
Config.SetSetting("API Key", APIKey.ToString()); Config.SetSetting("API Key", APIKey.ToString());
} }
// kick off any delayed upgrade tasks
// run 1002 background updates in the background on every start
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
// start the task
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade,
1,
new List<ProcessQueue.QueueItemType>
{
ProcessQueue.QueueItemType.All
},
false,
true
);
queueItem.ForceExecute();
ProcessQueue.QueueItems.Add(queueItem);
// set up server // set up server
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -164,13 +198,13 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
}) })
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScan, 30, new List<ProcessQueue.QueueItemType> ProcessQueue.QueueItemType.LibraryScan, 1440, new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.TitleIngestor,
ProcessQueue.QueueItemType.OrganiseLibrary ProcessQueue.QueueItemType.OrganiseLibrary
}) })
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 5, false));
Logging.WriteToDiskOnly = false;
// start the app // start the app
app.Run(); app.Run();

File diff suppressed because one or more lines are too long

View File

@@ -33,9 +33,23 @@ namespace gaseous_server
//_logger.LogInformation( //_logger.LogInformation(
// "Timed Hosted Service is working. Count: {Count}", count); // "Timed Hosted Service is working. Count: {Count}", count);
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) { List<ProcessQueue.QueueItem> ActiveList = new List<ProcessQueue.QueueItem>();
if ((DateTime.UtcNow > qi.NextRunTime || qi.Force == true) && CheckProcessBlockList(qi) == true) { ActiveList.AddRange(ProcessQueue.QueueItems);
qi.Execute(); foreach (ProcessQueue.QueueItem qi in ActiveList) {
if (CheckIfProcessIsBlockedByOthers(qi) == false) {
qi.BlockedState(false);
if (DateTime.UtcNow > qi.NextRunTime || qi.Force == true)
{
qi.Execute();
if (qi.RemoveWhenStopped == true && qi.ItemState == ProcessQueue.QueueItemState.Stopped)
{
ProcessQueue.QueueItems.Remove(qi);
}
}
}
else
{
qi.BlockedState(true);
} }
} }
} }
@@ -55,24 +69,24 @@ namespace gaseous_server
_timer?.Dispose(); _timer?.Dispose();
} }
private bool CheckProcessBlockList(ProcessQueue.QueueItem queueItem) private bool CheckIfProcessIsBlockedByOthers(ProcessQueue.QueueItem queueItem)
{ {
if (queueItem.Blocks.Count > 0) foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems)
{ {
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) if (qi.ItemState == ProcessQueue.QueueItemState.Running) {
{ // other service is running, check if queueItem is blocked by it
if (queueItem.Blocks.Contains(qi.ItemType) && qi.ItemState == ProcessQueue.QueueItemState.Running) if (
qi.Blocks.Contains(queueItem.ItemType) ||
qi.Blocks.Contains(ProcessQueue.QueueItemType.All)
)
{ {
return false; //Console.WriteLine(queueItem.ItemType.ToString() + " is blocked by " + qi.ItemType.ToString());
return true;
} }
} }
}
return true; return false;
}
else
{
return true;
}
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 17H12.01M12 14C12.8906 12.0938 15 12.2344 15 10C15 8.5 14 7 12 7C10.4521 7 9.50325 7.89844 9.15332 9M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 541 B

View File

@@ -394,7 +394,10 @@
} }
modalAlwaysInclude = alwaysInclude; modalAlwaysInclude = alwaysInclude;
console.log(JSON.stringify(modalAlwaysInclude));
if (!alwaysInclude) {
alwaysInclude = [];
}
var item = { var item = {
"name": document.getElementById('collection_name').value, "name": document.getElementById('collection_name').value,
@@ -414,6 +417,8 @@
"alwaysInclude": alwaysInclude "alwaysInclude": alwaysInclude
} }
console.log("Item: " + JSON.stringify(item));
return item; return item;
} }

View File

@@ -0,0 +1,27 @@
<p>Are you sure you want to delete this library?</p>
<p><strong>Warning:</strong> This cannot be undone!</p>
<div style="width: 100%; text-align: center;">
<div style="display: inline-block; margin-right: 20px;">
<button class="redbutton" value="Delete" onclick="deleteLibrary();">Delete</button>
</div>
<div style="display: inline-block; margin-left: 20px;">
<button value="Cancel" onclick="closeSubDialog();">Cancel</button>
</div>
</div>
<script type="text/javascript">
function deleteLibrary() {
ajaxCall(
'/api/v1/Library/' + subModalVariables,
'DELETE',
function (result) {
drawLibrary();
closeSubDialog();
},
function (error) {
drawLibrary();
closeSubDialog();
}
);
}
</script>

View File

@@ -0,0 +1,91 @@
<div style="padding-top: 5px;">
<strong>New Library</strong>
</div>
<div style="width: 300px;">
<table style="width: 98%; margin-top: 15px; margin-bottom: 15px;">
<tr>
<th>Name</th>
<td><input type="text" id="newlibrary_name" style="width: 95%;" /></td>
</tr>
<tr>
<th>Default Platform</th>
<td><select id="newlibrary_defaultplatform" style="width: 100%;"></select></td>
</tr>
<tr>
<th>Path</th>
<td><input type="text" id="newlibrary_path" style="width: 95%;" /></td>
</tr>
</table>
<div style="width: 100%; text-align: right;">
<div style="display: inline-block; margin-right: 20px;">
<button value="OK" onclick="newLibrary();">OK</button>
</div>
<div style="display: inline-block;">
<button value="Cancel" onclick="closeSubDialog();">Cancel</button>
</div>
</div>
</div>
<script type="text/javascript">
$('#newlibrary_defaultplatform').select2({
minimumInputLength: 3,
ajax: {
url: '/api/v1/Search/Platform',
data: function (params) {
var query = {
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
arr.push({
id: 0,
text: 'Any'
});
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name
});
}
return {
results: arr
};
}
}
});
document.getElementById('newlibrary_defaultplatform').innerHTML = "<option value='" + 0 + "' selected='selected'>Any</option>";
function newLibrary() {
var libName = document.getElementById('newlibrary_name').value;
var libPlatform = $('#newlibrary_defaultplatform').select2('data');
var libPath = document.getElementById('newlibrary_path').value;
if (libName.length == 0) {
alert("A library name must be provided.")
} else if (libPath.length == 0) {
alert("A path must be provided.");
} else {
ajaxCall(
'/api/v1/Library?Name=' + encodeURIComponent(libName) + '&DefaultPlatformId=' + libPlatform[0].id + '&Path=' + encodeURIComponent(libPath),
'POST',
function(result) {
drawLibrary();
closeSubDialog();
},
function(error) {
alert('An error occurred while creating the library:\n\n' + JSON.stringify(error.responseText));
}
);
}
}
</script>

View File

@@ -27,7 +27,7 @@
<h4>Standard Directory Naming</h4> <h4>Standard Directory Naming</h4>
</td> </td>
<td style="text-align: right;"> <td style="text-align: right;">
<input id="mapping_edit_igdbslug" readonly="readonly" type="text" style="width: 95%;"/> <input id="mapping_edit_igdbslug" readonly="readonly" type="text" style="width: 98%;"/>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -38,7 +38,7 @@
<h4>RetroPie Directory Naming</h4> <h4>RetroPie Directory Naming</h4>
</td> </td>
<td style="text-align: right;"> <td style="text-align: right;">
<input id="mapping_edit_retropie" type="text" style="width: 95%;"/> <input id="mapping_edit_retropie" type="text" style="width: 98%;"/>
</td> </td>
</tr> </tr>
</table> </table>
@@ -53,23 +53,32 @@
<input id="mapping_edit_enablewebemulator" type="checkbox"><label for="mapping_edit_enablewebemulator" style="margin-left: 5px;">Enabled</label> <input id="mapping_edit_enablewebemulator" type="checkbox"><label for="mapping_edit_enablewebemulator" style="margin-left: 5px;">Enabled</label>
</td> </td>
</tr> </tr>
<tr> <tr name="mapping_edit_webemulator">
<td style="width: 25%;"> <td style="width: 25%;">
<h4>Engine</h4> <h4>Engine</h4>
</td> </td>
<td> <td>
<select id="mapping_edit_webemulatorengine" style="width: 100%;"> <select id="mapping_edit_webemulatorengine" style="width: 100%;">
<option value="">-</option>
<option value="EmulatorJS">EmulatorJS</option>
</select> </select>
</td> </td>
</tr> </tr>
<tr> <tr name="mapping_edit_webemulator">
<td style="width: 25%;"> <td style="width: 25%;">
<h4>Core</h4> <h4>Core</h4>
</td> </td>
<td style="text-align: right;"> <td>
<input id="mapping_edit_webemulatorcore" type="text" style="width: 95%;"/> <select id="mapping_edit_webemulatorcore" style="width: 100%;">
</select>
</td>
</tr>
<tr name="mapping_edit_webemulator">
<td style="width: 25%;">
</td>
<td id="mapping_edit_webemulatorhelp">
</td> </td>
</tr> </tr>
</table> </table>
@@ -91,6 +100,10 @@
modalContent[0].classList.add('collections_modal'); modalContent[0].classList.add('collections_modal');
} }
var availableWebEmulators = [];
DisplayWebEmulatorContent(false);
ajaxCall( ajaxCall(
'/api/v1/PlatformMaps/' + modalVariables, '/api/v1/PlatformMaps/' + modalVariables,
'GET', 'GET',
@@ -125,14 +138,48 @@
document.getElementById('mapping_edit_igdbslug').value = result.igdbSlug; document.getElementById('mapping_edit_igdbslug').value = result.igdbSlug;
document.getElementById('mapping_edit_retropie').value = result.retroPieDirectoryName; document.getElementById('mapping_edit_retropie').value = result.retroPieDirectoryName;
// set up web emulator drop downs
$('#mapping_edit_webemulatorengine').select2(); $('#mapping_edit_webemulatorengine').select2();
if (result.webEmulator.type.length > 0) { $('#mapping_edit_webemulatorcore').select2();
document.getElementById('mapping_edit_enablewebemulator').checked = true;
$('#mapping_edit_webemulatorengine').val(result.webEmulator.type); // start populating drop downs
$('#mapping_edit_webemulatorengine').trigger('change'); if (result.webEmulator) {
document.getElementById('mapping_edit_webemulatorcore').value = result.webEmulator.core; if (result.webEmulator.availableWebEmulators.length > 0) {
availableWebEmulators = result.webEmulator.availableWebEmulators;
var offOption = new Option("-", "", false, false);
$('#mapping_edit_webemulatorengine').append(offOption).trigger('change');
for (var e = 0; e < result.webEmulator.availableWebEmulators.length; e++) {
var newOption = new Option(result.webEmulator.availableWebEmulators[e].emulatorType, result.webEmulator.availableWebEmulators[e].emulatorType, false, false);
$('#mapping_edit_webemulatorengine').append(newOption).trigger('change');
}
$('#mapping_edit_webemulatorengine').val(result.webEmulator.type);
$('#mapping_edit_webemulatorengine').trigger('change');
// select cores
RenderWebEmulatorCores(result.webEmulator.core);
if (result.webEmulator.type.length > 0) {
document.getElementById('mapping_edit_enablewebemulator').checked = true;
}
DisplayWebEmulatorHelp(result.webEmulator.type);
$('#mapping_edit_webemulatorengine').on('change', function(e) {
RenderWebEmulatorCores();
});
if (result.webEmulator.type.length > 0) {
DisplayWebEmulatorContent(true);
} else {
DisplayWebEmulatorContent(false);
}
} else {
// no emulators available
DisplayWebEmulatorContent(false);
}
} else { } else {
document.getElementById('mapping_edit_enablewebemulator').checked = false; // no emulators available
DisplayWebEmulatorContent(false);
} }
var biosTableHeaders = [ var biosTableHeaders = [
@@ -160,6 +207,55 @@
} }
); );
function RenderWebEmulatorCores(preSelectCore) {
var selectedEngine = document.getElementById('mapping_edit_webemulatorengine').value;
console.log("Engine: " + selectedEngine);
console.log("Preselect: " + preSelectCore);
console.log(JSON.stringify(availableWebEmulators));
$('#mapping_edit_webemulatorcore').empty().trigger("change");
// get cores for currently selected emulator
if (availableWebEmulators && (selectedEngine != undefined && selectedEngine != "")) {
if (availableWebEmulators.length > 0) {
var emuFound = false;
for (var e = 0; e < availableWebEmulators.length; e++) {
if (availableWebEmulators[e].emulatorType == selectedEngine) {
emuFound = true;
for (var c = 0; c < availableWebEmulators[e].availableWebEmulatorCores.length; c++) {
var coreName = availableWebEmulators[e].availableWebEmulatorCores[c].core;
if (availableWebEmulators[e].availableWebEmulatorCores[c].alternateCoreName) {
coreName += " (Maps to core: " + availableWebEmulators[e].availableWebEmulatorCores[c].alternateCoreName + ")";
}
if (availableWebEmulators[e].availableWebEmulatorCores[c].default == true) {
coreName += " (Default)";
}
console.log(coreName);
var newOption;
if (availableWebEmulators[e].availableWebEmulatorCores[c].core == preSelectCore) {
newOption = new Option(coreName, availableWebEmulators[e].availableWebEmulatorCores[c].core, true, true);
} else {
newOption = new Option(coreName, availableWebEmulators[e].availableWebEmulatorCores[c].core, false, false);
}
$('#mapping_edit_webemulatorcore').append(newOption).trigger('change');
}
}
}
if (emuFound == false) {
var newOption = new Option("-", "", true, true);
$('#mapping_edit_webemulatorcore').append(newOption).trigger('change');
}
} else {
var newOption = new Option("-", "", true, true);
$('#mapping_edit_webemulatorcore').append(newOption).trigger('change');
}
} else {
var newOption = new Option("-", "", true, true);
$('#mapping_edit_webemulatorcore').append(newOption).trigger('change');
}
}
function AddTokensFromList(selectObj, tagList) { function AddTokensFromList(selectObj, tagList) {
for (var i = 0; i < tagList.length; i++) { for (var i = 0; i < tagList.length; i++) {
var data = { var data = {
@@ -254,4 +350,39 @@
JSON.stringify(item) JSON.stringify(item)
); );
} }
$('#mapping_edit_webemulatorengine').on('select2:select', function (e) {
DisplayWebEmulatorHelp(e.params.data.id);
});
function DisplayWebEmulatorHelp(Emulator) {
var helpCell = document.getElementById('mapping_edit_webemulatorhelp');
switch (Emulator) {
case 'EmulatorJS':
helpCell.innerHTML = '<img src="/images/help.svg" class="banner_button_image banner_button_image_smaller" alt="Help" title="Help" /> See <a href="https://emulatorjs.org/docs4devs/Cores.html" target="_blank" class="romlink">https://emulatorjs.org/docs4devs/Cores.html</a> for more information regarding EmulatorJS cores.';
break;
default:
helpCell.innerHTML = '';
break;
}
}
$('#mapping_edit_enablewebemulator').change(function() {
DisplayWebEmulatorContent(this.checked);
});
function DisplayWebEmulatorContent(showContent) {
console.log(showContent);
var webEmulatorRows = document.getElementsByName('mapping_edit_webemulator');
for (var i = 0; i < webEmulatorRows.length; i++) {
if (showContent == true) {
webEmulatorRows[i].style.display = '';
} else {
webEmulatorRows[i].style.display = 'none';
}
}
}
</script> </script>

View File

@@ -7,6 +7,10 @@
<div id="properties_bodypanel"> <div id="properties_bodypanel">
<div id="properties_bodypanel_general" name="properties_tab" style="display: none;"> <div id="properties_bodypanel_general" name="properties_tab" style="display: none;">
<table cellspacing="0" style="width: 100%;"> <table cellspacing="0" style="width: 100%;">
<tr>
<th>Library</th>
<td id="rominfo_library"></td>
</tr>
<tr> <tr>
<th>Platform</th> <th>Platform</th>
<td id="rominfo_platform"></td> <td id="rominfo_platform"></td>
@@ -117,6 +121,7 @@
ajaxCall('/api/v1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) { ajaxCall('/api/v1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) {
romData = result; romData = result;
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_platform').innerHTML = result.platform.name; document.getElementById('rominfo_platform').innerHTML = result.platform.name;
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);
@@ -130,6 +135,10 @@
document.getElementById('properties_fixplatform').innerHTML = "<option value='" + result.platform.id + "' selected='selected'>" + result.platform.name + "</option>"; document.getElementById('properties_fixplatform').innerHTML = "<option value='" + result.platform.id + "' selected='selected'>" + result.platform.name + "</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) {
document.getElementById('romDelete').style.display = 'none';
}
if (result.attributes.length > 0) { if (result.attributes.length > 0) {
document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source)); document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
} else { } else {

View File

@@ -375,7 +375,11 @@
var launchButton = ''; var launchButton = '';
if (result[i].emulator) { if (result[i].emulator) {
launchButton = '<a href="/index.html?page=emulator&engine=' + result[i].emulator.type + '&core=' + result[i].emulator.core + '&platformid=' + result[i].platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/' + encodeURIComponent(result[i].name)) + '" class="romstart">Launch</a>'; if (result[i].emulator.type) {
if (result[i].emulator.type.length > 0) {
launchButton = '<a href="/index.html?page=emulator&engine=' + result[i].emulator.type + '&core=' + result[i].emulator.core + '&platformid=' + result[i].platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/' + encodeURIComponent(result[i].name)) + '" class="romstart">Launch</a>';
}
}
} }
var newRow = [ var newRow = [

View File

@@ -6,6 +6,7 @@
<div id="properties_toc" class="settings_toc"> <div id="properties_toc" class="settings_toc">
<div class="filter_header">Settings</div> <div class="filter_header">Settings</div>
<div id="properties_toc_system" name="properties_toc_item" onclick="SelectTab('system');">System</div> <div id="properties_toc_system" name="properties_toc_item" onclick="SelectTab('system');">System</div>
<div id="properties_toc_settings" name="properties_toc_item" onclick="SelectTab('settings');">Settings</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');">Platform Mapping</div> <div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');">Platform Mapping</div>
<div id="properties_toc_bios" name="properties_toc_item" onclick="SelectTab('bios');">Firmware</div> <div id="properties_toc_bios" name="properties_toc_item" onclick="SelectTab('bios');">Firmware</div>
<div id="properties_toc_logs" name="properties_toc_item" onclick="SelectTab('logs');">Logs</div> <div id="properties_toc_logs" name="properties_toc_item" onclick="SelectTab('logs');">Logs</div>

View File

@@ -4,13 +4,17 @@
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tr>
<th>Home Page</th> <th style="width: 20%;">Home Page</th>
<td><a href="https://github.com/gaseous-project/gaseous-server" class="romlink">https://github.com/gaseous-project/gaseous-server</a></td> <td><a href="https://github.com/gaseous-project/gaseous-server" class="romlink">https://github.com/gaseous-project/gaseous-server</a></td>
</tr> </tr>
<tr> <tr>
<th>Bugs and Feature Requests</th> <th>Bugs and Feature Requests</th>
<td><a href="https://github.com/gaseous-project/gaseous-server/issues" class="romlink">https://github.com/gaseous-project/gaseous-server/issues</a></td> <td><a href="https://github.com/gaseous-project/gaseous-server/issues" class="romlink">https://github.com/gaseous-project/gaseous-server/issues</a></td>
</tr> </tr>
<tr>
<th>Join our Discord</th>
<td><a href="https://discord.gg/Nhu7wpT3k4" class="romlink">https://discord.gg/Nhu7wpT3k4</a></td>
</tr>
<tr> <tr>
<th>Server Version</th> <th>Server Version</th>
<td id="settings_appversion"></td> <td id="settings_appversion"></td>
@@ -19,6 +23,18 @@
<th>Database Schema Version</th> <th>Database Schema Version</th>
<td id="settings_dbversion"></td> <td id="settings_dbversion"></td>
</tr> </tr>
<tr>
<td colspan="2">
<h3>Projects That Make Gaseous Possible</h3>
</td>
</tr>
<tr>
<td style="text-align: center;"><a href="https://github.com/EmulatorJS/EmulatorJS" target="_blank"><img src="/images/EmulatorJS.png" style="height: 36px;" /></a></td>
<td>
The EmulatorJS Project<br />
<a href="https://github.com/EmulatorJS/EmulatorJS" target="_blank" class="romlink">https://github.com/EmulatorJS/EmulatorJS</a>
</td>
</tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<h3>Data Sources</h2> <h3>Data Sources</h2>
@@ -26,11 +42,12 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td style="text-align: center;">
<a href="https://www.igdb.com/" target="_blank"><img src="/images/IGDB_logo.svg" style="filter: invert(100%); height: 36px;" /></a> <a href="https://www.igdb.com/" target="_blank"><img src="/images/IGDB_logo.svg" style="filter: invert(100%); height: 36px;" /></a>
</td> </td>
<td> <td>
The Internet Game Database The Internet Game Database<br />
<a href="https://www.igdb.com/" target="_blank" class="romlink">https://www.igdb.com/</a>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -39,19 +56,21 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td style="text-align: center;">
<a href="https://www.tosecdev.org/" target="_blank"><img src="/images/TOSEC_logo.gif" style="height: 36px;" /></a> <a href="https://www.tosecdev.org/" target="_blank"><img src="/images/TOSEC_logo.gif" style="height: 36px;" /></a>
</td> </td>
<td> <td>
The Old School Emulation Center The Old School Emulation Center<br />
<a href="https://www.tosecdev.org/" target="_blank" class="romlink">https://www.tosecdev.org/</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td style="text-align: center;">
<a href="https://www.progettosnaps.net/index.php" target="_blank"><img src="/images/ProgettoSnaps.gif" style="height: 36px;" /></a> <a href="https://www.progettosnaps.net/index.php" target="_blank"><img src="/images/ProgettoSnaps.gif" style="height: 36px;" /></a>
</td> </td>
<td> <td>
Progetto-Snaps Progetto-Snaps<br />
<a href="https://www.progettosnaps.net/index.php" target="_blank" class="romlink">https://www.progettosnaps.net/index.php</a>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -7,30 +7,53 @@
</table> </table>
<div style="width: 960px; text-align: center;">
<button value="Load More" onclick="loadLogs(lastStartIndex, currentPage);">Load More</button>
</div>
<script type="text/javascript"> <script type="text/javascript">
function loadLogs() { var lastStartIndex = 0;
var currentPage = 1;
function loadLogs(StartIndex, PageNumber) {
var apiQuery = '';
if (StartIndex && PageNumber) {
currentPage += 1;
apiQuery = '?StartIndex=' + StartIndex + '&PageNumber=' + PageNumber;
} else {
currentPage = 1;
}
ajaxCall( ajaxCall(
'/api/v1/Logs', '/api/v1/Logs' + apiQuery,
'GET', 'GET',
function (result) { function (result) {
var newTable = document.getElementById('settings_events_table'); var newTable = document.getElementById('settings_events_table');
newTable.innerHTML = ''; if (currentPage == 1) {
newTable.appendChild( newTable.innerHTML = '';
createTableRow(
true, newTable.appendChild(
[ createTableRow(
['Event Time', 'logs_table_cell_150px'], true,
['Severity', 'logs_table_cell_150px'], [
'Process', //'Id',
'Message' ['Event Time', 'logs_table_cell_150px'],
], ['Severity', 'logs_table_cell_150px'],
'', 'Process',
'' 'Message'
) ],
); '',
''
)
);
}
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
lastStartIndex = result[i].id;
var newRow = [ var newRow = [
//result[i].id,
moment(result[i].eventTime).fromNow(), moment(result[i].eventTime).fromNow(),
result[i].eventType, result[i].eventType,
result[i].process, result[i].process,

View File

@@ -48,7 +48,7 @@
} }
var newRow = [ var newRow = [
'<span onclick="ShowPlatformMappingDialog(' + result[i].igdbId + ');" class="romlink">' + result[i].igdbName + '</span>', '<a href="#/" onclick="ShowPlatformMappingDialog(' + result[i].igdbId + ');" class="romlink">' + result[i].igdbName + '</a>',
result[i].extensions.supportedFileExtensions.join(', '), result[i].extensions.supportedFileExtensions.join(', '),
result[i].extensions.uniqueFileExtensions.join(', '), result[i].extensions.uniqueFileExtensions.join(', '),
hasWebEmulator hasWebEmulator

View File

@@ -0,0 +1,63 @@
<div id="gametitle">
<h1 id="gametitle_label">Settings</h1>
</div>
<h3>Libraries</h3>
<table id="settings_libraries" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_newlibrary" onclick="showSubDialog('librarynew');">New Library</button></div>
<script type="text/javascript">
function drawLibrary() {
ajaxCall(
'/api/v1/Library',
'GET',
function (result) {
var newTable = document.getElementById('settings_libraries');
newTable.innerHTML = '';
newTable.appendChild(createTableRow(true, ['Name', 'Path', 'Default Platform', 'Default Library', '']));
for (var i = 0; i < result.length; i++) {
var platformName = '';
if (result[i].defaultPlatformId == 0) {
if (result[i].isDefaultLibrary == true) {
platformName = "n/a";
} else {
platformName = "";
}
} else {
platformName = result[i].defaultPlatformName;
}
var defaultLibrary = '';
if (result[i].isDefaultLibrary == true) {
defaultLibrary = "Yes";
} else {
defaultLibrary = "";
}
var deleteButton = '';
if (result[i].isDefaultLibrary == false) {
var deleteButton = '<a href="#" onclick="showSubDialog(\'librarydelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
}
newTable.appendChild(createTableRow(
false,
[
result[i].name,
result[i].path,
platformName,
defaultLibrary,
'<div style="text-align: right;">' + deleteButton + '</div>'
],
'romrow',
'romcell'
));
}
}
);
}
drawLibrary();
</script>

View File

@@ -52,7 +52,10 @@
itemTypeName = "Library scan"; itemTypeName = "Library scan";
break; break;
case 'CollectionCompiler': case 'CollectionCompiler':
itemTypeName = "Compress collections"; itemTypeName = "Compress collection id: " + result[i].options;
break;
case 'BackgroundDatabaseUpgrade':
itemTypeName = "Background database upgrade";
break; break;
default: default:
itemTypeName = result[i].itemType; itemTypeName = result[i].itemType;
@@ -61,36 +64,48 @@
var itemStateName; var itemStateName;
var itemLastStart; var itemLastStart;
switch (result[i].itemState) { if (result[i].isBlocked == false) {
case 'NeverStarted': switch (result[i].itemState) {
itemStateName = "Never started"; case 'NeverStarted':
itemLastStart = '-'; itemStateName = "Never started";
break; itemLastStart = '-';
case 'Stopped': break;
itemStateName = "Stopped"; case 'Stopped':
itemLastStart = moment(result[i].lastRunTime).fromNow(); itemStateName = "Stopped";
break; itemLastStart = moment(result[i].lastRunTime).fromNow();
case 'Running': break;
itemStateName = "Running"; case 'Running':
itemLastStart = moment(result[i].lastRunTime).fromNow(); itemStateName = "Running";
break; itemLastStart = moment(result[i].lastRunTime).fromNow();
default: break;
itemStateName = "Unknown status"; default:
itemLastStart = moment(result[i].lastRunTime).fromNow(); itemStateName = "Unknown status";
break; itemLastStart = moment(result[i].lastRunTime).fromNow();
break;
}
} else {
itemStateName = "Blocked";
itemLastStart = moment(result[i].lastRunTime).fromNow();
} }
var itemInterval = result[i].interval;
var nextRunTime = moment(result[i].nextRunTime).fromNow();
var startButton = ''; var startButton = '';
if (result[i].allowManualStart == true && result[i].itemState != "Running") { if (result[i].allowManualStart == true && result[i].itemState != "Running") {
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>"; startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
} }
if (result[i].allowManualStart == false && result[i].removeWhenStopped == true) {
itemInterval = '';
nextRunTime = '';
}
var newRow = [ var newRow = [
itemTypeName, itemTypeName,
itemStateName, itemStateName,
result[i].interval, itemInterval,
itemLastStart, itemLastStart,
moment(result[i].nextRunTime).fromNow(), nextRunTime,
startButton startButton
]; ];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell')); newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));

View File

@@ -61,7 +61,7 @@ namespace gaseous_tools
{ {
get get
{ {
return _md5hash; return _md5hash.ToLower();
} }
set set
{ {
@@ -73,7 +73,7 @@ namespace gaseous_tools
{ {
get get
{ {
return _sha1hash; return _sha1hash.ToLower();
} }
set set
{ {
@@ -104,6 +104,12 @@ namespace gaseous_tools
".DS_STORE", ".DS_STORE",
"desktop.ini" "desktop.ini"
}; };
public static string NormalizePath(string path)
{
return Path.GetFullPath(new Uri(path).LocalPath)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
} }
} }

View File

@@ -183,7 +183,7 @@ namespace gaseous_tools
else else
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Settings WHERE Setting = @SettingName"; string sql = "SELECT Value FROM Settings WHERE Setting = @SettingName";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("SettingName", SettingName); dbDict.Add("SettingName", SettingName);
dbDict.Add("Value", DefaultValue); dbDict.Add("Value", DefaultValue);
@@ -337,13 +337,13 @@ namespace gaseous_tools
} }
} }
public string LibraryDataDirectory // public string LibraryDataDirectory
{ // {
get // get
{ // {
return Path.Combine(LibraryRootDirectory, "Library"); // return Path.Combine(LibraryRootDirectory, "Library");
} // }
} // }
public string LibraryBIOSDirectory public string LibraryBIOSDirectory
{ {
@@ -418,7 +418,6 @@ namespace gaseous_tools
{ {
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); } if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); } if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); }
if (!Directory.Exists(LibraryDataDirectory)) { Directory.CreateDirectory(LibraryDataDirectory); }
if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); } if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); }
if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); } if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); }
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); } if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }

View File

@@ -206,6 +206,18 @@ namespace gaseous_tools
} }
} }
public bool TestConnection()
{
switch (_ConnectorType)
{
case databaseType.MySql:
MySQLServerConnector conn = new MySQLServerConnector(_ConnectionString);
return conn.TestConnection();
default:
return false;
}
}
public class SQLTransactionItem public class SQLTransactionItem
{ {
public SQLTransactionItem() public SQLTransactionItem()
@@ -314,6 +326,20 @@ namespace gaseous_tools
return cmd; return cmd;
} }
public bool TestConnection()
{
MySqlConnection conn = new MySqlConnection(DBConn);
try
{
conn.Open();
conn.Close();
return true;
}
catch
{
return false;
}
}
} }
} }
} }

View File

@@ -0,0 +1,2 @@
ALTER TABLE `PlatformMap`
ADD COLUMN `AvailableWebEmulators` JSON NULL;

View File

@@ -0,0 +1,11 @@
CREATE TABLE `GameLibraries` (
`Id` int NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(255) NOT NULL,
`Path` longtext NOT NULL,
`DefaultLibrary` int NOT NULL DEFAULT '0',
`DefaultPlatform` bigint NOT NULL DEFAULT '0',
PRIMARY KEY (`Id`)
);
ALTER TABLE `Games_Roms`
ADD COLUMN `LibraryId` INT NULL DEFAULT 0 AFTER `MetadataVersion`;

View File

@@ -5,26 +5,66 @@ namespace gaseous_tools
{ {
public static class DatabaseMigration public static class DatabaseMigration
{ {
public static void PreUpgradeScript(int TargetSchemaVersion, gaseous_tools.Database.databaseType? DatabaseType) { public static List<int> BackgroundUpgradeTargetSchemaVersions = new List<int>();
public static void PreUpgradeScript(int TargetSchemaVersion, gaseous_tools.Database.databaseType? DatabaseType)
{
} }
public static void PostUpgradeScript(int TargetSchemaVersion, gaseous_tools.Database.databaseType? DatabaseType) { public static void PostUpgradeScript(int TargetSchemaVersion, gaseous_tools.Database.databaseType? DatabaseType)
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> dbDict = new Dictionary<string, object>();
switch(DatabaseType) switch(DatabaseType)
{ {
case gaseous_tools.Database.databaseType.MySql: case Database.databaseType.MySql:
switch (TargetSchemaVersion) switch (TargetSchemaVersion)
{ {
case 1002: case 1002:
MySql_1002_MigrateMetadataVersion(); // this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1002);
break; break;
case 1004:
// needs to run on start up
// copy root path to new libraries format
string oldRoot = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "Library");
string sql = "INSERT INTO GameLibraries (Name, Path, DefaultLibrary, DefaultPlatform) VALUES (@name, @path, @defaultlibrary, @defaultplatform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
dbDict.Add("name", "Default");
dbDict.Add("path", oldRoot);
dbDict.Add("defaultlibrary", 1);
dbDict.Add("defaultplatform", 0);
DataTable data = db.ExecuteCMD(sql, dbDict);
// apply the new library id to the existing roms
sql = "UPDATE Games_Roms SET LibraryId=@libraryid;";
dbDict.Clear();
dbDict.Add("libraryid", data.Rows[0][0]);
db.ExecuteCMD(sql, dbDict);
break;
} }
break; break;
} }
} }
private static void MySql_1002_MigrateMetadataVersion() { public static void UpgradeScriptBackgroundTasks()
{
foreach (int TargetSchemaVersion in BackgroundUpgradeTargetSchemaVersions)
{
switch (TargetSchemaVersion)
{
case 1002:
MySql_1002_MigrateMetadataVersion();
break;
}
}
}
public static void MySql_1002_MigrateMetadataVersion() {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();

View File

@@ -8,6 +8,8 @@ namespace gaseous_tools
{ {
public class Logging public class Logging
{ {
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)
{ {
LogItem logItem = new LogItem LogItem logItem = new LogItem
@@ -61,6 +63,11 @@ namespace gaseous_tools
Console.WriteLine(TraceOutput); Console.WriteLine(TraceOutput);
Console.ResetColor(); Console.ResetColor();
if (WriteToDiskOnly == true)
{
LogToDiskOnly = true;
}
if (LogToDiskOnly == false) if (LogToDiskOnly == false)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -104,17 +111,30 @@ namespace gaseous_tools
File.AppendAllText(Config.LogFilePath, TraceOutput); File.AppendAllText(Config.LogFilePath, TraceOutput);
} }
static public List<LogItem> GetLogs() static public List<LogItem> GetLogs(long? StartIndex, int PageNumber = 1, int PageSize = 100)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM ServerLogs ORDER BY Id DESC"; string sql = "";
DataTable dataTable = db.ExecuteCMD(sql); if (StartIndex == null)
{
sql = "SELECT * FROM ServerLogs ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
}
else
{
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex ORDER BY 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);
List<LogItem> logs = new List<LogItem>(); List<LogItem> logs = new List<LogItem>();
foreach (DataRow row in dataTable.Rows) foreach (DataRow row in dataTable.Rows)
{ {
LogItem log = new LogItem LogItem log = new LogItem
{ {
Id = (long)row["Id"],
EventTime = DateTime.Parse(((DateTime)row["EventTime"]).ToString("yyyy-MM-ddThh:mm:ss") + 'Z'), EventTime = DateTime.Parse(((DateTime)row["EventTime"]).ToString("yyyy-MM-ddThh:mm:ss") + 'Z'),
EventType = (LogType)row["EventType"], EventType = (LogType)row["EventType"],
Process = (string)row["Process"], Process = (string)row["Process"],
@@ -138,6 +158,7 @@ namespace gaseous_tools
public class LogItem public class LogItem
{ {
public long Id { get; set; }
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; } = "";

View File

@@ -18,6 +18,8 @@
<None Remove="Database\MySQL\gaseous-1000.sql" /> <None Remove="Database\MySQL\gaseous-1000.sql" />
<None Remove="Database\MySQL\gaseous-1001.sql" /> <None Remove="Database\MySQL\gaseous-1001.sql" />
<None Remove="Database\MySQL\gaseous-1002.sql" /> <None Remove="Database\MySQL\gaseous-1002.sql" />
<None Remove="Database\MySQL\gaseous-1003.sql" />
<None Remove="Database\MySQL\gaseous-1004.sql" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Database\" /> <Folder Include="Database\" />
@@ -27,5 +29,7 @@
<EmbeddedResource Include="Database\MySQL\gaseous-1000.sql" /> <EmbeddedResource Include="Database\MySQL\gaseous-1000.sql" />
<EmbeddedResource Include="Database\MySQL\gaseous-1001.sql" /> <EmbeddedResource Include="Database\MySQL\gaseous-1001.sql" />
<EmbeddedResource Include="Database\MySQL\gaseous-1002.sql" /> <EmbeddedResource Include="Database\MySQL\gaseous-1002.sql" />
<EmbeddedResource Include="Database\MySQL\gaseous-1003.sql" />
<EmbeddedResource Include="Database\MySQL\gaseous-1004.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>