diff --git a/.gitignore b/.gitignore index d97ed973..5d44f117 100644 --- a/.gitignore +++ b/.gitignore @@ -403,3 +403,4 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +gaseous-server/.DS_Store diff --git a/gaseous-server/Classes/Collections.cs b/gaseous-server/Classes/Collections.cs index ec37710c..5b9a69ba 100644 --- a/gaseous-server/Classes/Collections.cs +++ b/gaseous-server/Classes/Collections.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Filters; using Newtonsoft.Json; +using SharpCompress.Common; namespace gaseous_server.Classes { @@ -66,7 +67,7 @@ public static CollectionItem GetCollection(long Id) { public static CollectionItem NewCollection(CollectionItem item) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; + string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, ArchiveType, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @archivetype, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; Dictionary dbDict = new Dictionary(); dbDict.Add("name", item.Name); dbDict.Add("description", item.Description); @@ -82,6 +83,7 @@ public static CollectionItem NewCollection(CollectionItem item) dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1)); dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous)); dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0)); + dbDict.Add("archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip)); dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List()))); dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild); DataTable romDT = db.ExecuteCMD(sql, dbDict); @@ -97,7 +99,7 @@ public static CollectionItem NewCollection(CollectionItem item) public static CollectionItem EditCollection(long Id, CollectionItem item, bool ForceRebuild = true) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id"; + string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, ArchiveType=@archivetype, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id"; Dictionary dbDict = new Dictionary(); dbDict.Add("id", Id); dbDict.Add("name", item.Name); @@ -115,8 +117,9 @@ public static CollectionItem EditCollection(long Id, CollectionItem item, bool F dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous)); dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0)); dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List()))); + dbDict.Add("archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip)); - string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip"); + string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + item.ArchiveExtension); if (ForceRebuild == true) { dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild); @@ -389,7 +392,7 @@ public static void CompileCollections(long CollectionId) db.ExecuteCMD(sql, dbDict); List collectionPlatformItems = GetCollectionContent(collectionItem).Collection; - string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + ".zip"); + string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + collectionItem.ArchiveExtension); string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString()); try @@ -508,7 +511,21 @@ public static void CompileCollections(long CollectionId) // compress to zip Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection"); - ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false); + switch(collectionItem.ArchiveType) + { + case CollectionItem.ArchiveTypes.Zip: + ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false); + break; + + case CollectionItem.ArchiveTypes.RAR: + + break; + + case CollectionItem.ArchiveTypes.SevenZip: + + break; + } + // clean up if (Directory.Exists(ZipFileTempPath)) @@ -567,6 +584,7 @@ private static CollectionItem BuildCollectionItem(DataRow row) { item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1); item.FolderStructure = (CollectionItem.FolderStructures)(int)Common.ReturnValueIfNull(row["FolderStructure"], 0); item.IncludeBIOSFiles = (bool)row["IncludeBIOSFiles"]; + item.ArchiveType = (CollectionItem.ArchiveTypes)(int)Common.ReturnValueIfNull(row["ArchiveType"], 0); item.AlwaysInclude = Newtonsoft.Json.JsonConvert.DeserializeObject>(strAlwaysInclude); item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0); @@ -595,6 +613,32 @@ public CollectionItem() public long? MaximumCollectionSizeInBytes { get; set; } public FolderStructures FolderStructure { get; set; } = FolderStructures.Gaseous; public bool IncludeBIOSFiles { get; set; } = true; + public ArchiveTypes ArchiveType { get; set; } = CollectionItem.ArchiveTypes.Zip; + public string ArchiveExtension + { + get + { + if (ArchiveType != null) + { + switch (ArchiveType) + { + case ArchiveTypes.Zip: + default: + return ".zip"; + + case ArchiveTypes.RAR: + return ".rar"; + + case ArchiveTypes.SevenZip: + return ".7z"; + } + } + else + { + return ".zip"; + } + } + } public List AlwaysInclude { get; set; } [JsonIgnore] @@ -604,7 +648,7 @@ public CollectionBuildStatus BuildStatus { if (_BuildStatus == CollectionBuildStatus.Completed) { - if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip"))) + if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension))) { return CollectionBuildStatus.Completed; } @@ -632,7 +676,7 @@ public long CollectionBuiltSizeBytes { if (BuildStatus == CollectionBuildStatus.Completed) { - string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip"); + string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension); if (File.Exists(ZipFilePath)) { FileInfo fi = new FileInfo(ZipFilePath); @@ -665,6 +709,13 @@ public enum FolderStructures RetroPie = 1 } + public enum ArchiveTypes + { + Zip = 0, + RAR = 1, + SevenZip = 2 + } + public class AlwaysIncludeItem { public long PlatformId { get; set; } diff --git a/gaseous-server/Classes/Config.cs b/gaseous-server/Classes/Config.cs index 5c887fd9..77d97a46 100644 --- a/gaseous-server/Classes/Config.cs +++ b/gaseous-server/Classes/Config.cs @@ -503,6 +503,14 @@ private static HasheousClient.Models.MetadataModel.SignatureSources _SignatureSo } } + private static int _MaxLibraryScanWorkers + { + get + { + return 4; + } + } + private static string _HasheousHost { get @@ -522,6 +530,8 @@ private static string _HasheousHost public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource; + public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers; + public string HasheousHost = _HasheousHost; } diff --git a/gaseous-server/Classes/FileSignature.cs b/gaseous-server/Classes/FileSignature.cs index e0807656..9478d89d 100644 --- a/gaseous-server/Classes/FileSignature.cs +++ b/gaseous-server/Classes/FileSignature.cs @@ -1,5 +1,7 @@ +using System.Collections.Concurrent; using System.IO.Compression; using HasheousClient.Models; +using NuGet.Common; using SevenZip; using SharpCompress.Archives; using SharpCompress.Archives.Rar; @@ -10,11 +12,11 @@ namespace gaseous_server.Classes { public class FileSignature { - public static gaseous_server.Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath) + 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, GameFileImportPath, false); + discoveredSignature = _GetFileSignature(hash, fi.Name, fi.Extension, fi.Length, GameFileImportPath, false); string[] CompressionExts = { ".zip", ".rar", ".7z" }; string ImportedFileExtension = Path.GetExtension(GameFileImportPath); @@ -23,7 +25,8 @@ public static gaseous_server.Models.Signatures_Games GetFileSignature(Common.has { // file is a zip and less than 1 GiB // extract the zip file and search the contents - string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName()); + + 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 @@ -129,7 +132,7 @@ public static gaseous_server.Models.Signatures_Games GetFileSignature(Common.has if (signatureFound == false) { - gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file, true); + 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) @@ -162,24 +165,20 @@ public static gaseous_server.Models.Signatures_Games GetFileSignature(Common.has { Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing compressed file: " + GameFileImportPath, ex); } - - if (Directory.Exists(ExtractPath)) - { - Logging.Log(Logging.LogType.Information, "Get Signature", "Deleting temporary decompress folder: " + ExtractPath); - - Directory.Delete(ExtractPath, true); - } } return discoveredSignature; } - private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath, bool IsInZip) + 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, fi, GameFileImportPath); + gaseous_server.Models.Signatures_Games? dbSignature = _GetFileSignatureFromDatabase(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath); if (dbSignature != null) { @@ -190,7 +189,7 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.h else { // no local signature attempt to pull from Hasheous - dbSignature = _GetFileSignatureFromHasheous(hash, fi, GameFileImportPath); + dbSignature = _GetFileSignatureFromHasheous(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath); if (dbSignature != null) { @@ -202,25 +201,32 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.h else { // construct a signature from file data - dbSignature = _GetFileSignatureFromFileData(hash, fi, GameFileImportPath); + 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 static gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, FileInfo fi, string GameFileImportPath) + 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 signatures = sc.GetSignature(hash.md5hash); - if (signatures.Count == 0) + 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); } @@ -230,19 +236,19 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.h { // only 1 signature found! discoveredSignature = signatures.ElementAt(0); - gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false); 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; - gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false); } } @@ -252,7 +258,7 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.h return null; } - private static gaseous_server.Models.Signatures_Games? _GetFileSignatureFromHasheous(Common.hashObject hash, FileInfo fi, string GameFileImportPath) + 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) @@ -285,8 +291,6 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.h } } } - - gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref signature, fi, false); return signature; } @@ -296,20 +300,19 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignature(Common.h return null; } - private static gaseous_server.Models.Signatures_Games _GetFileSignatureFromFileData(Common.hashObject hash, FileInfo fi, string GameFileImportPath) + 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 signatures = signatureManagement.GetByTosecName(fi.Name); + List signatures = signatureManagement.GetByTosecName(ImageName); if (signatures.Count == 1) { // only 1 signature found! discoveredSignature = signatures.ElementAt(0); - gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false); return discoveredSignature; } @@ -321,7 +324,6 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignatureFromFileD if (Sig.Score > discoveredSignature.Score) { discoveredSignature = Sig; - gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false); } } @@ -348,14 +350,11 @@ private static gaseous_server.Models.Signatures_Games _GetFileSignatureFromFileD // remove special characters like dashes gi.Name = gi.Name.Replace("-", "").Trim(); - // 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.Size = ImageSize; ri.SignatureSource = gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.None; return discoveredSignature; diff --git a/gaseous-server/Classes/GameLibrary.cs b/gaseous-server/Classes/GameLibrary.cs index 7bfd7f68..161c1d6b 100644 --- a/gaseous-server/Classes/GameLibrary.cs +++ b/gaseous-server/Classes/GameLibrary.cs @@ -34,6 +34,12 @@ public class CannotDeleteDefaultLibrary : Exception {} } + public class CannotDeleteLibraryWhileScanIsActive : Exception + { + public CannotDeleteLibraryWhileScanIsActive() : base("Unable to delete library while a library scan is active. Wait for all scans to complete and try again") + {} + } + // code public static LibraryItem GetDefaultLibrary { @@ -110,21 +116,42 @@ public static LibraryItem AddLibrary(string Name, string Path, long DefaultPlatf int newLibraryId = (int)(long)data.Rows[0][0]; - return GetLibrary(newLibraryId); + Logging.Log(Logging.LogType.Information, "Library Management", "Created library " + Name + " at directory " + PathName); + + LibraryItem library = GetLibrary(newLibraryId); + + return library; } public static void DeleteLibrary(int LibraryId) { - if (GetLibrary(LibraryId).IsDefaultLibrary == false) + LibraryItem library = GetLibrary(LibraryId); + if (library.IsDefaultLibrary == false) { + // check for active library scans + foreach(ProcessQueue.QueueItem item in ProcessQueue.QueueItems) + { + if ( + (item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState == ProcessQueue.QueueItemState.Running) || + (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker && item.ItemState == ProcessQueue.QueueItemState.Running) + ) + { + Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete libraries while a library scan is running. Wait until the the library scan is completed and try again."); + throw new CannotDeleteLibraryWhileScanIsActive(); + } + } + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;"; Dictionary dbDict = new Dictionary(); dbDict.Add("id", LibraryId); db.ExecuteCMD(sql, dbDict); + + Logging.Log(Logging.LogType.Information, "Library Management", "Deleted library " + library.Name + " at path " + library.Path); } else { + Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete the default library."); throw new CannotDeleteDefaultLibrary(); } } diff --git a/gaseous-server/Classes/ImportGames.cs b/gaseous-server/Classes/ImportGames.cs index 13144941..f519233d 100644 --- a/gaseous-server/Classes/ImportGames.cs +++ b/gaseous-server/Classes/ImportGames.cs @@ -98,7 +98,8 @@ public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? Over { Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing"); - gaseous_server.Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath); + FileSignature fileSignature = new FileSignature(); + gaseous_server.Models.Signatures_Games discoveredSignature = fileSignature.GetFileSignature(GameLibrary.GetDefaultLibrary, hash, fi, GameFileImportPath); // get discovered platform IGDB.Models.Platform? determinedPlatform = null; @@ -117,7 +118,7 @@ public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? Over discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name; } - IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature, discoveredSignature.Flags.IGDBPlatformId); + IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature, discoveredSignature.Flags.IGDBPlatformId, true); // add to database StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath); @@ -148,16 +149,16 @@ public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? Over } } - public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId) + public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload) { if (Signature.Flags != null) { - if (Signature.Flags.IGDBGameId != null) + if (Signature.Flags.IGDBGameId != null && Signature.Flags.IGDBGameId != 0) { // game was determined elsewhere - probably a Hasheous server try { - return Games.GetGame(Signature.Flags.IGDBGameId, false, false, false); + return Games.GetGame(Signature.Flags.IGDBGameId, false, false, FullDownload); } catch (Exception ex) { @@ -482,12 +483,22 @@ public static void DeleteOrphanedDirectories(string startLocation) } } - public void LibraryScan() + public void LibraryScan(GameLibrary.LibraryItem? singleLibrary = null) { - int maxWorkers = 4; + int maxWorkers = Config.MetadataConfiguration.MaxLibraryScanWorkers; + + List libraries = new List(); + if (singleLibrary == null) + { + libraries.AddRange(GameLibrary.GetLibraries); + } + else + { + libraries.Add(singleLibrary); + } // setup background tasks for each library - foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries) + 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( @@ -623,31 +634,28 @@ public void LibrarySpecificScan(GameLibrary.LibraryItem library) Common.hashObject hash = new Common.hashObject(LibraryFile); FileInfo fi = new FileInfo(LibraryFile); - gaseous_server.Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile); + FileSignature fileSignature = new FileSignature(); + gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, LibraryFile); - // get discovered platform - IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId); - - IGDB.Models.Game determinedGame = new Game(); try { - if (determinedPlatform == null) + // get discovered platform + long PlatformId; + IGDB.Models.Platform determinedPlatform; + + if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 ) { - if (library.DefaultPlatformId == 0) - { - determinedPlatform = new IGDB.Models.Platform(); - determinedGame = SearchForGame(sig, sig.Flags.IGDBPlatformId); - } - else - { - determinedPlatform = Platforms.GetPlatform(library.DefaultPlatformId); - determinedGame = SearchForGame(sig, library.DefaultPlatformId); - } + // no platform discovered in the signature + PlatformId = library.DefaultPlatformId; } else { - determinedGame = SearchForGame(sig, (long)determinedPlatform.Id); + // use the platform discovered in the signature + PlatformId = sig.Flags.IGDBPlatformId; } + determinedPlatform = Platforms.GetPlatform(PlatformId); + + IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true); StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile); } @@ -718,64 +726,78 @@ public void Rematcher(bool ForceExecute = false) Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting"); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = ""; - if (ForceExecute == false) - { - sql = "SELECT * FROM Games_Roms WHERE (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;"; - } - else - { - sql = "SELECT * FROM Games_Roms WHERE (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0));"; - } - Dictionary dbDict = new Dictionary(); - dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7)); - DataTable data = db.ExecuteCMD(sql, dbDict); - int StatusCount = -0; - foreach (DataRow row in data.Rows) - { - SetStatus(StatusCount, data.Rows.Count, "Running rematcher"); - // get library - GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]); + foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries) + { + Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch on library " + library.Name); - // get rom info - long romId = (long)row["Id"]; - string romPath = (string)row["Path"]; - Common.hashObject hash = new Common.hashObject + string sql = ""; + if (ForceExecute == false) { - md5hash = (string)row["MD5"], - sha1hash = (string)row["SHA1"] - }; - FileInfo fi = new FileInfo(romPath); - - Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath); - - // determine rom signature - gaseous_server.Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath); - - // determine rom platform - IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId); - if (determinedPlatform == null) + 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 { - determinedPlatform = new IGDB.Models.Platform(); + sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND LibraryId = @libraryid;"; } + Dictionary dbDict = new Dictionary(); + dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7)); + dbDict.Add("libraryid", library.Id); + DataTable data = db.ExecuteCMD(sql, dbDict); + int StatusCount = -0; + foreach (DataRow row in data.Rows) + { + SetStatus(StatusCount, data.Rows.Count, "Running rematcher"); - IGDB.Models.Game determinedGame = SearchForGame(sig, sig.Flags.IGDBPlatformId); + // get rom info + long romId = (long)row["Id"]; + string romPath = (string)row["Path"]; + Common.hashObject hash = new Common.hashObject + { + md5hash = (string)row["MD5"], + sha1hash = (string)row["SHA1"] + }; + FileInfo fi = new FileInfo(romPath); + + Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath); + + // determine rom signature + FileSignature fileSignature = new FileSignature(); + gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, romPath); + + // get discovered platform + long PlatformId; + IGDB.Models.Platform determinedPlatform; + + if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 ) + { + // no platform discovered in the signature + PlatformId = library.DefaultPlatformId; + } + else + { + // use the platform discovered in the signature + PlatformId = sig.Flags.IGDBPlatformId; + } + determinedPlatform = Platforms.GetPlatform(PlatformId); - StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId); + IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true); - string attemptSql = "UPDATE Games_Roms SET LastMatchAttemptDate=@lastmatchattemptdate WHERE Id=@id;"; - Dictionary dbLastAttemptDict = new Dictionary(); - dbLastAttemptDict.Add("id", romId); - dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow); - db.ExecuteCMD(attemptSql, dbLastAttemptDict); + StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId); - StatusCount += 1; - } - ClearStatus(); + string attemptSql = "UPDATE Games_Roms SET LastMatchAttemptDate=@lastmatchattemptdate WHERE Id=@id;"; + Dictionary dbLastAttemptDict = new Dictionary(); + dbLastAttemptDict.Add("id", romId); + dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow); + db.ExecuteCMD(attemptSql, dbLastAttemptDict); - Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed"); - ClearStatus(); + StatusCount += 1; + } + ClearStatus(); + + Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed"); + ClearStatus(); + } } } } diff --git a/gaseous-server/Classes/Logging.cs b/gaseous-server/Classes/Logging.cs index 69f1514c..9688e496 100644 --- a/gaseous-server/Classes/Logging.cs +++ b/gaseous-server/Classes/Logging.cs @@ -127,9 +127,8 @@ static public void Log(LogType EventType, string ServerProcess, string Message, } 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, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);"; + string sql = "INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);"; Dictionary dbDict = new Dictionary(); - dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1)); dbDict.Add("EventTime", logItem.EventTime); dbDict.Add("EventType", logItem.EventType); dbDict.Add("Process", logItem.Process); diff --git a/gaseous-server/Classes/Maintenance.cs b/gaseous-server/Classes/Maintenance.cs index 662b96ec..652e12fb 100644 --- a/gaseous-server/Classes/Maintenance.cs +++ b/gaseous-server/Classes/Maintenance.cs @@ -11,6 +11,32 @@ public class Maintenance : QueueItemStatus public void RunMaintenance() { + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = ""; + Dictionary dbDict = new Dictionary(); + + // remove any entries from the library that have an invalid id + string LibraryWhereClause = ""; + foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries) + { + if (LibraryWhereClause.Length > 0) + { + LibraryWhereClause += ", "; + } + LibraryWhereClause += library.Id; + } + string sqlLibraryWhereClause = ""; + if (LibraryWhereClause.Length > 0) + { + sqlLibraryWhereClause = "DELETE FROM Games_Roms WHERE LibraryId NOT IN ( " + LibraryWhereClause + " );"; + db.ExecuteCMD(sqlLibraryWhereClause); + } + + // delete old logs + sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate;"; + dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1)); + db.ExecuteCMD(sql, dbDict); + // delete files and directories older than 7 days in PathsToClean List PathsToClean = new List(); PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory); @@ -45,8 +71,7 @@ public void RunMaintenance() } Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables"); - Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SHOW TABLES;"; + sql = "SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE';"; DataTable tables = db.ExecuteCMD(sql); int StatusCounter = 1; diff --git a/gaseous-server/Classes/Metadata/Artworks.cs b/gaseous-server/Classes/Metadata/Artworks.cs index d0847ab4..ff90cb9f 100644 --- a/gaseous-server/Classes/Metadata/Artworks.cs +++ b/gaseous-server/Classes/Metadata/Artworks.cs @@ -67,14 +67,14 @@ private static async Task _GetArtwork(SearchUsing searchUsing, object s case Storage.CacheStatus.NotPresent: returnValue = await GetObjectFromServer(WhereClause, ImagePath); Storage.NewCacheValue(returnValue); - if (GetImages == true) { forceImageDownload = true; } + forceImageDownload = true; break; case Storage.CacheStatus.Expired: try { returnValue = await GetObjectFromServer(WhereClause, ImagePath); Storage.NewCacheValue(returnValue, true); - if (GetImages == true) { forceImageDownload = true; } + forceImageDownload = true; } catch (Exception ex) { @@ -91,12 +91,15 @@ private static async Task _GetArtwork(SearchUsing searchUsing, object s // 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 (GetImages == true) { - Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Artwork download forced."); + if ((!File.Exists(localFile)) || forceImageDownload == true) + { + Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Artwork download forced."); - Communications comms = new Communications(); - comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null); + Communications comms = new Communications(); + comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null); + } } return returnValue; diff --git a/gaseous-server/Classes/Metadata/Covers.cs b/gaseous-server/Classes/Metadata/Covers.cs index 1e337b3c..df56a35c 100644 --- a/gaseous-server/Classes/Metadata/Covers.cs +++ b/gaseous-server/Classes/Metadata/Covers.cs @@ -69,14 +69,14 @@ private static async Task _GetCover(SearchUsing searchUsing, object searc case Storage.CacheStatus.NotPresent: returnValue = await GetObjectFromServer(WhereClause, ImagePath); Storage.NewCacheValue(returnValue); - if (GetImages == true) { forceImageDownload = true; } + forceImageDownload = true; break; case Storage.CacheStatus.Expired: try { returnValue = await GetObjectFromServer(WhereClause, ImagePath); Storage.NewCacheValue(returnValue, true); - if (GetImages == true) { forceImageDownload = true; } + forceImageDownload = true; } catch (Exception ex) { @@ -92,24 +92,27 @@ private static async Task _GetCover(SearchUsing searchUsing, object searc } string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg"); - if ((!File.Exists(localFile)) || forceImageDownload == true) + if (GetImages == true) { - Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Cover download forced."); + if ((!File.Exists(localFile)) || forceImageDownload == true) + { + Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Cover download forced."); - // check for presence of image file - download if absent or force download is true - List imageSizes = new List{ - Communications.IGDBAPI_ImageSize.cover_big, - Communications.IGDBAPI_ImageSize.cover_small, - Communications.IGDBAPI_ImageSize.original - }; + // check for presence of image file - download if absent or force download is true + List imageSizes = new List{ + 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) + Communications comms = new Communications(); + foreach (Communications.IGDBAPI_ImageSize size in imageSizes) { - comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, size, null); + localFile = Path.Combine(ImagePath, size.ToString(), returnValue.ImageId + ".jpg"); + if ((!File.Exists(localFile)) || forceImageDownload == true) + { + comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, size, null); + } } } } diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs index 35b8760f..c13a671b 100644 --- a/gaseous-server/Classes/Metadata/Games.cs +++ b/gaseous-server/Classes/Metadata/Games.cs @@ -478,7 +478,7 @@ private static async Task _SearchForGameRemote(string SearchString, long } // check search cache - List? games = Communications.GetSearchCache>(searchFields, searchBody); + Game[]? games = Communications.GetSearchCache(searchFields, searchBody); if (games == null) { @@ -489,6 +489,8 @@ private static async Task _SearchForGameRemote(string SearchString, long if (allowSearch == true) { results = await comms.APIComm(IGDBClient.Endpoints.Games, searchFields, searchBody); + + Communications.SetSearchCache(searchFields, searchBody, results); } return results; @@ -499,6 +501,24 @@ private static async Task _SearchForGameRemote(string SearchString, long } } + public static List> 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 dbDict = new Dictionary(); + dbDict.Add("gameid", GameId); + DataTable data = db.ExecuteCMD(sql, dbDict); + + List> platforms = new List>(); + foreach (DataRow row in data.Rows) + { + KeyValuePair valuePair = new KeyValuePair((long)row["PlatformId"], (string)row["Name"]); + platforms.Add(valuePair); + } + + return platforms; + } + public enum SearchType { where = 0, diff --git a/gaseous-server/Classes/Metadata/Screenshots.cs b/gaseous-server/Classes/Metadata/Screenshots.cs index 9f7a531a..ce7f4560 100644 --- a/gaseous-server/Classes/Metadata/Screenshots.cs +++ b/gaseous-server/Classes/Metadata/Screenshots.cs @@ -67,14 +67,14 @@ private static async Task _GetScreenshot(SearchUsing searchUsing, ob case Storage.CacheStatus.NotPresent: returnValue = await GetObjectFromServer(WhereClause, ImagePath); Storage.NewCacheValue(returnValue); - if (GetImages == true) { forceImageDownload = true; } + forceImageDownload = true; break; case Storage.CacheStatus.Expired: try { returnValue = await GetObjectFromServer(WhereClause, ImagePath); Storage.NewCacheValue(returnValue, true); - if (GetImages == true) { forceImageDownload = true; } + forceImageDownload = true; } catch (Exception ex) { @@ -91,12 +91,15 @@ private static async Task _GetScreenshot(SearchUsing searchUsing, ob // 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 (GetImages == true) { - Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Screenshot download forced."); + if ((!File.Exists(localFile)) || forceImageDownload == true) + { + Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Screenshot download forced."); - Communications comms = new Communications(); - comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null); + Communications comms = new Communications(); + comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null); + } } return returnValue; diff --git a/gaseous-server/Classes/RomMediaGroup.cs b/gaseous-server/Classes/RomMediaGroup.cs index 79f7fe7c..887b9c64 100644 --- a/gaseous-server/Classes/RomMediaGroup.cs +++ b/gaseous-server/Classes/RomMediaGroup.cs @@ -91,7 +91,7 @@ public static List GetMediaGroupsFromGameId(long GameId) mediaGroupItems.Add(BuildMediaGroupFromRow(row)); } - mediaGroupItems.Sort((x, y) => x.PlatformName.CompareTo(y.PlatformName)); + mediaGroupItems.Sort((x, y) => x.Platform.CompareTo(y.Platform)); return mediaGroupItems; } @@ -176,6 +176,7 @@ internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row) mediaGroupItem.PlatformId = (long)row["PlatformId"]; mediaGroupItem.GameId = (long)row["GameId"]; mediaGroupItem.RomIds = new List(); + mediaGroupItem.Roms = new List(); // get members Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); @@ -186,8 +187,28 @@ internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row) foreach (DataRow dataRow in data.Rows) { mediaGroupItem.RomIds.Add((long)dataRow["RomId"]); + try + { + mediaGroupItem.Roms.Add(Roms.GetRom((long)dataRow["RomId"])); + } + catch (Exception ex) + { + Logging.Log(Logging.LogType.Warning, "Rom Group", "Unable to load ROM data", ex); + } } + // check for a web emulator and update the romItem + foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap) + { + if (platformMapping.IGDBId == mediaGroupItem.PlatformId) + { + if (platformMapping.WebEmulator != null) + { + mediaGroupItem.Emulator = platformMapping.WebEmulator; + } + } + } + return mediaGroupItem; } @@ -360,7 +381,7 @@ public class GameRomMediaGroupItem public long Id { get; set; } public long GameId { get; set; } public long PlatformId { get; set; } - public string PlatformName { + public string Platform { get { try @@ -373,7 +394,9 @@ public string PlatformName { } } } + public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; } public List RomIds { get; set; } + public List Roms { get; set; } private GroupBuildStatus _Status { get; set; } public GroupBuildStatus Status { get diff --git a/gaseous-server/Classes/Roms.cs b/gaseous-server/Classes/Roms.cs index 4a73b590..d732cab0 100644 --- a/gaseous-server/Classes/Roms.cs +++ b/gaseous-server/Classes/Roms.cs @@ -71,17 +71,6 @@ public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string Na } } - // get rom media groups - GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId); - - // sort the platforms - GameRoms.Platforms = new List>(); - foreach (DataRow platformRow in platformDT.Rows) - { - KeyValuePair valuePair = new KeyValuePair((long)platformRow["PlatformId"], (string)platformRow["Name"]); - GameRoms.Platforms.Add(valuePair); - } - return GameRoms; } else @@ -190,46 +179,17 @@ private static GameRomItem BuildRom(DataRow romDR) public class GameRomObject { - public List MediaGroups { get; set; } = new List(); public List GameRomItems { get; set; } = new List(); public int Count { get; set; } - public List> Platforms { get; set; } } public class GameRomItem : HasheousClient.Models.LookupResponseModel.RomItem { - //public long Id { get; set; } public long PlatformId { get; set; } public string Platform { get; set; } - //public Dictionary? Emulator { get; set; } public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; } public long GameId { get; set; } - //public string? Name { get; set; } - //public long Size { get; set; } - //public string? CRC { get; set; } - //public string? MD5 { get; set; } - //public string? SHA1 { get; set; } - //public string? DevelopmentStatus { get; set; } - //public string[]? Flags { get; set; } - //public List>? 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 SignatureSourceType Source { get; set; } public string? SignatureSourceGameTitle { get; set;} public GameLibrary.LibraryItem Library { get; set; } } diff --git a/gaseous-server/Controllers/V1.0/GamesController.cs b/gaseous-server/Controllers/V1.0/GamesController.cs index d4a20247..49bb3a01 100644 --- a/gaseous-server/Controllers/V1.0/GamesController.cs +++ b/gaseous-server/Controllers/V1.0/GamesController.cs @@ -288,15 +288,15 @@ public static List GetGames( [ProducesResponseType(typeof(Game), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ResponseCache(CacheProfileName = "5Minute")] - public ActionResult Game(long GameId, bool forceRefresh = false) + public ActionResult Game(long GameId) { try { - GaseousGame gameObject = new GaseousGame(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 { @@ -767,6 +767,24 @@ public ActionResult GameCompanyImage(long GameId, long CompanyId) } } + [MapToApiVersion("1.0")] + [MapToApiVersion("1.1")] + [HttpGet] + [Route("{GameId}/platforms")] + [ProducesResponseType(typeof(List>), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult GamePlatforms(long GameId) + { + try + { + return Ok(Games.GetAvailablePlatforms(GameId)); + } + catch + { + return NotFound(); + } + } + [MapToApiVersion("1.0")] [MapToApiVersion("1.1")] [HttpGet] @@ -1022,6 +1040,35 @@ public ActionResult GameRomGroup(long GameId, long RomGroupId) } } + [MapToApiVersion("1.0")] + [MapToApiVersion("1.1")] + [HttpGet] + [Authorize(Roles = "Admin,Gamer")] + [Route("{GameId}/romgroup")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult GetGameRomGroup(long GameId) + { + try + { + Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); + + try + { + return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId)); + } + catch (Exception ex) + { + Logging.Log(Logging.LogType.Critical, "Rom Group", "An error occurred", ex); + return NotFound(); + } + } + catch + { + return NotFound(); + } + } + [MapToApiVersion("1.0")] [MapToApiVersion("1.1")] [HttpPost] @@ -1174,7 +1221,8 @@ public ActionResult GameSearch(long RomId = 0, string SearchString = "") { Classes.Roms.GameRomItem romItem = Classes.Roms.GetRom(RomId); Common.hashObject hash = new Common.hashObject(romItem.Path); - gaseous_server.Models.Signatures_Games romSig = FileSignature.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 searchResults = Classes.ImportGame.SearchForGame_GetAll(romSig.Game.Name, romSig.Flags.IGDBPlatformId); return Ok(searchResults); diff --git a/gaseous-server/Models/GaseousGame.cs b/gaseous-server/Models/GaseousGame.cs index 9a97f6ae..8f6e053c 100644 --- a/gaseous-server/Models/GaseousGame.cs +++ b/gaseous-server/Models/GaseousGame.cs @@ -47,48 +47,48 @@ public IGDB.Models.Cover? CoverItem } } - public List? ArtworksItem - { - get - { - if (this.Artworks != null) - { - if (this.Artworks.Ids != null) - { - List artworks = new List(); - foreach (long id in this.Artworks.Ids) - { - artworks.Add(gaseous_server.Classes.Metadata.Artworks.GetArtwork(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false)); - } + // public List? ArtworksItem + // { + // get + // { + // if (this.Artworks != null) + // { + // if (this.Artworks.Ids != null) + // { + // List artworks = new List(); + // 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 artworks; + // } + // } - return null; - } - } + // return null; + // } + // } - public List? ScreenshotsItem - { - get - { - if (this.Screenshots != null) - { - if (this.Screenshots.Ids != null) - { - List screenshots = new List(); - foreach (long id in this.Screenshots.Ids) - { - screenshots.Add(gaseous_server.Classes.Metadata.Screenshots.GetScreenshot(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false)); - } + // public List? ScreenshotsItem + // { + // get + // { + // if (this.Screenshots != null) + // { + // if (this.Screenshots.Ids != null) + // { + // List screenshots = new List(); + // 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 screenshots; + // } + // } - return null; - } - } + // return null; + // } + // } } } \ No newline at end of file diff --git a/gaseous-server/Models/PlatformMapping.cs b/gaseous-server/Models/PlatformMapping.cs index 0503e787..10732c21 100644 --- a/gaseous-server/Models/PlatformMapping.cs +++ b/gaseous-server/Models/PlatformMapping.cs @@ -371,14 +371,19 @@ static PlatformMapItem BuildPlatformMapItem(DataRow row) return null; } - public static void GetIGDBPlatformMapping(ref gaseous_server.Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName) + public static void GetIGDBPlatformMapping(ref gaseous_server.Models.Signatures_Games Signature, string ImageExtension, bool SetSystemName) { + if (Signature.Game != null) + { + Logging.Log(Logging.LogType.Information, "Platform Mapping", "Determining platform based on extension " + ImageExtension + " or \"" + Signature.Game.System + "\""); + } + bool PlatformFound = false; foreach (Models.PlatformMapping.PlatformMapItem PlatformMapping in Models.PlatformMapping.PlatformMap) { if (PlatformMapping.Extensions != null) { - if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(RomFileInfo.Extension, StringComparer.OrdinalIgnoreCase)) + if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(ImageExtension, StringComparer.OrdinalIgnoreCase)) { if (SetSystemName == true) { @@ -388,6 +393,8 @@ public static void GetIGDBPlatformMapping(ref gaseous_server.Models.Signatures_G Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName; PlatformFound = true; + + Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from file extension"); break; } } @@ -410,10 +417,17 @@ public static void GetIGDBPlatformMapping(ref gaseous_server.Models.Signatures_G Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName; PlatformFound = true; + + Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from signature system to platform map"); break; } } } + + if (PlatformFound == false) + { + Logging.Log(Logging.LogType.Information, "Platform Mapping", "Unable to determine platform"); + } } public class PlatformMapItem diff --git a/gaseous-server/ProcessQueue.cs b/gaseous-server/ProcessQueue.cs index 8cfd7c20..51282001 100644 --- a/gaseous-server/ProcessQueue.cs +++ b/gaseous-server/ProcessQueue.cs @@ -2,6 +2,8 @@ using System.ComponentModel.Design.Serialization; using System.Data; using gaseous_server.Classes; +using NuGet.Common; +using NuGet.Packaging; namespace gaseous_server { @@ -239,6 +241,32 @@ public void Execute() maintenance.RunMaintenance(); break; + case QueueItemType.TempCleanup: + try + { + foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries) + { + string rootPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, libraryItem.Id.ToString()); + if (Directory.Exists(rootPath)) + { + foreach (string directory in Directory.GetDirectories(rootPath)) + { + DirectoryInfo info = new DirectoryInfo(directory); + if (info.LastWriteTimeUtc.AddMinutes(5) < DateTime.UtcNow) + { + Logging.Log(Logging.LogType.Information, "Get Signature", "Deleting temporary decompress folder: " + directory); + Directory.Delete(directory, true); + } + } + } + } + } + catch (Exception tcEx) + { + Logging.Log(Logging.LogType.Warning, "Get Signature", "An error occurred while cleaning temporary files", tcEx); + } + break; + } } catch (Exception ex) @@ -394,7 +422,12 @@ public enum QueueItemType /// /// Performs a clean up of old files, and optimises the database /// - Maintainer + Maintainer, + + /// + /// Cleans up marked paths in the temporary directory + /// + TempCleanup } public enum QueueItemState diff --git a/gaseous-server/Program.cs b/gaseous-server/Program.cs index ca1477ba..57026e7f 100644 --- a/gaseous-server/Program.cs +++ b/gaseous-server/Program.cs @@ -476,6 +476,16 @@ }) ); +ProcessQueue.QueueItem tempCleanup = new ProcessQueue.QueueItem( + ProcessQueue.QueueItemType.TempCleanup, + 1, + new List(), + false, + false + ); +tempCleanup.ForceExecute(); +ProcessQueue.QueueItems.Add(tempCleanup); + Logging.WriteToDiskOnly = false; // start the app diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1014.sql b/gaseous-server/Support/Database/MySQL/gaseous-1014.sql new file mode 100644 index 00000000..04bac008 --- /dev/null +++ b/gaseous-server/Support/Database/MySQL/gaseous-1014.sql @@ -0,0 +1,2 @@ +ALTER TABLE `RomCollections` +ADD COLUMN `ArchiveType` INT NULL AFTER `IncludeBIOSFiles`; \ No newline at end of file diff --git a/gaseous-server/Support/PlatformMap.json b/gaseous-server/Support/PlatformMap.json index 3216f830..1e778f4c 100644 --- a/gaseous-server/Support/PlatformMap.json +++ b/gaseous-server/Support/PlatformMap.json @@ -188,6 +188,91 @@ } ] }, + { + "igdbId": 114, + "igdbName": "Amiga CD32", + "igdbSlug": "amiga-cd32", + "alternateNames": [ + "Amiga CD32", + "Commodore Amiga CD32" + ], + "extensions": { + "supportedFileExtensions": [ + ".ZIP" + ], + "uniqueFileExtensions": [ + ] + }, + "retroPieDirectoryName": "amiga", + "webEmulator": { + "type": "EmulatorJS", + "core": "amiga", + "availableWebEmulators": [ + { + "emulatorType": "EmulatorJS", + "availableWebEmulatorCores": [ + { + "core": "amiga", + "alternateCoreName": "puae", + "default": true + } + ] + } + ] + }, + "bios": [ + { + "hash": "85ad74194e87c08904327de1a9443b7a", + "description": "Kickstart v1.2 rev 33.180", + "filename": "kick33180.A500" + }, + { + "hash": "82a21c1890cae844b3df741f2762d48d", + "description": "Kickstart v1.3 rev 34.005", + "filename": "kick34005.A500" + }, + { + "hash": "89da1838a24460e4b93f4f0c5d92d48d", + "description": "CDTV extended ROM v1.00", + "filename": "kick34005.CDTV" + }, + { + "hash": "dc10d7bdd1b6f450773dfb558477c230", + "description": "Kickstart v2.04 rev 37.175", + "filename": "kick37175.A500" + }, + { + "hash": "5f8924d013dd57a89cf349f4cdedc6b1", + "description": "CD32 Kickstart v3.1 rev 40.060", + "filename": "kick40060.CD32" + }, + { + "hash": "f2f241bf094168cfb9e7805dc2856433", + "description": "CD32 KS + extended v3.1 rev 40.060", + "filename": "kick40060.CD32" + }, + { + "hash": "bb72565701b1b6faece07d68ea5da639", + "description": "CD32 extended ROM rev 40.060", + "filename": "kick40060.CD32.ext" + }, + { + "hash": "e40a5dfb3d017ba8779faba30cbd1c8e", + "description": "Kickstart v3.1 rev 40.063", + "filename": "kick40063.A600" + }, + { + "hash": "646773759326fbac3b2311fd8c8793ee", + "description": "Kickstart v3.1 rev 40.068", + "filename": "kick40068.A1200" + }, + { + "hash": "9bdedde6a4f33555b4a270c8ca53297d", + "description": "Kickstart v3.1 rev 40.068", + "filename": "kick40068.A4000" + } + ] + }, { "igdbId": 25, "igdbName": "Amstrad CPC", @@ -543,6 +628,48 @@ } ] }, + { + "igdbId": 68, + "igdbName": "ColecoVision", + "igdbSlug": "colecovision", + "alternateNames": [ + "ColecoVision" + ], + "extensions": { + "supportedFileExtensions": [ + ".BIN", + ".COL", + ".ROM", + ".ZIP" + ], + "uniqueFileExtensions": [ + ] + }, + "retroPieDirectoryName": "coleco", + "webEmulator": { + "type": "EmulatorJS", + "core": "coleco", + "availableWebEmulators": [ + { + "emulatorType": "EmulatorJS", + "availableWebEmulatorCores": [ + { + "core": "coleco", + "alternateCoreName": "coleco", + "default": true + } + ] + } + ] + }, + "bios": [ + { + "hash": "2c66f5911e5b42b8ebe113403548eee7", + "description": "ColecoVision BIOS - Mandatory", + "filename": "colecovision.rom" + } + ] + }, { "igdbId": 15, "igdbName": "Commodore C64/128/MAX", @@ -620,6 +747,92 @@ } ] }, + { + "igdbId": 158, + "igdbName": "Commodore CDTV", + "igdbSlug": "commodore-cdtv", + "alternateNames": [ + "Commodore CDTV", + "Amiga CDTV", + "Commodore Amiga CDTV" + ], + "extensions": { + "supportedFileExtensions": [ + ".ZIP" + ], + "uniqueFileExtensions": [ + ] + }, + "retroPieDirectoryName": "amiga", + "webEmulator": { + "type": "EmulatorJS", + "core": "amiga", + "availableWebEmulators": [ + { + "emulatorType": "EmulatorJS", + "availableWebEmulatorCores": [ + { + "core": "amiga", + "alternateCoreName": "puae", + "default": true + } + ] + } + ] + }, + "bios": [ + { + "hash": "85ad74194e87c08904327de1a9443b7a", + "description": "Kickstart v1.2 rev 33.180", + "filename": "kick33180.A500" + }, + { + "hash": "82a21c1890cae844b3df741f2762d48d", + "description": "Kickstart v1.3 rev 34.005", + "filename": "kick34005.A500" + }, + { + "hash": "89da1838a24460e4b93f4f0c5d92d48d", + "description": "CDTV extended ROM v1.00", + "filename": "kick34005.CDTV" + }, + { + "hash": "dc10d7bdd1b6f450773dfb558477c230", + "description": "Kickstart v2.04 rev 37.175", + "filename": "kick37175.A500" + }, + { + "hash": "5f8924d013dd57a89cf349f4cdedc6b1", + "description": "CD32 Kickstart v3.1 rev 40.060", + "filename": "kick40060.CD32" + }, + { + "hash": "f2f241bf094168cfb9e7805dc2856433", + "description": "CD32 KS + extended v3.1 rev 40.060", + "filename": "kick40060.CD32" + }, + { + "hash": "bb72565701b1b6faece07d68ea5da639", + "description": "CD32 extended ROM rev 40.060", + "filename": "kick40060.CD32.ext" + }, + { + "hash": "e40a5dfb3d017ba8779faba30cbd1c8e", + "description": "Kickstart v3.1 rev 40.063", + "filename": "kick40063.A600" + }, + { + "hash": "646773759326fbac3b2311fd8c8793ee", + "description": "Kickstart v3.1 rev 40.068", + "filename": "kick40068.A1200" + }, + { + "hash": "9bdedde6a4f33555b4a270c8ca53297d", + "description": "Kickstart v3.1 rev 40.068", + "filename": "kick40068.A4000" + } + ] + }, { "igdbId": 33, "igdbName": "Game Boy", diff --git a/gaseous-server/gaseous-server.csproj b/gaseous-server/gaseous-server.csproj index 41fcf666..efe47df9 100644 --- a/gaseous-server/gaseous-server.csproj +++ b/gaseous-server/gaseous-server.csproj @@ -58,6 +58,7 @@ + @@ -93,5 +94,6 @@ + diff --git a/gaseous-server/wwwroot/pages/collections.html b/gaseous-server/wwwroot/pages/collections.html index 763d0bf6..ce3f0578 100644 --- a/gaseous-server/wwwroot/pages/collections.html +++ b/gaseous-server/wwwroot/pages/collections.html @@ -42,15 +42,18 @@

Collections

var statusText = result[i].buildStatus; var downloadLink = ''; var packageSize = '-'; + var inProgress = false; switch (result[i].buildStatus) { case 'NoStatus': statusText = '-'; break; case "WaitingForBuild": statusText = 'Build pending'; + inProgress = true; break; case "Building": statusText = 'Building'; + inProgress = true; break; case "Completed": statusText = 'Available'; @@ -64,6 +67,10 @@

Collections

statusText = result[i].buildStatus; break; } + + if (inProgress == true) { + setTimeout(GetCollections, 10000); + } var editButton = ''; var deleteButton = ''; diff --git a/gaseous-server/wwwroot/pages/dialogs/collectionedit.html b/gaseous-server/wwwroot/pages/dialogs/collectionedit.html index 2cdef568..7cd27f8f 100644 --- a/gaseous-server/wwwroot/pages/dialogs/collectionedit.html +++ b/gaseous-server/wwwroot/pages/dialogs/collectionedit.html @@ -97,6 +97,18 @@

Options

BIOS files for each platform will be stored in /BIOS + diff --git a/gaseous-server/wwwroot/pages/dialogs/mediagroupdelete.html b/gaseous-server/wwwroot/pages/dialogs/mediagroupdelete.html index af6bcc9e..70cc2ee0 100644 --- a/gaseous-server/wwwroot/pages/dialogs/mediagroupdelete.html +++ b/gaseous-server/wwwroot/pages/dialogs/mediagroupdelete.html @@ -16,10 +16,12 @@ 'DELETE', function (result) { loadRoms(); + loadMediaGroups(); closeSubDialog(); }, function (error) { loadRoms(); + loadMediaGroups(); closeSubDialog(); } ); diff --git a/gaseous-server/wwwroot/pages/game.html b/gaseous-server/wwwroot/pages/game.html index 9c97841a..43159c73 100644 --- a/gaseous-server/wwwroot/pages/game.html +++ b/gaseous-server/wwwroot/pages/game.html @@ -71,6 +71,7 @@

Age Ratings

Edit @@ -85,6 +86,21 @@

ROMs/Images

+
+
+ +
+
+ +
+
+ +
+
+ 0 ROMs +
+
+
-
@@ -38,9 +37,8 @@