Compare commits

...

16 Commits

Author SHA1 Message Date
Michael Green
7f2e186d06 Refactored code, and added Amiga CDTV and CD32, and ColecoVision to the PlatformMap (#252)
* More bug fixes

* Update PlatformMap for Amiga CDTV and CD32, and ColecoVision

* Fixed default platform setting for library scan

* Refactor of rematcher

* Temp unzips are no longer deleted immediately - now kept and cleaned up after 5 minutes

* Library Scan now spawns worker processes to perform scans in parallel. Number of workers is limited by MaxWorkers default = 4

* More logging

* More null reference checks

* Overhaul of ROM and MediaGroup handling in web page

* Minor collections updates

* Newlines are now replaced with breaks in HTML on Game summary page
2024-01-10 12:57:31 +11:00
Michael Green
7d5419d33c Multiple Updates (#251)
* Added more error logging to zip expansion

* Added more logging

* More logging, and archive contents can now be seen in rom info

* Bug fixes and caching enhancements

* Import path now cleaned after import
2024-01-07 01:07:10 +11:00
Michael Green
ce9ab91e5b Limit library scan workers to four concurrent processes
* Formatting fixes

* Library scans are now limited to 4 worker processes
2024-01-04 16:01:33 +11:00
Michael Green
eac35ee8a3 A new library scanner process will now be started for each library
* A new library scanner process will now be started for each library
2024-01-04 12:04:46 +11:00
Michael Green
49f36a2b99 Many bug and UI fixes, and improved client side caching of images (#248) 2024-01-03 23:24:26 +11:00
Michael Green
47c2fc8069 Fixed ROM count and platform display
* Fixed ROM count and platform display
2024-01-02 22:10:04 +11:00
Michael Green
9a215123f6 Remove broken dependency reference
* Remove broken dependency reference
2024-01-02 11:32:08 +11:00
dependabot[bot]
40597b4386 chore(deps): bump gaseous-server/wwwroot/emulators/EmulatorJS (#239)
Bumps [gaseous-server/wwwroot/emulators/EmulatorJS](https://github.com/EmulatorJS/EmulatorJS) from `1b3a17f` to `c1a9d9b`.
- [Release notes](https://github.com/EmulatorJS/EmulatorJS/releases)
- [Commits](1b3a17f6f1...c1a9d9b266)

---
updated-dependencies:
- dependency-name: gaseous-server/wwwroot/emulators/EmulatorJS
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-02 00:38:55 +11:00
Michael Green
eb9c1ce1a4 Improve UX (#244)
* Removed insta-search and added a search button - closes #203 
* Pagination is constrained to 5 pages on either side of the current page (10 page numbers displayed in total) - closes #223
* Reviewed all dialogs and made behaviour consistent - closes #225
2024-01-02 00:33:56 +11:00
Michael Green
7be1ec7080 More refactoring and bug fixes (#243) 2023-12-31 00:21:40 +11:00
Michael Green
311c7733fa Assorted bug fixes (#242)
* Fixed DBNull error when updating metadata

* Fixed platform id bug with media group launching

* Updates to support Hasheous - testing only

* Refactored alot of code, initial support for Hasheous
2023-12-30 20:28:23 +11:00
Michael Green
ea0a5a6a71 Add missing indexes (#238)
* Add missing index

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

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

* Moved AgeGroups to it's own class

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

* Improved first set up logging

* Updated logging display
2023-12-17 00:51:46 +11:00
109 changed files with 4146 additions and 2127 deletions

1
.gitignore vendored
View File

@@ -403,3 +403,4 @@ ASALocalRun/
# Local History for Visual Studio
.localhistory/
gaseous-server/.DS_Store

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -12,6 +12,7 @@ using IGDB.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using SharpCompress.Common;
namespace gaseous_server.Classes
{
@@ -66,7 +67,7 @@ namespace gaseous_server.Classes
public static CollectionItem NewCollection(CollectionItem item)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, ArchiveType, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @archivetype, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("name", item.Name);
dbDict.Add("description", item.Description);
@@ -82,6 +83,7 @@ namespace gaseous_server.Classes
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
dbDict.Add("archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip));
dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())));
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -97,7 +99,7 @@ namespace gaseous_server.Classes
public static CollectionItem EditCollection(long Id, CollectionItem item, bool ForceRebuild = true)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id";
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, ArchiveType=@archivetype, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id);
dbDict.Add("name", item.Name);
@@ -115,8 +117,9 @@ namespace gaseous_server.Classes
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())));
dbDict.Add("archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip));
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + item.ArchiveExtension);
if (ForceRebuild == true)
{
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
@@ -220,7 +223,7 @@ namespace gaseous_server.Classes
}
} else {
// get all platforms to pull from
Dictionary<string, object> FilterDict = Filters.Filter(AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, true);
Dictionary<string, List<Filters.FilterItem>> FilterDict = Filters.Filter(AgeGroups.AgeRestrictionGroupings.Adult, true);
List<Classes.Filters.FilterItem> filteredPlatforms = (List<Classes.Filters.FilterItem>)FilterDict["platforms"];
foreach (Filters.FilterItem filterItem in filteredPlatforms) {
platforms.Add(Platforms.GetPlatform(filterItem.Id));
@@ -304,7 +307,7 @@ namespace gaseous_server.Classes
// calculate total rom size for the game
long GameRomSize = 0;
foreach (Roms.GameRomItem gameRom in gameRoms) {
GameRomSize += gameRom.Size;
GameRomSize += (long)gameRom.Size;
}
if (collectionItem.MaximumBytesPerPlatform > 0) {
if ((TotalRomSize + GameRomSize) < collectionItem.MaximumBytesPerPlatform) {
@@ -389,7 +392,7 @@ namespace gaseous_server.Classes
db.ExecuteCMD(sql, dbDict);
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = GetCollectionContent(collectionItem).Collection;
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + ".zip");
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + collectionItem.ArchiveExtension);
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString());
try
@@ -499,7 +502,7 @@ namespace gaseous_server.Classes
if (File.Exists(gameRomItem.Path))
{
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name);
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name));
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name), true);
}
}
}
@@ -508,7 +511,21 @@ namespace gaseous_server.Classes
// compress to zip
Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection");
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
switch(collectionItem.ArchiveType)
{
case CollectionItem.ArchiveTypes.Zip:
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
break;
case CollectionItem.ArchiveTypes.RAR:
break;
case CollectionItem.ArchiveTypes.SevenZip:
break;
}
// clean up
if (Directory.Exists(ZipFileTempPath))
@@ -567,6 +584,7 @@ namespace gaseous_server.Classes
item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1);
item.FolderStructure = (CollectionItem.FolderStructures)(int)Common.ReturnValueIfNull(row["FolderStructure"], 0);
item.IncludeBIOSFiles = (bool)row["IncludeBIOSFiles"];
item.ArchiveType = (CollectionItem.ArchiveTypes)(int)Common.ReturnValueIfNull(row["ArchiveType"], 0);
item.AlwaysInclude = Newtonsoft.Json.JsonConvert.DeserializeObject<List<CollectionItem.AlwaysIncludeItem>>(strAlwaysInclude);
item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0);
@@ -595,6 +613,32 @@ namespace gaseous_server.Classes
public long? MaximumCollectionSizeInBytes { get; set; }
public FolderStructures FolderStructure { get; set; } = FolderStructures.Gaseous;
public bool IncludeBIOSFiles { get; set; } = true;
public ArchiveTypes ArchiveType { get; set; } = CollectionItem.ArchiveTypes.Zip;
public string ArchiveExtension
{
get
{
if (ArchiveType != null)
{
switch (ArchiveType)
{
case ArchiveTypes.Zip:
default:
return ".zip";
case ArchiveTypes.RAR:
return ".rar";
case ArchiveTypes.SevenZip:
return ".7z";
}
}
else
{
return ".zip";
}
}
}
public List<AlwaysIncludeItem> AlwaysInclude { get; set; }
[JsonIgnore]
@@ -604,7 +648,7 @@ namespace gaseous_server.Classes
{
if (_BuildStatus == CollectionBuildStatus.Completed)
{
if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip")))
if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension)))
{
return CollectionBuildStatus.Completed;
}
@@ -632,7 +676,7 @@ namespace gaseous_server.Classes
{
if (BuildStatus == CollectionBuildStatus.Completed)
{
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension);
if (File.Exists(ZipFilePath))
{
FileInfo fi = new FileInfo(ZipFilePath);
@@ -665,6 +709,13 @@ namespace gaseous_server.Classes
RetroPie = 1
}
public enum ArchiveTypes
{
Zip = 0,
RAR = 1,
SevenZip = 2
}
public class AlwaysIncludeItem
{
public long PlatformId { get; set; }
@@ -749,7 +800,7 @@ namespace gaseous_server.Classes
long Size = 0;
foreach (CollectionGameItem Game in Games) {
foreach (Roms.GameRomItem Rom in Game.Roms) {
Size += Rom.Size;
Size += (long)Rom.Size;
}
}
@@ -792,6 +843,15 @@ namespace gaseous_server.Classes
public string Name { get; set; }
public string Slug { get; set; }
public long Cover { get; set;}
public IGDB.Models.Cover CoverItem
{
get
{
IGDB.Models.Cover cover = Covers.GetCover(Cover, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory, "Games", Slug), false);
return cover;
}
}
public CollectionItem.AlwaysIncludeItem InclusionStatus { get; set; }
@@ -801,7 +861,7 @@ namespace gaseous_server.Classes
get {
long Size = 0;
foreach (Roms.GameRomItem Rom in Roms) {
Size += Rom.Size;
Size += (long)Rom.Size;
}
return Size;

View File

@@ -1,9 +1,11 @@
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;
using System.Security.Cryptography;
namespace gaseous_server.Classes
{
public class Common
public static class Common
{
/// <summary>
/// Returns IfNullValue if the ObjectToCheck is null
@@ -110,6 +112,14 @@ namespace gaseous_server.Classes
return Path.GetFullPath(new Uri(path).LocalPath)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
public static string GetDescription(this Enum value)
{
return ((DescriptionAttribute)Attribute.GetCustomAttribute(
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => x.GetValue(null).Equals(value)),
typeof(DescriptionAttribute)))?.Description ?? value.ToString();
}
}
/// <summary>

View File

@@ -473,22 +473,66 @@ namespace gaseous_server.Classes
public class MetadataAPI
{
private static Communications.MetadataSources _Source
private static HasheousClient.Models.MetadataModel.MetadataSources _MetadataSource
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
{
return (Communications.MetadataSources)Enum.Parse(typeof(Communications.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
return (HasheousClient.Models.MetadataModel.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
}
else
{
return Communications.MetadataSources.IGDB;
return HasheousClient.Models.MetadataModel.MetadataSources.IGDB;
}
}
}
public Communications.MetadataSources Source = _Source;
private static HasheousClient.Models.MetadataModel.SignatureSources _SignatureSource
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("signaturesource")))
{
return (HasheousClient.Models.MetadataModel.SignatureSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.SignatureSources), Environment.GetEnvironmentVariable("signaturesource"));
}
else
{
return HasheousClient.Models.MetadataModel.SignatureSources.LocalOnly;
}
}
}
private static int _MaxLibraryScanWorkers
{
get
{
return 4;
}
}
private static string _HasheousHost
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("hasheoushoust")))
{
return Environment.GetEnvironmentVariable("hasheoushoust");
}
else
{
return "https://hasheous.org/";
}
}
}
public HasheousClient.Models.MetadataModel.MetadataSources MetadataSource = _MetadataSource;
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers;
public string HasheousHost = _HasheousHost;
}
public class IGDB

View File

@@ -107,20 +107,28 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer);
if (SchemaVer < i)
{
// run pre-upgrade code
DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
// apply schema!
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
ExecuteCMD(dbScript, dbDict);
try
{
// run pre-upgrade code
DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
// apply schema!
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
ExecuteCMD(dbScript, dbDict, 180);
sql = "UPDATE schema_version SET schema_version=@schemaver";
dbDict = new Dictionary<string, object>();
dbDict.Add("schemaver", i);
ExecuteCMD(sql, dbDict);
sql = "UPDATE schema_version SET schema_version=@schemaver";
dbDict = new Dictionary<string, object>();
dbDict.Add("schemaver", i);
ExecuteCMD(sql, dbDict);
// run post-upgrade code
DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
// run post-upgrade code
DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Database", "Schema upgrade failed! Unable to continue.", ex);
System.Environment.Exit(1);
}
}
}
}
@@ -227,7 +235,8 @@ namespace gaseous_server.Classes
{
case databaseType.MySql:
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
return (int)conn.ExecNonQuery(Command, Parameters, Timeout);
int retVal = conn.ExecNonQuery(Command, Parameters, Timeout);
return retVal;
default:
return 0;
}
@@ -321,38 +330,40 @@ namespace gaseous_server.Classes
DataTable RetTable = new DataTable();
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
MySqlConnection conn = new MySqlConnection(DBConn);
conn.Open();
MySqlCommand cmd = new MySqlCommand
using (MySqlConnection conn = new MySqlConnection(DBConn))
{
Connection = conn,
CommandText = SQL,
CommandTimeout = Timeout
};
conn.Open();
foreach (string Parameter in Parameters.Keys)
{
cmd.Parameters.AddWithValue(Parameter, Parameters[Parameter]);
}
try
{
Logging.Log(Logging.LogType.Debug, "Database", "Executing sql: '" + SQL + "'", null, true);
if (Parameters.Count > 0)
MySqlCommand cmd = new MySqlCommand
{
string dictValues = string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
}
RetTable.Load(cmd.ExecuteReader());
} catch (Exception ex) {
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
Trace.WriteLine("Error executing " + SQL);
Trace.WriteLine("Full exception: " + ex.ToString());
}
Connection = conn,
CommandText = SQL,
CommandTimeout = Timeout
};
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close();
foreach (string Parameter in Parameters.Keys)
{
cmd.Parameters.AddWithValue(Parameter, Parameters[Parameter]);
}
try
{
Logging.Log(Logging.LogType.Debug, "Database", "Executing sql: '" + SQL + "'", null, true);
if (Parameters.Count > 0)
{
string dictValues = string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
}
RetTable.Load(cmd.ExecuteReader());
} catch (Exception ex) {
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
Trace.WriteLine("Error executing " + SQL);
Trace.WriteLine("Full exception: " + ex.ToString());
}
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close();
}
return RetTable;
}
@@ -362,60 +373,64 @@ namespace gaseous_server.Classes
int result = 0;
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
MySqlConnection conn = new MySqlConnection(DBConn);
conn.Open();
MySqlCommand cmd = new MySqlCommand
using (MySqlConnection conn = new MySqlConnection(DBConn))
{
Connection = conn,
CommandText = SQL,
CommandTimeout = Timeout
};
conn.Open();
foreach (string Parameter in Parameters.Keys)
{
cmd.Parameters.AddWithValue(Parameter, Parameters[Parameter]);
}
try
{
Logging.Log(Logging.LogType.Debug, "Database", "Executing sql: '" + SQL + "'", null, true);
if (Parameters.Count > 0)
MySqlCommand cmd = new MySqlCommand
{
string dictValues = string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
}
result = cmd.ExecuteNonQuery();
} catch (Exception ex) {
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
Trace.WriteLine("Error executing " + SQL);
Trace.WriteLine("Full exception: " + ex.ToString());
}
Connection = conn,
CommandText = SQL,
CommandTimeout = Timeout
};
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close();
foreach (string Parameter in Parameters.Keys)
{
cmd.Parameters.AddWithValue(Parameter, Parameters[Parameter]);
}
try
{
Logging.Log(Logging.LogType.Debug, "Database", "Executing sql: '" + SQL + "'", null, true);
if (Parameters.Count > 0)
{
string dictValues = string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
}
result = cmd.ExecuteNonQuery();
} catch (Exception ex) {
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
Trace.WriteLine("Error executing " + SQL);
Trace.WriteLine("Full exception: " + ex.ToString());
}
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close();
}
return result;
}
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
{
var conn = new MySqlConnection(DBConn);
conn.Open();
var command = conn.CreateCommand();
MySqlTransaction transaction;
transaction = conn.BeginTransaction();
command.Connection = conn;
command.Transaction = transaction;
foreach (Dictionary<string, object> Parameter in Parameters)
using (MySqlConnection conn = new MySqlConnection(DBConn))
{
var cmd = buildcommand(conn, Parameter["sql"].ToString(), (Dictionary<string, object>)Parameter["values"], Timeout);
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
}
conn.Open();
var command = conn.CreateCommand();
MySqlTransaction transaction;
transaction = conn.BeginTransaction();
command.Connection = conn;
command.Transaction = transaction;
foreach (Dictionary<string, object> Parameter in Parameters)
{
var cmd = buildcommand(conn, Parameter["sql"].ToString(), (Dictionary<string, object>)Parameter["values"], Timeout);
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
}
transaction.Commit();
conn.Close();
transaction.Commit();
conn.Close();
}
}
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
@@ -441,16 +456,18 @@ namespace gaseous_server.Classes
public bool TestConnection()
{
MySqlConnection conn = new MySqlConnection(DBConn);
try
using (MySqlConnection conn = new MySqlConnection(DBConn))
{
conn.Open();
conn.Close();
return true;
}
catch
{
return false;
try
{
conn.Open();
conn.Close();
return true;
}
catch
{
return false;
}
}
}
}

View File

@@ -0,0 +1,373 @@
using System.Collections.Concurrent;
using System.IO.Compression;
using HasheousClient.Models;
using NuGet.Common;
using SevenZip;
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
namespace gaseous_server.Classes
{
public class FileSignature
{
public gaseous_server.Models.Signatures_Games GetFileSignature(GameLibrary.LibraryItem library, Common.hashObject hash, FileInfo fi, string GameFileImportPath)
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Getting signature for file: " + GameFileImportPath);
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
discoveredSignature = _GetFileSignature(hash, fi.Name, fi.Extension, fi.Length, GameFileImportPath, false);
string[] CompressionExts = { ".zip", ".rar", ".7z" };
string ImportedFileExtension = Path.GetExtension(GameFileImportPath);
if (CompressionExts.Contains(ImportedFileExtension) && (fi.Length < 1073741824))
{
// file is a zip and less than 1 GiB
// extract the zip file and search the contents
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, library.Id.ToString(), Path.GetRandomFileName());
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing " + GameFileImportPath + " to " + ExtractPath + " examine contents");
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try
{
switch(ImportedFileExtension)
{
case ".zip":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip");
try
{
using (var archive = SharpCompress.Archives.Zip.ZipArchive.Open(GameFileImportPath))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
}
catch (Exception zipEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Unzip error", zipEx);
throw;
}
break;
case ".rar":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using rar");
try
{
using (var archive = RarArchive.Open(GameFileImportPath))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
}
catch (Exception zipEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Unrar error", zipEx);
throw;
}
break;
case ".7z":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using 7z");
try
{
using (var archive = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(GameFileImportPath))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
}
catch (Exception zipEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "7z error", zipEx);
throw;
}
break;
}
Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches");
// loop through contents until we find the first signature match
List<ArchiveData> archiveFiles = new List<ArchiveData>();
bool signatureFound = false;
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
{
if (File.Exists(file))
{
FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file);
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file);
if (zfi != null)
{
ArchiveData archiveData = new ArchiveData{
FileName = Path.GetFileName(file),
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
Size = zfi.Length,
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
};
archiveFiles.Add(archiveData);
if (signatureFound == false)
{
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi.Name, zfi.Extension, zfi.Length, file, true);
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ImportedFileExtension);
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{
if (
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEArcade ||
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEMess
)
{
zDiscoveredSignature.Rom.Name = zDiscoveredSignature.Game.Description + ImportedFileExtension;
}
zDiscoveredSignature.Rom.Crc = discoveredSignature.Rom.Crc;
zDiscoveredSignature.Rom.Md5 = discoveredSignature.Rom.Md5;
zDiscoveredSignature.Rom.Sha1 = discoveredSignature.Rom.Sha1;
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
discoveredSignature = zDiscoveredSignature;
signatureFound = true;
}
}
}
}
}
discoveredSignature.Rom.Attributes.Add(new KeyValuePair<string, object>(
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing compressed file: " + GameFileImportPath, ex);
}
}
return discoveredSignature;
}
private gaseous_server.Models.Signatures_Games _GetFileSignature(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath, bool IsInZip)
{
Logging.Log(Logging.LogType.Information, "Import Game", "Checking signature for file: " + GameFileImportPath + "\nMD5 hash: " + hash.md5hash + "\nSHA1 hash: " + hash.sha1hash);
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
// do database search first
gaseous_server.Models.Signatures_Games? dbSignature = _GetFileSignatureFromDatabase(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
if (dbSignature != null)
{
// local signature found
Logging.Log(Logging.LogType.Information, "Import Game", "Signature found in local database for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
else
{
// no local signature attempt to pull from Hasheous
dbSignature = _GetFileSignatureFromHasheous(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
if (dbSignature != null)
{
// signature retrieved from Hasheous
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
else
{
// construct a signature from file data
dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
}
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, ImageExtension, false);
Logging.Log(Logging.LogType.Information, "Import Game", " Determined import file as: " + discoveredSignature.Game.Name + " (" + discoveredSignature.Game.Year + ") " + discoveredSignature.Game.System);
Logging.Log(Logging.LogType.Information, "Import Game", " Platform determined to be: " + discoveredSignature.Flags.IGDBPlatformName + " (" + discoveredSignature.Flags.IGDBPlatformId + ")");
return discoveredSignature;
}
private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for MD5: " + hash.md5hash);
// check 1: do we have a signature for it?
gaseous_server.Classes.SignatureManagement sc = new SignatureManagement();
List<gaseous_server.Models.Signatures_Games> signatures = sc.GetSignature(hash.md5hash);
if (signatures == null || signatures.Count == 0)
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for SHA1: " + hash.sha1hash);
// no md5 signature found - try sha1
signatures = sc.GetSignature("", hash.sha1hash);
}
gaseous_server.Models.Signatures_Games? discoveredSignature = null;
if (signatures.Count == 1)
{
// only 1 signature found!
discoveredSignature = signatures.ElementAt(0);
return discoveredSignature;
}
else if (signatures.Count > 1)
{
// more than one signature found - find one with highest score
// start with first returned element
discoveredSignature = signatures.First();
foreach (gaseous_server.Models.Signatures_Games Sig in signatures)
{
if (Sig.Score > discoveredSignature.Score)
{
discoveredSignature = Sig;
}
}
return discoveredSignature;
}
return null;
}
private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromHasheous(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
{
// check if hasheous is enabled, and if so use it's signature database
if (Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous)
{
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
SignatureLookupItem? HasheousResult = hasheous.RetrieveFromHasheousAsync(new HashLookupModel{
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
});
if (HasheousResult != null)
{
if (HasheousResult.Signature != null)
{
gaseous_server.Models.Signatures_Games signature = new Models.Signatures_Games();
signature.Game = HasheousResult.Signature.Game;
signature.Rom = HasheousResult.Signature.Rom;
if (HasheousResult.MetadataResults != null)
{
if (HasheousResult.MetadataResults.Count > 0)
{
foreach (SignatureLookupItem.MetadataResult metadataResult in HasheousResult.MetadataResults)
{
if (metadataResult.Source == MetadataModel.MetadataSources.IGDB)
{
signature.Flags.IGDBPlatformId = (long)metadataResult.PlatformId;
signature.Flags.IGDBGameId = (long)metadataResult.GameId;
}
}
}
}
return signature;
}
}
}
return null;
}
private gaseous_server.Models.Signatures_Games _GetFileSignatureFromFileData(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
{
SignatureManagement signatureManagement = new SignatureManagement();
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
// no signature match found - try name search
List<gaseous_server.Models.Signatures_Games> signatures = signatureManagement.GetByTosecName(ImageName);
if (signatures.Count == 1)
{
// only 1 signature found!
discoveredSignature = signatures.ElementAt(0);
return discoveredSignature;
}
else if (signatures.Count > 1)
{
// more than one signature found - find one with highest score
foreach (gaseous_server.Models.Signatures_Games Sig in signatures)
{
if (Sig.Score > discoveredSignature.Score)
{
discoveredSignature = Sig;
}
}
return discoveredSignature;
}
else
{
// still no search - try alternate method
gaseous_server.Models.Signatures_Games.GameItem gi = new gaseous_server.Models.Signatures_Games.GameItem();
gaseous_server.Models.Signatures_Games.RomItem ri = new gaseous_server.Models.Signatures_Games.RomItem();
discoveredSignature.Game = gi;
discoveredSignature.Rom = ri;
// game title is the file name without the extension or path
gi.Name = Path.GetFileNameWithoutExtension(GameFileImportPath);
// remove everything after brackets - leaving (hopefully) only the name
if (gi.Name.Contains("("))
{
gi.Name = gi.Name.Substring(0, gi.Name.IndexOf("(")).Trim();
}
// remove special characters like dashes
gi.Name = gi.Name.Replace("-", "").Trim();
// get rom data
ri.Name = Path.GetFileName(GameFileImportPath);
ri.Md5 = hash.md5hash;
ri.Sha1 = hash.sha1hash;
ri.Size = ImageSize;
ri.SignatureSource = gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.None;
return discoveredSignature;
}
}
public class ArchiveData
{
public string FileName { get; set; }
public string FilePath { get; set; }
public long Size { get; set; }
public string MD5 { get; set; }
public string SHA1 { get; set; }
}
}
}

View File

@@ -7,24 +7,24 @@ namespace gaseous_server.Classes
{
public class Filters
{
public static Dictionary<string, object> Filter(Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
public static Dictionary<string, List<FilterItem>> Filter(Metadata.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> FilterSet = new Dictionary<string, object>();
Dictionary<string, List<FilterItem>> FilterSet = new Dictionary<string, List<FilterItem>>();
// platforms
List<FilterItem> platforms = new List<FilterItem>();
string ageRestriction_Platform = "Game.AgeGroupId <= " + (int)MaximumAgeRestriction;
string ageRestriction_Platform = "AgeGroup.AgeGroupId <= " + (int)MaximumAgeRestriction;
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
if (IncludeUnrated == true)
{
ageRestriction_Platform += " OR Game.AgeGroupId IS NULL";
ageRestriction_Platform += " OR AgeGroup.AgeGroupId IS NULL";
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
}
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(view_Games.Id) AS GameCount FROM view_Games JOIN Relation_Game_Platforms ON Relation_Game_Platforms.GameId = view_Games.Id AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) JOIN Platform ON Platform.Id = Relation_Game_Platforms.PlatformsId WHERE (" + ageRestriction_Generic + ") GROUP BY Platform.`Name` ORDER BY Platform.`Name`;";
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, Games_Roms.PlatformId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id , Games_Roms.PlatformId HAVING RomCount > 0) Game JOIN Platform ON Game.PlatformId = Platform.Id GROUP BY Platform.`Name`;";
DataTable dbResponse = db.ExecuteCMD(sql);
@@ -38,7 +38,7 @@ namespace gaseous_server.Classes
// genres
List<FilterItem> genres = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic);
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
@@ -49,7 +49,7 @@ namespace gaseous_server.Classes
// game modes
List<FilterItem> gameModes = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic);
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
@@ -60,7 +60,7 @@ namespace gaseous_server.Classes
// player perspectives
List<FilterItem> playerPerspectives = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic);
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
@@ -71,7 +71,7 @@ namespace gaseous_server.Classes
// themes
List<FilterItem> themes = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic);
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
@@ -81,22 +81,24 @@ namespace gaseous_server.Classes
FilterSet.Add("themes", themes);
// age groups
List<FilterAgeGrouping> agegroupings = new List<FilterAgeGrouping>();
sql = "SELECT view_Games.Id, view_Games.AgeGroupId, COUNT(view_Games.Id) AS GameCount FROM view_Games WHERE (" + ageRestriction_Generic + ") GROUP BY view_Games.AgeGroupId ORDER BY view_Games.AgeGroupId DESC;";
List<FilterItem> agegroupings = new List<FilterItem>();
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterAgeGrouping filterAgeGrouping = new FilterAgeGrouping();
FilterItem filterAgeGrouping = new FilterItem();
if (dr["AgeGroupId"] == DBNull.Value)
{
filterAgeGrouping.Id = (long)AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
filterAgeGrouping.AgeGroup = AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
filterAgeGrouping.Id = (int)(long)AgeGroups.AgeRestrictionGroupings.Unclassified;
filterAgeGrouping.Name = AgeGroups.AgeRestrictionGroupings.Unclassified.ToString();
}
else
{
filterAgeGrouping.Id = (long)(AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
filterAgeGrouping.AgeGroup = (AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
int ageGroupLong = (int)dr["AgeGroupId"];
AgeGroups.AgeRestrictionGroupings ageGroup = (AgeGroups.AgeRestrictionGroupings)ageGroupLong;
filterAgeGrouping.Id = ageGroupLong;
filterAgeGrouping.Name = ageGroup.ToString();
}
filterAgeGrouping.GameCount = (int)(long)dr["GameCount"];
agegroupings.Add(filterAgeGrouping);
@@ -106,9 +108,11 @@ namespace gaseous_server.Classes
return FilterSet;
}
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction_Generic)
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction)
{
string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
//string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + AgeRestriction + ") GROUP BY Game.Id HAVING RomCount > 0) Game JOIN Relation_Game_<ITEMNAME>s ON Game.Id = Relation_Game_<ITEMNAME>s.GameId JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
sql = sql.Replace("<ITEMNAME>", Name);
DataTable dbResponse = db.ExecuteCMD(sql);
@@ -117,6 +121,11 @@ namespace gaseous_server.Classes
public class FilterItem
{
public FilterItem()
{
}
public FilterItem(DataRow dr)
{
this.Id = (long)dr["Id"];
@@ -130,22 +139,5 @@ namespace gaseous_server.Classes
public int GameCount { get; set; }
}
public class FilterAgeGrouping
{
public long Id { get; set; }
public AgeRatings.AgeGroups.AgeRestrictionGroupings AgeGroup { get ; set; }
public string Name
{
get
{
return this.AgeGroup.ToString();
}
}
public int GameCount { get; set; }
}
}
}

View File

@@ -34,6 +34,12 @@ namespace gaseous_server
{}
}
public class CannotDeleteLibraryWhileScanIsActive : Exception
{
public CannotDeleteLibraryWhileScanIsActive() : base("Unable to delete library while a library scan is active. Wait for all scans to complete and try again")
{}
}
// code
public static LibraryItem GetDefaultLibrary
{
@@ -110,21 +116,42 @@ namespace gaseous_server
int newLibraryId = (int)(long)data.Rows[0][0];
return GetLibrary(newLibraryId);
Logging.Log(Logging.LogType.Information, "Library Management", "Created library " + Name + " at directory " + PathName);
LibraryItem library = GetLibrary(newLibraryId);
return library;
}
public static void DeleteLibrary(int LibraryId)
{
if (GetLibrary(LibraryId).IsDefaultLibrary == false)
LibraryItem library = GetLibrary(LibraryId);
if (library.IsDefaultLibrary == false)
{
// check for active library scans
foreach(ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
{
if (
(item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState == ProcessQueue.QueueItemState.Running) ||
(item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker && item.ItemState == ProcessQueue.QueueItemState.Running)
)
{
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete libraries while a library scan is running. Wait until the the library scan is completed and try again.");
throw new CannotDeleteLibraryWhileScanIsActive();
}
}
Database db = new 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);
Logging.Log(Logging.LogType.Information, "Library Management", "Deleted library " + library.Name + " at path " + library.Path);
}
else
{
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete the default library.");
throw new CannotDeleteDefaultLibrary();
}
}

View File

@@ -6,51 +6,47 @@ using System.Security.Policy;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
using NuGet.Common;
using NuGet.LibraryModel;
using static gaseous_server.Classes.Metadata.Games;
using static gaseous_server.Classes.FileSignature;
using HasheousClient.Models;
namespace gaseous_server.Classes
{
public class ImportGames : QueueItemStatus
public class ImportGame : QueueItemStatus
{
public ImportGames(string ImportPath)
{
if (Directory.Exists(ImportPath))
public void ProcessDirectory(string ImportPath)
{
if (Directory.Exists(ImportPath))
{
string[] importContents_Files = Directory.GetFiles(ImportPath);
string[] importContents_Directories = Directory.GetDirectories(ImportPath);
string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
// import files first
int importCount = 1;
foreach (string importContent in importContents_Files) {
SetStatus(importCount, importContents_Files.Length, "Importing file: " + importContent);
foreach (string importContent in importContents) {
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
ImportGame.ImportGameFile(importContent, null);
ImportGameFile(importContent, null);
importCount += 1;
}
ClearStatus();
// import sub directories
foreach (string importDir in importContents_Directories) {
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
}
DeleteOrphanedDirectories(ImportPath);
}
else
{
Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
}
}
}
}
public class ImportGame : QueueItemStatus
{
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
@@ -102,7 +98,8 @@ namespace gaseous_server.Classes
{
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing");
Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath);
FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games discoveredSignature = fileSignature.GetFileSignature(GameLibrary.GetDefaultLibrary, hash, fi, GameFileImportPath);
// get discovered platform
IGDB.Models.Platform? determinedPlatform = null;
@@ -121,7 +118,7 @@ namespace gaseous_server.Classes
discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name;
}
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature, discoveredSignature.Flags.IGDBPlatformId, true);
// add to database
StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
@@ -152,155 +149,29 @@ namespace gaseous_server.Classes
}
}
public static Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
{
Models.Signatures_Games discoveredSignature = _GetFileSignature(hash, fi, GameFileImportPath);
if ((Path.GetExtension(GameFileImportPath) == ".zip") && (fi.Length < 1073741824))
if (Signature.Flags != null)
{
// file is a zip and less than 1 GiB
// extract the zip file and search the contents
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName());
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try
if (Signature.Flags.IGDBGameId != null && Signature.Flags.IGDBGameId != 0)
{
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath);
// loop through contents until we find the first signature match
foreach (string file in Directory.GetFiles(ExtractPath))
// game was determined elsewhere - probably a Hasheous server
try
{
FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file);
Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file);
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ".zip");
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{
if (
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEArcade ||
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEMess
)
{
zDiscoveredSignature.Rom.Name = zDiscoveredSignature.Game.Description + ".zip";
}
zDiscoveredSignature.Rom.Crc = discoveredSignature.Rom.Crc;
zDiscoveredSignature.Rom.Md5 = discoveredSignature.Rom.Md5;
zDiscoveredSignature.Rom.Sha1 = discoveredSignature.Rom.Sha1;
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
discoveredSignature = zDiscoveredSignature;
break;
}
return Games.GetGame(Signature.Flags.IGDBGameId, false, false, FullDownload);
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing zip file: " + GameFileImportPath, ex);
}
if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); }
}
return discoveredSignature;
}
private static Models.Signatures_Games _GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
{
// check 1: do we have a signature for it?
gaseous_server.Controllers.SignaturesController sc = new Controllers.SignaturesController();
List<Models.Signatures_Games> signatures = sc.GetSignature(hash.md5hash);
if (signatures.Count == 0)
{
// no md5 signature found - try sha1
signatures = sc.GetSignature("", hash.sha1hash);
}
Models.Signatures_Games discoveredSignature = new Models.Signatures_Games();
if (signatures.Count == 1)
{
// only 1 signature found!
discoveredSignature = signatures.ElementAt(0);
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
}
else if (signatures.Count > 1)
{
// more than one signature found - find one with highest score
foreach (Models.Signatures_Games Sig in signatures)
{
if (Sig.Score > discoveredSignature.Score)
catch (Exception ex)
{
discoveredSignature = Sig;
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
Logging.Log(Logging.LogType.Warning, "Import Game", "Provided game id resulted in a failed game lookup", ex);
}
}
}
else
{
// no signature match found - try name search
signatures = sc.GetByTosecName(fi.Name);
if (signatures.Count == 1)
{
// only 1 signature found!
discoveredSignature = signatures.ElementAt(0);
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
}
else if (signatures.Count > 1)
{
// more than one signature found - find one with highest score
foreach (Models.Signatures_Games Sig in signatures)
{
if (Sig.Score > discoveredSignature.Score)
{
discoveredSignature = Sig;
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
}
}
}
else
{
// still no search - try alternate method
Models.Signatures_Games.GameItem gi = new Models.Signatures_Games.GameItem();
Models.Signatures_Games.RomItem ri = new Models.Signatures_Games.RomItem();
discoveredSignature.Game = gi;
discoveredSignature.Rom = ri;
// game title is the file name without the extension or path
gi.Name = Path.GetFileNameWithoutExtension(GameFileImportPath);
// remove everything after brackets - leaving (hopefully) only the name
if (gi.Name.Contains("("))
{
gi.Name = gi.Name.Substring(0, gi.Name.IndexOf("("));
}
// remove special characters like dashes
gi.Name = gi.Name.Replace("-", "");
// guess platform
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, true);
// get rom data
ri.Name = Path.GetFileName(GameFileImportPath);
ri.Md5 = hash.md5hash;
ri.Sha1 = hash.sha1hash;
ri.Size = fi.Length;
ri.SignatureSource = gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None;
}
}
Logging.Log(Logging.LogType.Information, "Import Game", " Determined import file as: " + discoveredSignature.Game.Name + " (" + discoveredSignature.Game.Year + ") " + discoveredSignature.Game.System);
return discoveredSignature;
}
public static IGDB.Models.Game SearchForGame(string GameName, long PlatformId)
{
// search discovered game - case insensitive exact match first
IGDB.Models.Game determinedGame = new IGDB.Models.Game();
string GameName = Signature.Game.Name;
List<string> SearchCandidates = GetSearchCandidates(GameName);
foreach (string SearchCandidate in SearchCandidates)
@@ -423,7 +294,7 @@ namespace gaseous_server.Classes
return SearchCandidates;
}
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)
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, gaseous_server.Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -566,7 +437,7 @@ namespace gaseous_server.Classes
}
}
public static void OrganiseLibrary()
public void OrganiseLibrary()
{
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
@@ -600,162 +471,253 @@ namespace gaseous_server.Classes
foreach (var directory in Directory.GetDirectories(startLocation))
{
DeleteOrphanedDirectories(directory);
if (Directory.GetFiles(directory).Length == 0 &&
Directory.GetDirectories(directory).Length == 0)
string[] files = Directory.GetFiles(directory);
string[] directories = Directory.GetDirectories(directory);
if (files.Length == 0 &&
directories.Length == 0)
{
Directory.Delete(directory, false);
}
}
}
public void LibraryScan()
public void LibraryScan(GameLibrary.LibraryItem? singleLibrary = null)
{
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
int maxWorkers = Config.MetadataConfiguration.MaxLibraryScanWorkers;
List<GameLibrary.LibraryItem> libraries = new List<GameLibrary.LibraryItem>();
if (singleLibrary == null)
{
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting library scan. Library " + library.Name);
libraries.AddRange(GameLibrary.GetLibraries);
}
else
{
libraries.Add(singleLibrary);
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// setup background tasks for each library
foreach (GameLibrary.LibraryItem library in libraries)
{
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting worker process for library " + library.Name);
ProcessQueue.QueueItem queue = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScanWorker,
1,
new List<ProcessQueue.QueueItemType>
{
ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.Rematcher
},
false,
true);
queue.Options = library;
queue.ForceExecute();
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);
ProcessQueue.QueueItems.Add(queue);
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)
// check number of running tasks is less than maxWorkers
bool allowContinue;
do
{
allowContinue = true;
int currentWorkerCount = 0;
List<ProcessQueue.QueueItem> queueItems = new List<ProcessQueue.QueueItem>();
queueItems.AddRange(ProcessQueue.QueueItems);
foreach (ProcessQueue.QueueItem item in queueItems)
{
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
{
currentWorkerCount += 1;
}
}
if (currentWorkerCount >= maxWorkers)
{
allowContinue = false;
Thread.Sleep(60000);
}
} while (allowContinue == false);
}
bool WorkersStillWorking;
do
{
WorkersStillWorking = false;
List<ProcessQueue.QueueItem> queueItems = new List<ProcessQueue.QueueItem>();
queueItems.AddRange(ProcessQueue.QueueItems);
foreach (ProcessQueue.QueueItem item in queueItems)
{
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
{
// workers are still running - sleep and keep looping
WorkersStillWorking = true;
Thread.Sleep(30000);
}
}
} while (WorkersStillWorking == true);
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan complete. All workers stopped");
}
public void LibrarySpecificScan(GameLibrary.LibraryItem library)
{
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting scan of 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)
{
for (var i = 0; i < dtRoms.Rows.Count; i++)
{
long romId = (long)dtRoms.Rows[i]["Id"];
string romPath = (string)dtRoms.Rows[i]["Path"];
if (!romPath.StartsWith(library.Path))
{
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 AND LibraryId=@libraryid";
Dictionary<string, object> deleteDict = new Dictionary<string, object>();
deleteDict.Add("Id", romId);
deleteDict.Add("libraryid", library.Id);
db.ExecuteCMD(deleteSql, deleteDict);
}
}
}
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
// 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(library.Path, "*.*", SearchOption.AllDirectories);
int StatusCount = 0;
foreach (string LibraryFile in LibraryFiles)
{
SetStatus(StatusCount, LibraryFiles.Length, "Processing file " + LibraryFile);
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++)
{
long romId = (long)dtRoms.Rows[i]["Id"];
string romPath = (string)dtRoms.Rows[i]["Path"];
string romMd5 = (string)dtRoms.Rows[i]["MD5"];
if (!romPath.StartsWith(library.Path))
if ((LibraryFile == romPath) || (LibraryFileHash.md5hash == romMd5))
{
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 AND LibraryId=@libraryid";
Dictionary<string, object> deleteDict = new Dictionary<string, object>();
deleteDict.Add("Id", romId);
deleteDict.Add("libraryid", library.Id);
db.ExecuteCMD(deleteSql, deleteDict);
romFound = true;
break;
}
}
}
sql = "SELECT * FROM Games_Roms ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
// 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(library.Path, "*.*", SearchOption.AllDirectories);
int StatusCount = 0;
foreach (string LibraryFile in LibraryFiles)
{
SetStatus(StatusCount, LibraryFiles.Length, "Processing file " + LibraryFile);
if (!Common.SkippableFiles.Contains<string>(Path.GetFileName(LibraryFile), StringComparer.OrdinalIgnoreCase))
if (romFound == false)
{
Common.hashObject LibraryFileHash = new Common.hashObject(LibraryFile);
// file is not in database - process it
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile);
// check if file is in database
bool romFound = false;
for (var i = 0; i < dtRoms.Rows.Count; i++)
FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, LibraryFile);
try
{
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))
{
romFound = true;
break;
}
}
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);
IGDB.Models.Game determinedGame = new Game();
if (determinedPlatform == null)
long PlatformId;
IGDB.Models.Platform determinedPlatform;
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
{
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);
}
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;
}
else
{
determinedGame = SearchForGame(sig.Game.Name, (long)determinedPlatform.Id);
// use the platform discovered in the signature
PlatformId = sig.Flags.IGDBPlatformId;
}
determinedPlatform = Platforms.GetPlatform(PlatformId);
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile);
}
}
StatusCount += 1;
}
ClearStatus();
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
// 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))
catch (Exception ex)
{
if (library.IsDefaultLibrary == true)
Logging.Log(Logging.LogType.Warning, "Library Scan", "An error occurred while matching orphaned file: " + LibraryFile + ". Skipping.", ex);
}
}
}
StatusCount += 1;
}
ClearStatus();
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
// check all roms to see if their local file still exists
Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk");
StatusCount = 0;
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_server.Models.Signatures_Games.RomItem.SignatureSourceType romMetadataSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(int)dtRoms.Rows[i]["MetadataSource"];
SetStatus(StatusCount, dtRoms.Rows.Count, "Processing file " + romPath);
Logging.Log(Logging.LogType.Information, "Library Scan", "Processing ROM at path " + romPath);
if (File.Exists(romPath))
{
if (library.IsDefaultLibrary == true)
{
if (romPath != ComputeROMPath(romId))
{
if (romPath != ComputeROMPath(romId))
{
MoveGameFile(romId);
}
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found, but needs to be moved");
MoveGameFile(romId);
}
else
{
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found");
}
}
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);
}
}
}
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);
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
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);
}
StatusCount += 1;
}
}
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
}
public void Rematcher(bool ForceExecute = false)
@@ -764,64 +726,78 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
if (ForceExecute == false)
{
sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;";
}
else
{
sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
DataTable data = db.ExecuteCMD(sql, dbDict);
int StatusCount = -0;
foreach (DataRow row in data.Rows)
{
SetStatus(StatusCount, data.Rows.Count, "Running rematcher");
// get library
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch on library " + library.Name);
// get rom info
long romId = (long)row["Id"];
string romPath = (string)row["Path"];
Common.hashObject hash = new Common.hashObject
string sql = "";
if (ForceExecute == false)
{
md5hash = (string)row["MD5"],
sha1hash = (string)row["SHA1"]
};
FileInfo fi = new FileInfo(romPath);
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath);
// determine rom signature
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath);
// determine rom platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) AND LibraryId = @libraryid LIMIT 100;";
}
else
{
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND LibraryId = @libraryid;";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
dbDict.Add("libraryid", library.Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
int StatusCount = -0;
foreach (DataRow row in data.Rows)
{
SetStatus(StatusCount, data.Rows.Count, "Running rematcher");
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
// get rom info
long romId = (long)row["Id"];
string romPath = (string)row["Path"];
Common.hashObject hash = new Common.hashObject
{
md5hash = (string)row["MD5"],
sha1hash = (string)row["SHA1"]
};
FileInfo fi = new FileInfo(romPath);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath);
string attemptSql = "UPDATE Games_Roms SET LastMatchAttemptDate=@lastmatchattemptdate WHERE Id=@id;";
Dictionary<string, object> dbLastAttemptDict = new Dictionary<string, object>();
dbLastAttemptDict.Add("id", romId);
dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow);
db.ExecuteCMD(attemptSql, dbLastAttemptDict);
// determine rom signature
FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, romPath);
StatusCount += 1;
// get discovered platform
long PlatformId;
IGDB.Models.Platform determinedPlatform;
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
{
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;
}
else
{
// use the platform discovered in the signature
PlatformId = sig.Flags.IGDBPlatformId;
}
determinedPlatform = Platforms.GetPlatform(PlatformId);
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
string attemptSql = "UPDATE Games_Roms SET LastMatchAttemptDate=@lastmatchattemptdate WHERE Id=@id;";
Dictionary<string, object> dbLastAttemptDict = new Dictionary<string, object>();
dbLastAttemptDict.Add("id", romId);
dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow);
db.ExecuteCMD(attemptSql, dbLastAttemptDict);
StatusCount += 1;
}
ClearStatus();
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed");
ClearStatus();
}
ClearStatus();
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed");
ClearStatus();
}
}
}

View File

@@ -109,10 +109,26 @@ namespace gaseous_server.Classes
callingProcess = "";
}
string callingUser;
try
{
if (CallContext.GetData("CallingUser").ToString() == null)
{
callingUser = "";
}
else
{
callingUser = CallContext.GetData("CallingUser").ToString();
}
}
catch
{
callingUser = "";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess);";
string sql = "INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
dbDict.Add("EventTime", logItem.EventTime);
dbDict.Add("EventType", logItem.EventType);
dbDict.Add("Process", logItem.Process);
@@ -120,6 +136,7 @@ namespace gaseous_server.Classes
dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString());
dbDict.Add("correlationid", correlationId);
dbDict.Add("callingprocess", callingProcess);
dbDict.Add("callinguser", callingUser);
try
{
@@ -238,6 +255,15 @@ namespace gaseous_server.Classes
}
}
if (model.CallingUser != null)
{
if (model.CallingUser.Length > 0)
{
dbDict.Add("callingUser", model.CallingUser);
whereClauses.Add("CallingUser = @callingUser");
}
}
// compile WHERE clause
string whereClause = "";
if (whereClauses.Count > 0)
@@ -252,7 +278,8 @@ namespace gaseous_server.Classes
{
whereClause = "WHERE " + whereClause;
}
sql = "SELECT * FROM ServerLogs " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
sql = "SELECT ServerLogs.Id, ServerLogs.EventTime, ServerLogs.EventType, ServerLogs.`Process`, ServerLogs.Message, ServerLogs.Exception, ServerLogs.CorrelationId, ServerLogs.CallingProcess, Users.Email FROM ServerLogs LEFT JOIN Users ON ServerLogs.CallingUser = Users.Id " + whereClause + " ORDER BY ServerLogs.Id DESC LIMIT @PageSize OFFSET @PageNumber;";
}
else
{
@@ -260,7 +287,8 @@ namespace gaseous_server.Classes
{
whereClause = "AND " + whereClause;
}
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
sql = "SELECT ServerLogs.Id, ServerLogs.EventTime, ServerLogs.EventType, ServerLogs.`Process`, ServerLogs.Message, ServerLogs.Exception, ServerLogs.CorrelationId, ServerLogs.CallingProcess, Users.Email FROM ServerLogs LEFT JOIN Users ON ServerLogs.CallingUser = Users.Id WHERE ServerLogs.Id < @StartIndex " + whereClause + " ORDER BY ServerLogs.Id DESC LIMIT @PageSize OFFSET @PageNumber;";
}
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
@@ -276,7 +304,8 @@ namespace gaseous_server.Classes
Message = (string)row["Message"],
ExceptionValue = (string)row["Exception"],
CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""),
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], "")
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], ""),
CallingUser = (string)Common.ReturnValueIfNull(row["Email"], "")
};
logs.Add(log);
@@ -301,6 +330,7 @@ namespace gaseous_server.Classes
public string Process { get; set; } = "";
public string CorrelationId { get; set; } = "";
public string? CallingProcess { get; set; } = "";
public string? CallingUser { get; set; } = "";
private string _Message = "";
public string Message
{
@@ -327,6 +357,7 @@ namespace gaseous_server.Classes
public string? SearchText { get; set; }
public string? CorrelationId { get; set; }
public string? CallingProcess { get; set; }
public string? CallingUser { get; set; }
}
}
}

View File

@@ -11,6 +11,32 @@ namespace gaseous_server.Classes
public void RunMaintenance()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
// remove any entries from the library that have an invalid id
string LibraryWhereClause = "";
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{
if (LibraryWhereClause.Length > 0)
{
LibraryWhereClause += ", ";
}
LibraryWhereClause += library.Id;
}
string sqlLibraryWhereClause = "";
if (LibraryWhereClause.Length > 0)
{
sqlLibraryWhereClause = "DELETE FROM Games_Roms WHERE LibraryId NOT IN ( " + LibraryWhereClause + " );";
db.ExecuteCMD(sqlLibraryWhereClause);
}
// delete old logs
sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate;";
dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
db.ExecuteCMD(sql, dbDict);
// delete files and directories older than 7 days in PathsToClean
List<string> PathsToClean = new List<string>();
PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory);
@@ -45,8 +71,7 @@ namespace gaseous_server.Classes
}
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SHOW TABLES;";
sql = "SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE';";
DataTable tables = db.ExecuteCMD(sql);
int StatusCounter = 1;

View File

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

View File

@@ -186,158 +186,6 @@ namespace gaseous_server.Classes.Metadata
}
}
}
public class AgeGroups
{
public AgeGroups()
{
}
public static Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>> AgeGroupings
{
get
{
return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
{
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
}
};
}
}
public static Dictionary<AgeRestrictionGroupings, AgeGroupItem> AgeGroupingsFlat
{
get
{
return new Dictionary<AgeRestrictionGroupings, AgeGroupItem>{
{
AgeRestrictionGroupings.Adult, Adult_Item
},
{
AgeRestrictionGroupings.Mature, Mature_Item
},
{
AgeRestrictionGroupings.Teen, Teen_Item
},
{
AgeRestrictionGroupings.Child, Child_Item
}
};
}
}
public enum AgeRestrictionGroupings
{
Adult = 4,
Mature = 3,
Teen = 2,
Child = 1,
Unclassified = 0
}
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_Z },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Eighteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.RP, AgeRatingTitle.AO },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Eighteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Eighteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_18}
};
readonly static AgeGroupItem Mature_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Sixteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.M },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Fifteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Sixteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_16}
};
readonly static AgeGroupItem Teen_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_PG },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_B },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.T },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Twelve },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Twelve},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_12}
};
readonly static AgeGroupItem Child_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_G },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_A },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.E, AgeRatingTitle.E10 },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_All },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Three, AgeRatingTitle.Seven},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_0, AgeRatingTitle.USK_6}
};
public class AgeGroupItem
{
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
[JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public List<long> AgeGroupItemValues
{
get
{
List<long> values = new List<long>();
{
foreach (AgeRatingTitle ageRatingTitle in ACB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CERO)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in ESRB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in GRAC)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in PEGI)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in USK)
{
values.Add((long)ageRatingTitle);
}
}
return values;
}
}
}
}
}
}

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
public static Artwork? GetArtwork(long? Id, string LogoPath)
public static Artwork? GetArtwork(long? Id, string ImagePath, bool GetImages)
{
if ((Id == 0) || (Id == null))
{
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Artwork> RetVal = _GetArtwork(SearchUsing.id, Id, LogoPath);
Task<Artwork> RetVal = _GetArtwork(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result;
}
}
public static Artwork GetArtwork(string Slug, string LogoPath)
public static Artwork GetArtwork(string Slug, string ImagePath, bool GetImages)
{
Task<Artwork> RetVal = _GetArtwork(SearchUsing.slug, Slug, LogoPath);
Task<Artwork> RetVal = _GetArtwork(SearchUsing.slug, Slug, ImagePath, GetImages);
return RetVal.Result;
}
private static async Task<Artwork> _GetArtwork(SearchUsing searchUsing, object searchValue, string LogoPath)
private static async Task<Artwork> _GetArtwork(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -61,19 +61,20 @@ namespace gaseous_server.Classes.Metadata
Artwork returnValue = new Artwork();
bool forceImageDownload = false;
LogoPath = Path.Combine(LogoPath, "Artwork");
ImagePath = Path.Combine(ImagePath, "Artwork");
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
catch (Exception ex)
{
@@ -88,11 +89,17 @@ namespace gaseous_server.Classes.Metadata
throw new Exception("How did you get here?");
}
if ((!File.Exists(Path.Combine(LogoPath, returnValue.ImageId + ".jpg"))) || forceImageDownload == true)
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if (GetImages == true)
{
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb, returnValue.ImageId);
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId);
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId);
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Artwork download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
}
return returnValue;
@@ -104,65 +111,15 @@ namespace gaseous_server.Classes.Metadata
slug
}
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string LogoPath)
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<Artwork>(IGDBClient.Endpoints.Artworks, fieldList, WhereClause);
var result = results.First();
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med, result.ImageId);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_original, result.ImageId);
return result;
}
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize, string ImageId)
{
using (var client = new HttpClient())
{
string fileName = "Artwork.jpg";
string extension = "jpg";
switch (logoSize)
{
case LogoSize.t_thumb:
fileName = "_Thumb";
extension = "jpg";
break;
case LogoSize.t_logo_med:
fileName = "_Medium";
extension = "png";
break;
case LogoSize.t_original:
fileName = "";
extension = "png";
break;
default:
fileName = "Artwork";
extension = "jpg";
break;
}
fileName = ImageId + fileName;
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
using (var s = client.GetStreamAsync("https:" + imageUrl))
{
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
}
private enum LogoSize
{
t_thumb,
t_logo_med,
t_original
}
}
}

View File

@@ -1,5 +1,10 @@
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Net;
using Humanizer;
using IGDB;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using RestEase;
namespace gaseous_server.Classes.Metadata
@@ -9,16 +14,28 @@ namespace gaseous_server.Classes.Metadata
/// </summary>
public class Communications
{
static Communications()
{
var handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip");
client.DefaultRequestHeaders.Add("Accept-Encoding", "deflate");
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
private static HttpClient client = new HttpClient();
/// <summary>
/// Configure metadata API communications
/// </summary>
public static MetadataSources MetadataSource
public static HasheousClient.Models.MetadataModel.MetadataSources MetadataSource
{
get
{
@@ -30,7 +47,7 @@ namespace gaseous_server.Classes.Metadata
switch (value)
{
case MetadataSources.IGDB:
case HasheousClient.Models.MetadataModel.MetadataSources.IGDB:
// set rate limiter avoidance values
RateLimitAvoidanceWait = 1500;
RateLimitAvoidanceThreshold = 3;
@@ -46,7 +63,7 @@ namespace gaseous_server.Classes.Metadata
}
}
}
private static MetadataSources _MetadataSource = MetadataSources.None;
private static HasheousClient.Models.MetadataModel.MetadataSources _MetadataSource = HasheousClient.Models.MetadataModel.MetadataSources.None;
// rate limit avoidance - what can we do to ensure that rate limiting is avoided?
// these values affect all communications
@@ -142,22 +159,6 @@ namespace gaseous_server.Classes.Metadata
private int RetryAttempts = 0;
private int RetryAttemptsMax = 3;
/// <summary>
/// Supported metadata sources
/// </summary>
public enum MetadataSources
{
/// <summary>
/// None - always returns null for metadata requests - should not really be using this source
/// </summary>
None,
/// <summary>
/// IGDB - queries the IGDB service for metadata
/// </summary>
IGDB
}
/// <summary>
/// Request data from the metadata API
/// </summary>
@@ -170,9 +171,9 @@ namespace gaseous_server.Classes.Metadata
{
switch (_MetadataSource)
{
case MetadataSources.None:
case HasheousClient.Models.MetadataModel.MetadataSources.None:
return null;
case MetadataSources.IGDB:
case HasheousClient.Models.MetadataModel.MetadataSources.IGDB:
return await IGDBAPI<T>(Endpoint, Fields, Query);
default:
return null;
@@ -234,5 +235,343 @@ namespace gaseous_server.Classes.Metadata
throw;
}
}
/// <summary>
/// Download from the specified uri
/// </summary>
/// <param name="uri">The uri to download from</param>
/// <param name="DestinationFile">The file name and path the download should be stored as</param>
public Task<bool?> DownloadFile(Uri uri, string DestinationFile)
{
var result = _DownloadFile(uri, DestinationFile);
return result;
}
private async Task<bool?> _DownloadFile(Uri uri, string DestinationFile)
{
string DestinationDirectory = new FileInfo(DestinationFile).Directory.FullName;
if (!Directory.Exists(DestinationDirectory))
{
Directory.CreateDirectory(DestinationDirectory);
}
Logging.Log(Logging.LogType.Information, "Communications", "Downloading from " + uri.ToString() + " to " + DestinationFile);
try
{
using (HttpResponseMessage response = client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).Result)
{
response.EnsureSuccessStatusCode();
using (Stream contentStream = await response.Content.ReadAsStreamAsync(), fileStream = new FileStream(DestinationFile, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
{
var totalRead = 0L;
var totalReads = 0L;
var buffer = new byte[8192];
var isMoreToRead = true;
do
{
var read = await contentStream.ReadAsync(buffer, 0, buffer.Length);
if (read == 0)
{
isMoreToRead = false;
}
else
{
await fileStream.WriteAsync(buffer, 0, read);
totalRead += read;
totalReads += 1;
if (totalReads % 2000 == 0)
{
Console.WriteLine(string.Format("total bytes downloaded so far: {0:n0}", totalRead));
}
}
}
while (isMoreToRead);
}
}
return true;
}
catch (HttpRequestException ex)
{
if (ex.StatusCode == HttpStatusCode.NotFound)
{
if (File.Exists(DestinationFile))
{
FileInfo fi = new FileInfo(DestinationFile);
if (fi.Length == 0)
{
File.Delete(DestinationFile);
}
}
}
Logging.Log(Logging.LogType.Warning, "Download Images", "Error downloading file: ", ex);
}
return false;
}
public async Task<string> GetSpecificImageFromServer(string ImagePath, string ImageId, IGDBAPI_ImageSize size, List<IGDBAPI_ImageSize>? FallbackSizes = null)
{
string returnPath = "";
// check for artificial sizes first
switch (size)
{
case IGDBAPI_ImageSize.screenshot_small:
case IGDBAPI_ImageSize.screenshot_thumb:
string BasePath = Path.Combine(ImagePath, size.ToString());
if (!Directory.Exists(BasePath))
{
Directory.CreateDirectory(BasePath);
}
returnPath = Path.Combine(BasePath, ImageId + ".jpg");
if (!File.Exists(returnPath))
{
// get original size image and resize
string originalSizePath = await GetSpecificImageFromServer(ImagePath, ImageId, IGDBAPI_ImageSize.original, null);
int width = 0;
int height = 0;
switch (size)
{
case IGDBAPI_ImageSize.screenshot_small:
// 235x128
width = 235;
height = 128;
break;
case IGDBAPI_ImageSize.screenshot_thumb:
// 165x90
width = 165;
height = 90;
break;
}
using (var image = new ImageMagick.MagickImage(originalSizePath))
{
image.Resize(width, height);
image.Strip();
image.Write(returnPath);
}
}
break;
default:
// these sizes are IGDB native
if (RateLimitResumeTime > DateTime.UtcNow)
{
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB rate limit hit. Pausing API communications until " + RateLimitResumeTime.ToString() + ". Attempt " + RetryAttempts + " of " + RetryAttemptsMax + " retries.");
Thread.Sleep(RateLimitRecoveryWaitTime);
}
if (InRateLimitAvoidanceMode == true)
{
// sleep for a moment to help avoid hitting the rate limiter
Thread.Sleep(RateLimitAvoidanceWait);
}
Communications comms = new Communications();
List<IGDBAPI_ImageSize> imageSizes = new List<IGDBAPI_ImageSize>
{
size
};
// get the image
try
{
returnPath = Path.Combine(ImagePath, size.ToString(), ImageId + ".jpg");
// fail early if the file is already downloaded
if (!File.Exists(returnPath))
{
await comms.IGDBAPI_GetImage(imageSizes, ImageId, ImagePath);
}
}
catch (HttpRequestException ex)
{
if (ex.StatusCode == HttpStatusCode.NotFound)
{
Logging.Log(Logging.LogType.Information, "Image Download", "Image not found, trying a different size.");
if (FallbackSizes != null)
{
foreach (Communications.IGDBAPI_ImageSize imageSize in FallbackSizes)
{
returnPath = await GetSpecificImageFromServer(ImagePath, ImageId, imageSize, null);
}
}
}
}
// increment rate limiter avoidance call count
RateLimitAvoidanceCallCount += 1;
break;
}
return returnPath;
}
public static T? GetSearchCache<T>(string SearchFields, string SearchString)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM SearchCache WHERE SearchFields = @searchfields AND SearchString = @searchstring;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "searchfields", SearchFields },
{ "searchstring", SearchString }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 0)
{
// cache hit
string rawString = data.Rows[0]["Content"].ToString();
T ReturnValue = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(rawString);
if (ReturnValue != null)
{
Logging.Log(Logging.LogType.Information, "Search Cache", "Found search result in cache. Search string: " + SearchString);
return ReturnValue;
}
else
{
Logging.Log(Logging.LogType.Information, "Search Cache", "Search result not found in cache.");
return default;
}
}
else
{
// cache miss
Logging.Log(Logging.LogType.Information, "Search Cache", "Search result not found in cache.");
return default;
}
}
public static void SetSearchCache<T>(string SearchFields, string SearchString, T SearchResult)
{
Logging.Log(Logging.LogType.Information, "Search Cache", "Storing search results in cache. Search string: " + SearchString);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO SearchCache (SearchFields, SearchString, Content, LastSearch) VALUES (@searchfields, @searchstring, @content, @lastsearch);";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "searchfields", SearchFields },
{ "searchstring", SearchString },
{ "content", Newtonsoft.Json.JsonConvert.SerializeObject(SearchResult) },
{ "lastsearch", DateTime.UtcNow }
};
db.ExecuteNonQuery(sql, dbDict);
}
/// <summary>
/// See https://api-docs.igdb.com/?javascript#images for more information about the image url structure
/// </summary>
/// <param name="ImageId"></param>
/// <param name="outputPath">The path to save the downloaded files to
public async Task IGDBAPI_GetImage(List<IGDBAPI_ImageSize> ImageSizes, string ImageId, string OutputPath)
{
string urlTemplate = "https://images.igdb.com/igdb/image/upload/t_{size}/{hash}.jpg";
foreach (IGDBAPI_ImageSize ImageSize in ImageSizes)
{
string url = urlTemplate.Replace("{size}", Common.GetDescription(ImageSize)).Replace("{hash}", ImageId);
string newOutputPath = Path.Combine(OutputPath, Common.GetDescription(ImageSize));
string OutputFile = ImageId + ".jpg";
string fullPath = Path.Combine(newOutputPath, OutputFile);
await _DownloadFile(new Uri(url), fullPath);
}
}
public enum IGDBAPI_ImageSize
{
/// <summary>
/// 90x128 Fit
/// </summary>
[Description("cover_small")]
cover_small,
/// <summary>
/// 264x374 Fit
/// </summary>
[Description("cover_big")]
cover_big,
/// <summary>
/// 165x90 Lfill, Centre gravity - resized by Gaseous and is not a real IGDB size
/// </summary>
[Description("screenshot_thumb")]
screenshot_thumb,
/// <summary>
/// 235x128 Lfill, Centre gravity - resized by Gaseous and is not a real IGDB size
/// </summary>
[Description("screenshot_small")]
screenshot_small,
/// <summary>
/// 589x320 Lfill, Centre gravity
/// </summary>
[Description("screenshot_med")]
screenshot_med,
/// <summary>
/// 889x500 Lfill, Centre gravity
/// </summary>
[Description("screenshot_big")]
screenshot_big,
/// <summary>
/// 1280x720 Lfill, Centre gravity
/// </summary>
[Description("screenshot_huge")]
screenshot_huge,
/// <summary>
/// 284x160 Fit
/// </summary>
[Description("logo_med")]
logo_med,
/// <summary>
/// 90x90 Thumb, Centre gravity
/// </summary>
[Description("thumb")]
thumb,
/// <summary>
/// 35x35 Thumb, Centre gravity
/// </summary>
[Description("micro")]
micro,
/// <summary>
/// 1280x720 Fit, Centre gravity
/// </summary>
[Description("720p")]
r720p,
/// <summary>
/// 1920x1080 Fit, Centre gravity
/// </summary>
[Description("1080p")]
r1080p,
/// <summary>
/// The originally uploaded image
/// </summary>
[Description("original")]
original
}
}
}

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath)
public static CompanyLogo? GetCompanyLogo(long? Id, string ImagePath)
{
if ((Id == 0) || (Id == null))
{
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, LogoPath);
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, ImagePath);
return RetVal.Result;
}
}
public static CompanyLogo GetCompanyLogo(string Slug, string LogoPath)
public static CompanyLogo GetCompanyLogo(string Slug, string ImagePath)
{
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.slug, Slug, LogoPath);
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.slug, Slug, ImagePath);
return RetVal.Result;
}
private static async Task<CompanyLogo> _GetCompanyLogo(SearchUsing searchUsing, object searchValue, string LogoPath)
private static async Task<CompanyLogo> _GetCompanyLogo(SearchUsing searchUsing, object searchValue, string ImagePath)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -64,7 +64,7 @@ namespace gaseous_server.Classes.Metadata
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
@@ -74,7 +74,7 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
@@ -91,13 +91,14 @@ namespace gaseous_server.Classes.Metadata
throw new Exception("How did you get here?");
}
if (returnValue != null)
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
if ((!File.Exists(Path.Combine(LogoPath, "Logo.jpg"))) || forceImageDownload == true)
{
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb);
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med);
}
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Company logo download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
return returnValue;
@@ -109,64 +110,14 @@ namespace gaseous_server.Classes.Metadata
slug
}
private static async Task<CompanyLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
private static async Task<CompanyLogo> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get CompanyLogo metadata
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First();
var result = results.First();
GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med);
return result;
}
else
{
return null;
}
}
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize)
{
using (var client = new HttpClient())
{
string fileName = "Logo.jpg";
string extension = "jpg";
switch (logoSize)
{
case LogoSize.t_thumb:
fileName = "Logo_Thumb";
extension = "jpg";
break;
case LogoSize.t_logo_med:
fileName = "Logo_Medium";
extension = "png";
break;
default:
fileName = "Logo";
extension = "jpg";
break;
}
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
using (var s = client.GetStreamAsync("https:" + imageUrl))
{
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
}
private enum LogoSize
{
t_thumb,
t_logo_med
return result;
}
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Net;
using IGDB;
using IGDB.Models;
using Microsoft.CodeAnalysis.Elfie.Model.Strings;
namespace gaseous_server.Classes.Metadata
@@ -13,7 +15,7 @@ namespace gaseous_server.Classes.Metadata
{
}
public static Cover? GetCover(long? Id, string LogoPath)
public static Cover? GetCover(long? Id, string ImagePath, bool GetImages)
{
if ((Id == 0) || (Id == null))
{
@@ -21,18 +23,18 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Cover> RetVal = _GetCover(SearchUsing.id, Id, LogoPath);
Task<Cover> RetVal = _GetCover(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result;
}
}
public static Cover GetCover(string Slug, string LogoPath)
public static Cover GetCover(string Slug, string ImagePath, bool GetImages)
{
Task<Cover> RetVal = _GetCover(SearchUsing.slug, Slug, LogoPath);
Task<Cover> RetVal = _GetCover(SearchUsing.slug, Slug, ImagePath, GetImages);
return RetVal.Result;
}
private static async Task<Cover> _GetCover(SearchUsing searchUsing, object searchValue, string LogoPath)
private static async Task<Cover> _GetCover(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -61,17 +63,18 @@ namespace gaseous_server.Classes.Metadata
Cover returnValue = new Cover();
bool forceImageDownload = false;
ImagePath = Path.Combine(ImagePath, "Covers");
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
@@ -88,11 +91,30 @@ namespace gaseous_server.Classes.Metadata
throw new Exception("How did you get here?");
}
if ((!File.Exists(Path.Combine(LogoPath, "Cover.jpg"))) || forceImageDownload == true)
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if (GetImages == true)
{
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb, returnValue.ImageId);
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId);
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId);
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Cover download forced.");
// check for presence of image file - download if absent or force download is true
List<Communications.IGDBAPI_ImageSize> imageSizes = new List<Communications.IGDBAPI_ImageSize>{
Communications.IGDBAPI_ImageSize.cover_big,
Communications.IGDBAPI_ImageSize.cover_small,
Communications.IGDBAPI_ImageSize.original
};
Communications comms = new Communications();
foreach (Communications.IGDBAPI_ImageSize size in imageSizes)
{
localFile = Path.Combine(ImagePath, size.ToString(), returnValue.ImageId + ".jpg");
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, size, null);
}
}
}
}
return returnValue;
@@ -104,64 +126,15 @@ namespace gaseous_server.Classes.Metadata
slug
}
private static async Task<Cover> GetObjectFromServer(string WhereClause, string LogoPath)
private static async Task<Cover> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Cover metadata
Communications comms = new Communications();
var results = await comms.APIComm<Cover>(IGDBClient.Endpoints.Covers, fieldList, WhereClause);
var result = results.First();
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med, result.ImageId);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_original, result.ImageId);
return result;
}
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize, string ImageId)
{
using (var client = new HttpClient())
{
string fileName = "Cover.jpg";
string extension = "jpg";
switch (logoSize)
{
case LogoSize.t_thumb:
fileName = "Cover_Thumb";
extension = "jpg";
break;
case LogoSize.t_logo_med:
fileName = "Cover_Medium";
extension = "png";
break;
case LogoSize.t_original:
fileName = "Cover";
extension = "png";
break;
default:
fileName = "Cover";
extension = "jpg";
break;
}
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
using (var s = client.GetStreamAsync("https:" + imageUrl))
{
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
}
private enum LogoSize
{
t_thumb,
t_logo_med,
t_original
}
}
}

View File

@@ -98,14 +98,14 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
return returnValue;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
}
catch (Exception ex)
{
@@ -114,18 +114,27 @@ namespace gaseous_server.Classes.Metadata
}
return returnValue;
case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
UpdateSubClasses(returnValue, false, false, false);
return returnValue;
default:
throw new Exception("How did you get here?");
}
}
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames)
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames, bool forceRefresh)
{
// required metadata
if (Game.Cover != null)
{
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
try
{
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
}
}
if (Game.Genres != null)
@@ -175,6 +184,7 @@ namespace gaseous_server.Classes.Metadata
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
AgeGroups.GetAgeGroup(Game);
if (Game.ReleaseDates != null)
{
@@ -199,7 +209,14 @@ namespace gaseous_server.Classes.Metadata
{
foreach (long ArtworkId in Game.Artworks.Ids)
{
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
try
{
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch artwork id: " + ArtworkId, ex);
}
}
}
@@ -266,7 +283,14 @@ namespace gaseous_server.Classes.Metadata
{
foreach (long ScreenshotId in Game.Screenshots.Ids)
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
try
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch screenshot id: " + ScreenshotId, ex);
}
}
}
@@ -307,6 +331,37 @@ namespace gaseous_server.Classes.Metadata
ids: platformIds.ToArray<long>()
);
// get cover art from parent if this has no cover
if (result.Cover == null)
{
if (result.ParentGame != null)
{
if (result.ParentGame.Id != null)
{
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no cover art, fetching cover art from parent game");
Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false);
result.Cover = parentGame.Cover;
}
}
}
// get missing metadata from parent if this is a port
if (result.Category == Category.Port)
{
if (result.Summary == null)
{
if (result.ParentGame != null)
{
if (result.ParentGame.Id != null)
{
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no summary, fetching summary from parent game");
Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false);
result.Summary = parentGame.Summary;
}
}
}
}
return result;
}
@@ -324,41 +379,146 @@ namespace gaseous_server.Classes.Metadata
}
}
private static bool AllowNoPlatformSearch = false;
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType)
{
Task<Game[]> games = _SearchForGame(SearchString, PlatformId, searchType);
// search local first
Logging.Log(Logging.LogType.Information, "Game Search", "Attempting local search of type '" + searchType.ToString() + "' for " + SearchString);
Task<Game[]> games = _SearchForGameDatabase(SearchString, PlatformId, searchType);
if (games.Result.Length == 0)
{
// fall back to online search
Logging.Log(Logging.LogType.Information, "Game Search", "Falling back to remote search of type '" + searchType.ToString() + "' for " + SearchString);
games = _SearchForGameRemote(SearchString, PlatformId, searchType);
}
return games.Result;
}
private static async Task<Game[]> _SearchForGame(string SearchString, long PlatformId, SearchType searchType)
private static async Task<Game[]> _SearchForGameDatabase(string SearchString, long PlatformId, SearchType searchType)
{
string searchBody = "";
string searchFields = "fields id,name,slug,platforms,summary; ";
string whereClause = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
bool allowSearch = true;
switch (searchType)
{
case SearchType.searchNoPlatform:
searchBody += "search \"" + SearchString + "\"; ";
whereClause = "MATCH(`Name`) AGAINST (@gamename)";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
allowSearch = AllowNoPlatformSearch;
break;
case SearchType.search:
searchBody += "search \"" + SearchString + "\"; ";
searchBody += "where platforms = (" + PlatformId + ");";
whereClause = "PlatformsId = @platformid AND MATCH(`Name`) AGAINST (@gamename)";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
break;
case SearchType.wherefuzzy:
searchBody += "where platforms = (" + PlatformId + ") & name ~ *\"" + SearchString + "\"*;";
whereClause = "PlatformsId = @platformid AND `Name` LIKE @gamename";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", "%" + SearchString + "%");
break;
case SearchType.where:
searchBody += "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";";
whereClause = "PlatformsId = @platformid AND `Name` = @gamename";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
break;
}
string sql = "SELECT Game.Id, Game.`Name`, Game.Slug, Relation_Game_Platforms.PlatformsId AS PlatformsId, Game.Summary FROM gaseous.Game JOIN Relation_Game_Platforms ON Game.Id = Relation_Game_Platforms.GameId WHERE " + whereClause + ";";
// get Game metadata
Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
Game[]? results = new Game[0];
if (allowSearch == true)
{
List<Game> searchResults = new List<Game>();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
DataTable data = db.ExecuteCMD(sql, dbDict);
foreach (DataRow row in data.Rows)
{
Game game = new Game{
Id = (long)row["Id"],
Name = (string)Common.ReturnValueIfNull(row["Name"], ""),
Slug = (string)Common.ReturnValueIfNull(row["Slug"], ""),
Summary = (string)Common.ReturnValueIfNull(row["Summary"], "")
};
searchResults.Add(game);
}
results = searchResults.ToArray();
}
return results;
}
private static async Task<Game[]> _SearchForGameRemote(string SearchString, long PlatformId, SearchType searchType)
{
string searchBody = "";
string searchFields = "fields id,name,slug,platforms,summary; ";
bool allowSearch = true;
switch (searchType)
{
case SearchType.searchNoPlatform:
searchBody = "search \"" + SearchString + "\"; ";
allowSearch = AllowNoPlatformSearch;
break;
case SearchType.search:
searchBody = "search \"" + SearchString + "\"; where platforms = (" + PlatformId + ");";
break;
case SearchType.wherefuzzy:
searchBody = "where platforms = (" + PlatformId + ") & name ~ *\"" + SearchString + "\"*;";
break;
case SearchType.where:
searchBody = "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";";
break;
}
// check search cache
Game[]? games = Communications.GetSearchCache<Game[]?>(searchFields, searchBody);
if (games == null)
{
// cache miss
// get Game metadata
Communications comms = new Communications();
Game[]? results = new Game[0];
if (allowSearch == true)
{
results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
Communications.SetSearchCache<Game[]?>(searchFields, searchBody, results);
}
return results;
}
else
{
return games.ToArray();
}
}
public static List<KeyValuePair<long, string>> GetAvailablePlatforms(long GameId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @gameid ORDER BY Platform.`Name`;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("gameid", GameId);
DataTable data = db.ExecuteCMD(sql, dbDict);
List<KeyValuePair<long, string>> platforms = new List<KeyValuePair<long, string>>();
foreach (DataRow row in data.Rows)
{
KeyValuePair<long, string> valuePair = new KeyValuePair<long, string>((long)row["PlatformId"], (string)row["Name"]);
platforms.Add(valuePair);
}
return platforms;
}
public enum SearchType
{
where = 0,
@@ -377,6 +537,7 @@ namespace gaseous_server.Classes.Metadata
this.TotalRatingCount = gameObject.TotalRatingCount;
this.Cover = gameObject.Cover;
this.Artworks = gameObject.Artworks;
this.FirstReleaseDate = gameObject.FirstReleaseDate;
// compile age ratings
this.AgeRatings = new List<AgeRating>();
@@ -397,6 +558,7 @@ namespace gaseous_server.Classes.Metadata
public string Name { get; set; }
public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; }
public DateTimeOffset? FirstReleaseDate { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
public List<IGDB.Models.AgeRating> AgeRatings { get; set; }

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath)
public static PlatformLogo? GetPlatformLogo(long? Id, string ImagePath)
{
if ((Id == 0) || (Id == null))
{
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, LogoPath);
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, ImagePath);
return RetVal.Result;
}
}
public static PlatformLogo GetPlatformLogo(string Slug, string LogoPath)
public static PlatformLogo GetPlatformLogo(string Slug, string ImagePath)
{
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.slug, Slug, LogoPath);
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.slug, Slug, ImagePath);
return RetVal.Result;
}
private static async Task<PlatformLogo> _GetPlatformLogo(SearchUsing searchUsing, object searchValue, string LogoPath)
private static async Task<PlatformLogo> _GetPlatformLogo(SearchUsing searchUsing, object searchValue, string ImagePath)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -64,7 +64,7 @@ namespace gaseous_server.Classes.Metadata
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
@@ -74,7 +74,7 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
@@ -93,10 +93,14 @@ namespace gaseous_server.Classes.Metadata
if (returnValue != null)
{
if ((!File.Exists(Path.Combine(LogoPath, "Logo.jpg"))) || forceImageDownload == true)
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb);
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med);
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Platform logo download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
}
@@ -109,64 +113,14 @@ namespace gaseous_server.Classes.Metadata
slug
}
private static async Task<PlatformLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
private static async Task<PlatformLogo> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get PlatformLogo metadata
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First();
var result = results.First();
GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med);
return result;
}
else
{
return null;
}
}
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize)
{
using (var client = new HttpClient())
{
string fileName = "Logo.jpg";
string extension = "jpg";
switch (logoSize)
{
case LogoSize.t_thumb:
fileName = "Logo_Thumb";
extension = "jpg";
break;
case LogoSize.t_logo_med:
fileName = "Logo_Medium";
extension = "png";
break;
default:
fileName = "Logo";
extension = "jpg";
break;
}
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
using (var s = client.GetStreamAsync("https:" + imageUrl))
{
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
}
private enum LogoSize
{
t_thumb,
t_logo_med
return result;
}
}
}

View File

@@ -94,7 +94,14 @@ namespace gaseous_server.Classes.Metadata
{
if (platformVersion.PlatformLogo != null)
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
try
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
}
}
}

View File

@@ -39,8 +39,16 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
return RetVal.Result;
try
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
return RetVal.Result;
}
catch(Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata", "An error occurred fetching Platform Id " + Id, ex);
return null;
}
}
}
@@ -124,7 +132,14 @@ namespace gaseous_server.Classes.Metadata
if (platform.PlatformLogo != null)
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
try
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
}
}
}

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
public static Screenshot? GetScreenshot(long? Id, string LogoPath)
public static Screenshot? GetScreenshot(long? Id, string ImagePath, bool GetImages)
{
if ((Id == 0) || (Id == null))
{
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, LogoPath);
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result;
}
}
public static Screenshot GetScreenshot(string Slug, string LogoPath)
public static Screenshot GetScreenshot(string Slug, string ImagePath, bool GetImages)
{
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.slug, Slug, LogoPath);
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.slug, Slug, ImagePath, GetImages);
return RetVal.Result;
}
private static async Task<Screenshot> _GetScreenshot(SearchUsing searchUsing, object searchValue, string LogoPath)
private static async Task<Screenshot> _GetScreenshot(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -61,18 +61,18 @@ namespace gaseous_server.Classes.Metadata
Screenshot returnValue = new Screenshot();
bool forceImageDownload = false;
LogoPath = Path.Combine(LogoPath, "Screenshots");
ImagePath = Path.Combine(ImagePath, "Screenshots");
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
forceImageDownload = true;
}
@@ -89,11 +89,17 @@ namespace gaseous_server.Classes.Metadata
throw new Exception("How did you get here?");
}
if ((!File.Exists(Path.Combine(LogoPath, "Screenshot.jpg"))) || forceImageDownload == true)
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if (GetImages == true)
{
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb, returnValue.ImageId);
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId);
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId);
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Screenshot download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
}
return returnValue;
@@ -105,65 +111,15 @@ namespace gaseous_server.Classes.Metadata
slug
}
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string LogoPath)
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Screenshot metadata
Communications comms = new Communications();
var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause);
var result = results.First();
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med, result.ImageId);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_original, result.ImageId);
return result;
}
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize, string ImageId)
{
using (var client = new HttpClient())
{
string fileName = "Artwork.jpg";
string extension = "jpg";
switch (logoSize)
{
case LogoSize.t_thumb:
fileName = "_Thumb";
extension = "jpg";
break;
case LogoSize.t_logo_med:
fileName = "_Medium";
extension = "png";
break;
case LogoSize.t_original:
fileName = "";
extension = "png";
break;
default:
fileName = "Artwork";
extension = "jpg";
break;
}
fileName = ImageId + fileName;
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
using (var s = client.GetStreamAsync("https:" + imageUrl))
{
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
}
private enum LogoSize
{
t_thumb,
t_logo_med,
t_original
}
}
}

View File

@@ -372,9 +372,12 @@ namespace gaseous_server.Classes.Metadata
case "[igdb.models.releasedateregion":
property.SetValue(EndpointType, (ReleaseDateRegion)dataRow[property.Name]);
break;
case "[igdb.models.releasedatecategory":
case "[igdb.models.releasedatecategory":
property.SetValue(EndpointType, (ReleaseDateCategory)dataRow[property.Name]);
break;
case "[gaseous_server.classes.metadata.agegroups+agerestrictiongroupings":
property.SetValue(EndpointType, (AgeGroups.AgeRestrictionGroupings)dataRow[property.Name]);
break;
default:
property.SetValue(EndpointType, dataRow[property.Name]);
break;

View File

@@ -59,7 +59,7 @@ namespace gaseous_server.Classes
try
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh);
Metadata.Games.GetGame((long)dr["id"], true, false, true);
}
catch (Exception ex)
{

View File

@@ -91,7 +91,7 @@ namespace gaseous_server.Classes
mediaGroupItems.Add(BuildMediaGroupFromRow(row));
}
mediaGroupItems.Sort((x, y) => x.PlatformName.CompareTo(y.PlatformName));
mediaGroupItems.Sort((x, y) => x.Platform.CompareTo(y.Platform));
return mediaGroupItems;
}
@@ -176,6 +176,7 @@ namespace gaseous_server.Classes
mediaGroupItem.PlatformId = (long)row["PlatformId"];
mediaGroupItem.GameId = (long)row["GameId"];
mediaGroupItem.RomIds = new List<long>();
mediaGroupItem.Roms = new List<Roms.GameRomItem>();
// get members
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -186,8 +187,28 @@ namespace gaseous_server.Classes
foreach (DataRow dataRow in data.Rows)
{
mediaGroupItem.RomIds.Add((long)dataRow["RomId"]);
try
{
mediaGroupItem.Roms.Add(Roms.GetRom((long)dataRow["RomId"]));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Rom Group", "Unable to load ROM data", ex);
}
}
// check for a web emulator and update the romItem
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{
if (platformMapping.IGDBId == mediaGroupItem.PlatformId)
{
if (platformMapping.WebEmulator != null)
{
mediaGroupItem.Emulator = platformMapping.WebEmulator;
}
}
}
return mediaGroupItem;
}
@@ -360,7 +381,7 @@ namespace gaseous_server.Classes
public long Id { get; set; }
public long GameId { get; set; }
public long PlatformId { get; set; }
public string PlatformName {
public string Platform {
get
{
try
@@ -373,7 +394,9 @@ namespace gaseous_server.Classes
}
}
}
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public List<long> RomIds { get; set; }
public List<Roms.GameRomItem> Roms { get; set; }
private GroupBuildStatus _Status { get; set; }
public GroupBuildStatus Status {
get

View File

@@ -3,6 +3,7 @@ using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
namespace gaseous_server.Classes
{
@@ -14,32 +15,61 @@ namespace gaseous_server.Classes
{}
}
public static GameRomObject GetRoms(long GameId, long PlatformId = -1)
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0)
{
GameRomObject GameRoms = new GameRomObject();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
string sqlCount = "";
string sqlPlatform = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId);
string NameSearchWhere = "";
if (NameSearch.Length > 0)
{
NameSearchWhere = " AND Games_Roms.`Name` LIKE @namesearch";
dbDict.Add("namesearch", '%' + NameSearch + '%');
}
// platform query
sqlPlatform = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE GameId = @id ORDER BY Platform.`Name`;";
if (PlatformId == -1) {
sql = "SELECT * FROM Games_Roms WHERE GameId = @id ORDER BY `Name`";
// data query
sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
// count query
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
} else {
sql = "SELECT * FROM Games_Roms WHERE GameId = @id AND PlatformId = @platformid ORDER BY `Name`";
// data query
sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
// count query
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
dbDict.Add("platformid", PlatformId);
}
DataTable romDT = db.ExecuteCMD(sql, dbDict);
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict)[0];
DataTable platformDT = db.ExecuteCMD(sqlPlatform, dbDict);
if (romDT.Rows.Count > 0)
{
foreach (DataRow romDR in romDT.Rows)
{
GameRoms.GameRomItems.Add(BuildRom(romDR));
}
// set count of roms
GameRoms.Count = int.Parse((string)rowCount["RomCount"]);
// get rom media groups
GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId);
int pageOffset = pageSize * (pageNumber - 1);
for (int i = 0; i < romDT.Rows.Count; i++)
{
GameRomItem gameRomItem = BuildRom(romDT.Rows[i]);
if ((i >= pageOffset && i < pageOffset + pageSize) || pageSize == 0)
{
GameRoms.GameRomItems.Add(gameRomItem);
}
}
return GameRoms;
}
@@ -52,7 +82,7 @@ namespace gaseous_server.Classes
public static GameRomItem GetRom(long RomId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE Id = @id";
string sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -114,20 +144,20 @@ namespace gaseous_server.Classes
{
Id = (long)romDR["id"],
PlatformId = (long)romDR["platformid"],
Platform = Classes.Metadata.Platforms.GetPlatform((long)romDR["platformid"]),
Platform = (string)romDR["platformname"],
GameId = (long)romDR["gameid"],
Name = (string)romDR["name"],
Size = (long)romDR["size"],
CRC = ((string)romDR["crc"]).ToLower(),
MD5 = ((string)romDR["md5"]).ToLower(),
SHA1 = ((string)romDR["sha1"]).ToLower(),
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"],
RomType = (HasheousClient.Models.LookupResponseModel.RomItem.RomTypes)(int)romDR["romtype"],
RomTypeMedia = (string)romDR["romtypemedia"],
MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"],
Source = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)romDR["metadatasource"],
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
};
@@ -149,134 +179,20 @@ namespace gaseous_server.Classes
public class GameRomObject
{
public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>();
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>();
public int Count { get; set; }
}
public class GameRomItem
public class GameRomItem : HasheousClient.Models.LookupResponseModel.RomItem
{
public long Id { get; set; }
public long PlatformId { get; set; }
public IGDB.Models.Platform Platform { get; set; }
//public Dictionary<string, object>? Emulator { get; set; }
public string Platform { get; set; }
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public long GameId { get; set; }
public string? Name { get; set; }
public long Size { get; set; }
public string? CRC { get; set; }
public string? MD5 { get; set; }
public string? SHA1 { get; set; }
public string? DevelopmentStatus { get; set; }
public string[]? Flags { get; set; }
public List<KeyValuePair<string, object>>? Attributes { get; set;}
public int RomType { get; set; }
public string? RomTypeMedia { get; set; }
public MediaType? MediaDetail {
get
{
if (RomTypeMedia != null)
{
return new MediaType(Source, RomTypeMedia);
}
else
{
return null;
}
}
}
public string? MediaLabel { get; set; }
public string? Path { get; set; }
public RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; }
public string? SignatureSourceGameTitle { get; set;}
public GameLibrary.LibraryItem Library { get; set; }
}
public class MediaType
{
public MediaType(RomSignatureObject.Game.Rom.SignatureSourceType Source, string MediaTypeString)
{
switch (Source)
{
case RomSignatureObject.Game.Rom.SignatureSourceType.TOSEC:
string[] typeString = MediaTypeString.Split(" ");
string inType = "";
foreach (string typeStringVal in typeString)
{
if (inType == "")
{
switch (typeStringVal.ToLower())
{
case "disk":
Media = RomSignatureObject.Game.Rom.RomTypes.Disk;
inType = typeStringVal;
break;
case "disc":
Media = RomSignatureObject.Game.Rom.RomTypes.Disc;
inType = typeStringVal;
break;
case "file":
Media = RomSignatureObject.Game.Rom.RomTypes.File;
inType = typeStringVal;
break;
case "part":
Media = RomSignatureObject.Game.Rom.RomTypes.Part;
inType = typeStringVal;
break;
case "tape":
Media = RomSignatureObject.Game.Rom.RomTypes.Tape;
inType = typeStringVal;
break;
case "of":
inType = typeStringVal;
break;
case "side":
inType = typeStringVal;
break;
}
}
else {
switch (inType.ToLower())
{
case "disk":
case "disc":
case "file":
case "part":
case "tape":
Number = int.Parse(typeStringVal);
break;
case "of":
Count = int.Parse(typeStringVal);
break;
case "side":
Side = typeStringVal;
break;
}
inType = "";
}
}
break;
default:
break;
}
}
public RomSignatureObject.Game.Rom.RomTypes? Media { get; set; }
public int? Number { get; set; }
public int? Count { get; set; }
public string? Side { get; set; }
}
}
}

View File

@@ -0,0 +1,81 @@
using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Classes
{
public class SignatureManagement
{
public List<gaseous_server.Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
{
if (md5.Length > 0)
{
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
} else
{
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
}
}
public List<gaseous_server.Models.Signatures_Games> GetByTosecName(string TosecName = "")
{
if (TosecName.Length > 0)
{
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName);
} else
{
return null;
}
}
private List<gaseous_server.Models.Signatures_Games> _GetSignature(string sqlWhere, string searchString)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Signatures_Games.*, Signatures_Roms.Id AS romid, Signatures_Roms.Name AS romname, Signatures_Roms.Size, Signatures_Roms.CRC, Signatures_Roms.MD5, Signatures_Roms.SHA1, Signatures_Roms.DevelopmentStatus, Signatures_Roms.Attributes, Signatures_Roms.RomType, Signatures_Roms.RomTypeMedia, Signatures_Roms.MediaLabel, Signatures_Roms.MetadataSource FROM Signatures_Roms INNER JOIN view_Signatures_Games ON Signatures_Roms.GameId = view_Signatures_Games.Id WHERE " + sqlWhere;
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("searchString", searchString);
DataTable sigDb = db.ExecuteCMD(sql, dbDict);
List<gaseous_server.Models.Signatures_Games> GamesList = new List<gaseous_server.Models.Signatures_Games>();
foreach (DataRow sigDbRow in sigDb.Rows)
{
gaseous_server.Models.Signatures_Games gameItem = new gaseous_server.Models.Signatures_Games
{
Game = new gaseous_server.Models.Signatures_Games.GameItem
{
Id = (Int32)sigDbRow["Id"],
Name = (string)sigDbRow["Name"],
Description = (string)sigDbRow["Description"],
Year = (string)sigDbRow["Year"],
Publisher = (string)sigDbRow["Publisher"],
Demo = (gaseous_server.Models.Signatures_Games.GameItem.DemoTypes)(int)sigDbRow["Demo"],
System = (string)sigDbRow["Platform"],
SystemVariant = (string)sigDbRow["SystemVariant"],
Video = (string)sigDbRow["Video"],
Country = (string)sigDbRow["Country"],
Language = (string)sigDbRow["Language"],
Copyright = (string)sigDbRow["Copyright"]
},
Rom = new gaseous_server.Models.Signatures_Games.RomItem
{
Id = (Int32)sigDbRow["romid"],
Name = (string)sigDbRow["romname"],
Size = (Int64)sigDbRow["Size"],
Crc = (string)sigDbRow["CRC"],
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 = (gaseous_server.Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"],
RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
MediaLabel = (string)sigDbRow["MediaLabel"],
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)sigDbRow["MetadataSource"]
}
};
GamesList.Add(gameItem);
}
return GamesList;
}
}
}

View File

@@ -8,6 +8,7 @@ using System.Reflection;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -287,15 +288,15 @@ namespace gaseous_server.Controllers
[ProducesResponseType(typeof(Game), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ResponseCache(CacheProfileName = "5Minute")]
public ActionResult Game(long GameId, bool forceRefresh = false)
public ActionResult Game(long GameId)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, forceRefresh, false, forceRefresh);
Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
if (game != null)
{
return Ok(gameObject);
return Ok(game);
}
else
{
@@ -374,87 +375,6 @@ namespace gaseous_server.Controllers
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/agerating/{RatingId}/image")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameAgeClassification(long GameId, long RatingId)
{
try
{
GameAgeRating gameAgeRating = GetConsolidatedAgeRating(RatingId);
string fileExtension = "";
string fileType = "";
switch (gameAgeRating.RatingBoard)
{
case AgeRatingCategory.ESRB:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
case AgeRatingCategory.PEGI:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
case AgeRatingCategory.ACB:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
case AgeRatingCategory.CERO:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
case AgeRatingCategory.USK:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
case AgeRatingCategory.GRAC:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
case AgeRatingCategory.CLASS_IND:
fileExtension = "svg";
fileType = "image/svg+xml";
break;
}
string resourceName = "gaseous_server.Assets.Ratings." + gameAgeRating.RatingBoard.ToString() + "." + gameAgeRating.RatingTitle.ToString() + "." + fileExtension;
var assembly = Assembly.GetExecutingAssembly();
string[] resources = assembly.GetManifestResourceNames();
if (resources.Contains(resourceName))
{
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
byte[] filedata = new byte[stream.Length];
stream.Read(filedata, 0, filedata.Length);
string filename = gameAgeRating.RatingBoard.ToString() + "-" + gameAgeRating.RatingTitle.ToString() + "." + fileExtension;
string contentType = fileType;
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
}
}
return NotFound();
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
@@ -473,7 +393,7 @@ namespace gaseous_server.Controllers
{
foreach (long ArtworkId in gameObject.Artworks.Ids)
{
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
artworks.Add(GameArtwork);
}
}
@@ -501,7 +421,7 @@ namespace gaseous_server.Controllers
try
{
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
if (artworkObject != null)
{
return Ok(artworkObject);
@@ -525,10 +445,11 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/artwork/{ArtworkId}/image")]
[Route("{GameId}/artwork/{ArtworkId}/image/{size}")]
[Route("{GameId}/artwork/{ArtworkId}/image/{size}/{ImageName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameCoverImage(long GameId, long ArtworkId)
public ActionResult GameCoverImage(long GameId, long ArtworkId, Communications.IGDBAPI_ImageSize size, string ImageName)
{
try
{
@@ -536,15 +457,25 @@ namespace gaseous_server.Controllers
try
{
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
if (artworkObject != null) {
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork", artworkObject.ImageId + ".png");
//string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork", size.ToString(), artworkObject.ImageId + ".jpg");
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork");
Communications comms = new Communications();
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, artworkObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.original });
string coverFilePath = ImgFetch.Result;
if (System.IO.File.Exists(coverFilePath))
{
string filename = artworkObject.ImageId + ".png";
string filename = artworkObject.ImageId + ".jpg";
string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/png";
string contentType = "image/jpg";
var cd = new System.Net.Mime.ContentDisposition
{
@@ -592,7 +523,7 @@ namespace gaseous_server.Controllers
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
{
IGDB.Models.Cover coverObject = Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
IGDB.Models.Cover coverObject = Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
if (coverObject != null)
{
return Ok(coverObject);
@@ -616,37 +547,48 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/cover/image")]
[Route("{GameId}/cover/image/{size}")]
[Route("{GameId}/cover/image/{size}/{imagename}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameCoverImage(long GameId)
public ActionResult GameCoverImage(long GameId, Communications.IGDBAPI_ImageSize size, string imagename = "")
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Cover.png");
if (System.IO.File.Exists(coverFilePath)) {
string filename = "Cover.png";
string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/png";
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
}
else
if (gameObject.Cover != null)
{
return NotFound();
if (gameObject.Cover.Id != null)
{
IGDB.Models.Cover cover = Classes.Metadata.Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Covers");
Communications comms = new Communications();
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, cover.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
string coverFilePath = ImgFetch.Result;
if (System.IO.File.Exists(coverFilePath)) {
string filename = cover.ImageId + ".jpg";
string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/jpg";
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
}
}
}
return NotFound();
}
catch
{
@@ -825,6 +767,24 @@ namespace gaseous_server.Controllers
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/platforms")]
[ProducesResponseType(typeof(List<KeyValuePair<long, string>>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GamePlatforms(long GameId)
{
try
{
return Ok(Games.GetAvailablePlatforms(GameId));
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
@@ -870,13 +830,13 @@ namespace gaseous_server.Controllers
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
//[ResponseCache(CacheProfileName = "5Minute")]
public ActionResult GameRom(long GameId)
public ActionResult GameRom(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1, string NameSearch = "")
{
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
return Ok(Classes.Roms.GetRoms(GameId));
return Ok(Classes.Roms.GetRoms(GameId, PlatformId, NameSearch, pageNumber, pageSize));
}
catch
{
@@ -1080,6 +1040,35 @@ namespace gaseous_server.Controllers
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup")]
[ProducesResponseType(typeof(List<RomMediaGroup.GameRomMediaGroupItem>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetGameRomGroup(long GameId)
{
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
try
{
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Rom Group", "An error occurred", ex);
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
@@ -1232,7 +1221,8 @@ namespace gaseous_server.Controllers
{
Classes.Roms.GameRomItem romItem = Classes.Roms.GetRom(RomId);
Common.hashObject hash = new Common.hashObject(romItem.Path);
Models.Signatures_Games romSig = Classes.ImportGame.GetFileSignature(hash, new FileInfo(romItem.Path), romItem.Path);
FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games romSig = fileSignature.GetFileSignature(romItem.Library, hash, new FileInfo(romItem.Path), romItem.Path);
List<Game> searchResults = Classes.ImportGame.SearchForGame_GetAll(romSig.Game.Name, romSig.Flags.IGDBPlatformId);
return Ok(searchResults);
@@ -1275,7 +1265,7 @@ namespace gaseous_server.Controllers
{
foreach (long ScreenshotId in gameObject.Screenshots.Ids)
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
screenshots.Add(GameScreenshot);
}
}
@@ -1301,7 +1291,7 @@ namespace gaseous_server.Controllers
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null) {
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
if (screenshotObject != null)
{
return Ok(screenshotObject);
@@ -1325,24 +1315,31 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}/image")]
[Route("{GameId}/screenshots/{ScreenshotId}/image/{size}")]
[Route("{GameId}/screenshots/{ScreenshotId}/image/{size}/{ImageName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameScreenshotImage(long GameId, long ScreenshotId)
public ActionResult GameScreenshotImage(long GameId, long ScreenshotId, Communications.IGDBAPI_ImageSize Size, string ImageName)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Screenshots");
Communications comms = new Communications();
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, screenshotObject.ImageId, Size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.original });
string coverFilePath = ImgFetch.Result;
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Screenshots", screenshotObject.ImageId + ".png");
if (System.IO.File.Exists(coverFilePath))
{
string filename = screenshotObject.ImageId + ".png";
string filename = screenshotObject.ImageId + ".jpg";
string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/png";
string contentType = "image/jpg";
var cd = new System.Net.Mime.ContentDisposition
{

View File

@@ -77,7 +77,8 @@ namespace gaseous_server.Controllers
// Process uploaded files
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
{
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
Classes.ImportGame uploadImport = new ImportGame();
uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
}
if (Directory.Exists(workPath))

View File

@@ -1,13 +1,16 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NuGet.Common;
using static gaseous_server.Classes.Metadata.Games;
@@ -37,36 +40,88 @@ namespace gaseous_server.Controllers
string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
searchBody += "where name ~ *\"" + SearchString + "\"*;";
// get Platform metadata
Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
List<Platform>? searchCache = Communications.GetSearchCache<List<Platform>>(searchFields, searchBody);
return results.ToList();
if (searchCache == null)
{
// cache miss
// get Platform metadata from data source
Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
Communications.SetSearchCache<List<Platform>>(searchFields, searchBody, results.ToList());
return results.ToList();
}
else
{
return searchCache;
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Game")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<GaseousGame>), StatusCodes.Status200OK)]
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
{
List<Game> RetVal = await _SearchForGame(PlatformId, SearchString);
List<GaseousGame> RetVal = await _SearchForGame(PlatformId, SearchString);
return Ok(RetVal);
}
private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString)
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
{
string searchBody = "";
string searchFields = "fields cover.*,first_release_date,name,platforms,slug; ";
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
searchBody += "search \"" + SearchString + "\";";
searchBody += "where platforms = (" + PlatformId + ");";
// get Platform metadata
Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
List<GaseousGame>? searchCache = Communications.GetSearchCache<List<GaseousGame>>(searchFields, searchBody);
return results.ToList();
if (searchCache == null)
{
// cache miss
// get Game metadata from data source
Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
List<GaseousGame> games = new List<GaseousGame>();
foreach (Game game in results.ToList())
{
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
switch(cacheStatus)
{
case Storage.CacheStatus.NotPresent:
Storage.NewCacheValue(game, false);
break;
case Storage.CacheStatus.Expired:
Storage.NewCacheValue(game, true);
break;
}
games.Add(new GaseousGame(game));
}
Communications.SetSearchCache<List<GaseousGame>>(searchFields, searchBody, games);
return games;
}
else
{
// get full version of results from database
// this is a hacky workaround due to the readonly nature of IGDB.Model.Game IdentityOrValue fields
List<GaseousGame> gamesToReturn = new List<GaseousGame>();
foreach (GaseousGame game in searchCache)
{
Game tempGame = Games.GetGame((long)game.Id, false, false, false);
gamesToReturn.Add(new GaseousGame(tempGame));
}
return gamesToReturn;
}
}
}
}

View File

@@ -37,82 +37,27 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
public List<gaseous_server.Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
{
if (md5.Length > 0)
{
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
} else
{
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
}
SignatureManagement signatureManagement = new SignatureManagement();
return signatureManagement.GetSignature(md5, sha1);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetByTosecName(string TosecName = "")
public List<gaseous_server.Models.Signatures_Games> GetByTosecName(string TosecName = "")
{
if (TosecName.Length > 0)
{
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName);
SignatureManagement signatureManagement = new SignatureManagement();
return signatureManagement.GetByTosecName(TosecName);
} else
{
return null;
}
}
private List<Models.Signatures_Games> _GetSignature(string sqlWhere, string searchString)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Signatures_Games.*, Signatures_Roms.Id AS romid, Signatures_Roms.Name AS romname, Signatures_Roms.Size, Signatures_Roms.CRC, Signatures_Roms.MD5, Signatures_Roms.SHA1, Signatures_Roms.DevelopmentStatus, Signatures_Roms.Attributes, Signatures_Roms.RomType, Signatures_Roms.RomTypeMedia, Signatures_Roms.MediaLabel, Signatures_Roms.MetadataSource FROM Signatures_Roms INNER JOIN view_Signatures_Games ON Signatures_Roms.GameId = view_Signatures_Games.Id WHERE " + sqlWhere;
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("searchString", searchString);
DataTable sigDb = db.ExecuteCMD(sql, dbDict);
List<Models.Signatures_Games> GamesList = new List<Models.Signatures_Games>();
foreach (DataRow sigDbRow in sigDb.Rows)
{
Models.Signatures_Games gameItem = new Models.Signatures_Games
{
Game = new Models.Signatures_Games.GameItem
{
Id = (Int32)sigDbRow["Id"],
Name = (string)sigDbRow["Name"],
Description = (string)sigDbRow["Description"],
Year = (string)sigDbRow["Year"],
Publisher = (string)sigDbRow["Publisher"],
Demo = (Models.Signatures_Games.GameItem.DemoTypes)(int)sigDbRow["Demo"],
System = (string)sigDbRow["Platform"],
SystemVariant = (string)sigDbRow["SystemVariant"],
Video = (string)sigDbRow["Video"],
Country = (string)sigDbRow["Country"],
Language = (string)sigDbRow["Language"],
Copyright = (string)sigDbRow["Copyright"]
},
Rom = new Models.Signatures_Games.RomItem
{
Id = (Int32)sigDbRow["romid"],
Name = (string)sigDbRow["romname"],
Size = (Int64)sigDbRow["Size"],
Crc = (string)sigDbRow["CRC"],
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 = (RomSignatureObject.Game.Rom.RomTypes)(int)sigDbRow["RomType"],
RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
MediaLabel = (string)sigDbRow["MediaLabel"],
SignatureSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)sigDbRow["MetadataSource"]
}
};
GamesList.Add(gameItem);
}
return GamesList;
}
}
}

View File

@@ -103,7 +103,7 @@ namespace gaseous_server.Controllers
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeRatings.AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
WriteIndented = true
}) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(ver);

View File

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

View File

@@ -192,11 +192,14 @@ namespace gaseous_server.Controllers.v1_1
string tempVal = "";
string nameWhereClause = "";
if (model.Name.Length > 0)
{
tempVal = "`Name` LIKE @Name";
whereParams.Add("@Name", "%" + model.Name + "%");
havingClauses.Add(tempVal);
// tempVal = "`Name` LIKE @Name";
// whereParams.Add("@Name", "%" + model.Name + "%");
// havingClauses.Add(tempVal);
nameWhereClause = "WHERE (MATCH(Game.`Name`) AGAINST (@Name IN BOOLEAN MODE) OR MATCH(AlternativeName.`Name`) AGAINST (@Name IN BOOLEAN MODE))";
whereParams.Add("@Name", "(*" + model.Name + "*) (" + model.Name + ") ");
}
if (model.GameRating != null)
@@ -263,9 +266,10 @@ namespace gaseous_server.Controllers.v1_1
}
}
string platformWhereClause = "";
if (model.Platform.Count > 0)
{
tempVal = "Relation_Game_Platforms.PlatformsId IN (";
tempVal = " AND Games_Roms.PlatformId IN (";
for (int i = 0; i < model.Platform.Count; i++)
{
if (i > 0)
@@ -277,7 +281,8 @@ namespace gaseous_server.Controllers.v1_1
whereParams.Add(platformLabel, model.Platform[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
//whereClauses.Add(tempVal);
platformWhereClause = tempVal;
}
if (model.Genre.Count > 0)
@@ -352,7 +357,7 @@ namespace gaseous_server.Controllers.v1_1
{
if (model.GameAgeRating.AgeGroupings.Count > 0)
{
tempVal = "(AgeGroupId IN (";
tempVal = "(Game.AgeGroupId IN (";
for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++)
{
if (i > 0)
@@ -367,7 +372,7 @@ namespace gaseous_server.Controllers.v1_1
if (model.GameAgeRating.IncludeUnrated == true)
{
tempVal += " OR AgeGroupId IS NULL";
tempVal += " OR Game.AgeGroupId IS NULL";
}
tempVal += ")";
@@ -439,7 +444,9 @@ namespace gaseous_server.Controllers.v1_1
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
//string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
string sql = "SELECT DISTINCT Game.Id, Game.`Name`, Game.NameThe, Game.PlatformId, Game.TotalRating, Game.TotalRatingCount, Game.Cover, Game.Artworks, Game.FirstReleaseDate, Game.Category, Game.ParentGame, Game.AgeRatings, Game.AgeGroupId, Game.RomCount FROM (SELECT DISTINCT Game.*, CASE WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The') ELSE Game.`Name` END AS NameThe, Games_Roms.PlatformId, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + " LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + " GROUP BY Game.Id HAVING RomCount > 0) Game LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
@@ -457,7 +464,8 @@ namespace gaseous_server.Controllers.v1_1
break;
}
RetVal.Add(Classes.Metadata.Games.GetGame(dbResponse.Rows[i]));
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
RetVal.Add(retGame);
}
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);

View File

@@ -0,0 +1,94 @@
using System.Reflection;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace gaseous_server.Models
{
public class GaseousGame : IGDB.Models.Game
{
public GaseousGame()
{
}
public GaseousGame(IGDB.Models.Game game)
{
var targetType = this.GetType();
var sourceType = game.GetType();
foreach(var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public| BindingFlags.SetProperty))
{
// check whether source object has the the property
var sp = sourceType.GetProperty(prop.Name);
if (sp != null)
{
// if yes, copy the value to the matching property
var value = sp.GetValue(game, null);
prop.SetValue(this, value, null);
}
}
}
public IGDB.Models.Cover? CoverItem
{
get
{
if (this.Cover != null)
{
if (this.Cover.Id != null)
{
IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
return cover;
}
}
return null;
}
}
// public List<IGDB.Models.Artwork>? ArtworksItem
// {
// get
// {
// if (this.Artworks != null)
// {
// if (this.Artworks.Ids != null)
// {
// List<IGDB.Models.Artwork> artworks = new List<IGDB.Models.Artwork>();
// foreach (long id in this.Artworks.Ids)
// {
// artworks.Add(gaseous_server.Classes.Metadata.Artworks.GetArtwork(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false));
// }
// return artworks;
// }
// }
// return null;
// }
// }
// public List<IGDB.Models.Screenshot>? ScreenshotsItem
// {
// get
// {
// if (this.Screenshots != null)
// {
// if (this.Screenshots.Ids != null)
// {
// List<IGDB.Models.Screenshot> screenshots = new List<IGDB.Models.Screenshot>();
// foreach (long id in this.Screenshots.Ids)
// {
// screenshots.Add(gaseous_server.Classes.Metadata.Screenshots.GetScreenshot(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false));
// }
// return screenshots;
// }
// }
// return null;
// }
// }
}
}

View File

@@ -106,11 +106,19 @@ namespace gaseous_server.Models
long mapId = (long)row["Id"];
if (PlatformMapCache.ContainsKey(mapId.ToString()))
{
platformMaps.Add(PlatformMapCache[mapId.ToString()]);
PlatformMapItem mapItem = PlatformMapCache[mapId.ToString()];
if (mapItem != null)
{
platformMaps.Add(mapItem);
}
}
else
{
platformMaps.Add(BuildPlatformMapItem(row));
PlatformMapItem mapItem = BuildPlatformMapItem(row);
if (mapItem != null)
{
platformMaps.Add(mapItem);
}
}
}
@@ -253,119 +261,129 @@ namespace gaseous_server.Models
string sql = "";
// get platform data
IGDB.Models.Platform platform = Platforms.GetPlatform(IGDBId);
IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId);
// get platform alternate names
sql = "SELECT * FROM PlatformMap_AlternateNames WHERE Id = @Id ORDER BY Name";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable altTable = db.ExecuteCMD(sql, dbDict);
List<string> alternateNames = new List<string>();
foreach (DataRow altRow in altTable.Rows)
if (platform != null)
{
string altVal = (string)altRow["Name"];
if (!alternateNames.Contains(altVal, StringComparer.OrdinalIgnoreCase))
// get platform alternate names
sql = "SELECT * FROM PlatformMap_AlternateNames WHERE Id = @Id ORDER BY Name";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable altTable = db.ExecuteCMD(sql, dbDict);
List<string> alternateNames = new List<string>();
foreach (DataRow altRow in altTable.Rows)
{
alternateNames.Add(altVal);
string altVal = (string)altRow["Name"];
if (!alternateNames.Contains(altVal, StringComparer.OrdinalIgnoreCase))
{
alternateNames.Add(altVal);
}
}
}
if (platform.AlternativeName != null)
{
if (!alternateNames.Contains(platform.AlternativeName, StringComparer.OrdinalIgnoreCase))
if (platform.AlternativeName != null)
{
alternateNames.Add(platform.AlternativeName);
if (!alternateNames.Contains(platform.AlternativeName, StringComparer.OrdinalIgnoreCase))
{
alternateNames.Add(platform.AlternativeName);
}
}
}
// get platform known extensions
sql = "SELECT * FROM PlatformMap_Extensions WHERE Id = @Id ORDER BY Extension";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable extTable = db.ExecuteCMD(sql, dbDict);
// get platform known extensions
sql = "SELECT * FROM PlatformMap_Extensions WHERE Id = @Id ORDER BY Extension";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable extTable = db.ExecuteCMD(sql, dbDict);
List<string> knownExtensions = new List<string>();
foreach (DataRow extRow in extTable.Rows)
{
string extVal = (string)extRow["Extension"];
if (!knownExtensions.Contains(extVal, StringComparer.OrdinalIgnoreCase))
List<string> knownExtensions = new List<string>();
foreach (DataRow extRow in extTable.Rows)
{
knownExtensions.Add(extVal);
string extVal = (string)extRow["Extension"];
if (!knownExtensions.Contains(extVal, StringComparer.OrdinalIgnoreCase))
{
knownExtensions.Add(extVal);
}
}
}
// get platform unique extensions
sql = "SELECT * FROM PlatformMap_UniqueExtensions WHERE Id = @Id ORDER BY Extension";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable uextTable = db.ExecuteCMD(sql, dbDict);
// get platform unique extensions
sql = "SELECT * FROM PlatformMap_UniqueExtensions WHERE Id = @Id ORDER BY Extension";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable uextTable = db.ExecuteCMD(sql, dbDict);
List<string> uniqueExtensions = new List<string>();
foreach (DataRow uextRow in uextTable.Rows)
{
string uextVal = (string)uextRow["Extension"];
if (!uniqueExtensions.Contains(uextVal, StringComparer.OrdinalIgnoreCase))
List<string> uniqueExtensions = new List<string>();
foreach (DataRow uextRow in uextTable.Rows)
{
uniqueExtensions.Add(uextVal);
string uextVal = (string)uextRow["Extension"];
if (!uniqueExtensions.Contains(uextVal, StringComparer.OrdinalIgnoreCase))
{
uniqueExtensions.Add(uextVal);
}
}
}
// get platform bios
sql = "SELECT * FROM PlatformMap_Bios WHERE Id = @Id ORDER BY Filename";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable biosTable = db.ExecuteCMD(sql, dbDict);
// get platform bios
sql = "SELECT * FROM PlatformMap_Bios WHERE Id = @Id ORDER BY Filename";
dbDict.Clear();
dbDict.Add("Id", IGDBId);
DataTable biosTable = db.ExecuteCMD(sql, dbDict);
List<PlatformMapItem.EmulatorBiosItem> bioss = new List<PlatformMapItem.EmulatorBiosItem>();
foreach (DataRow biosRow in biosTable.Rows)
{
PlatformMapItem.EmulatorBiosItem bios = new PlatformMapItem.EmulatorBiosItem
List<PlatformMapItem.EmulatorBiosItem> bioss = new List<PlatformMapItem.EmulatorBiosItem>();
foreach (DataRow biosRow in biosTable.Rows)
{
filename = (string)Common.ReturnValueIfNull(biosRow["Filename"], ""),
description = (string)Common.ReturnValueIfNull(biosRow["Description"], ""),
hash = ((string)Common.ReturnValueIfNull(biosRow["Hash"], "")).ToLower()
PlatformMapItem.EmulatorBiosItem bios = new PlatformMapItem.EmulatorBiosItem
{
filename = (string)Common.ReturnValueIfNull(biosRow["Filename"], ""),
description = (string)Common.ReturnValueIfNull(biosRow["Description"], ""),
hash = ((string)Common.ReturnValueIfNull(biosRow["Hash"], "")).ToLower()
};
bioss.Add(bios);
}
// build item
PlatformMapItem mapItem = new PlatformMapItem();
mapItem.IGDBId = IGDBId;
mapItem.IGDBName = platform.Name;
mapItem.IGDBSlug = platform.Slug;
mapItem.AlternateNames = alternateNames;
mapItem.Extensions = new PlatformMapItem.FileExtensions{
SupportedFileExtensions = knownExtensions,
UniqueFileExtensions = uniqueExtensions
};
bioss.Add(bios);
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"], ""),
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;
}
// build item
PlatformMapItem mapItem = new PlatformMapItem();
mapItem.IGDBId = IGDBId;
mapItem.IGDBName = platform.Name;
mapItem.IGDBSlug = platform.Slug;
mapItem.AlternateNames = alternateNames;
mapItem.Extensions = new PlatformMapItem.FileExtensions{
SupportedFileExtensions = knownExtensions,
UniqueFileExtensions = uniqueExtensions
};
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"], ""),
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;
return null;
}
public static void GetIGDBPlatformMapping(ref Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName)
public static void GetIGDBPlatformMapping(ref gaseous_server.Models.Signatures_Games Signature, string ImageExtension, bool SetSystemName)
{
if (Signature.Game != null)
{
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Determining platform based on extension " + ImageExtension + " or \"" + Signature.Game.System + "\"");
}
bool PlatformFound = false;
foreach (Models.PlatformMapping.PlatformMapItem PlatformMapping in Models.PlatformMapping.PlatformMap)
{
if (PlatformMapping.Extensions != null)
{
if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(RomFileInfo.Extension, StringComparer.OrdinalIgnoreCase))
if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(ImageExtension, StringComparer.OrdinalIgnoreCase))
{
if (SetSystemName == true)
{
@@ -375,6 +393,8 @@ namespace gaseous_server.Models
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
PlatformFound = true;
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from file extension");
break;
}
}
@@ -397,10 +417,17 @@ namespace gaseous_server.Models
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
PlatformFound = true;
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from signature system to platform map");
break;
}
}
}
if (PlatformFound == false)
{
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Unable to determine platform");
}
}
public class PlatformMapItem

View File

@@ -4,197 +4,19 @@ using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models
{
public class Signatures_Games
public class Signatures_Games : HasheousClient.Models.LookupResponseModel
{
public Signatures_Games()
{
}
public GameItem? Game { get; set; }
public RomItem? Rom { get; set; }
//[JsonIgnore]
public int Score
{
get
{
int _score = 0;
if (Game != null)
{
_score = _score + Game.Score;
}
if (Rom != null)
{
_score = _score + Rom.Score;
}
return _score;
}
}
public SignatureFlags Flags = new SignatureFlags();
public class GameItem
{
public Int32? Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public string? Year { get; set; }
public string? Publisher { get; set; }
public DemoTypes Demo { get; set; }
public string? System { get; set; }
public string? SystemVariant { get; set; }
public string? Video { get; set; }
public string? Country { get; set; }
public string? Language { get; set; }
public string? Copyright { get; set; }
public enum DemoTypes
{
NotDemo = 0,
demo = 1,
demo_kiosk = 2,
demo_playable = 3,
demo_rolling = 4,
demo_slideshow = 5
}
[JsonIgnore]
public int Score
{
get
{
// calculate a score based on the availablility of data
int _score = 0;
var properties = this.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
switch (prop.Name.ToLower())
{
case "id":
case "score":
break;
case "name":
case "year":
case "publisher":
case "system":
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 10;
}
}
}
break;
default:
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 1;
}
}
}
break;
}
}
}
return _score;
}
}
}
public class RomItem
{
public Int32? Id { get; set; }
public string? Name { get; set; }
public Int64? Size { get; set; }
public string? Crc { get; set; }
public string? Md5 { get; set; }
public string? Sha1 { get; set; }
public string? DevelopmentStatus { get; set; }
public List<KeyValuePair<string, object>> Attributes { get; set; } = new List<KeyValuePair<string, object>>();
public RomSignatureObject.Game.Rom.RomTypes RomType { get; set; }
public string? RomTypeMedia { get; set; }
public string? MediaLabel { get; set; }
public RomSignatureObject.Game.Rom.SignatureSourceType SignatureSource { get; set; }
[JsonIgnore]
public int Score
{
get
{
// calculate a score based on the availablility of data
int _score = 0;
var properties = this.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
switch (prop.Name.ToLower())
{
case "name":
case "size":
case "crc":
case "developmentstatus":
case "flags":
case "attributes":
case "romtypemedia":
case "medialabel":
if (prop.PropertyType == typeof(string) || prop.PropertyType == typeof(Int64) || prop.PropertyType == typeof(List<string>))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 10;
}
}
}
break;
default:
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 1;
}
}
}
break;
}
}
}
return _score;
}
}
}
public class SignatureFlags
{
public long IGDBPlatformId { get; set; }
public string IGDBPlatformName { get; set; }
public long IGDBGameId { get; set; }
}
}
}

View File

@@ -2,6 +2,8 @@
using System.ComponentModel.Design.Serialization;
using System.Data;
using gaseous_server.Classes;
using NuGet.Common;
using NuGet.Packaging;
namespace gaseous_server
{
@@ -113,6 +115,7 @@ namespace gaseous_server
_CorrelationId = correlationId.ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", _ItemType.ToString());
CallContext.SetData("CallingUser", "System");
// log the start
Logging.Log(Logging.LogType.Debug, "Timered Event", "Executing " + _ItemType + " with correlation id " + _CorrelationId);
@@ -143,11 +146,13 @@ namespace gaseous_server
case QueueItemType.TitleIngestor:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory)
Classes.ImportGame import = new ImportGame
{
CallingQueueItem = this
};
import.ProcessDirectory(Config.LibraryConfiguration.LibraryImportDirectory);
// clean up
Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory);
_SaveLastRunTime = true;
@@ -168,24 +173,39 @@ namespace gaseous_server
case QueueItemType.OrganiseLibrary:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser");
Classes.ImportGame.OrganiseLibrary();
Classes.ImportGame importLibraryOrg = new ImportGame
{
CallingQueueItem = this
};
importLibraryOrg.OrganiseLibrary();
_SaveLastRunTime = true;
break;
case QueueItemType.LibraryScan:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner");
Classes.ImportGame import = new ImportGame
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanners");
Classes.ImportGame libScan = new ImportGame
{
CallingQueueItem = this
};
import.LibraryScan();
libScan.LibraryScan();
_SaveLastRunTime = true;
break;
case QueueItemType.LibraryScanWorker:
GameLibrary.LibraryItem library = (GameLibrary.LibraryItem)Options;
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner worker for library " + library.Name);
Classes.ImportGame importLibraryScan = new ImportGame
{
CallingQueueItem = this
};
importLibraryScan.LibrarySpecificScan(library);
break;
case QueueItemType.Rematcher:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Rematch");
Classes.ImportGame importRematch = new ImportGame
@@ -221,6 +241,32 @@ namespace gaseous_server
maintenance.RunMaintenance();
break;
case QueueItemType.TempCleanup:
try
{
foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries)
{
string rootPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, libraryItem.Id.ToString());
if (Directory.Exists(rootPath))
{
foreach (string directory in Directory.GetDirectories(rootPath))
{
DirectoryInfo info = new DirectoryInfo(directory);
if (info.LastWriteTimeUtc.AddMinutes(5) < DateTime.UtcNow)
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Deleting temporary decompress folder: " + directory);
Directory.Delete(directory, true);
}
}
}
}
}
catch (Exception tcEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "An error occurred while cleaning temporary files", tcEx);
}
break;
}
}
catch (Exception ex)
@@ -348,6 +394,11 @@ namespace gaseous_server
/// </summary>
LibraryScan,
/// <summary>
/// Performs the work for the LibraryScan task
/// </summary>
LibraryScanWorker,
/// <summary>
/// Looks for roms in the library that have an unknown platform or game match
/// </summary>
@@ -371,7 +422,12 @@ namespace gaseous_server
/// <summary>
/// Performs a clean up of old files, and optimises the database
/// </summary>
Maintainer
Maintainer,
/// <summary>
/// Cleans up marked paths in the temporary directory
/// </summary>
TempCleanup
}
public enum QueueItemState

View File

@@ -50,7 +50,10 @@ Config.InitSettings();
Config.UpdateConfig();
// set api metadata source from config
Communications.MetadataSource = Config.MetadataConfiguration.Source;
Communications.MetadataSource = Config.MetadataConfiguration.MetadataSource;
// set up hasheous client
HasheousClient.WebApp.HttpHelper.BaseUri = Config.MetadataConfiguration.HasheousHost;
// set initial values
Guid APIKey = Guid.NewGuid();
@@ -106,6 +109,11 @@ builder.Services.AddControllers().AddJsonOptions(x =>
builder.Services.AddResponseCaching();
builder.Services.AddControllers(options =>
{
options.CacheProfiles.Add("None",
new CacheProfile()
{
Duration = 1
});
options.CacheProfiles.Add("Default30",
new CacheProfile()
{
@@ -297,20 +305,6 @@ using (var scope = app.Services.CreateScope())
}
}
app.Use(async (context, next) =>
{
// set the correlation id
string correlationId = Guid.NewGuid().ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", context.Request.Method + ": " + context.Request.Path);
context.Response.Headers.Add("x-correlation-id", correlationId.ToString());
await next();
});
app.UseAuthorization();
app.UseDefaultFiles();
@@ -322,6 +316,28 @@ app.UseStaticFiles(new StaticFileOptions
app.MapControllers();
app.Use(async (context, next) =>
{
// set the correlation id
string correlationId = Guid.NewGuid().ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", context.Request.Method + ": " + context.Request.Path);
string userIdentity;
try
{
userIdentity = context.User.Claims.Where(x=>x.Type==System.Security.Claims.ClaimTypes.NameIdentifier).FirstOrDefault().Value;
}
catch
{
userIdentity = "";
}
CallContext.SetData("CallingUser", userIdentity);
context.Response.Headers.Add("x-correlation-id", correlationId.ToString());
await next();
});
// emergency password recovery if environment variable is set
// process:
// - set the environment variable "recoveraccount" to the email address of the account to be recovered
@@ -460,6 +476,16 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
})
);
ProcessQueue.QueueItem tempCleanup = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.TempCleanup,
1,
new List<ProcessQueue.QueueItemType>(),
false,
false
);
tempCleanup.ForceExecute();
ProcessQueue.QueueItems.Add(tempCleanup);
Logging.WriteToDiskOnly = false;
// start the app

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
ALTER TABLE `Games_Roms`
ADD INDEX `id_IdAndLibraryId` (`Id` ASC, `LibraryId` ASC) VISIBLE;
ALTER TABLE `ServerLogs`
ADD INDEX `idx_EventDate` (`EventTime` ASC) VISIBLE;

View File

@@ -0,0 +1,9 @@
CREATE TABLE `SearchCache` (
`SearchFields` varchar(384) NOT NULL,
`SearchString` varchar(128) NOT NULL,
`Content` longtext DEFAULT NULL,
`LastSearch` datetime DEFAULT NULL,
PRIMARY KEY (`SearchFields`,`SearchString`),
KEY `idx_SearchString` (`SearchFields`,`SearchString`),
KEY `idx_LastSearch` (`LastSearch`)
);

View File

@@ -0,0 +1,2 @@
ALTER TABLE `RomCollections`
ADD COLUMN `ArchiveType` INT NULL AFTER `IncludeBIOSFiles`;

View File

@@ -188,6 +188,91 @@
}
]
},
{
"igdbId": 114,
"igdbName": "Amiga CD32",
"igdbSlug": "amiga-cd32",
"alternateNames": [
"Amiga CD32",
"Commodore Amiga CD32"
],
"extensions": {
"supportedFileExtensions": [
".ZIP"
],
"uniqueFileExtensions": [
]
},
"retroPieDirectoryName": "amiga",
"webEmulator": {
"type": "EmulatorJS",
"core": "amiga",
"availableWebEmulators": [
{
"emulatorType": "EmulatorJS",
"availableWebEmulatorCores": [
{
"core": "amiga",
"alternateCoreName": "puae",
"default": true
}
]
}
]
},
"bios": [
{
"hash": "85ad74194e87c08904327de1a9443b7a",
"description": "Kickstart v1.2 rev 33.180",
"filename": "kick33180.A500"
},
{
"hash": "82a21c1890cae844b3df741f2762d48d",
"description": "Kickstart v1.3 rev 34.005",
"filename": "kick34005.A500"
},
{
"hash": "89da1838a24460e4b93f4f0c5d92d48d",
"description": "CDTV extended ROM v1.00",
"filename": "kick34005.CDTV"
},
{
"hash": "dc10d7bdd1b6f450773dfb558477c230",
"description": "Kickstart v2.04 rev 37.175",
"filename": "kick37175.A500"
},
{
"hash": "5f8924d013dd57a89cf349f4cdedc6b1",
"description": "CD32 Kickstart v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "f2f241bf094168cfb9e7805dc2856433",
"description": "CD32 KS + extended v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "bb72565701b1b6faece07d68ea5da639",
"description": "CD32 extended ROM rev 40.060",
"filename": "kick40060.CD32.ext"
},
{
"hash": "e40a5dfb3d017ba8779faba30cbd1c8e",
"description": "Kickstart v3.1 rev 40.063",
"filename": "kick40063.A600"
},
{
"hash": "646773759326fbac3b2311fd8c8793ee",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A1200"
},
{
"hash": "9bdedde6a4f33555b4a270c8ca53297d",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A4000"
}
]
},
{
"igdbId": 25,
"igdbName": "Amstrad CPC",
@@ -543,6 +628,48 @@
}
]
},
{
"igdbId": 68,
"igdbName": "ColecoVision",
"igdbSlug": "colecovision",
"alternateNames": [
"ColecoVision"
],
"extensions": {
"supportedFileExtensions": [
".BIN",
".COL",
".ROM",
".ZIP"
],
"uniqueFileExtensions": [
]
},
"retroPieDirectoryName": "coleco",
"webEmulator": {
"type": "EmulatorJS",
"core": "coleco",
"availableWebEmulators": [
{
"emulatorType": "EmulatorJS",
"availableWebEmulatorCores": [
{
"core": "coleco",
"alternateCoreName": "coleco",
"default": true
}
]
}
]
},
"bios": [
{
"hash": "2c66f5911e5b42b8ebe113403548eee7",
"description": "ColecoVision BIOS - Mandatory",
"filename": "colecovision.rom"
}
]
},
{
"igdbId": 15,
"igdbName": "Commodore C64/128/MAX",
@@ -620,6 +747,92 @@
}
]
},
{
"igdbId": 158,
"igdbName": "Commodore CDTV",
"igdbSlug": "commodore-cdtv",
"alternateNames": [
"Commodore CDTV",
"Amiga CDTV",
"Commodore Amiga CDTV"
],
"extensions": {
"supportedFileExtensions": [
".ZIP"
],
"uniqueFileExtensions": [
]
},
"retroPieDirectoryName": "amiga",
"webEmulator": {
"type": "EmulatorJS",
"core": "amiga",
"availableWebEmulators": [
{
"emulatorType": "EmulatorJS",
"availableWebEmulatorCores": [
{
"core": "amiga",
"alternateCoreName": "puae",
"default": true
}
]
}
]
},
"bios": [
{
"hash": "85ad74194e87c08904327de1a9443b7a",
"description": "Kickstart v1.2 rev 33.180",
"filename": "kick33180.A500"
},
{
"hash": "82a21c1890cae844b3df741f2762d48d",
"description": "Kickstart v1.3 rev 34.005",
"filename": "kick34005.A500"
},
{
"hash": "89da1838a24460e4b93f4f0c5d92d48d",
"description": "CDTV extended ROM v1.00",
"filename": "kick34005.CDTV"
},
{
"hash": "dc10d7bdd1b6f450773dfb558477c230",
"description": "Kickstart v2.04 rev 37.175",
"filename": "kick37175.A500"
},
{
"hash": "5f8924d013dd57a89cf349f4cdedc6b1",
"description": "CD32 Kickstart v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "f2f241bf094168cfb9e7805dc2856433",
"description": "CD32 KS + extended v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "bb72565701b1b6faece07d68ea5da639",
"description": "CD32 extended ROM rev 40.060",
"filename": "kick40060.CD32.ext"
},
{
"hash": "e40a5dfb3d017ba8779faba30cbd1c8e",
"description": "Kickstart v3.1 rev 40.063",
"filename": "kick40063.A600"
},
{
"hash": "646773759326fbac3b2311fd8c8793ee",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A1200"
},
{
"hash": "9bdedde6a4f33555b4a270c8ca53297d",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A4000"
}
]
},
{
"igdbId": 33,
"igdbName": "Game Boy",

View File

@@ -20,11 +20,15 @@
<ItemGroup>
<PackageReference Include="gaseous-signature-parser" Version="2.0.0" />
<PackageReference Include="gaseous.IGDB" Version="1.0.1" />
<PackageReference Include="hasheous-client" Version="0.1.0" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="sharpcompress" Version="0.35.0" />
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.1.23" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" />
<PackageReference Include="MySqlConnector" Version="2.3.1" />
@@ -50,60 +54,12 @@
<None Remove="Support\Database\MySQL\gaseous-1007.sql" />
<None Remove="Support\Database\MySQL\gaseous-1008.sql" />
<None Remove="Support\Database\MySQL\gaseous-1009.sql" />
<None Remove="Support\Database\MySQL\gaseous-1010.sql" />
<None Remove="Support\Database\MySQL\gaseous-1011.sql" />
<None Remove="Support\Database\MySQL\gaseous-1012.sql" />
<None Remove="Support\Database\MySQL\gaseous-1013.sql" />
<None Remove="Support\Database\MySQL\gaseous-1014.sql" />
<None Remove="Classes\Metadata\" />
<None Remove="Assets\" />
<None Remove="Assets\Ratings\" />
<None Remove="Assets\Ratings\ESRB\" />
<None Remove="Assets\Ratings\ACB\" />
<None Remove="Assets\Ratings\PEGI\" />
<None Remove="Assets\Ratings\ESRB\AO.svg" />
<None Remove="Assets\Ratings\ESRB\E.svg" />
<None Remove="Assets\Ratings\ESRB\E10plus.svg" />
<None Remove="Assets\Ratings\ESRB\M.svg" />
<None Remove="Assets\Ratings\ESRB\RP.svg" />
<None Remove="Assets\Ratings\ESRB\RP-LM17-English.svg" />
<None Remove="Assets\Ratings\ESRB\T.svg" />
<None Remove="Assets\Ratings\CERO\" />
<None Remove="Assets\Ratings\CERO\CERO_A.png" />
<None Remove="Assets\Ratings\CERO\CERO_B.png" />
<None Remove="Assets\Ratings\CERO\CERO_C.png" />
<None Remove="Assets\Ratings\CERO\CERO_D.png" />
<None Remove="Assets\Ratings\CERO\CERO_Z.png" />
<None Remove="Assets\Ratings\USK\" />
<None Remove="Assets\Ratings\USK\USK_0.svg" />
<None Remove="Assets\Ratings\USK\USK_12.svg" />
<None Remove="Assets\Ratings\USK\USK_16.svg" />
<None Remove="Assets\Ratings\USK\USK_18.svg" />
<None Remove="Assets\Ratings\USK\USK_6.svg" />
<None Remove="Assets\Ratings\ACB\ACB_G.svg" />
<None Remove="Assets\Ratings\ACB\ACB_M.svg" />
<None Remove="Assets\Ratings\ACB\ACB_MA15.svg" />
<None Remove="Assets\Ratings\ACB\ACB_PG.svg" />
<None Remove="Assets\Ratings\ACB\ACB_R18.svg" />
<None Remove="Assets\Ratings\ACB\ACB_RC.svg" />
<None Remove="Assets\Ratings\CERO\CERO_A.svg" />
<None Remove="Assets\Ratings\CERO\CERO_B.svg" />
<None Remove="Assets\Ratings\CERO\CERO_C.svg" />
<None Remove="Assets\Ratings\CERO\CERO_D.svg" />
<None Remove="Assets\Ratings\CERO\CERO_Z.svg" />
<None Remove="Assets\Ratings\PEGI\Eighteen.svg" />
<None Remove="Assets\Ratings\PEGI\Seven.svg" />
<None Remove="Assets\Ratings\PEGI\Sixteen.svg" />
<None Remove="Assets\Ratings\PEGI\Three.svg" />
<None Remove="Assets\Ratings\PEGI\Twelve.svg" />
<None Remove="Assets\Ratings\GRAC\" />
<None Remove="Assets\Ratings\GRAC\GRAC_All.svg" />
<None Remove="Assets\Ratings\GRAC\GRAC_Eighteen.svg" />
<None Remove="Assets\Ratings\GRAC\GRAC_Fifteen.svg" />
<None Remove="Assets\Ratings\GRAC\GRAC_Testing.svg" />
<None Remove="Assets\Ratings\GRAC\GRAC_Twelve.svg" />
<None Remove="Assets\Ratings\CLASS_IND\" />
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Eighteen.svg" />
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Fourteen.svg" />
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_L.svg" />
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Sixteen.svg" />
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Ten.svg" />
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Twelve.svg" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
@@ -112,15 +68,6 @@
<Folder Include="Classes\SignatureIngestors\" />
<Folder Include="Support\" />
<Folder Include="Classes\Metadata\" />
<Folder Include="Assets\" />
<Folder Include="Assets\Ratings\" />
<Folder Include="Assets\Ratings\ESRB\" />
<Folder Include="Assets\Ratings\ACB\" />
<Folder Include="Assets\Ratings\PEGI\" />
<Folder Include="Assets\Ratings\CERO\" />
<Folder Include="Assets\Ratings\USK\" />
<Folder Include="Assets\Ratings\GRAC\" />
<Folder Include="Assets\Ratings\CLASS_IND\" />
<Folder Remove="Reference" />
</ItemGroup>
<ItemGroup>
@@ -133,45 +80,6 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Assets\Ratings\ESRB\AO.svg" />
<EmbeddedResource Include="Assets\Ratings\ESRB\E.svg" />
<EmbeddedResource Include="Assets\Ratings\ESRB\E10.svg" />
<EmbeddedResource Include="Assets\Ratings\ESRB\M.svg" />
<EmbeddedResource Include="Assets\Ratings\ESRB\RP.svg" />
<EmbeddedResource Include="Assets\Ratings\ESRB\RP-LM17-English.svg" />
<EmbeddedResource Include="Assets\Ratings\ESRB\T.svg" />
<EmbeddedResource Include="Assets\Ratings\USK\USK_0.svg" />
<EmbeddedResource Include="Assets\Ratings\USK\USK_12.svg" />
<EmbeddedResource Include="Assets\Ratings\USK\USK_16.svg" />
<EmbeddedResource Include="Assets\Ratings\USK\USK_18.svg" />
<EmbeddedResource Include="Assets\Ratings\USK\USK_6.svg" />
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_G.svg" />
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_M.svg" />
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_MA15.svg" />
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_PG.svg" />
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_R18.svg" />
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_RC.svg" />
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_A.svg" />
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_B.svg" />
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_C.svg" />
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_D.svg" />
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_Z.svg" />
<EmbeddedResource Include="Assets\Ratings\PEGI\Eighteen.svg" />
<EmbeddedResource Include="Assets\Ratings\PEGI\Seven.svg" />
<EmbeddedResource Include="Assets\Ratings\PEGI\Sixteen.svg" />
<EmbeddedResource Include="Assets\Ratings\PEGI\Three.svg" />
<EmbeddedResource Include="Assets\Ratings\PEGI\Twelve.svg" />
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_All.svg" />
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Eighteen.svg" />
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Fifteen.svg" />
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Testing.svg" />
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Twelve.svg" />
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Eighteen.svg" />
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Fourteen.svg" />
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_L.svg" />
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Sixteen.svg" />
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Ten.svg" />
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Twelve.svg" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1000.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />
@@ -182,5 +90,10 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1008.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1009.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1010.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1011.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1012.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1013.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1014.sql" />
</ItemGroup>
</Project>

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,4 +1,5 @@
<div style='width:640px;height:480px;max-width:100%'>
<!-- <div style='width:640px;height:480px;max-width:100%'> -->
<div style='width:100%;height:100%;'>
<div id='game'></div>
</div>
@@ -28,5 +29,7 @@
EJS_backgroundBlur = true;
EJS_gameName = emuGameTitle;
EJS_threads = false;
</script>
<script src='/emulators/EmulatorJS/data/loader.js'></script>

View File

@@ -42,15 +42,18 @@
var statusText = result[i].buildStatus;
var downloadLink = '';
var packageSize = '-';
var inProgress = false;
switch (result[i].buildStatus) {
case 'NoStatus':
statusText = '-';
break;
case "WaitingForBuild":
statusText = 'Build pending';
inProgress = true;
break;
case "Building":
statusText = 'Building';
inProgress = true;
break;
case "Completed":
statusText = 'Available';
@@ -64,6 +67,10 @@
statusText = result[i].buildStatus;
break;
}
if (inProgress == true) {
setTimeout(GetCollections, 10000);
}
var editButton = '';
var deleteButton = '';

View File

@@ -71,11 +71,11 @@
</td>
<td>
<span id="collection_directorylayout_gaseous_label" name="collection_directorylayout_label">
<p>Standard layout: /&lt;IGDB Platform Slug&gt;/&lt;IGDB Game Slug&gt;/Game ROM's</p>
<p>Standard layout: /&lt;IGDB Platform Slug&gt;/&lt;IGDB Game Slug&gt;/Game ROMs</p>
<p>Example: /genesis-slash-megadrive/sonic-the-hedgehog/Sonic the Hedgehog.smd</p>
</span>
<span id="collection_directorylayout_retropie_label" style="display: none;" name="collection_directorylayout_label">
<p>RetroPie layout: /roms/&lt;RetroPie Platform Label&gt;/Game ROM's</p>
<p>RetroPie layout: /roms/&lt;RetroPie Platform Label&gt;/Game ROMs</p>
<p>Example: /roms/megadrive/Sonic the Hedgehog.smd</p>
</span>
</td>
@@ -97,6 +97,18 @@
BIOS files for each platform will be stored in /BIOS
</td>
</tr>
<!-- <tr>
<th>
Archive format
</th>
<td>
<select id="collection_archiveformat" style="width: 100%;">
<option id="collection_archiveformat_zip" selected="selected" value="Zip">Zip</option>
<option id="collection_archiveformat_rar" value="RAR">RAR</option>
<option id="collection_archiveformat_7z" value="SevenZip">7z</option>
</select>
</td>
</tr> -->
</table>
</div>
<table style="position: absolute; top: 0px; right: 0px; bottom: 0px; width: 60%;">
@@ -480,7 +492,7 @@
}
function DisplayPreview(data, targetDiv) {
console.log(JSON.stringify(data));
console.log(data);
var container = document.getElementById(targetDiv);
container.innerHTML = '';
@@ -579,7 +591,7 @@
var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image game_tile_image_small';
if (gameItem.cover) {
gameImage.src = '/api/v1.1/Games/' + gameItem.id + '/cover/image';
gameImage.src = '/api/v1.1/Games/' + gameItem.id + '/cover/image/cover_small/' + gameItem.coverItem.imageId + '.jpg';
} else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image game_tile_image_small unknown';

View File

@@ -16,10 +16,12 @@
'DELETE',
function (result) {
loadRoms();
loadMediaGroups();
closeSubDialog();
},
function (error) {
loadRoms();
loadMediaGroups();
closeSubDialog();
}
);

View File

@@ -31,7 +31,7 @@
</td>
</tr>
<tr>
<td colspan="2"><strong>Note</strong>: Standard directory naming uses the IGDB slug for the platform is not editable.</td>
<td colspan="2"><strong>Note</strong>: Standard directory naming uses the IGDB slug for the platform and is not editable.</td>
</tr>
<tr>
<td>
@@ -58,7 +58,7 @@
<h4>Engine</h4>
</td>
<td>
<select id="mapping_edit_webemulatorengine" style="width: 100%;">
<select id="mapping_edit_webemulatorengine" data-minimum-results-for-search="Infinity" style="width: 100%;">
</select>
</td>
@@ -68,7 +68,7 @@
<h4>Core</h4>
</td>
<td>
<select id="mapping_edit_webemulatorcore" style="width: 100%;">
<select id="mapping_edit_webemulatorcore" data-minimum-results-for-search="Infinity" style="width: 100%;">
</select>
</td>

View File

@@ -1,6 +1,7 @@
<div id="properties_toc">
<div id="properties_toc_general" name="properties_toc_item" onclick="SelectTab('general');">General</div>
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');">Attributes</div>
<div id="properties_toc_archive" name="properties_toc_item" onclick="SelectTab('archive');" style="display: none;">Archive Contents</div>
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');" style="display: none;">Attributes</div>
<div id="properties_toc_match" name="properties_toc_item" onclick="SelectTab('match');">Title Match</div>
<!--<div id="properties_toc_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>-->
</div>
@@ -53,6 +54,10 @@
</table>
</div>
<div id="properties_bodypanel_archive" name="properties_tab" style="display: none;">
<div id="properties_bodypanel_archive_content" style="height: 315px; overflow-x: scroll;"></div>
</div>
<div id="properties_bodypanel_attributes" name="properties_tab" style="display: none;">
</div>
@@ -122,17 +127,17 @@
romData = result;
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;
document.getElementById('rominfo_size').innerHTML = formatBytes(result.size, 2);
document.getElementById('rominfo_type').innerHTML = getRomType(result.romType);
document.getElementById('rominfo_mediatype').innerHTML = result.romTypeMedia;
document.getElementById('rominfo_medialabel').innerHTML = result.mediaLabel;
document.getElementById('rominfo_md5').innerHTML = result.mD5;
document.getElementById('rominfo_sha1').innerHTML = result.shA1;
document.getElementById('rominfo_signaturematch').innerHTML = result.source;
document.getElementById('rominfo_md5').innerHTML = result.md5;
document.getElementById('rominfo_sha1').innerHTML = result.sha1;
document.getElementById('rominfo_signaturematch').innerHTML = result.signatureSource;
document.getElementById('rominfo_signaturetitle').innerHTML = result.signatureSourceGameTitle;
document.getElementById('properties_fixplatform').innerHTML = "<option value='" + result.platform.id + "' selected='selected'>" + result.platform.name + "</option>";
document.getElementById('properties_fixplatform').innerHTML = "<option value='" + result.platformId + "' selected='selected'>" + result.platform + "</option>";
document.getElementById('properties_fixgame').innerHTML = "<option value='" + gameData.id + "' selected='selected'>" + gameData.name + "</option>";
if (result.library.isDefaultLibrary == false) {
@@ -141,8 +146,7 @@
if (result.attributes.length > 0) {
document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
} else {
document.getElementById('properties_toc_attributes').style.display = 'none';
document.getElementById('properties_bodypanel_archive_content').appendChild(BuildArchiveTable(result.attributes, result.source));
}
});
@@ -196,40 +200,54 @@
}
});
$('#properties_fixgame').select2({
minimumInputLength: 3,
templateResult: DropDownRenderGameOption,
ajax: {
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#properties_fixplatform').select2('data');
$('#properties_fixplatform').on('select2:select', function (e) {
var platformData = e.params.data;
var query = {
PlatformId: fixplatform[0].id,
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover
});
}
return {
results: arr
};
}
var gameValue = $('#properties_fixgame').select2('data');
if (gameValue) {
setFixGameDropDown();
}
});
function setFixGameDropDown() {
$('#properties_fixgame').empty().select2({
minimumInputLength: 3,
placeholder: 'Game',
templateResult: DropDownRenderGameOption,
ajax: {
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#properties_fixplatform').select2('data');
var query = {
PlatformId: fixplatform[0].id,
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover,
releaseDate: data[i].firstReleaseDate
});
}
return {
results: arr
};
}
}
});
}
setFixGameDropDown();
function SaveFixedGame() {
var fixplatform = $('#properties_fixplatform').select2('data');
var fixgame = $('#properties_fixgame').select2('data');
@@ -246,23 +264,75 @@
aTable.style.width = '100%';
for (var i = 0; i < attributes.length; i++) {
var aRow = document.createElement('tr');
if (attributes[i].key != "ZipContents") {
// show attributes button
document.getElementById('properties_toc_attributes').style.display = '';
var aRow = document.createElement('tr');
var aTitleCell = document.createElement('th');
aTitleCell.width = "25%";
if (sourceName == "TOSEC") {
aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key);
} else {
aTitleCell.innerHTML = attributes[i].key;
var aTitleCell = document.createElement('th');
aTitleCell.width = "25%";
if (sourceName == "TOSEC") {
aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key);
} else {
aTitleCell.innerHTML = attributes[i].key;
}
aRow.appendChild(aTitleCell);
var aValueCell = document.createElement('td');
aValueCell.width = "75%";
aValueCell.innerHTML = attributes[i].value;
aRow.appendChild(aValueCell);
aTable.appendChild(aRow);
}
aRow.appendChild(aTitleCell);
}
var aValueCell = document.createElement('td');
aValueCell.width = "75%";
aValueCell.innerHTML = attributes[i].value;
aRow.appendChild(aValueCell);
return aTable;
}
aTable.appendChild(aRow);
function BuildArchiveTable(attributes, sourceName) {
for (var i = 0; i < attributes.length; i++) {
if (attributes[i].key == "ZipContents") {
var archiveContent = JSON.parse(attributes[i].value);
// show archive button
document.getElementById('properties_toc_archive').style.display = '';
var aTable = document.createElement('table');
aTable.className = 'romtable';
aTable.setAttribute('cellspacing', 0);
aTable.style.width = '100%';
for (var r = 0; r < archiveContent.length; r++) {
var aBody = document.createElement('tbody');
aBody.className = 'romrow';
var aRow = document.createElement('tr');
var aNameCell = document.createElement('th');
aNameCell.className = 'romcell';
aNameCell.innerHTML = archiveContent[r].FilePath + '/' + archiveContent[r].FileName;
aRow.appendChild(aNameCell);
var aSizeCell = document.createElement('td');
aSizeCell.className = 'romcell';
aSizeCell.innerHTML = formatBytes(archiveContent[r].Size);
aRow.appendChild(aSizeCell);
aBody.appendChild(aRow);
var hRow = document.createElement('tr');
var aHashCell = document.createElement('td');
aHashCell.setAttribute('colspan', 2);
aHashCell.style.paddingLeft = '20px';
aHashCell.innerHTML = "MD5: " + archiveContent[r].MD5 + "<br />SHA1: " + archiveContent[r].SHA1;
hRow.appendChild(aHashCell);
aBody.appendChild(hRow);
aTable.appendChild(aBody);
}
}
}
return aTable;

View File

@@ -50,7 +50,7 @@
</th>
</tr>
<tr>
<th colspan="4"><h3>Games and ROM's</h3></td>
<th colspan="4"><h3>Games and ROMs</h3></td>
</tr>
<tr class="romrow">
<td class="romcell">Play games</td>
@@ -208,7 +208,7 @@
for (var classBoard in classBoards) {
for (var rating in classBoards[classBoard]) {
ratingsValues += "<img src='/api/v1.1/Ratings/Images/" + classBoard + "/" + classBoards[classBoard][rating] + "/image.svg' class='rating_image_mini' />";
ratingsValues += "<img src='/images/Ratings/" + classBoard + "/" + AgeRatingStrings[classBoards[classBoard][rating]] + ".svg' class='rating_image_mini' />";
}
}

View File

@@ -18,7 +18,7 @@
</tr>
<tr>
<td>
<select id="profile_pref-LibraryPagination" data-pref="LibraryPagination" onchange="SavePrefValue_Value(this);">
<select id="profile_pref-LibraryPagination" data-pref="LibraryPagination" data-minimum-results-for-search="Infinity">
<option value="paged">Paged</option>
<option value="infinite">Infinite scrolling</option>
</select>
@@ -34,17 +34,17 @@
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameTitle" data-pref="LibraryShowGameTitle" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameTitle"> Show title</label>
<input type="checkbox" id="profile_pref_LibraryShowGameTitle" data-pref="LibraryShowGameTitle"><label for="profile_pref_LibraryShowGameTitle"> Show title</label>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameRating" data-pref="LibraryShowGameRating" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameRating"> Show rating</label>
<input type="checkbox" id="profile_pref_LibraryShowGameRating" data-pref="LibraryShowGameRating"><label for="profile_pref_LibraryShowGameRating"> Show rating</label>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameClassification" data-pref="LibraryShowGameClassification" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameClassification"> Show age classification badges</label>
<input type="checkbox" id="profile_pref_LibraryShowGameClassification" data-pref="LibraryShowGameClassification"><label for="profile_pref_LibraryShowGameClassification"> Show age classification badges</label>
</td>
</tr>
<tr>
@@ -55,7 +55,7 @@
</tr>
<tr>
<td>
<select id="profile_pref_LibraryPrimaryClassificationBadge" data-primary="primary" onchange="SavePrefValue_ClassBadge(this);">
<select id="profile_pref_LibraryPrimaryClassificationBadge" data-primary="primary" data-minimum-results-for-search="Infinity" onchange="SavePrefValue_ClassBadge(this);">
</select>
</td>
</tr>
@@ -64,13 +64,18 @@
</tr>
<tr>
<td>
<select id="profile_pref_LibraryFallbackClassificationBadge" onchange="SavePrefValue_ClassBadge(this);">
<select id="profile_pref_LibraryFallbackClassificationBadge" onchange="SavePrefValue_ClassBadge(this);" data-minimum-results-for-search="Infinity">
</select>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="text-align: right;">
<button id="profile_pref_ok" value="OK" onclick="SavePrefs();">OK</button>
</td>
</tr>
</table>
</div>
<div id="properties_bodypanel_account" name="properties_profile_tab" style="display: none;">
@@ -183,31 +188,53 @@
}
}
function SavePrefs() {
var model = [];
model.push(SavePrefValue_Checkbox(document.getElementById('profile_pref_LibraryShowGameTitle')));
model.push(SavePrefValue_Checkbox(document.getElementById('profile_pref_LibraryShowGameRating')));
model.push(SavePrefValue_Checkbox(document.getElementById('profile_pref_LibraryShowGameClassification')));
model.push(SavePrefValue_Value(document.getElementById('profile_pref-LibraryPagination')));
model.push(
{
"setting": "LibraryGameClassificationDisplayOrder",
"value": JSON.stringify(SavePrefValue_ClassBadge(document.getElementById('profile_pref_LibraryPrimaryClassificationBadge')))
}
);
SetPreference_Batch(model);
if (getQueryString('page', 'string') == 'home' || getQueryString('page', 'string') == undefined) {
executeFilter1_1(1);
}
closeDialog();
}
function SavePrefValue_Checkbox(e) {
var ValueName = e.getAttribute("data-pref");
SetPreference(ValueName, e.checked);
updateDisplay(ValueName, e.checked);
executeFilter1_1(1);
return { "setting": ValueName, "value": e.checked.toString() };
}
function SavePrefValue_Value(e) {
var ValueName = e.getAttribute("data-pref");
SetPreference(ValueName, e.value);
executeFilter1_1(1);
return { "setting": ValueName, "value": e.value };
}
function updateDisplay(ValueName, ValueSetting) {
switch(ValueName) {
case "LibraryShowGameClassification":
var badgeSelector = document.getElementById("profile_pref_LibraryClassificationBadgeSelect");
if (ValueSetting == true || ValueSetting == "true") {
badgeSelector.style.display = '';
} else {
badgeSelector.style.display = 'none';
}
// var badgeSelector = document.getElementById("profile_pref_LibraryClassificationBadgeSelect");
// if (ValueSetting == true || ValueSetting == "true") {
// badgeSelector.style.display = '';
// } else {
// badgeSelector.style.display = 'none';
// }
break;
}
}
@@ -232,6 +259,7 @@
}
}
// save values
var model = [];
if (secondary.value == '-') {
@@ -239,10 +267,8 @@
} else {
model = [ primary.value, secondary.value ];
}
SetPreference('LibraryGameClassificationDisplayOrder', JSON.stringify(model));
executeFilter1_1(1);
return model;
}
function checkPasswordsMatch() {
@@ -320,4 +346,8 @@
ProfileSelectTab('general');
GetPrefInitialValues();
$('#profile_pref-LibraryPagination').select2();
$('#profile_pref_LibraryPrimaryClassificationBadge').select2();
$('#profile_pref_LibraryFallbackClassificationBadge').select2();
</script>

View File

@@ -2,7 +2,7 @@
<div id="bgImage_Opacity"></div>
</div>
<div id="emulator"></div>
<div id="emulator" class="emulator_fullscreen"></div>
<script type="text/javascript">
var gameId = getQueryString('gameid', 'int');
@@ -27,12 +27,12 @@
} else {
if (result.cover) {
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/cover/image/original/' + result.cover.imageId + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
}
}
if (result.cover) {
emuBackground = '/api/v1.1/Games/' + gameId + '/cover/image';
emuBackground = '/api/v1.1/Games/' + gameId + '/cover/image/original/' + result.cover.imageId + '.jpg';
}
emuGameTitle = gameData.name;
@@ -59,7 +59,7 @@
artworksPosition = 0;
}
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image/original/' + artworks[artworksPosition] + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
}
}
</script>

View File

@@ -52,7 +52,7 @@
<div id="loginwindow_header_label" style="display: block; text-align: center;">Gaseous Games</div>
<button type="button" value="Get Started" onclick="document.getElementById('first_welcome').style.display = 'none'; document.getElementById('first_newadmin').style.display = '';" style="margin-top: 50px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Get Started</button>
<button type="button" value="Get Started" onclick="document.getElementById('first_welcome').style.display = 'none'; document.getElementById('first_newadmin').style.display = '';" class="bigbutton">Get Started</button>
</div>
</div>
<div class="loginwindow" id="first_newadmin" style="display: none;">
@@ -85,7 +85,7 @@
</tr>
<tr>
<td colspan="2" style="padding-top: 20px;">
<button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Create Account</button>
<button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" class="bigbutton">Create Account</button>
</td>
</tr>
</table>

Some files were not shown because too many files have changed in this diff Show More