Compare commits

...

16 Commits

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

* Update PlatformMap for Amiga CDTV and CD32, and ColecoVision

* Fixed default platform setting for library scan

* Refactor of rematcher

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

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

* More logging

* More null reference checks

* Overhaul of ROM and MediaGroup handling in web page

* Minor collections updates

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

* Added more logging

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

* Bug fixes and caching enhancements

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

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

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

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

* Fixed platform id bug with media group launching

* Updates to support Hasheous - testing only

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

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

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

* Moved AgeGroups to it's own class

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

* Improved first set up logging

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

1
.gitignore vendored
View File

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

View File

@@ -1,6 +1,6 @@
# Gaseous Server # 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 ## Warning

Binary file not shown.

Binary file not shown.

View File

@@ -5,13 +5,13 @@ namespace Authentication
public class SecurityProfileViewModel public class SecurityProfileViewModel
{ {
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{ 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 IncludeUnrated = true
}; };
public class AgeRestrictionItem 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; } public bool IncludeUnrated { get; set; }
} }
} }

View File

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

View File

@@ -1,9 +1,11 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class Common public static class Common
{ {
/// <summary> /// <summary>
/// Returns IfNullValue if the ObjectToCheck is null /// Returns IfNullValue if the ObjectToCheck is null
@@ -110,6 +112,14 @@ namespace gaseous_server.Classes
return Path.GetFullPath(new Uri(path).LocalPath) return Path.GetFullPath(new Uri(path).LocalPath)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); .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> /// <summary>

View File

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

View File

@@ -106,13 +106,15 @@ namespace gaseous_server.Classes
int SchemaVer = (int)SchemaVersion.Rows[0][0]; int SchemaVer = (int)SchemaVersion.Rows[0][0];
Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer); Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer);
if (SchemaVer < i) if (SchemaVer < i)
{
try
{ {
// run pre-upgrade code // run pre-upgrade code
DatabaseMigration.PreUpgradeScript(i, _ConnectorType); DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
// apply schema! // apply schema!
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i); Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
ExecuteCMD(dbScript, dbDict); ExecuteCMD(dbScript, dbDict, 180);
sql = "UPDATE schema_version SET schema_version=@schemaver"; sql = "UPDATE schema_version SET schema_version=@schemaver";
dbDict = new Dictionary<string, object>(); dbDict = new Dictionary<string, object>();
@@ -122,6 +124,12 @@ namespace gaseous_server.Classes
// run post-upgrade code // run post-upgrade code
DatabaseMigration.PostUpgradeScript(i, _ConnectorType); 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: case databaseType.MySql:
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString); MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
return (int)conn.ExecNonQuery(Command, Parameters, Timeout); int retVal = conn.ExecNonQuery(Command, Parameters, Timeout);
return retVal;
default: default:
return 0; return 0;
} }
@@ -321,7 +330,8 @@ namespace gaseous_server.Classes
DataTable RetTable = new DataTable(); DataTable RetTable = new DataTable();
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true); Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
MySqlConnection conn = new MySqlConnection(DBConn); using (MySqlConnection conn = new MySqlConnection(DBConn))
{
conn.Open(); conn.Open();
MySqlCommand cmd = new MySqlCommand MySqlCommand cmd = new MySqlCommand
@@ -353,6 +363,7 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true); Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close(); conn.Close();
}
return RetTable; return RetTable;
} }
@@ -362,7 +373,8 @@ namespace gaseous_server.Classes
int result = 0; int result = 0;
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true); Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
MySqlConnection conn = new MySqlConnection(DBConn); using (MySqlConnection conn = new MySqlConnection(DBConn))
{
conn.Open(); conn.Open();
MySqlCommand cmd = new MySqlCommand MySqlCommand cmd = new MySqlCommand
@@ -394,13 +406,15 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true); Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close(); conn.Close();
}
return result; return result;
} }
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout) public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
{ {
var conn = new MySqlConnection(DBConn); using (MySqlConnection conn = new MySqlConnection(DBConn))
{
conn.Open(); conn.Open();
var command = conn.CreateCommand(); var command = conn.CreateCommand();
MySqlTransaction transaction; MySqlTransaction transaction;
@@ -417,6 +431,7 @@ namespace gaseous_server.Classes
transaction.Commit(); transaction.Commit();
conn.Close(); conn.Close();
} }
}
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout) private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
{ {
@@ -441,7 +456,8 @@ namespace gaseous_server.Classes
public bool TestConnection() public bool TestConnection()
{ {
MySqlConnection conn = new MySqlConnection(DBConn); using (MySqlConnection conn = new MySqlConnection(DBConn))
{
try try
{ {
conn.Open(); conn.Open();
@@ -456,4 +472,5 @@ namespace gaseous_server.Classes
} }
} }
} }
}

View File

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

View File

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

View File

@@ -34,6 +34,12 @@ namespace gaseous_server
{} {}
} }
public class CannotDeleteLibraryWhileScanIsActive : Exception
{
public CannotDeleteLibraryWhileScanIsActive() : base("Unable to delete library while a library scan is active. Wait for all scans to complete and try again")
{}
}
// code // code
public static LibraryItem GetDefaultLibrary public static LibraryItem GetDefaultLibrary
{ {
@@ -110,21 +116,42 @@ namespace gaseous_server
int newLibraryId = (int)(long)data.Rows[0][0]; 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) 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); 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;"; string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", LibraryId); dbDict.Add("id", LibraryId);
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
Logging.Log(Logging.LogType.Information, "Library Management", "Deleted library " + library.Name + " at path " + library.Path);
} }
else else
{ {
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete the default library.");
throw new CannotDeleteDefaultLibrary(); throw new CannotDeleteDefaultLibrary();
} }
} }

View File

@@ -6,37 +6,38 @@ using System.Security.Policy;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models; using IGDB.Models;
using NuGet.Common; using NuGet.Common;
using NuGet.LibraryModel; using NuGet.LibraryModel;
using static gaseous_server.Classes.Metadata.Games; using static gaseous_server.Classes.Metadata.Games;
using static gaseous_server.Classes.FileSignature;
using HasheousClient.Models;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class ImportGames : QueueItemStatus public class ImportGame : QueueItemStatus
{ {
public ImportGames(string ImportPath) public void ProcessDirectory(string ImportPath)
{ {
if (Directory.Exists(ImportPath)) if (Directory.Exists(ImportPath))
{ {
string[] importContents_Files = Directory.GetFiles(ImportPath); string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
string[] importContents_Directories = Directory.GetDirectories(ImportPath);
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
// import files first // import files first
int importCount = 1; int importCount = 1;
foreach (string importContent in importContents_Files) { foreach (string importContent in importContents) {
SetStatus(importCount, importContents_Files.Length, "Importing file: " + importContent); SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
ImportGame.ImportGameFile(importContent, null); ImportGameFile(importContent, null);
importCount += 1; importCount += 1;
} }
ClearStatus(); ClearStatus();
// import sub directories DeleteOrphanedDirectories(ImportPath);
foreach (string importDir in importContents_Directories) {
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
}
} }
else else
{ {
@@ -45,12 +46,7 @@ namespace gaseous_server.Classes
} }
} }
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
}
public class ImportGame : QueueItemStatus
{
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
@@ -102,7 +98,8 @@ namespace gaseous_server.Classes
{ {
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing"); 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 // get discovered platform
IGDB.Models.Platform? determinedPlatform = null; IGDB.Models.Platform? determinedPlatform = null;
@@ -121,7 +118,7 @@ namespace gaseous_server.Classes
discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name; 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 // add to database
StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath); StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
@@ -152,155 +149,29 @@ namespace gaseous_server.Classes
} }
} }
public static Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath) public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
{ {
Models.Signatures_Games discoveredSignature = _GetFileSignature(hash, fi, GameFileImportPath); if (Signature.Flags != null)
if ((Path.GetExtension(GameFileImportPath) == ".zip") && (fi.Length < 1073741824))
{ {
// file is a zip and less than 1 GiB if (Signature.Flags.IGDBGameId != null && Signature.Flags.IGDBGameId != 0)
// extract the zip file and search the contents {
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName()); // game was determined elsewhere - probably a Hasheous server
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try try
{ {
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath); return Games.GetGame(Signature.Flags.IGDBGameId, false, false, FullDownload);
// loop through contents until we find the first signature match
foreach (string file in Directory.GetFiles(ExtractPath))
{
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;
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing zip file: " + GameFileImportPath, ex); Logging.Log(Logging.LogType.Warning, "Import Game", "Provided game id resulted in a failed game lookup", 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)
{
discoveredSignature = Sig;
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
}
}
}
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 // search discovered game - case insensitive exact match first
IGDB.Models.Game determinedGame = new IGDB.Models.Game(); IGDB.Models.Game determinedGame = new IGDB.Models.Game();
string GameName = Signature.Game.Name;
List<string> SearchCandidates = GetSearchCandidates(GameName); List<string> SearchCandidates = GetSearchCandidates(GameName);
foreach (string SearchCandidate in SearchCandidates) foreach (string SearchCandidate in SearchCandidates)
@@ -423,7 +294,7 @@ namespace gaseous_server.Classes
return SearchCandidates; 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); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -566,7 +437,7 @@ namespace gaseous_server.Classes
} }
} }
public static void OrganiseLibrary() public void OrganiseLibrary()
{ {
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation"); Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
@@ -600,19 +471,98 @@ namespace gaseous_server.Classes
foreach (var directory in Directory.GetDirectories(startLocation)) foreach (var directory in Directory.GetDirectories(startLocation))
{ {
DeleteOrphanedDirectories(directory); 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); Directory.Delete(directory, false);
} }
} }
} }
public void LibraryScan() public void LibraryScan(GameLibrary.LibraryItem? singleLibrary = null)
{ {
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries) int maxWorkers = Config.MetadataConfiguration.MaxLibraryScanWorkers;
List<GameLibrary.LibraryItem> libraries = new List<GameLibrary.LibraryItem>();
if (singleLibrary == null)
{ {
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting library scan. Library " + library.Name); libraries.AddRange(GameLibrary.GetLibraries);
}
else
{
libraries.Add(singleLibrary);
}
// 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();
ProcessQueue.QueueItems.Add(queue);
// 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); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -647,7 +597,7 @@ namespace gaseous_server.Classes
} }
} }
sql = "SELECT * FROM Games_Roms ORDER BY `name`"; sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict); dtRoms = db.ExecuteCMD(sql, dbDict);
// search for files in the library that aren't in the database // search for files in the library that aren't in the database
@@ -679,37 +629,41 @@ namespace gaseous_server.Classes
if (romFound == false) if (romFound == false)
{ {
// file is not in database - process it // 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); Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile); FileInfo fi = new FileInfo(LibraryFile);
Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile); FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, LibraryFile);
Logging.Log(Logging.LogType.Information, "Library Scan", " Orphaned file found in library: " + LibraryFile);
try
{
// get discovered platform // get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId); long PlatformId;
IGDB.Models.Platform determinedPlatform;
IGDB.Models.Game determinedGame = new Game(); if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
if (determinedPlatform == null)
{ {
if (library.DefaultPlatformId == 0) // no platform discovered in the signature
{ PlatformId = library.DefaultPlatformId;
determinedPlatform = new IGDB.Models.Platform();
determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
} }
else else
{ {
determinedPlatform = Platforms.GetPlatform(library.DefaultPlatformId); // use the platform discovered in the signature
determinedGame = SearchForGame(sig.Game.Name, library.DefaultPlatformId); PlatformId = sig.Flags.IGDBPlatformId;
}
}
else
{
determinedGame = SearchForGame(sig.Game.Name, (long)determinedPlatform.Id);
} }
determinedPlatform = Platforms.GetPlatform(PlatformId);
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile); 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; StatusCount += 1;
} }
@@ -720,14 +674,16 @@ namespace gaseous_server.Classes
// check all roms to see if their local file still exists // check all roms to see if their local file still exists
Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk"); Logging.Log(Logging.LogType.Information, "Library Scan", "Checking library files exist on disk");
StatusCount = 0;
if (dtRoms.Rows.Count > 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"]; long romId = (long)dtRoms.Rows[i]["Id"];
string romPath = (string)dtRoms.Rows[i]["Path"]; 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"]; 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); Logging.Log(Logging.LogType.Information, "Library Scan", "Processing ROM at path " + romPath);
if (File.Exists(romPath)) if (File.Exists(romPath))
@@ -736,8 +692,13 @@ namespace gaseous_server.Classes
{ {
if (romPath != ComputeROMPath(romId)) if (romPath != ComputeROMPath(romId))
{ {
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found, but needs to be moved");
MoveGameFile(romId); MoveGameFile(romId);
} }
else
{
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found");
}
} }
} }
else else
@@ -751,12 +712,13 @@ namespace gaseous_server.Classes
deleteDict.Add("libraryid", library.Id); deleteDict.Add("libraryid", library.Id);
db.ExecuteCMD(deleteSql, deleteDict); db.ExecuteCMD(deleteSql, deleteDict);
} }
StatusCount += 1;
} }
} }
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed"); Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
} }
}
public void Rematcher(bool ForceExecute = false) public void Rematcher(bool ForceExecute = false)
{ {
@@ -764,26 +726,29 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting"); Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch on library " + library.Name);
string sql = ""; string sql = "";
if (ForceExecute == false) 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;"; 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 else
{ {
sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);"; 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>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7)); dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
dbDict.Add("libraryid", library.Id);
DataTable data = db.ExecuteCMD(sql, dbDict); DataTable data = db.ExecuteCMD(sql, dbDict);
int StatusCount = -0; int StatusCount = -0;
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
SetStatus(StatusCount, data.Rows.Count, "Running rematcher"); SetStatus(StatusCount, data.Rows.Count, "Running rematcher");
// get library
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
// get rom info // get rom info
long romId = (long)row["Id"]; long romId = (long)row["Id"];
string romPath = (string)row["Path"]; string romPath = (string)row["Path"];
@@ -797,16 +762,26 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath); Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath);
// determine rom signature // determine rom signature
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath); FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, romPath);
// determine rom platform // get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId); long PlatformId;
if (determinedPlatform == null) IGDB.Models.Platform determinedPlatform;
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
{ {
determinedPlatform = new IGDB.Models.Platform(); // 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.Game.Name, sig.Flags.IGDBPlatformId); IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId); StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
@@ -825,4 +800,5 @@ namespace gaseous_server.Classes
} }
} }
} }
}

View File

@@ -109,10 +109,26 @@ namespace gaseous_server.Classes
callingProcess = ""; 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); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess);"; string sql = "INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); 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("EventTime", logItem.EventTime);
dbDict.Add("EventType", logItem.EventType); dbDict.Add("EventType", logItem.EventType);
dbDict.Add("Process", logItem.Process); dbDict.Add("Process", logItem.Process);
@@ -120,6 +136,7 @@ namespace gaseous_server.Classes
dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString()); dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString());
dbDict.Add("correlationid", correlationId); dbDict.Add("correlationid", correlationId);
dbDict.Add("callingprocess", callingProcess); dbDict.Add("callingprocess", callingProcess);
dbDict.Add("callinguser", callingUser);
try try
{ {
@@ -238,6 +255,15 @@ namespace gaseous_server.Classes
} }
} }
if (model.CallingUser != null)
{
if (model.CallingUser.Length > 0)
{
dbDict.Add("callingUser", model.CallingUser);
whereClauses.Add("CallingUser = @callingUser");
}
}
// compile WHERE clause // compile WHERE clause
string whereClause = ""; string whereClause = "";
if (whereClauses.Count > 0) if (whereClauses.Count > 0)
@@ -252,7 +278,8 @@ namespace gaseous_server.Classes
{ {
whereClause = "WHERE " + whereClause; 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 else
{ {
@@ -260,7 +287,8 @@ namespace gaseous_server.Classes
{ {
whereClause = "AND " + whereClause; 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); DataTable dataTable = db.ExecuteCMD(sql, dbDict);
@@ -276,7 +304,8 @@ namespace gaseous_server.Classes
Message = (string)row["Message"], Message = (string)row["Message"],
ExceptionValue = (string)row["Exception"], ExceptionValue = (string)row["Exception"],
CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""), CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""),
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], "") CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], ""),
CallingUser = (string)Common.ReturnValueIfNull(row["Email"], "")
}; };
logs.Add(log); logs.Add(log);
@@ -301,6 +330,7 @@ namespace gaseous_server.Classes
public string Process { get; set; } = ""; public string Process { get; set; } = "";
public string CorrelationId { get; set; } = ""; public string CorrelationId { get; set; } = "";
public string? CallingProcess { get; set; } = ""; public string? CallingProcess { get; set; } = "";
public string? CallingUser { get; set; } = "";
private string _Message = ""; private string _Message = "";
public string Message public string Message
{ {
@@ -327,6 +357,7 @@ namespace gaseous_server.Classes
public string? SearchText { get; set; } public string? SearchText { get; set; }
public string? CorrelationId { get; set; } public string? CorrelationId { get; set; }
public string? CallingProcess { get; set; } public string? CallingProcess { get; set; }
public string? CallingUser { get; set; }
} }
} }
} }

View File

@@ -11,6 +11,32 @@ namespace gaseous_server.Classes
public void RunMaintenance() 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 // delete files and directories older than 7 days in PathsToClean
List<string> PathsToClean = new List<string>(); List<string> PathsToClean = new List<string>();
PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory); PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory);
@@ -45,8 +71,7 @@ namespace gaseous_server.Classes
} }
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables"); Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); sql = "SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE';";
string sql = "SHOW TABLES;";
DataTable tables = db.ExecuteCMD(sql); DataTable tables = db.ExecuteCMD(sql);
int StatusCounter = 1; int StatusCounter = 1;

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath) public static CompanyLogo? GetCompanyLogo(long? Id, string ImagePath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
{ {
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
} }
else else
{ {
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, LogoPath); Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, ImagePath);
return RetVal.Result; 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; 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 // check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus(); Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -64,7 +64,7 @@ namespace gaseous_server.Classes.Metadata
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); returnValue = await GetObjectFromServer(WhereClause, ImagePath);
if (returnValue != null) if (returnValue != null)
{ {
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
@@ -74,7 +74,7 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
try try
{ {
returnValue = await GetObjectFromServer(WhereClause, LogoPath); returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
forceImageDownload = true; forceImageDownload = true;
} }
@@ -91,13 +91,14 @@ namespace gaseous_server.Classes.Metadata
throw new Exception("How did you get here?"); 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) Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Company logo download forced.");
{
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb); Communications comms = new Communications();
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med); comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
} }
return returnValue; return returnValue;
@@ -109,65 +110,15 @@ namespace gaseous_server.Classes.Metadata
slug slug
} }
private static async Task<CompanyLogo?> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<CompanyLogo> GetObjectFromServer(string WhereClause, string ImagePath)
{ {
// get CompanyLogo metadata // get Artwork metadata
Communications comms = new Communications(); Communications comms = new Communications();
var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause); var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First(); var result = results.First();
GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med);
return result; 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
}
} }
} }

View File

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

View File

@@ -98,14 +98,14 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames); UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
return returnValue; return returnValue;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
try try
{ {
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames); UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -114,18 +114,27 @@ namespace gaseous_server.Classes.Metadata
} }
return returnValue; return returnValue;
case Storage.CacheStatus.Current: 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: default:
throw new Exception("How did you get here?"); 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 // required metadata
if (Game.Cover != null) 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) if (Game.Genres != null)
@@ -175,6 +184,7 @@ namespace gaseous_server.Classes.Metadata
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId); AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
} }
} }
AgeGroups.GetAgeGroup(Game);
if (Game.ReleaseDates != null) if (Game.ReleaseDates != null)
{ {
@@ -199,7 +209,14 @@ namespace gaseous_server.Classes.Metadata
{ {
foreach (long ArtworkId in Game.Artworks.Ids) foreach (long ArtworkId in Game.Artworks.Ids)
{ {
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game)); try
{
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch artwork id: " + ArtworkId, ex);
}
} }
} }
@@ -266,7 +283,14 @@ namespace gaseous_server.Classes.Metadata
{ {
foreach (long ScreenshotId in Game.Screenshots.Ids) foreach (long ScreenshotId in Game.Screenshots.Ids)
{ {
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game)); try
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch screenshot id: " + ScreenshotId, ex);
}
} }
} }
@@ -307,6 +331,37 @@ namespace gaseous_server.Classes.Metadata
ids: platformIds.ToArray<long>() 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; return result;
} }
@@ -324,41 +379,146 @@ namespace gaseous_server.Classes.Metadata
} }
} }
private static bool AllowNoPlatformSearch = false;
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType) 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; return games.Result;
} }
private static async Task<Game[]> _SearchForGame(string SearchString, long PlatformId, SearchType searchType) private static async Task<Game[]> _SearchForGameDatabase(string SearchString, long PlatformId, SearchType searchType)
{ {
string searchBody = ""; string whereClause = "";
string searchFields = "fields id,name,slug,platforms,summary; "; Dictionary<string, object> dbDict = new Dictionary<string, object>();
bool allowSearch = true;
switch (searchType) switch (searchType)
{ {
case SearchType.searchNoPlatform: case SearchType.searchNoPlatform:
searchBody += "search \"" + SearchString + "\"; "; whereClause = "MATCH(`Name`) AGAINST (@gamename)";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
allowSearch = AllowNoPlatformSearch;
break; break;
case SearchType.search: case SearchType.search:
searchBody += "search \"" + SearchString + "\"; "; whereClause = "PlatformsId = @platformid AND MATCH(`Name`) AGAINST (@gamename)";
searchBody += "where platforms = (" + PlatformId + ");"; dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
break; break;
case SearchType.wherefuzzy: 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; break;
case SearchType.where: case SearchType.where:
searchBody += "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";"; whereClause = "PlatformsId = @platformid AND `Name` = @gamename";
dbDict.Add("platformid", PlatformId);
dbDict.Add("gamename", SearchString);
break; 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 // get Game metadata
Communications comms = new Communications(); Game[]? results = new Game[0];
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody); 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; 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 public enum SearchType
{ {
where = 0, where = 0,
@@ -377,6 +537,7 @@ namespace gaseous_server.Classes.Metadata
this.TotalRatingCount = gameObject.TotalRatingCount; this.TotalRatingCount = gameObject.TotalRatingCount;
this.Cover = gameObject.Cover; this.Cover = gameObject.Cover;
this.Artworks = gameObject.Artworks; this.Artworks = gameObject.Artworks;
this.FirstReleaseDate = gameObject.FirstReleaseDate;
// compile age ratings // compile age ratings
this.AgeRatings = new List<AgeRating>(); this.AgeRatings = new List<AgeRating>();
@@ -397,6 +558,7 @@ namespace gaseous_server.Classes.Metadata
public string Name { get; set; } public string Name { get; set; }
public double? TotalRating { get; set; } public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; } public int? TotalRatingCount { get; set; }
public DateTimeOffset? FirstReleaseDate { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; } public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; } public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
public List<IGDB.Models.AgeRating> AgeRatings { get; set; } public List<IGDB.Models.AgeRating> AgeRatings { get; set; }

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath) public static PlatformLogo? GetPlatformLogo(long? Id, string ImagePath)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
{ {
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
} }
else else
{ {
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, LogoPath); Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, ImagePath);
return RetVal.Result; 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; 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 // check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus(); Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -64,7 +64,7 @@ namespace gaseous_server.Classes.Metadata
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); returnValue = await GetObjectFromServer(WhereClause, ImagePath);
if (returnValue != null) if (returnValue != null)
{ {
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
@@ -74,7 +74,7 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
try try
{ {
returnValue = await GetObjectFromServer(WhereClause, LogoPath); returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
forceImageDownload = true; forceImageDownload = true;
} }
@@ -93,10 +93,14 @@ namespace gaseous_server.Classes.Metadata
if (returnValue != null) 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); Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Platform logo download forced.");
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med);
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
} }
} }
@@ -109,65 +113,15 @@ namespace gaseous_server.Classes.Metadata
slug slug
} }
private static async Task<PlatformLogo?> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<PlatformLogo> GetObjectFromServer(string WhereClause, string ImagePath)
{ {
// get PlatformLogo metadata // get Artwork metadata
Communications comms = new Communications(); Communications comms = new Communications();
var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause); var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First(); var result = results.First();
GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb);
GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med);
return result; 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
}
} }
} }

View File

@@ -93,9 +93,16 @@ namespace gaseous_server.Classes.Metadata
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion) private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion)
{ {
if (platformVersion.PlatformLogo != null) if (platformVersion.PlatformLogo != null)
{
try
{ {
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug)); 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);
}
}
} }
private enum SearchUsing private enum SearchUsing

View File

@@ -38,10 +38,18 @@ namespace gaseous_server.Classes.Metadata
} }
} }
else else
{
try
{ {
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh); Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
return RetVal.Result; return RetVal.Result;
} }
catch(Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata", "An error occurred fetching Platform Id " + Id, ex);
return null;
}
}
} }
public static Platform GetPlatform(string Slug, bool forceRefresh = false) public static Platform GetPlatform(string Slug, bool forceRefresh = false)
@@ -123,9 +131,16 @@ namespace gaseous_server.Classes.Metadata
} }
if (platform.PlatformLogo != null) if (platform.PlatformLogo != null)
{
try
{ {
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform)); 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);
}
}
} }
private static void AddPlatformMapping(Platform platform) private static void AddPlatformMapping(Platform platform)

View File

@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
{ {
} }
public static Screenshot? GetScreenshot(long? Id, string LogoPath) public static Screenshot? GetScreenshot(long? Id, string ImagePath, bool GetImages)
{ {
if ((Id == 0) || (Id == null)) if ((Id == 0) || (Id == null))
{ {
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
} }
else else
{ {
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, LogoPath); Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result; 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; 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 // check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus(); Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -61,18 +61,18 @@ namespace gaseous_server.Classes.Metadata
Screenshot returnValue = new Screenshot(); Screenshot returnValue = new Screenshot();
bool forceImageDownload = false; bool forceImageDownload = false;
LogoPath = Path.Combine(LogoPath, "Screenshots"); ImagePath = Path.Combine(ImagePath, "Screenshots");
switch (cacheStatus) switch (cacheStatus)
{ {
case Storage.CacheStatus.NotPresent: case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, LogoPath); returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue); Storage.NewCacheValue(returnValue);
forceImageDownload = true; forceImageDownload = true;
break; break;
case Storage.CacheStatus.Expired: case Storage.CacheStatus.Expired:
try try
{ {
returnValue = await GetObjectFromServer(WhereClause, LogoPath); returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
forceImageDownload = true; forceImageDownload = true;
} }
@@ -89,11 +89,17 @@ namespace gaseous_server.Classes.Metadata
throw new Exception("How did you get here?"); 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); if ((!File.Exists(localFile)) || forceImageDownload == true)
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId); {
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId); 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; return returnValue;
@@ -105,65 +111,15 @@ namespace gaseous_server.Classes.Metadata
slug slug
} }
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string LogoPath) private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string ImagePath)
{ {
// get Screenshot metadata // get Screenshot metadata
Communications comms = new Communications(); Communications comms = new Communications();
var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause); var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause);
var result = results.First(); 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; 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

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

View File

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

View File

@@ -91,7 +91,7 @@ namespace gaseous_server.Classes
mediaGroupItems.Add(BuildMediaGroupFromRow(row)); mediaGroupItems.Add(BuildMediaGroupFromRow(row));
} }
mediaGroupItems.Sort((x, y) => x.PlatformName.CompareTo(y.PlatformName)); mediaGroupItems.Sort((x, y) => x.Platform.CompareTo(y.Platform));
return mediaGroupItems; return mediaGroupItems;
} }
@@ -176,6 +176,7 @@ namespace gaseous_server.Classes
mediaGroupItem.PlatformId = (long)row["PlatformId"]; mediaGroupItem.PlatformId = (long)row["PlatformId"];
mediaGroupItem.GameId = (long)row["GameId"]; mediaGroupItem.GameId = (long)row["GameId"];
mediaGroupItem.RomIds = new List<long>(); mediaGroupItem.RomIds = new List<long>();
mediaGroupItem.Roms = new List<Roms.GameRomItem>();
// get members // get members
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -186,6 +187,26 @@ namespace gaseous_server.Classes
foreach (DataRow dataRow in data.Rows) foreach (DataRow dataRow in data.Rows)
{ {
mediaGroupItem.RomIds.Add((long)dataRow["RomId"]); 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; return mediaGroupItem;
@@ -360,7 +381,7 @@ namespace gaseous_server.Classes
public long Id { get; set; } public long Id { get; set; }
public long GameId { get; set; } public long GameId { get; set; }
public long PlatformId { get; set; } public long PlatformId { get; set; }
public string PlatformName { public string Platform {
get get
{ {
try 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<long> RomIds { get; set; }
public List<Roms.GameRomItem> Roms { get; set; }
private GroupBuildStatus _Status { get; set; } private GroupBuildStatus _Status { get; set; }
public GroupBuildStatus Status { public GroupBuildStatus Status {
get get

View File

@@ -3,6 +3,7 @@ using System.Data;
using gaseous_signature_parser.models.RomSignatureObject; using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup; using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using IGDB.Models;
namespace gaseous_server.Classes 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(); GameRomObject GameRoms = new GameRomObject();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
string sqlCount = "";
string sqlPlatform = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId); 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) { 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 { } 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); dbDict.Add("platformid", PlatformId);
} }
DataTable romDT = db.ExecuteCMD(sql, dbDict); 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) if (romDT.Rows.Count > 0)
{ {
foreach (DataRow romDR in romDT.Rows) // set count of roms
{ GameRoms.Count = int.Parse((string)rowCount["RomCount"]);
GameRoms.GameRomItems.Add(BuildRom(romDR));
}
// get rom media groups int pageOffset = pageSize * (pageNumber - 1);
GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId); 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; return GameRoms;
} }
@@ -52,7 +82,7 @@ namespace gaseous_server.Classes
public static GameRomItem GetRom(long RomId) public static GameRomItem GetRom(long RomId)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); 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>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
DataTable romDT = db.ExecuteCMD(sql, dbDict); DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -114,20 +144,20 @@ namespace gaseous_server.Classes
{ {
Id = (long)romDR["id"], Id = (long)romDR["id"],
PlatformId = (long)romDR["platformid"], PlatformId = (long)romDR["platformid"],
Platform = Classes.Metadata.Platforms.GetPlatform((long)romDR["platformid"]), Platform = (string)romDR["platformname"],
GameId = (long)romDR["gameid"], GameId = (long)romDR["gameid"],
Name = (string)romDR["name"], Name = (string)romDR["name"],
Size = (long)romDR["size"], Size = (long)romDR["size"],
CRC = ((string)romDR["crc"]).ToLower(), Crc = ((string)romDR["crc"]).ToLower(),
MD5 = ((string)romDR["md5"]).ToLower(), Md5 = ((string)romDR["md5"]).ToLower(),
SHA1 = ((string)romDR["sha1"]).ToLower(), Sha1 = ((string)romDR["sha1"]).ToLower(),
DevelopmentStatus = (string)romDR["developmentstatus"], DevelopmentStatus = (string)romDR["developmentstatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")), Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
RomType = (int)romDR["romtype"], RomType = (HasheousClient.Models.LookupResponseModel.RomItem.RomTypes)(int)romDR["romtype"],
RomTypeMedia = (string)romDR["romtypemedia"], RomTypeMedia = (string)romDR["romtypemedia"],
MediaLabel = (string)romDR["medialabel"], MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"], Path = (string)romDR["path"],
Source = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)romDR["metadatasource"], SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""), SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"]) Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
}; };
@@ -149,134 +179,20 @@ namespace gaseous_server.Classes
public class GameRomObject public class GameRomObject
{ {
public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>();
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>(); 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 long PlatformId { get; set; }
public IGDB.Models.Platform Platform { get; set; } public string Platform { get; set; }
//public Dictionary<string, object>? Emulator { get; set; }
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; } public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public long GameId { 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 string? Path { get; set; }
public RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; }
public string? SignatureSourceGameTitle { get; set;} public string? SignatureSourceGameTitle { get; set;}
public GameLibrary.LibraryItem Library { get; set; } public GameLibrary.LibraryItem Library { get; set; }
} }
public class MediaType
{
public MediaType(RomSignatureObject.Game.Rom.SignatureSourceType Source, string MediaTypeString)
{
switch (Source)
{
case RomSignatureObject.Game.Rom.SignatureSourceType.TOSEC:
string[] typeString = MediaTypeString.Split(" ");
string inType = "";
foreach (string typeStringVal in typeString)
{
if (inType == "")
{
switch (typeStringVal.ToLower())
{
case "disk":
Media = RomSignatureObject.Game.Rom.RomTypes.Disk;
inType = typeStringVal;
break;
case "disc":
Media = RomSignatureObject.Game.Rom.RomTypes.Disc;
inType = typeStringVal;
break;
case "file":
Media = RomSignatureObject.Game.Rom.RomTypes.File;
inType = typeStringVal;
break;
case "part":
Media = RomSignatureObject.Game.Rom.RomTypes.Part;
inType = typeStringVal;
break;
case "tape":
Media = RomSignatureObject.Game.Rom.RomTypes.Tape;
inType = typeStringVal;
break;
case "of":
inType = typeStringVal;
break;
case "side":
inType = typeStringVal;
break;
}
}
else {
switch (inType.ToLower())
{
case "disk":
case "disc":
case "file":
case "part":
case "tape":
Number = int.Parse(typeStringVal);
break;
case "of":
Count = int.Parse(typeStringVal);
break;
case "side":
Side = typeStringVal;
break;
}
inType = "";
}
}
break;
default:
break;
}
}
public RomSignatureObject.Game.Rom.RomTypes? Media { get; set; }
public int? Number { get; set; }
public int? Count { get; set; }
public string? Side { get; set; }
}
} }
} }

View File

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

View File

@@ -8,6 +8,7 @@ using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@@ -287,15 +288,15 @@ namespace gaseous_server.Controllers
[ProducesResponseType(typeof(Game), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Game), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[ResponseCache(CacheProfileName = "5Minute")] [ResponseCache(CacheProfileName = "5Minute")]
public ActionResult Game(long GameId, bool forceRefresh = false) public ActionResult Game(long GameId)
{ {
try 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 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.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
@@ -473,7 +393,7 @@ namespace gaseous_server.Controllers
{ {
foreach (long ArtworkId in gameObject.Artworks.Ids) 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); artworks.Add(GameArtwork);
} }
} }
@@ -501,7 +421,7 @@ namespace gaseous_server.Controllers
try 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) if (artworkObject != null)
{ {
return Ok(artworkObject); return Ok(artworkObject);
@@ -525,10 +445,11 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [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(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameCoverImage(long GameId, long ArtworkId) public ActionResult GameCoverImage(long GameId, long ArtworkId, Communications.IGDBAPI_ImageSize size, string ImageName)
{ {
try try
{ {
@@ -536,15 +457,25 @@ namespace gaseous_server.Controllers
try 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) { 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)) if (System.IO.File.Exists(coverFilePath))
{ {
string filename = artworkObject.ImageId + ".png"; string filename = artworkObject.ImageId + ".jpg";
string filepath = coverFilePath; string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath); byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/png"; string contentType = "image/jpg";
var cd = new System.Net.Mime.ContentDisposition 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); IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null) 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) if (coverObject != null)
{ {
return Ok(coverObject); return Ok(coverObject);
@@ -616,21 +547,33 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/cover/image")] [Route("{GameId}/cover/image/{size}")]
[Route("{GameId}/cover/image/{size}/{imagename}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameCoverImage(long GameId) public ActionResult GameCoverImage(long GameId, Communications.IGDBAPI_ImageSize size, string imagename = "")
{ {
try try
{ {
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Cover.png"); if (gameObject.Cover != null)
{
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)) { if (System.IO.File.Exists(coverFilePath)) {
string filename = "Cover.png"; string filename = cover.ImageId + ".jpg";
string filepath = coverFilePath; string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath); byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/png"; string contentType = "image/jpg";
var cd = new System.Net.Mime.ContentDisposition var cd = new System.Net.Mime.ContentDisposition
{ {
@@ -643,11 +586,10 @@ namespace gaseous_server.Controllers
return File(filedata, contentType); return File(filedata, contentType);
} }
else
{
return NotFound();
} }
} }
return NotFound();
}
catch catch
{ {
return NotFound(); return NotFound();
@@ -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.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
@@ -870,13 +830,13 @@ namespace gaseous_server.Controllers
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
//[ResponseCache(CacheProfileName = "5Minute")] //[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 try
{ {
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); 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 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.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
@@ -1232,7 +1221,8 @@ namespace gaseous_server.Controllers
{ {
Classes.Roms.GameRomItem romItem = Classes.Roms.GetRom(RomId); Classes.Roms.GameRomItem romItem = Classes.Roms.GetRom(RomId);
Common.hashObject hash = new Common.hashObject(romItem.Path); 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); List<Game> searchResults = Classes.ImportGame.SearchForGame_GetAll(romSig.Game.Name, romSig.Flags.IGDBPlatformId);
return Ok(searchResults); return Ok(searchResults);
@@ -1275,7 +1265,7 @@ namespace gaseous_server.Controllers
{ {
foreach (long ScreenshotId in gameObject.Screenshots.Ids) 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); screenshots.Add(GameScreenshot);
} }
} }
@@ -1301,7 +1291,7 @@ namespace gaseous_server.Controllers
{ {
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null) { 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) if (screenshotObject != null)
{ {
return Ok(screenshotObject); return Ok(screenshotObject);
@@ -1325,24 +1315,31 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [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(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameScreenshotImage(long GameId, long ScreenshotId) public ActionResult GameScreenshotImage(long GameId, long ScreenshotId, Communications.IGDBAPI_ImageSize Size, string ImageName)
{ {
try try
{ {
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); 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)) if (System.IO.File.Exists(coverFilePath))
{ {
string filename = screenshotObject.ImageId + ".png"; string filename = screenshotObject.ImageId + ".jpg";
string filepath = coverFilePath; string filepath = coverFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath); byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "image/png"; string contentType = "image/jpg";
var cd = new System.Net.Mime.ContentDisposition var cd = new System.Net.Mime.ContentDisposition
{ {

View File

@@ -77,7 +77,8 @@ namespace gaseous_server.Controllers
// Process uploaded files // Process uploaded files
foreach (Dictionary<string, object> UploadedFile in UploadedFiles) 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)) if (Directory.Exists(workPath))

View File

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

View File

@@ -37,82 +37,27 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [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) SignatureManagement signatureManagement = new SignatureManagement();
{ return signatureManagement.GetSignature(md5, sha1);
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
} else
{
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
}
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [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) if (TosecName.Length > 0)
{ {
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName); SignatureManagement signatureManagement = new SignatureManagement();
return signatureManagement.GetByTosecName(TosecName);
} else } else
{ {
return null; 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{ "var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
WriteIndented = true WriteIndented = true
}) + ";" + Environment.NewLine + }) + ";" + Environment.NewLine +
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeRatings.AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{ "var AgeRatingGroups = " + JsonSerializer.Serialize(AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
WriteIndented = true WriteIndented = true
}) + ";"; }) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(ver); byte[] bytes = Encoding.UTF8.GetBytes(ver);

View File

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

View File

@@ -192,11 +192,14 @@ namespace gaseous_server.Controllers.v1_1
string tempVal = ""; string tempVal = "";
string nameWhereClause = "";
if (model.Name.Length > 0) if (model.Name.Length > 0)
{ {
tempVal = "`Name` LIKE @Name"; // tempVal = "`Name` LIKE @Name";
whereParams.Add("@Name", "%" + model.Name + "%"); // whereParams.Add("@Name", "%" + model.Name + "%");
havingClauses.Add(tempVal); // 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) if (model.GameRating != null)
@@ -263,9 +266,10 @@ namespace gaseous_server.Controllers.v1_1
} }
} }
string platformWhereClause = "";
if (model.Platform.Count > 0) if (model.Platform.Count > 0)
{ {
tempVal = "Relation_Game_Platforms.PlatformsId IN ("; tempVal = " AND Games_Roms.PlatformId IN (";
for (int i = 0; i < model.Platform.Count; i++) for (int i = 0; i < model.Platform.Count; i++)
{ {
if (i > 0) if (i > 0)
@@ -277,7 +281,8 @@ namespace gaseous_server.Controllers.v1_1
whereParams.Add(platformLabel, model.Platform[i]); whereParams.Add(platformLabel, model.Platform[i]);
} }
tempVal += ")"; tempVal += ")";
whereClauses.Add(tempVal); //whereClauses.Add(tempVal);
platformWhereClause = tempVal;
} }
if (model.Genre.Count > 0) if (model.Genre.Count > 0)
@@ -352,7 +357,7 @@ namespace gaseous_server.Controllers.v1_1
{ {
if (model.GameAgeRating.AgeGroupings.Count > 0) if (model.GameAgeRating.AgeGroupings.Count > 0)
{ {
tempVal = "(AgeGroupId IN ("; tempVal = "(Game.AgeGroupId IN (";
for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++) for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++)
{ {
if (i > 0) if (i > 0)
@@ -367,7 +372,7 @@ namespace gaseous_server.Controllers.v1_1
if (model.GameAgeRating.IncludeUnrated == true) if (model.GameAgeRating.IncludeUnrated == true)
{ {
tempVal += " OR AgeGroupId IS NULL"; tempVal += " OR Game.AgeGroupId IS NULL";
} }
tempVal += ")"; tempVal += ")";
@@ -439,7 +444,9 @@ namespace gaseous_server.Controllers.v1_1
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder; string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause; //string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
string sql = "SELECT DISTINCT Game.Id, Game.`Name`, Game.NameThe, Game.PlatformId, Game.TotalRating, Game.TotalRatingCount, Game.Cover, Game.Artworks, Game.FirstReleaseDate, Game.Category, Game.ParentGame, Game.AgeRatings, Game.AgeGroupId, Game.RomCount FROM (SELECT DISTINCT Game.*, CASE WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The') ELSE Game.`Name` END AS NameThe, Games_Roms.PlatformId, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + " LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + " GROUP BY Game.Id HAVING RomCount > 0) Game LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>(); List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
@@ -457,7 +464,8 @@ namespace gaseous_server.Controllers.v1_1
break; break;
} }
RetVal.Add(Classes.Metadata.Games.GetGame(dbResponse.Rows[i])); Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
RetVal.Add(retGame);
} }
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal); 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"]; long mapId = (long)row["Id"];
if (PlatformMapCache.ContainsKey(mapId.ToString())) if (PlatformMapCache.ContainsKey(mapId.ToString()))
{ {
platformMaps.Add(PlatformMapCache[mapId.ToString()]); PlatformMapItem mapItem = PlatformMapCache[mapId.ToString()];
if (mapItem != null)
{
platformMaps.Add(mapItem);
}
} }
else else
{ {
platformMaps.Add(BuildPlatformMapItem(row)); PlatformMapItem mapItem = BuildPlatformMapItem(row);
if (mapItem != null)
{
platformMaps.Add(mapItem);
}
} }
} }
@@ -253,8 +261,10 @@ namespace gaseous_server.Models
string sql = ""; string sql = "";
// get platform data // get platform data
IGDB.Models.Platform platform = Platforms.GetPlatform(IGDBId); IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId);
if (platform != null)
{
// get platform alternate names // get platform alternate names
sql = "SELECT * FROM PlatformMap_AlternateNames WHERE Id = @Id ORDER BY Name"; sql = "SELECT * FROM PlatformMap_AlternateNames WHERE Id = @Id ORDER BY Name";
dbDict.Clear(); dbDict.Clear();
@@ -358,14 +368,22 @@ namespace gaseous_server.Models
return mapItem; return mapItem;
} }
public static void GetIGDBPlatformMapping(ref Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName) return null;
}
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; bool PlatformFound = false;
foreach (Models.PlatformMapping.PlatformMapItem PlatformMapping in Models.PlatformMapping.PlatformMap) foreach (Models.PlatformMapping.PlatformMapItem PlatformMapping in Models.PlatformMapping.PlatformMap)
{ {
if (PlatformMapping.Extensions != null) if (PlatformMapping.Extensions != null)
{ {
if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(RomFileInfo.Extension, StringComparer.OrdinalIgnoreCase)) if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(ImageExtension, StringComparer.OrdinalIgnoreCase))
{ {
if (SetSystemName == true) if (SetSystemName == true)
{ {
@@ -375,6 +393,8 @@ namespace gaseous_server.Models
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName; Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
PlatformFound = true; PlatformFound = true;
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from file extension");
break; break;
} }
} }
@@ -397,10 +417,17 @@ namespace gaseous_server.Models
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName; Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
PlatformFound = true; PlatformFound = true;
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from signature system to platform map");
break; break;
} }
} }
} }
if (PlatformFound == false)
{
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Unable to determine platform");
}
} }
public class PlatformMapItem public class PlatformMapItem

View File

@@ -4,197 +4,19 @@ using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models namespace gaseous_server.Models
{ {
public class Signatures_Games public class Signatures_Games : HasheousClient.Models.LookupResponseModel
{ {
public Signatures_Games() 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 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 class SignatureFlags
{ {
public long IGDBPlatformId { get; set; } public long IGDBPlatformId { get; set; }
public string IGDBPlatformName { get; set; } public string IGDBPlatformName { get; set; }
public long IGDBGameId { get; set; }
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -188,6 +188,91 @@
} }
] ]
}, },
{
"igdbId": 114,
"igdbName": "Amiga CD32",
"igdbSlug": "amiga-cd32",
"alternateNames": [
"Amiga CD32",
"Commodore Amiga CD32"
],
"extensions": {
"supportedFileExtensions": [
".ZIP"
],
"uniqueFileExtensions": [
]
},
"retroPieDirectoryName": "amiga",
"webEmulator": {
"type": "EmulatorJS",
"core": "amiga",
"availableWebEmulators": [
{
"emulatorType": "EmulatorJS",
"availableWebEmulatorCores": [
{
"core": "amiga",
"alternateCoreName": "puae",
"default": true
}
]
}
]
},
"bios": [
{
"hash": "85ad74194e87c08904327de1a9443b7a",
"description": "Kickstart v1.2 rev 33.180",
"filename": "kick33180.A500"
},
{
"hash": "82a21c1890cae844b3df741f2762d48d",
"description": "Kickstart v1.3 rev 34.005",
"filename": "kick34005.A500"
},
{
"hash": "89da1838a24460e4b93f4f0c5d92d48d",
"description": "CDTV extended ROM v1.00",
"filename": "kick34005.CDTV"
},
{
"hash": "dc10d7bdd1b6f450773dfb558477c230",
"description": "Kickstart v2.04 rev 37.175",
"filename": "kick37175.A500"
},
{
"hash": "5f8924d013dd57a89cf349f4cdedc6b1",
"description": "CD32 Kickstart v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "f2f241bf094168cfb9e7805dc2856433",
"description": "CD32 KS + extended v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "bb72565701b1b6faece07d68ea5da639",
"description": "CD32 extended ROM rev 40.060",
"filename": "kick40060.CD32.ext"
},
{
"hash": "e40a5dfb3d017ba8779faba30cbd1c8e",
"description": "Kickstart v3.1 rev 40.063",
"filename": "kick40063.A600"
},
{
"hash": "646773759326fbac3b2311fd8c8793ee",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A1200"
},
{
"hash": "9bdedde6a4f33555b4a270c8ca53297d",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A4000"
}
]
},
{ {
"igdbId": 25, "igdbId": 25,
"igdbName": "Amstrad CPC", "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, "igdbId": 15,
"igdbName": "Commodore C64/128/MAX", "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, "igdbId": 33,
"igdbName": "Game Boy", "igdbName": "Game Boy",

View File

@@ -20,11 +20,15 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="gaseous-signature-parser" Version="2.0.0" /> <PackageReference Include="gaseous-signature-parser" Version="2.0.0" />
<PackageReference Include="gaseous.IGDB" Version="1.0.1" /> <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.Identity.UI" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" /> <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.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <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="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" />
<PackageReference Include="MySqlConnector" Version="2.3.1" /> <PackageReference Include="MySqlConnector" Version="2.3.1" />
@@ -50,60 +54,12 @@
<None Remove="Support\Database\MySQL\gaseous-1007.sql" /> <None Remove="Support\Database\MySQL\gaseous-1007.sql" />
<None Remove="Support\Database\MySQL\gaseous-1008.sql" /> <None Remove="Support\Database\MySQL\gaseous-1008.sql" />
<None Remove="Support\Database\MySQL\gaseous-1009.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="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>
<ItemGroup> <ItemGroup>
<Folder Include="Controllers\" /> <Folder Include="Controllers\" />
@@ -112,15 +68,6 @@
<Folder Include="Classes\SignatureIngestors\" /> <Folder Include="Classes\SignatureIngestors\" />
<Folder Include="Support\" /> <Folder Include="Support\" />
<Folder Include="Classes\Metadata\" /> <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" /> <Folder Remove="Reference" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -133,45 +80,6 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile> <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</EmbeddedResource> </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-1000.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />
@@ -182,5 +90,10 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1008.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1008.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1009.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> </ItemGroup>
</Project> </Project>

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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