Compare commits

...

21 Commits

Author SHA1 Message Date
Michael Green
e4cffb6fb4 Revert "Added a check for the DB and a delay at start up if the database is not available (#148)"
This reverts commit f9d6cc4bdc.
2023-10-11 16:09:55 +11:00
Michael Green
0e125d42ec Revert "Create libraries based on external unmanaged directories (#147)"
This reverts commit 1934558595.
2023-10-11 16:09:34 +11:00
Michael Green
6d110731c4 Merge branch 'main' into branch-v1.6.0 2023-10-10 22:04:59 -07:00
Michael Green
de628e6766 Check for null during cache clean (#153) 2023-10-11 16:02:34 +11:00
Michael Green
308338580d Cache objects from database in memory to improve performance (#151)
* IGDB objects are now cached in memory

* Completed caching of PlatformMaps
2023-10-10 23:59:52 +11:00
Michael Green
49784dc325 Removed import block from LibraryScan (#150) 2023-10-10 11:00:13 +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
51 changed files with 2474 additions and 387 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.
## 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
![Library](./screenshots/Library.png)
![Game](./screenshots/Game.png)
![Emulator](./screenshots/Emulator.png)
## Requirements
* MySQL Server 8+
* MySQL Server 8+*
* 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
The following projects are used by Gaseous
* 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
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
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
## Source
### 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
* 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.

View File

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

View File

@@ -171,13 +171,10 @@ namespace gaseous_server.Classes
db.ExecuteCMD(sql, dbDict);
// start background task
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems)
{
if (qi.ItemType == ProcessQueue.QueueItemType.CollectionCompiler) {
qi.ForceExecute();
break;
}
}
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 1, false, true);
queueItem.Options = Id;
queueItem.ForceExecute();
ProcessQueue.QueueItems.Add(queueItem);
}
}
@@ -364,176 +361,173 @@ namespace gaseous_server.Classes
return collectionContents;
}
public static void CompileCollections()
public static void CompileCollections(long CollectionId)
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
List<CollectionItem> collectionItems = GetCollections();
foreach (CollectionItem collectionItem in collectionItems)
CollectionItem collectionItem = GetCollection(CollectionId);
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);
// 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
// clean up if needed
if (File.Exists(ZipFilePath))
{
// clean up if needed
if (File.Exists(ZipFilePath))
{
Logging.Log(Logging.LogType.Warning, "Collections", "Deleting existing build of collection: " + collectionItem.Name);
File.Delete(ZipFilePath);
}
Logging.Log(Logging.LogType.Warning, "Collections", "Deleting existing build of collection: " + collectionItem.Name);
File.Delete(ZipFilePath);
}
if (Directory.Exists(ZipFileTempPath))
{
Directory.Delete(ZipFileTempPath, true);
}
if (Directory.Exists(ZipFileTempPath))
{
Directory.Delete(ZipFileTempPath, true);
}
// gather collection files
Directory.CreateDirectory(ZipFileTempPath);
string ZipBiosPath = Path.Combine(ZipFileTempPath, "BIOS");
// gather collection files
Directory.CreateDirectory(ZipFileTempPath);
string ZipBiosPath = Path.Combine(ZipFileTempPath, "BIOS");
// get the games
foreach (CollectionContents.CollectionPlatformItem collectionPlatformItem in collectionPlatformItems)
// get the games
foreach (CollectionContents.CollectionPlatformItem collectionPlatformItem in collectionPlatformItems)
{
// get platform bios files if present
if (collectionItem.IncludeBIOSFiles == true)
{
// get platform bios files if present
if (collectionItem.IncludeBIOSFiles == true)
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
if (!Directory.Exists(ZipBiosPath)) {
Directory.CreateDirectory(ZipBiosPath);
}
foreach (Bios.BiosItem biosItem in bios)
{
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
if (!Directory.Exists(ZipBiosPath)) {
Directory.CreateDirectory(ZipBiosPath);
}
foreach (Bios.BiosItem biosItem in bios)
if (File.Exists(biosItem.biosPath))
{
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
string ZipPlatformPath = "";
switch (collectionItem.FolderStructure)
{
case CollectionItem.FolderStructures.Gaseous:
// create platform directory
string ZipPlatformPath = "";
switch (collectionItem.FolderStructure)
{
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);
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);
}
break;
break;
}
if (!Directory.Exists(ZipPlatformPath))
{
Directory.CreateDirectory(ZipPlatformPath);
}
}
if (!Directory.Exists(ZipPlatformPath))
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem in collectionPlatformItem.Games)
{
bool includeGame = false;
if (collectionGameItem.InclusionStatus == null)
{
Directory.CreateDirectory(ZipPlatformPath);
includeGame = true;
}
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem in collectionPlatformItem.Games)
else
{
bool includeGame = false;
if (collectionGameItem.InclusionStatus == null)
if (collectionGameItem.InclusionStatus.InclusionState == CollectionItem.AlwaysIncludeStatus.AlwaysInclude)
{
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 = "";
switch (collectionItem.FolderStructure)
{
case CollectionItem.FolderStructures.Gaseous:
// 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))
case CollectionItem.FolderStructures.Gaseous:
// create game directory
ZipGamePath = Path.Combine(ZipPlatformPath, collectionGameItem.Slug);
if (!Directory.Exists(ZipGamePath))
{
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name);
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name));
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);
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
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);
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)
{
// 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

@@ -77,10 +77,17 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue);
break;
try
{
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<AgeRating>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
break;
try
{
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:
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
break;
try
{
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:
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
break;

View File

@@ -78,10 +78,17 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
break;
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
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:
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
break;
try
{
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:
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
break;

View File

@@ -74,9 +74,16 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
if (returnValue != null) { Storage.NewCacheValue(returnValue, true); }
UpdateSubClasses(returnValue);
try
{
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;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);

View File

@@ -80,12 +80,17 @@ namespace gaseous_server.Classes.Metadata
}
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
if (returnValue != null)
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
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<CompanyLogo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);

View File

@@ -77,10 +77,18 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
break;
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
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:
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
break;

View File

@@ -78,12 +78,17 @@ namespace gaseous_server.Classes.Metadata
}
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
if (returnValue != null)
try
{
returnValue = await GetObjectFromServer(WhereClause);
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:
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
break;

View File

@@ -75,9 +75,17 @@ namespace gaseous_server.Classes.Metadata
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
break;
try
{
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:
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
break;

View File

@@ -68,18 +68,23 @@ namespace gaseous_server.Classes.Metadata
}
GameMode returnValue = new GameMode();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
try
{
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;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);

View File

@@ -68,19 +68,24 @@ namespace gaseous_server.Classes.Metadata
}
GameVideo returnValue = new GameVideo();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
break;
try
{
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<GameVideo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
break;

View File

@@ -102,9 +102,16 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
return returnValue;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
try
{
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;
case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);

View File

@@ -68,19 +68,24 @@ namespace gaseous_server.Classes.Metadata
}
Genre returnValue = new Genre();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
break;
try
{
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<Genre>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
break;

View File

@@ -74,9 +74,16 @@ namespace gaseous_server.Classes.Metadata
UpdateSubClasses(returnValue);
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue);
try
{
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;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);

View File

@@ -68,18 +68,23 @@ namespace gaseous_server.Classes.Metadata
}
MultiplayerMode returnValue = new MultiplayerMode();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
try
{
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;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);

View File

@@ -80,12 +80,17 @@ namespace gaseous_server.Classes.Metadata
}
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
if (returnValue != null)
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
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<PlatformLogo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);

View File

@@ -78,12 +78,17 @@ namespace gaseous_server.Classes.Metadata
}
return returnValue;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
if (returnValue != null)
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
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;
case Storage.CacheStatus.Current:
return Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);

View File

@@ -99,11 +99,19 @@ namespace gaseous_server.Classes.Metadata
AddPlatformMapping(returnValue);
return returnValue;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue);
AddPlatformMapping(returnValue);
return returnValue;
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
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:
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
default:
@@ -148,7 +156,7 @@ namespace gaseous_server.Classes.Metadata
IGDBSlug = platform.Slug,
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;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
try
{
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;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);

View File

@@ -78,10 +78,18 @@ namespace gaseous_server.Classes.Metadata
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
break;
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
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:
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
break;

View File

@@ -4,6 +4,7 @@ using System.Reflection;
using gaseous_tools;
using IGDB;
using IGDB.Models;
using Microsoft.Extensions.Caching.Memory;
namespace gaseous_server.Classes.Metadata
{
@@ -16,14 +17,32 @@ namespace gaseous_server.Classes.Metadata
Expired
}
private static Dictionary<string, MemoryCacheObject> ObjectCache = new Dictionary<string, MemoryCacheObject>();
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)
{
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)
@@ -164,6 +183,21 @@ namespace gaseous_server.Classes.Metadata
{
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);
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
@@ -181,7 +215,11 @@ namespace gaseous_server.Classes.Metadata
else
{
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;
}
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;
break;
case Storage.CacheStatus.Expired:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
break;
try
{
returnValue = await GetObjectFromServer(WhereClause);
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:
returnValue = Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
break;

View File

@@ -103,9 +103,9 @@ namespace gaseous_server.Classes
GameId = (long)romDR["gameid"],
Name = (string)romDR["name"],
Size = (long)romDR["size"],
CRC = (string)romDR["crc"],
MD5 = (string)romDR["md5"],
SHA1 = (string)romDR["sha1"],
CRC = ((string)romDR["crc"]).ToLower(),
MD5 = ((string)romDR["md5"]).ToLower(),
SHA1 = ((string)romDR["sha1"]).ToLower(),
DevelopmentStatus = (string)romDR["developmentstatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
RomType = (int)romDR["romtype"],

View File

@@ -188,9 +188,9 @@ namespace gaseous_server.SignatureIngestors.XML
dbDict.Add("gameid", gameId);
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, ""));
dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, ""));
dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, ""));
dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, ""));
dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower());
dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "").ToString().ToLower());
dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "").ToString().ToLower());
dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, ""));
if (romObject.Attributes != null)

View File

@@ -13,9 +13,9 @@ namespace gaseous_server.Controllers
{
[HttpGet]
[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 });
}
[HttpPost]
[Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public ActionResult NewPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
{
try
{
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId);
// [HttpPost]
// [Route("{PlatformId}")]
// [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
// [ProducesResponseType(StatusCodes.Status404NotFound)]
// [ProducesResponseType(StatusCodes.Status409Conflict)]
// public ActionResult NewPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
// {
// try
// {
// PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId);
if (platformMapItem != null)
{
return Conflict();
}
else
{
PlatformMapping.WritePlatformMap(Map, false);
return Ok(PlatformMapping.GetPlatformMap(PlatformId));
}
}
catch
{
return NotFound();
}
}
// if (platformMapItem != null)
// {
// return Conflict();
// }
// else
// {
// PlatformMapping.WritePlatformMap(Map, false, false);
// return Ok(PlatformMapping.GetPlatformMap(PlatformId));
// }
// }
// catch
// {
// return NotFound();
// }
// }
[HttpPatch]
[Route("{PlatformId}")]
@@ -149,7 +149,7 @@ namespace gaseous_server.Controllers
if (platformMapItem != null)
{
PlatformMapping.WritePlatformMap(Map, true);
PlatformMapping.WritePlatformMap(Map, true, false);
return Ok(PlatformMapping.GetPlatformMap(PlatformId));
}
else

View File

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

View File

@@ -15,6 +15,8 @@ namespace gaseous_server.Models
{
public class PlatformMapping
{
private static Dictionary<string, PlatformMapItem> PlatformMapCache = new Dictionary<string, PlatformMapItem>();
/// <summary>
/// Updates the platform map from the embedded platform map resource
/// </summary>
@@ -25,7 +27,10 @@ namespace gaseous_server.Models
{
string rawJson = reader.ReadToEnd();
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)
{
@@ -41,7 +46,7 @@ namespace gaseous_server.Models
}
else
{
WritePlatformMap(mapItem, true);
WritePlatformMap(mapItem, true, true);
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.");
// 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
Logging.Log(Logging.LogType.Information, "Platform Map", "Replacing " + mapItem.IGDBName + " from external JSON file.");
WritePlatformMap(mapItem, true);
WritePlatformMap(mapItem, true, true);
}
catch
{
// we caught a not found error, insert a new record
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>();
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));
@@ -106,27 +119,34 @@ namespace gaseous_server.Models
public static PlatformMapItem GetPlatformMap(long Id)
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
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)
if (PlatformMapCache.ContainsKey(Id.ToString()))
{
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]);
return platformMap;
return PlatformMapCache[Id.ToString()];
}
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;
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
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 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);
string sql = "";
@@ -134,12 +154,19 @@ namespace gaseous_server.Models
if (Update == false)
{
// 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
{
// 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("RetroPieDirectoryName", item.RetroPieDirectoryName);
@@ -147,11 +174,13 @@ namespace gaseous_server.Models
{
dbDict.Add("WebEmulator_Type", item.WebEmulator.Type);
dbDict.Add("WebEmulator_Core", item.WebEmulator.Core);
dbDict.Add("AvailableWebEmulators", Newtonsoft.Json.JsonConvert.SerializeObject(item.WebEmulator.AvailableWebEmulators));
}
else
{
dbDict.Add("WebEmulator_Type", "");
dbDict.Add("WebEmulator_Core", "");
dbDict.Add("AvailableWebEmulators", "");
}
db.ExecuteCMD(sql, dbDict);
@@ -206,6 +235,11 @@ namespace gaseous_server.Models
db.ExecuteCMD(sql, dbDict);
}
}
if (PlatformMapCache.ContainsKey(item.IGDBId.ToString()))
{
PlatformMapCache.Remove(item.IGDBId.ToString());
}
}
static PlatformMapItem BuildPlatformMapItem(DataRow row)
@@ -286,7 +320,7 @@ namespace gaseous_server.Models
{
filename = (string)Common.ReturnValueIfNull(biosRow["Filename"], ""),
description = (string)Common.ReturnValueIfNull(biosRow["Description"], ""),
hash = (string)Common.ReturnValueIfNull(biosRow["Hash"], "")
hash = ((string)Common.ReturnValueIfNull(biosRow["Hash"], "")).ToLower()
};
bioss.Add(bios);
}
@@ -304,10 +338,20 @@ namespace gaseous_server.Models
mapItem.RetroPieDirectoryName = (string)Common.ReturnValueIfNull(row["RetroPieDirectoryName"], "");
mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem{
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;
if (PlatformMapCache.ContainsKey(IGDBId.ToString()))
{
PlatformMapCache[IGDBId.ToString()] = mapItem;
}
else
{
PlatformMapCache.Add(IGDBId.ToString(), mapItem);
}
return mapItem;
}
@@ -378,6 +422,21 @@ namespace gaseous_server.Models
{
public string Type { 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; }

View File

@@ -9,34 +9,52 @@ namespace gaseous_server
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;
_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;
_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;
_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;
_AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped;
_Blocks = Blocks;
}
private QueueItemType _ItemType = QueueItemType.NotConfigured;
private QueueItemState _ItemState = QueueItemState.NeverStarted;
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 string _LastResult = "";
private string? _LastError = null;
private bool _ForceExecute = false;
private bool _AllowManualStart = true;
private bool _RemoveWhenStopped = false;
private bool _IsBlocked = false;
private List<QueueItemType> _Blocks = new List<QueueItemType>();
public QueueItemType ItemType => _ItemType;
@@ -54,6 +72,9 @@ namespace gaseous_server
public string? LastError => _LastError;
public bool Force => _ForceExecute;
public bool AllowManualStart => _AllowManualStart;
public bool RemoveWhenStopped => _RemoveWhenStopped;
public bool IsBlocked => _IsBlocked;
public object? Options { get; set; } = null;
public List<QueueItemType> Blocks => _Blocks;
public void Execute()
@@ -87,32 +108,52 @@ namespace gaseous_server
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);
_SaveLastRunTime = true;
break;
case QueueItemType.TitleIngestor:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory);
_SaveLastRunTime = true;
break;
case QueueItemType.MetadataRefresh:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher");
Classes.MetadataManagement.RefreshMetadata(true);
_SaveLastRunTime = true;
break;
case QueueItemType.OrganiseLibrary:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser");
Classes.ImportGame.OrganiseLibrary();
_SaveLastRunTime = true;
break;
case QueueItemType.LibraryScan:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner");
Classes.ImportGame.LibraryScan();
_SaveLastRunTime = true;
break;
case QueueItemType.CollectionCompiler:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler");
Classes.Collections.CompileCollections();
Classes.Collections.CompileCollections((long)Options);
break;
case QueueItemType.BackgroundDatabaseUpgrade:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Background Upgrade");
gaseous_tools.DatabaseMigration.UpgradeScriptBackgroundTasks();
break;
}
}
catch (Exception ex)
@@ -133,17 +174,59 @@ namespace gaseous_server
{
_ForceExecute = true;
}
public void BlockedState(bool BlockState)
{
_IsBlocked = BlockState;
}
}
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,
/// <summary>
/// Ingests signature DAT files into the database
/// </summary>
SignatureIngestor,
/// <summary>
/// Imports game files into the database and moves them to the required location on disk
/// </summary>
TitleIngestor,
/// <summary>
/// Forces stored metadata to be refreshed
/// </summary>
MetadataRefresh,
/// <summary>
/// Ensures all managed files are where they are supposed to be
/// </summary>
OrganiseLibrary,
/// <summary>
/// Looks for orphaned files in the library and re-adds them to the database
/// </summary>
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

View File

@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.OpenApi.Models;
Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
// set up db
@@ -29,6 +30,23 @@ if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
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
var builder = WebApplication.CreateBuilder(args);
@@ -164,13 +182,13 @@ 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.QueueItems.Add(new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 5, false));
Logging.WriteToDiskOnly = false;
// start the app
app.Run();

File diff suppressed because one or more lines are too long

View File

@@ -33,9 +33,23 @@ namespace gaseous_server
//_logger.LogInformation(
// "Timed Hosted Service is working. Count: {Count}", count);
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) {
if ((DateTime.UtcNow > qi.NextRunTime || qi.Force == true) && CheckProcessBlockList(qi) == true) {
qi.Execute();
List<ProcessQueue.QueueItem> ActiveList = new List<ProcessQueue.QueueItem>();
ActiveList.AddRange(ProcessQueue.QueueItems);
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();
}
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 (queueItem.Blocks.Contains(qi.ItemType) && qi.ItemState == ProcessQueue.QueueItemState.Running)
if (qi.ItemState == ProcessQueue.QueueItemState.Running) {
// other service is running, check if queueItem is blocked by it
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;
}
else
{
return true;
}
return false;
}
}
}

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

View File

@@ -27,7 +27,7 @@
<h4>Standard Directory Naming</h4>
</td>
<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>
</tr>
<tr>
@@ -38,7 +38,7 @@
<h4>RetroPie Directory Naming</h4>
</td>
<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>
</tr>
</table>
@@ -53,23 +53,32 @@
<input id="mapping_edit_enablewebemulator" type="checkbox"><label for="mapping_edit_enablewebemulator" style="margin-left: 5px;">Enabled</label>
</td>
</tr>
<tr>
<tr name="mapping_edit_webemulator">
<td style="width: 25%;">
<h4>Engine</h4>
</td>
<td>
<select id="mapping_edit_webemulatorengine" style="width: 100%;">
<option value="">-</option>
<option value="EmulatorJS">EmulatorJS</option>
</select>
</td>
</tr>
<tr>
<tr name="mapping_edit_webemulator">
<td style="width: 25%;">
<h4>Core</h4>
</td>
<td style="text-align: right;">
<input id="mapping_edit_webemulatorcore" type="text" style="width: 95%;"/>
<td>
<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>
</tr>
</table>
@@ -91,6 +100,10 @@
modalContent[0].classList.add('collections_modal');
}
var availableWebEmulators = [];
DisplayWebEmulatorContent(false);
ajaxCall(
'/api/v1/PlatformMaps/' + modalVariables,
'GET',
@@ -125,14 +138,48 @@
document.getElementById('mapping_edit_igdbslug').value = result.igdbSlug;
document.getElementById('mapping_edit_retropie').value = result.retroPieDirectoryName;
// set up web emulator drop downs
$('#mapping_edit_webemulatorengine').select2();
if (result.webEmulator.type.length > 0) {
document.getElementById('mapping_edit_enablewebemulator').checked = true;
$('#mapping_edit_webemulatorengine').val(result.webEmulator.type);
$('#mapping_edit_webemulatorengine').trigger('change');
document.getElementById('mapping_edit_webemulatorcore').value = result.webEmulator.core;
$('#mapping_edit_webemulatorcore').select2();
// start populating drop downs
if (result.webEmulator) {
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 {
document.getElementById('mapping_edit_enablewebemulator').checked = false;
// no emulators available
DisplayWebEmulatorContent(false);
}
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) {
for (var i = 0; i < tagList.length; i++) {
var data = {
@@ -254,4 +350,39 @@
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>

View File

@@ -375,7 +375,11 @@
var launchButton = '';
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 = [

View File

@@ -4,13 +4,17 @@
<table style="width: 100%;">
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<th>Join our Discord</th>
<td><a href="https://discord.gg/Nhu7wpT3k4" class="romlink">https://discord.gg/Nhu7wpT3k4</a></td>
</tr>
<tr>
<th>Server Version</th>
<td id="settings_appversion"></td>
@@ -19,6 +23,18 @@
<th>Database Schema Version</th>
<td id="settings_dbversion"></td>
</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>
<td colspan="2">
<h3>Data Sources</h2>
@@ -26,11 +42,12 @@
</td>
</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>
</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>
</tr>
<tr>
@@ -39,19 +56,21 @@
</td>
</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>
</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>
</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>
</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>
</tr>
</table>

View File

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

View File

@@ -48,7 +48,7 @@
}
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.uniqueFileExtensions.join(', '),
hasWebEmulator

View File

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

View File

@@ -61,7 +61,7 @@ namespace gaseous_tools
{
get
{
return _md5hash;
return _md5hash.ToLower();
}
set
{
@@ -73,7 +73,7 @@ namespace gaseous_tools
{
get
{
return _sha1hash;
return _sha1hash.ToLower();
}
set
{

View File

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

View File

@@ -5,26 +5,43 @@ namespace gaseous_tools
{
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)
{
switch(DatabaseType)
{
case gaseous_tools.Database.databaseType.MySql:
case Database.databaseType.MySql:
switch (TargetSchemaVersion)
{
case 1002:
MySql_1002_MigrateMetadataVersion();
// this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1002);
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);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();

View File

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

View File

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