Compare commits

...

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

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

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

* Added code to handle IGDB's rate limiter

* Revised IGDB rate limit avoidance and recovery times
2023-12-12 12:00:24 +11:00
130 changed files with 5172 additions and 2709 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

@@ -10,7 +10,9 @@ using gaseous_server.Controllers;
using gaseous_server.Models;
using IGDB.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using SharpCompress.Common;
namespace gaseous_server.Classes
{
@@ -65,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);
@@ -81,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);
@@ -96,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);
@@ -114,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);
@@ -219,8 +223,11 @@ namespace gaseous_server.Classes
}
} else {
// get all platforms to pull from
Dictionary<string, object> FilterDict = Filters.Filter(AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, true);
platforms.AddRange((List<Filters.FilterPlatform>)FilterDict["platforms"]);
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));
}
}
// build collection
@@ -300,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) {
@@ -385,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
@@ -495,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);
}
}
}
@@ -504,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))
@@ -563,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);
@@ -591,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]
@@ -600,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;
}
@@ -628,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);
@@ -661,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; }
@@ -745,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;
}
}
@@ -788,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; }
@@ -797,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;
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,38 @@ 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>
/// Provides a way to set contextual data that flows with the call and
/// async context of a test or invocation.
/// </summary>
public static class CallContext
{
static ConcurrentDictionary<string, AsyncLocal<object>> state = new ConcurrentDictionary<string, AsyncLocal<object>>();
/// <summary>
/// Stores a given object and associates it with the specified name.
/// </summary>
/// <param name="name">The name with which to associate the new item in the call context.</param>
/// <param name="data">The object to store in the call context.</param>
public static void SetData(string name, object data) =>
state.GetOrAdd(name, _ => new AsyncLocal<object>()).Value = data;
/// <summary>
/// Retrieves an object with the specified name from the <see cref="CallContext"/>.
/// </summary>
/// <param name="name">The name of the item in the call context.</param>
/// <returns>The object in the call context associated with the specified name, or <see langword="null"/> if not found.</returns>
public static object GetData(string name) =>
state.TryGetValue(name, out AsyncLocal<object> data) ? data.Value : null;
}
}

View File

@@ -2,6 +2,7 @@
using System.Data;
using Newtonsoft.Json;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
namespace gaseous_server.Classes
{
@@ -57,6 +58,14 @@ namespace gaseous_server.Classes
}
}
public static ConfigFile.MetadataAPI MetadataConfiguration
{
get
{
return _config.MetadataConfiguration;
}
}
public static ConfigFile.IGDB IGDB
{
get
@@ -247,6 +256,8 @@ namespace gaseous_server.Classes
[JsonIgnore]
public Library LibraryConfiguration = new Library();
public MetadataAPI MetadataConfiguration = new MetadataAPI();
public IGDB IGDBConfiguration = new IGDB();
public Logging LoggingConfiguration = new Logging();
@@ -460,6 +471,70 @@ namespace gaseous_server.Classes
}
}
public class MetadataAPI
{
private static HasheousClient.Models.MetadataModel.MetadataSources _MetadataSource
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
{
return (HasheousClient.Models.MetadataModel.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
}
else
{
return HasheousClient.Models.MetadataModel.MetadataSources.IGDB;
}
}
}
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
{
private static string _DefaultIGDBClientId

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,101 +7,98 @@ 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<FilterPlatform> platforms = new List<FilterPlatform>();
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 DISTINCT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId, view_Games.AgeGroupId FROM Games_Roms LEFT JOIN view_Games ON view_Games.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id AND (" + ageRestriction_Platform + ")) AS GameCount FROM Platform LEFT JOIN Relation_Game_Platforms ON Relation_Game_Platforms.PlatformsId = Platform.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_Platforms.GameId HAVING GameCount > 0 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);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"]));
platformItem.GameCount = (int)(long)dr["GameCount"];
FilterItem platformItem = new FilterItem(dr);
platforms.Add(platformItem);
}
FilterSet.Add("platforms", platforms);
// genres
List<FilterGenre> genres = new List<FilterGenre>();
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic);
List<FilterItem> genres = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterGenre genreItem = new FilterGenre(Classes.Metadata.Genres.GetGenres((long)dr["id"]));
genreItem.GameCount = (int)(long)dr["GameCount"];
FilterItem genreItem = new FilterItem(dr);
genres.Add(genreItem);
}
FilterSet.Add("genres", genres);
// game modes
List<FilterGameMode> gameModes = new List<FilterGameMode>();
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic);
List<FilterItem> gameModes = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterGameMode gameModeItem = new FilterGameMode(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"]));
gameModeItem.GameCount = (int)(long)dr["GameCount"];
FilterItem gameModeItem = new FilterItem(dr);
gameModes.Add(gameModeItem);
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<FilterPlayerPerspective> playerPerspectives = new List<FilterPlayerPerspective>();
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic);
List<FilterItem> playerPerspectives = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlayerPerspective playerPerspectiveItem = new FilterPlayerPerspective(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"]));
playerPerspectiveItem.GameCount = (int)(long)dr["GameCount"];
FilterItem playerPerspectiveItem = new FilterItem(dr);
playerPerspectives.Add(playerPerspectiveItem);
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<FilterTheme> themes = new List<FilterTheme>();
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic);
List<FilterItem> themes = new List<FilterItem>();
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Platform);
foreach (DataRow dr in dbResponse.Rows)
{
FilterTheme themeItem = new FilterTheme(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"]));
themeItem.GameCount = (int)(long)dr["GameCount"];
FilterItem themeItem = new FilterItem(dr);
themes.Add(themeItem);
}
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);
@@ -111,113 +108,34 @@ 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 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);
return dbResponse;
}
public class FilterPlatform : IGDB.Models.Platform
public class FilterItem
{
public FilterPlatform(Platform obj)
public FilterItem()
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterGenre : IGDB.Models.Genre
{
public FilterGenre(Genre obj)
public FilterItem(DataRow dr)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
this.Id = (long)dr["Id"];
this.Name = (string)dr["Name"];
this.GameCount = (int)(long)dr["GameCount"];
}
public int GameCount { get; set; }
}
public class FilterGameMode : IGDB.Models.GameMode
{
public FilterGameMode(GameMode obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterPlayerPerspective : IGDB.Models.PlayerPerspective
{
public FilterPlayerPerspective(PlayerPerspective obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterTheme : IGDB.Models.Theme
{
public FilterTheme(Theme obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
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 string Name { get; set; }
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

@@ -1,48 +1,52 @@
using System;
using System.Data;
using System.IO.Compression;
using System.Security.Authentication;
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
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
foreach (string importContent in importContents_Files) {
ImportGame.ImportGameFile(importContent, null);
}
int importCount = 1;
foreach (string importContent in importContents) {
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
// import sub directories
foreach (string importDir in importContents_Directories) {
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
}
ImportGameFile(importContent, null);
importCount += 1;
}
ClearStatus();
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
{
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 = "";
@@ -94,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;
@@ -113,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);
@@ -144,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)
@@ -415,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);
@@ -558,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");
@@ -592,217 +471,333 @@ 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 static 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);
foreach (string LibraryFile in LibraryFiles)
{
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);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Library Scan", "An error occurred while matching orphaned file: " + LibraryFile + ". Skipping.", ex);
}
}
}
StatusCount += 1;
}
ClearStatus();
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
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)
// 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++)
{
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))
{
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))
if (library.IsDefaultLibrary == true)
{
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 static void Rematcher(bool ForceExecute = false)
public void Rematcher(bool ForceExecute = false)
{
// rescan all titles with an unknown platform or title and see if we can get a match
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);
foreach (DataRow row in data.Rows)
{
// get library
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
// get rom info
long romId = (long)row["Id"];
string romPath = (string)row["Path"];
Common.hashObject hash = new Common.hashObject
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch on library " + library.Name);
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);
// 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();
}
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed");
}
}
}

View File

@@ -75,15 +75,68 @@ namespace gaseous_server.Classes
LogToDisk(logItem, TraceOutput, null);
}
string correlationId;
try
{
if (CallContext.GetData("CorrelationId").ToString() == null)
{
correlationId = "";
}
else
{
correlationId = CallContext.GetData("CorrelationId").ToString();
}
}
catch
{
correlationId = "";
}
string callingProcess;
try
{
if (CallContext.GetData("CallingProcess").ToString() == null)
{
callingProcess = "";
}
else
{
callingProcess = CallContext.GetData("CallingProcess").ToString();
}
}
catch
{
callingProcess = "";
}
string callingUser;
try
{
if (CallContext.GetData("CallingUser").ToString() == null)
{
callingUser = "";
}
else
{
callingUser = CallContext.GetData("CallingUser").ToString();
}
}
catch
{
callingUser = "";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception) VALUES (@EventTime, @EventType, @Process, @Message, @Exception);";
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);
dbDict.Add("Message", logItem.Message);
dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString());
dbDict.Add("correlationid", correlationId);
dbDict.Add("callingprocess", callingProcess);
dbDict.Add("callinguser", callingUser);
try
{
@@ -184,6 +237,33 @@ namespace gaseous_server.Classes
}
}
if (model.CorrelationId != null)
{
if (model.CorrelationId.Length > 0)
{
dbDict.Add("correlationId", model.CorrelationId);
whereClauses.Add("CorrelationId = @correlationId");
}
}
if (model.CallingProcess != null)
{
if (model.CallingProcess.Length > 0)
{
dbDict.Add("callingProcess", model.CallingProcess);
whereClauses.Add("CallingProcess = @callingProcess");
}
}
if (model.CallingUser != null)
{
if (model.CallingUser.Length > 0)
{
dbDict.Add("callingUser", model.CallingUser);
whereClauses.Add("CallingUser = @callingUser");
}
}
// compile WHERE clause
string whereClause = "";
if (whereClauses.Count > 0)
@@ -198,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
{
@@ -206,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);
@@ -220,7 +302,10 @@ namespace gaseous_server.Classes
EventType = (LogType)row["EventType"],
Process = (string)row["Process"],
Message = (string)row["Message"],
ExceptionValue = (string)row["Exception"]
ExceptionValue = (string)row["Exception"],
CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""),
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], ""),
CallingUser = (string)Common.ReturnValueIfNull(row["Email"], "")
};
logs.Add(log);
@@ -243,6 +328,9 @@ namespace gaseous_server.Classes
public DateTime EventTime { get; set; }
public LogType? EventType { get; set; }
public string Process { get; set; } = "";
public string CorrelationId { get; set; } = "";
public string? CallingProcess { get; set; } = "";
public string? CallingUser { get; set; } = "";
private string _Message = "";
public string Message
{
@@ -267,6 +355,9 @@ namespace gaseous_server.Classes
public DateTime? StartDateTime { get; set; }
public DateTime? EndDateTime { get; set; }
public string? SearchText { get; set; }
public string? CorrelationId { get; set; }
public string? CallingProcess { get; set; }
public string? CallingUser { get; set; }
}
}
}

View File

@@ -5,12 +5,38 @@ using Microsoft.VisualStudio.Web.CodeGeneration;
namespace gaseous_server.Classes
{
public class Maintenance
public class Maintenance : QueueItemStatus
{
const int MaxFileAge = 30;
public static void RunMaintenance()
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,12 +71,14 @@ 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;
foreach (DataRow row in tables.Rows)
{
SetStatus(StatusCounter, tables.Rows.Count, "Optimising table " + row[0].ToString());
sql = "OPTIMIZE TABLE " + row[0].ToString();
DataTable response = db.ExecuteCMD(sql);
foreach (DataRow responseRow in response.Rows)
@@ -60,9 +88,12 @@ namespace gaseous_server.Classes
{
retVal += responseRow.ItemArray[i] + "; ";
}
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimise table " + row[0].ToString() + ": " + retVal);
Logging.Log(Logging.LogType.Information, "Maintenance", "(" + StatusCounter + "/" + tables.Rows.Count + "): Optimise table " + row[0].ToString() + ": " + retVal);
}
StatusCounter += 1;
}
ClearStatus();
}
}
}

View File

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

View File

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

View File

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

View File

@@ -13,13 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Artwork? GetArtwork(long? Id, string LogoPath)
public static Artwork? GetArtwork(long? Id, string ImagePath, bool GetImages)
{
if ((Id == 0) || (Id == null))
{
@@ -27,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();
@@ -67,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)
{
@@ -94,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;
@@ -110,64 +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
var results = await igdb.QueryAsync<Artwork>(IGDBClient.Endpoints.Artworks, query: fieldList + " " + WhereClause + ";");
Communications comms = new Communications();
var results = await comms.APIComm<Artwork>(IGDBClient.Endpoints.Artworks, fieldList, WhereClause);
var result = results.First();
//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

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

View File

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

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

View File

@@ -13,13 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath)
public static CompanyLogo? GetCompanyLogo(long? Id, string ImagePath)
{
if ((Id == 0) || (Id == null))
{
@@ -27,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();
@@ -70,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);
@@ -80,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;
}
@@ -97,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;
@@ -115,63 +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
var results = await igdb.QueryAsync<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, query: fieldList + " " + WhereClause + ";");
if (results.Length > 0)
{
var result = results.First();
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause);
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,13 +15,7 @@ namespace gaseous_server.Classes.Metadata
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Cover? GetCover(long? Id, string LogoPath)
public static Cover? GetCover(long? Id, string ImagePath, bool GetImages)
{
if ((Id == 0) || (Id == null))
{
@@ -27,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();
@@ -67,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;
}
@@ -94,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;
@@ -110,63 +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
var results = await igdb.QueryAsync<Cover>(IGDBClient.Endpoints.Covers, query: fieldList + " " + WhereClause + ";");
Communications comms = new Communications();
var results = await comms.APIComm<Cover>(IGDBClient.Endpoints.Covers, fieldList, WhereClause);
var result = results.First();
//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

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

View File

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

View File

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

View File

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

View File

@@ -20,12 +20,6 @@ namespace gaseous_server.Classes.Metadata
{}
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh)
{
if (Id == 0)
@@ -104,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)
{
@@ -120,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)
@@ -181,6 +184,7 @@ namespace gaseous_server.Classes.Metadata
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
AgeGroups.GetAgeGroup(Game);
if (Game.ReleaseDates != null)
{
@@ -205,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);
}
}
}
@@ -272,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);
}
}
}
@@ -295,46 +313,212 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Game> GetObjectFromServer(string WhereClause)
{
// get Game metadata
var results = await igdb.QueryAsync<Game>(IGDBClient.Endpoints.Games, query: fieldList + " " + WhereClause + ";");
Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, fieldList, WhereClause);
var result = results.First();
// add artificial unknown platform mapping
List<long> platformIds = new List<long>();
platformIds.Add(0);
if (result.Platforms != null)
{
if (result.Platforms.Ids != null)
{
platformIds.AddRange(result.Platforms.Ids.ToList());
}
}
result.Platforms = new IdentitiesOrValues<Platform>(
ids: platformIds.ToArray<long>()
);
// get cover art from parent if this has no cover
if (result.Cover == null)
{
if (result.ParentGame != null)
{
if (result.ParentGame.Id != null)
{
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no cover art, fetching cover art from parent game");
Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false);
result.Cover = parentGame.Cover;
}
}
}
// get missing metadata from parent if this is a port
if (result.Category == Category.Port)
{
if (result.Summary == null)
{
if (result.ParentGame != null)
{
if (result.ParentGame.Id != null)
{
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no summary, fetching summary from parent game");
Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false);
result.Summary = parentGame.Summary;
}
}
}
}
return result;
}
public static void AssignAllGamesToPlatformIdZero()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Game;";
DataTable gamesTable = db.ExecuteCMD(sql);
foreach (DataRow gameRow in gamesTable.Rows)
{
sql = "DELETE FROM Relation_Game_Platforms WHERE PlatformsId = 0 AND GameId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (@Id, 0);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", (long)gameRow["Id"]);
db.ExecuteCMD(sql, dbDict);
}
}
private static bool AllowNoPlatformSearch = false;
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType)
{
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 = "";
searchBody += "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
var results = await igdb.QueryAsync<Game>(IGDBClient.Endpoints.Games, query: 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,
@@ -353,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>();
@@ -373,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,12 +13,6 @@ namespace gaseous_server.Classes.Metadata
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Genre? GetGenres(long? Id)
{
if ((Id == 0) || (Id == null))
@@ -103,7 +97,8 @@ namespace gaseous_server.Classes.Metadata
private static async Task<Genre> GetObjectFromServer(string WhereClause)
{
// get Genres metadata
var results = await igdb.QueryAsync<Genre>(IGDBClient.Endpoints.Genres, query: fieldList + " " + WhereClause + ";");
Communications comms = new Communications();
var results = await comms.APIComm<Genre>(IGDBClient.Endpoints.Genres, fieldList, WhereClause);
var result = results.First();
return result;

View File

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

View File

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

View File

@@ -13,13 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath)
public static PlatformLogo? GetPlatformLogo(long? Id, string ImagePath)
{
if ((Id == 0) || (Id == null))
{
@@ -27,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();
@@ -70,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);
@@ -80,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;
}
@@ -99,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);
}
}
@@ -115,63 +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
var results = await igdb.QueryAsync<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, query: fieldList + " " + WhereClause + ";");
if (results.Length > 0)
{
var result = results.First();
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause);
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

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

View File

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

View File

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

View File

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

View File

@@ -13,13 +13,7 @@ namespace gaseous_server.Classes.Metadata
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static Screenshot? GetScreenshot(long? Id, string LogoPath)
public static Screenshot? GetScreenshot(long? Id, string ImagePath, bool GetImages)
{
if ((Id == 0) || (Id == null))
{
@@ -27,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();
@@ -67,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;
}
@@ -95,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;
@@ -111,64 +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
var results = await igdb.QueryAsync<Screenshot>(IGDBClient.Endpoints.Screenshots, query: fieldList + " " + WhereClause + ";");
Communications comms = new Communications();
var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause);
var result = results.First();
//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

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

View File

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

View File

@@ -4,30 +4,39 @@ using gaseous_server.Models;
namespace gaseous_server.Classes
{
public class MetadataManagement
public class MetadataManagement : QueueItemStatus
{
public static void RefreshMetadata(bool forceRefresh = false)
public void RefreshMetadata(bool forceRefresh = false)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
DataTable dt = new DataTable();
// disabling forceRefresh
forceRefresh = false;
// update platforms
sql = "SELECT Id, `Name` FROM Platform;";
dt = db.ExecuteCMD(sql);
int StatusCounter = 1;
foreach (DataRow dr in dt.Rows)
{
SetStatus(StatusCounter, dt.Rows.Count, "Refreshing metadata for platform " + dr["name"]);
try
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")");
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Platforms.GetPlatform((long)dr["id"], true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex);
}
StatusCounter += 1;
}
ClearStatus();
// update games
if (forceRefresh == true)
@@ -42,18 +51,24 @@ namespace gaseous_server.Classes
}
dt = db.ExecuteCMD(sql);
StatusCounter = 1;
foreach (DataRow dr in dt.Rows)
{
SetStatus(StatusCounter, dt.Rows.Count, "Refreshing metadata for game " + dr["name"]);
try
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh);
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, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex);
}
StatusCounter += 1;
}
ClearStatus();
}
}
}

View File

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

View File

@@ -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

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

View File

@@ -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

@@ -18,7 +18,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class FilterController : ControllerBase
public class FilterController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;

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;
@@ -22,7 +23,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class GamesController : ControllerBase
public class GamesController : Controller
{
[MapToApiVersion("1.0")]
[HttpGet]
@@ -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

@@ -22,7 +22,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class RomsController : ControllerBase
public class RomsController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
@@ -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,12 +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;
@@ -19,12 +23,6 @@ namespace gaseous_server.Controllers
[Authorize]
public class SearchController : Controller
{
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
@@ -39,37 +37,91 @@ namespace gaseous_server.Controllers
private static async Task<List<Platform>> _SearchForPlatform(string SearchString)
{
string searchBody = "";
searchBody += "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
searchBody += "where name ~ *\"" + SearchString + "\"*;";
// get Platform metadata
var results = await igdb.QueryAsync<Platform>(IGDBClient.Endpoints.Platforms, query: 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 = "";
searchBody += "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
var results = await igdb.QueryAsync<Game>(IGDBClient.Endpoints.Games, query: 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

@@ -18,7 +18,7 @@ namespace gaseous_server.Controllers
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SignaturesController : ControllerBase
public class SignaturesController : Controller
{
/// <summary>
/// Get the current signature counts from the database
@@ -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 = "Games_Roms.PlatformId 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 Games_Roms ON view_Games.Id = Games_Roms.GameId 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>();
@@ -450,26 +457,15 @@ namespace gaseous_server.Controllers.v1_1
// compile data for return
int pageOffset = pageSize * (pageNumber - 1);
for (int i = 0; i < dbResponse.Rows.Count; i++)
for (int i = pageOffset; i < dbResponse.Rows.Count; i++)
{
DataRow dr = dbResponse.Rows[i];
bool includeGame = false;
if (pageSize == 0)
if (i >= (pageOffset + pageSize))
{
// page size is full size include all
includeGame = true;
}
else if (i >= pageOffset && i < (pageOffset + pageSize))
{
includeGame = true;
break;
}
if (includeGame == true)
{
RetVal.Add(Classes.Metadata.Games.GetGame(dr));
}
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);
}
}
}
@@ -157,18 +165,18 @@ namespace gaseous_server.Models
if (Update == false)
{
// insert
sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core, AvailableWebEmulators) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core, @AvailableWebEmulators)";
sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core, AvailableWebEmulators) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core, @AvailableWebEmulators);";
}
else
{
// update
if (AllowAvailableEmulatorOverwrite == true)
{
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core, AvailableWebEmulators=@AvailableWebEmulators WHERE Id = @Id";
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core, AvailableWebEmulators=@AvailableWebEmulators WHERE Id = @Id; ";
}
else
{
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core WHERE Id = @Id";
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core WHERE Id = @Id;";
}
}
dbDict.Add("Id", item.IGDBId);
@@ -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

@@ -1,5 +1,9 @@
using System;
using System.ComponentModel.Design.Serialization;
using System.Data;
using gaseous_server.Classes;
using NuGet.Common;
using NuGet.Packaging;
namespace gaseous_server
{
@@ -13,7 +17,7 @@ namespace gaseous_server
{
_ItemType = ItemType;
_ItemState = QueueItemState.NeverStarted;
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))).AddMinutes(-5);
_Interval = ExecutionInterval;
_AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped;
@@ -23,7 +27,7 @@ namespace gaseous_server
{
_ItemType = ItemType;
_ItemState = QueueItemState.NeverStarted;
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))).AddMinutes(-5);
_Interval = ExecutionInterval;
_AllowManualStart = AllowManualStart;
_RemoveWhenStopped = RemoveWhenStopped;
@@ -33,6 +37,7 @@ namespace gaseous_server
private QueueItemType _ItemType = QueueItemType.NotConfigured;
private QueueItemState _ItemState = QueueItemState.NeverStarted;
private DateTime _LastRunTime = DateTime.UtcNow;
private double _LastRunDuration = 0;
private DateTime _LastFinishTime
{
get
@@ -55,13 +60,16 @@ namespace gaseous_server
private bool _AllowManualStart = true;
private bool _RemoveWhenStopped = false;
private bool _IsBlocked = false;
private string _CorrelationId = "";
private List<QueueItemType> _Blocks = new List<QueueItemType>();
public QueueItemType ItemType => _ItemType;
public QueueItemState ItemState => _ItemState;
public DateTime LastRunTime => _LastRunTime;
public DateTime LastFinishTime => _LastFinishTime;
public DateTime NextRunTime {
public double LastRunDuration => _LastRunDuration;
public DateTime NextRunTime
{
get
{
return LastRunTime.AddMinutes(Interval);
@@ -85,6 +93,9 @@ namespace gaseous_server
public bool RemoveWhenStopped => _RemoveWhenStopped;
public bool IsBlocked => _IsBlocked;
public object? Options { get; set; } = null;
public string CurrentState { get; set; } = "";
public string CurrentStateProgress { get; set; } = "";
public string CorrelationId => _CorrelationId;
public List<QueueItemType> Blocks => _Blocks;
public void Execute()
@@ -99,7 +110,15 @@ namespace gaseous_server
_LastResult = "";
_LastError = null;
Logging.Log(Logging.LogType.Debug, "Timered Event", "Executing " + _ItemType);
// set the correlation id
Guid correlationId = Guid.NewGuid();
_CorrelationId = correlationId.ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", _ItemType.ToString());
CallContext.SetData("CallingUser", "System");
// log the start
Logging.Log(Logging.LogType.Debug, "Timered Event", "Executing " + _ItemType + " with correlation id " + _CorrelationId);
try
{
@@ -107,8 +126,11 @@ namespace gaseous_server
{
case QueueItemType.SignatureIngestor:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Signature Ingestor");
SignatureIngestors.XML.XMLIngestor tIngest = new SignatureIngestors.XML.XMLIngestor();
SignatureIngestors.XML.XMLIngestor tIngest = new SignatureIngestors.XML.XMLIngestor
{
CallingQueueItem = this
};
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC);
@@ -124,8 +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;
@@ -134,7 +161,11 @@ namespace gaseous_server
case QueueItemType.MetadataRefresh:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher");
Classes.MetadataManagement.RefreshMetadata(_ForceExecute);
Classes.MetadataManagement metadataManagement = new MetadataManagement
{
CallingQueueItem = this
};
metadataManagement.RefreshMetadata(_ForceExecute);
_SaveLastRunTime = true;
@@ -142,23 +173,46 @@ 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.LibraryScan();
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanners");
Classes.ImportGame libScan = new ImportGame
{
CallingQueueItem = this
};
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.Rematcher(_ForceExecute);
Classes.ImportGame importRematch = new ImportGame
{
CallingQueueItem = this
};
importRematch.Rematcher(_ForceExecute);
_SaveLastRunTime = true;
@@ -181,7 +235,36 @@ namespace gaseous_server
case QueueItemType.Maintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Maintenance");
Classes.Maintenance.RunMaintenance();
Classes.Maintenance maintenance = new Maintenance{
CallingQueueItem = this
};
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;
}
@@ -196,8 +279,9 @@ namespace gaseous_server
_ForceExecute = false;
_ItemState = QueueItemState.Stopped;
_LastFinishTime = DateTime.UtcNow;
_LastRunDuration = Math.Round((DateTime.UtcNow - _LastRunTime).TotalSeconds, 2);
Logging.Log(Logging.LogType.Information, "Timered Event", "Total " + _ItemType + " run time = " + (DateTime.UtcNow - _LastRunTime).TotalSeconds);
Logging.Log(Logging.LogType.Information, "Timered Event", "Total " + _ItemType + " run time = " + _LastRunDuration);
}
}
}
@@ -211,6 +295,66 @@ namespace gaseous_server
{
_IsBlocked = BlockState;
}
public HasErrorsItem HasErrors
{
get
{
return new HasErrorsItem(_CorrelationId);
}
}
public class HasErrorsItem
{
public HasErrorsItem(string? CorrelationId)
{
if (CorrelationId != null)
{
if (CorrelationId.Length > 0)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT EventType, COUNT(EventType) AS EventTypes FROM gaseous.ServerLogs WHERE CorrelationId = @correlationid GROUP BY EventType ORDER BY EventType DESC LIMIT 1;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("correlationid", CorrelationId);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count == 0)
{
ErrorType = null;
ErrorCount = 0;
}
else
{
Logging.LogType errorType = (Logging.LogType)data.Rows[0]["EventType"];
if (errorType != Logging.LogType.Information)
{
ErrorType = errorType;
ErrorCount = (int)(long)data.Rows[0]["EventTypes"];
}
else
{
ErrorType = null;
ErrorCount = 0;
}
}
}
else
{
ErrorType = null;
ErrorCount = 0;
}
}
else
{
ErrorType = null;
ErrorCount = 0;
}
}
public Logging.LogType? ErrorType { get; set; }
public int ErrorCount { get; set; }
}
}
public enum QueueItemType
@@ -250,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>
@@ -273,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

@@ -49,6 +49,12 @@ Config.InitSettings();
// write updated settings back to the config file
Config.UpdateConfig();
// set api metadata source from config
Communications.MetadataSource = Config.MetadataConfiguration.MetadataSource;
// set up hasheous client
HasheousClient.WebApp.HttpHelper.BaseUri = Config.MetadataConfiguration.HasheousHost;
// set initial values
Guid APIKey = Guid.NewGuid();
if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
@@ -103,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()
{
@@ -292,27 +303,6 @@ using (var scope = app.Services.CreateScope())
await roleManager.CreateAsync(applicationRole, CancellationToken.None);
}
}
// // set up administrator account
// var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
// if (await userManager.FindByNameAsync("admin@localhost", CancellationToken.None) == null)
// {
// ApplicationUser adminUser = new ApplicationUser{
// Id = Guid.NewGuid().ToString(),
// Email = "admin@localhost",
// NormalizedEmail = "ADMIN@LOCALHOST",
// EmailConfirmed = true,
// UserName = "administrator",
// NormalizedUserName = "ADMINISTRATOR"
// };
// //set user password
// PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
// adminUser.PasswordHash = ph.HashPassword(adminUser, "letmein");
// await userManager.CreateAsync(adminUser, CancellationToken.None);
// await userManager.AddToRoleAsync(adminUser, "Admin", CancellationToken.None);
// }
}
app.UseAuthorization();
@@ -326,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
@@ -397,7 +409,9 @@ Config.LibraryConfiguration.InitLibrary();
// insert unknown platform and game if not present
gaseous_server.Classes.Metadata.Games.GetGame(0, false, false, false);
gaseous_server.Classes.Metadata.Games.AssignAllGamesToPlatformIdZero();
gaseous_server.Classes.Metadata.Platforms.GetPlatform(0);
gaseous_server.Classes.Metadata.Platforms.AssignAllPlatformsToGameIdZero();
// extract platform map if not present
PlatformMapping.ExtractPlatformMap();
@@ -462,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,26 @@
ALTER TABLE `ClassificationMap`
ADD INDEX `idx_RatingId` (`RatingId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_AgeRatings`
ADD INDEX `idx_SecondaryColumn` (`AgeRatingsId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_GameModes`
ADD INDEX `idx_SecondaryColumn` (`GameModesId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_Genres`
ADD INDEX `idx_SecondaryColumn` (`GenresId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_Platforms`
ADD INDEX `idx_SecondaryColumn` (`PlatformsId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_PlayerPerspectives`
ADD INDEX `idx_SecondaryColumn` (`PlayerPerspectivesId` ASC) VISIBLE;
ALTER TABLE `Relation_Game_Themes`
ADD INDEX `idx_SecondaryColumn` (`ThemesId` ASC) VISIBLE;
ALTER TABLE `ServerLogs`
ADD COLUMN `CorrelationId` VARCHAR(45) NULL AFTER `Exception`,
ADD COLUMN `CallingProcess` VARCHAR(255) NULL AFTER `CorrelationId`,
ADD INDEX `idx_CorrelationId` (`CorrelationId` ASC) VISIBLE,
ADD INDEX `idx_CallingProcess` (`CallingProcess` ASC) VISIBLE;

View File

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

View File

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

View File

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

View File

@@ -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" />
@@ -48,60 +52,14 @@
<None Remove="Support\Database\MySQL\gaseous-1005.sql" />
<None Remove="Support\Database\MySQL\gaseous-1006.sql" />
<None Remove="Support\Database\MySQL\gaseous-1007.sql" />
<None Remove="Support\Database\MySQL\gaseous-1008.sql" />
<None Remove="Support\Database\MySQL\gaseous-1009.sql" />
<None Remove="Support\Database\MySQL\gaseous-1010.sql" />
<None Remove="Support\Database\MySQL\gaseous-1011.sql" />
<None Remove="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\" />
@@ -110,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>
@@ -131,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" />
@@ -178,5 +88,12 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1006.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1008.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1009.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1010.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1011.sql" />
<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

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