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
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System.IO.Compression;
|
||||
using HasheousClient.Models;
|
||||
using SevenZip;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
@@ -10,6 +12,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
public static gaseous_server.Models.Signatures_Games GetFileSignature(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);
|
||||
|
||||
@@ -20,81 +23,152 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
// file is a zip and less than 1 GiB
|
||||
// extract the zip file and search the contents
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing " + GameFileImportPath + " to examine contents");
|
||||
|
||||
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, 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":
|
||||
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath);
|
||||
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":
|
||||
using (var archive = RarArchive.Open(GameFileImportPath))
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using rar");
|
||||
try
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
using (var archive = RarArchive.Open(GameFileImportPath))
|
||||
{
|
||||
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
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":
|
||||
using (var archive = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(GameFileImportPath))
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using 7z");
|
||||
try
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
using (var archive = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(GameFileImportPath))
|
||||
{
|
||||
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
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))
|
||||
{
|
||||
FileInfo zfi = new FileInfo(file);
|
||||
Common.hashObject zhash = new Common.hashObject(file);
|
||||
|
||||
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file, true);
|
||||
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ImportedFileExtension);
|
||||
|
||||
if (zDiscoveredSignature.Score > discoveredSignature.Score)
|
||||
if (File.Exists(file))
|
||||
{
|
||||
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;
|
||||
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);
|
||||
|
||||
break;
|
||||
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, 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 zip file: " + GameFileImportPath, ex);
|
||||
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing compressed file: " + GameFileImportPath, ex);
|
||||
}
|
||||
|
||||
if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); }
|
||||
if (Directory.Exists(ExtractPath))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Deleting temporary decompress folder: " + ExtractPath);
|
||||
|
||||
Directory.Delete(ExtractPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredSignature;
|
||||
@@ -110,6 +184,7 @@ namespace gaseous_server.Classes
|
||||
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
|
||||
@@ -120,12 +195,16 @@ namespace gaseous_server.Classes
|
||||
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, fi, GameFileImportPath);
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
}
|
||||
@@ -282,5 +361,14 @@ namespace gaseous_server.Classes
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,44 +16,37 @@ using HasheousClient.Models;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class ImportGames : QueueItemStatus
|
||||
public class ImportGame : QueueItemStatus
|
||||
{
|
||||
public ImportGames(string ImportPath)
|
||||
{
|
||||
if (Directory.Exists(ImportPath))
|
||||
public void ProcessDirectory(string ImportPath)
|
||||
{
|
||||
if (Directory.Exists(ImportPath))
|
||||
{
|
||||
string[] importContents_Files = Directory.GetFiles(ImportPath);
|
||||
string[] importContents_Directories = Directory.GetDirectories(ImportPath);
|
||||
string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
|
||||
|
||||
// import files first
|
||||
int importCount = 1;
|
||||
foreach (string importContent in importContents_Files) {
|
||||
SetStatus(importCount, importContents_Files.Length, "Importing file: " + importContent);
|
||||
foreach (string importContent in importContents) {
|
||||
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
|
||||
|
||||
ImportGame.ImportGameFile(importContent, null);
|
||||
ImportGameFile(importContent, null);
|
||||
|
||||
importCount += 1;
|
||||
}
|
||||
ClearStatus();
|
||||
|
||||
// import sub directories
|
||||
foreach (string importDir in importContents_Directories) {
|
||||
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
|
||||
}
|
||||
DeleteOrphanedDirectories(ImportPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
|
||||
throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class ImportGame : QueueItemStatus
|
||||
{
|
||||
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
|
||||
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
@@ -443,7 +436,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static void OrganiseLibrary()
|
||||
public void OrganiseLibrary()
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
|
||||
|
||||
@@ -477,8 +470,12 @@ namespace gaseous_server.Classes
|
||||
foreach (var directory in Directory.GetDirectories(startLocation))
|
||||
{
|
||||
DeleteOrphanedDirectories(directory);
|
||||
if (Directory.GetFiles(directory).Length == 0 &&
|
||||
Directory.GetDirectories(directory).Length == 0)
|
||||
|
||||
string[] files = Directory.GetFiles(directory);
|
||||
string[] directories = Directory.GetDirectories(directory);
|
||||
|
||||
if (files.Length == 0 &&
|
||||
directories.Length == 0)
|
||||
{
|
||||
Directory.Delete(directory, false);
|
||||
}
|
||||
@@ -621,16 +618,16 @@ namespace gaseous_server.Classes
|
||||
if (romFound == false)
|
||||
{
|
||||
// file is not in database - process it
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
|
||||
|
||||
Common.hashObject hash = new Common.hashObject(LibraryFile);
|
||||
FileInfo fi = new FileInfo(LibraryFile);
|
||||
|
||||
gaseous_server.Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
|
||||
|
||||
// get discovered platform
|
||||
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
|
||||
|
||||
|
||||
IGDB.Models.Game determinedGame = new Game();
|
||||
try
|
||||
{
|
||||
@@ -687,8 +684,13 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
if (romPath != ComputeROMPath(romId))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found, but needs to be moved");
|
||||
MoveGameFile(romId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using Humanizer;
|
||||
@@ -422,6 +423,56 @@ namespace gaseous_server.Classes.Metadata
|
||||
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>
|
||||
|
@@ -477,16 +477,26 @@ namespace gaseous_server.Classes.Metadata
|
||||
break;
|
||||
}
|
||||
|
||||
// check search cache
|
||||
List<Game>? games = Communications.GetSearchCache<List<Game>>(searchFields, searchBody);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
else
|
||||
{
|
||||
return games.ToArray();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public enum SearchType
|
||||
|
@@ -77,7 +77,8 @@ namespace gaseous_server.Controllers
|
||||
// Process uploaded files
|
||||
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
|
||||
{
|
||||
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||
Classes.ImportGame uploadImport = new ImportGame();
|
||||
uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||
}
|
||||
|
||||
if (Directory.Exists(workPath))
|
||||
|
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using gaseous_server.Models;
|
||||
using IGDB;
|
||||
using IGDB.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NuGet.Common;
|
||||
using static gaseous_server.Classes.Metadata.Games;
|
||||
|
||||
|
||||
@@ -37,36 +40,88 @@ namespace gaseous_server.Controllers
|
||||
string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
|
||||
searchBody += "where name ~ *\"" + SearchString + "\"*;";
|
||||
|
||||
// get Platform metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
|
||||
List<Platform>? searchCache = Communications.GetSearchCache<List<Platform>>(searchFields, searchBody);
|
||||
|
||||
return results.ToList();
|
||||
if (searchCache == null)
|
||||
{
|
||||
// cache miss
|
||||
// get Platform metadata from data source
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
|
||||
|
||||
Communications.SetSearchCache<List<Platform>>(searchFields, searchBody, results.ToList());
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return searchCache;
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("Game")]
|
||||
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(List<GaseousGame>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
|
||||
{
|
||||
List<Game> RetVal = await _SearchForGame(PlatformId, SearchString);
|
||||
List<GaseousGame> RetVal = await _SearchForGame(PlatformId, SearchString);
|
||||
return Ok(RetVal);
|
||||
}
|
||||
|
||||
private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString)
|
||||
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
|
||||
{
|
||||
string searchBody = "";
|
||||
string searchFields = "fields cover.*,first_release_date,name,platforms,slug; ";
|
||||
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
|
||||
searchBody += "search \"" + SearchString + "\";";
|
||||
searchBody += "where platforms = (" + PlatformId + ");";
|
||||
|
||||
// get Platform metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
||||
List<GaseousGame>? searchCache = Communications.GetSearchCache<List<GaseousGame>>(searchFields, searchBody);
|
||||
|
||||
return results.ToList();
|
||||
if (searchCache == null)
|
||||
{
|
||||
// cache miss
|
||||
// get Game metadata from data source
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
||||
|
||||
List<GaseousGame> games = new List<GaseousGame>();
|
||||
foreach (Game game in results.ToList())
|
||||
{
|
||||
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
|
||||
switch(cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
Storage.NewCacheValue(game, false);
|
||||
break;
|
||||
|
||||
case Storage.CacheStatus.Expired:
|
||||
Storage.NewCacheValue(game, true);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
games.Add(new GaseousGame(game));
|
||||
}
|
||||
|
||||
Communications.SetSearchCache<List<GaseousGame>>(searchFields, searchBody, games);
|
||||
|
||||
return games;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get full version of results from database
|
||||
// this is a hacky workaround due to the readonly nature of IGDB.Model.Game IdentityOrValue fields
|
||||
List<GaseousGame> gamesToReturn = new List<GaseousGame>();
|
||||
foreach (GaseousGame game in searchCache)
|
||||
{
|
||||
Game tempGame = Games.GetGame((long)game.Id, false, false, false);
|
||||
gamesToReturn.Add(new GaseousGame(tempGame));
|
||||
}
|
||||
|
||||
return gamesToReturn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using System.Reflection;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using IGDB;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
|
@@ -144,10 +144,11 @@ namespace gaseous_server
|
||||
|
||||
case QueueItemType.TitleIngestor:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
|
||||
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory)
|
||||
Classes.ImportGame import = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
import.ProcessDirectory(Config.LibraryConfiguration.LibraryImportDirectory);
|
||||
|
||||
// clean up
|
||||
Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory);
|
||||
@@ -170,7 +171,11 @@ namespace gaseous_server
|
||||
|
||||
case QueueItemType.OrganiseLibrary:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser");
|
||||
Classes.ImportGame.OrganiseLibrary();
|
||||
Classes.ImportGame importLibraryOrg = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
importLibraryOrg.OrganiseLibrary();
|
||||
|
||||
_SaveLastRunTime = true;
|
||||
|
||||
@@ -178,11 +183,11 @@ namespace gaseous_server
|
||||
|
||||
case QueueItemType.LibraryScan:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanners");
|
||||
Classes.ImportGame import = new ImportGame
|
||||
Classes.ImportGame libScan = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
import.LibraryScan();
|
||||
libScan.LibraryScan();
|
||||
|
||||
_SaveLastRunTime = true;
|
||||
|
||||
|
5
gaseous-server/Support/Database/MySQL/gaseous-1012.sql
Normal file
5
gaseous-server/Support/Database/MySQL/gaseous-1012.sql
Normal 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;
|
9
gaseous-server/Support/Database/MySQL/gaseous-1013.sql
Normal file
9
gaseous-server/Support/Database/MySQL/gaseous-1013.sql
Normal 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`)
|
||||
);
|
@@ -56,6 +56,8 @@
|
||||
<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="Classes\Metadata\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -89,5 +91,7 @@
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<div id="properties_toc">
|
||||
<div id="properties_toc_general" name="properties_toc_item" onclick="SelectTab('general');">General</div>
|
||||
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');">Attributes</div>
|
||||
<div id="properties_toc_archive" name="properties_toc_item" onclick="SelectTab('archive');" style="display: none;">Archive Contents</div>
|
||||
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');" style="display: none;">Attributes</div>
|
||||
<div id="properties_toc_match" name="properties_toc_item" onclick="SelectTab('match');">Title Match</div>
|
||||
<!--<div id="properties_toc_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>-->
|
||||
</div>
|
||||
@@ -53,6 +54,10 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="properties_bodypanel_archive" name="properties_tab" style="display: none;">
|
||||
<div id="properties_bodypanel_archive_content" style="height: 315px; overflow-x: scroll;"></div>
|
||||
</div>
|
||||
|
||||
<div id="properties_bodypanel_attributes" name="properties_tab" style="display: none;">
|
||||
|
||||
</div>
|
||||
@@ -120,7 +125,6 @@
|
||||
|
||||
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) {
|
||||
romData = result;
|
||||
console.log(romData);
|
||||
document.getElementById('modal-heading').innerHTML = result.name;
|
||||
document.getElementById('rominfo_library').innerHTML = result.library.name;
|
||||
document.getElementById('rominfo_platform').innerHTML = result.platform;
|
||||
@@ -142,8 +146,7 @@
|
||||
|
||||
if (result.attributes.length > 0) {
|
||||
document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
|
||||
} else {
|
||||
document.getElementById('properties_toc_attributes').style.display = 'none';
|
||||
document.getElementById('properties_bodypanel_archive_content').appendChild(BuildArchiveTable(result.attributes, result.source));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -199,12 +202,9 @@
|
||||
|
||||
$('#properties_fixplatform').on('select2:select', function (e) {
|
||||
var platformData = e.params.data;
|
||||
console.log(platformData);
|
||||
|
||||
var gameValue = $('#properties_fixgame').select2('data');
|
||||
if (gameValue) {
|
||||
console.log(gameValue[0]);
|
||||
|
||||
setFixGameDropDown();
|
||||
}
|
||||
});
|
||||
@@ -264,23 +264,75 @@
|
||||
aTable.style.width = '100%';
|
||||
|
||||
for (var i = 0; i < attributes.length; i++) {
|
||||
var aRow = document.createElement('tr');
|
||||
if (attributes[i].key != "ZipContents") {
|
||||
// show attributes button
|
||||
document.getElementById('properties_toc_attributes').style.display = '';
|
||||
var aRow = document.createElement('tr');
|
||||
|
||||
var aTitleCell = document.createElement('th');
|
||||
aTitleCell.width = "25%";
|
||||
if (sourceName == "TOSEC") {
|
||||
aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key);
|
||||
} else {
|
||||
aTitleCell.innerHTML = attributes[i].key;
|
||||
var aTitleCell = document.createElement('th');
|
||||
aTitleCell.width = "25%";
|
||||
if (sourceName == "TOSEC") {
|
||||
aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key);
|
||||
} else {
|
||||
aTitleCell.innerHTML = attributes[i].key;
|
||||
}
|
||||
aRow.appendChild(aTitleCell);
|
||||
|
||||
var aValueCell = document.createElement('td');
|
||||
aValueCell.width = "75%";
|
||||
aValueCell.innerHTML = attributes[i].value;
|
||||
aRow.appendChild(aValueCell);
|
||||
|
||||
aTable.appendChild(aRow);
|
||||
}
|
||||
aRow.appendChild(aTitleCell);
|
||||
}
|
||||
|
||||
var aValueCell = document.createElement('td');
|
||||
aValueCell.width = "75%";
|
||||
aValueCell.innerHTML = attributes[i].value;
|
||||
aRow.appendChild(aValueCell);
|
||||
return aTable;
|
||||
}
|
||||
|
||||
aTable.appendChild(aRow);
|
||||
function BuildArchiveTable(attributes, sourceName) {
|
||||
for (var i = 0; i < attributes.length; i++) {
|
||||
if (attributes[i].key == "ZipContents") {
|
||||
var archiveContent = JSON.parse(attributes[i].value);
|
||||
|
||||
// show archive button
|
||||
document.getElementById('properties_toc_archive').style.display = '';
|
||||
|
||||
var aTable = document.createElement('table');
|
||||
aTable.className = 'romtable';
|
||||
aTable.setAttribute('cellspacing', 0);
|
||||
aTable.style.width = '100%';
|
||||
|
||||
for (var r = 0; r < archiveContent.length; r++) {
|
||||
var aBody = document.createElement('tbody');
|
||||
aBody.className = 'romrow';
|
||||
|
||||
var aRow = document.createElement('tr');
|
||||
|
||||
var aNameCell = document.createElement('th');
|
||||
aNameCell.className = 'romcell';
|
||||
aNameCell.innerHTML = archiveContent[r].FilePath + '/' + archiveContent[r].FileName;
|
||||
aRow.appendChild(aNameCell);
|
||||
|
||||
var aSizeCell = document.createElement('td');
|
||||
aSizeCell.className = 'romcell';
|
||||
aSizeCell.innerHTML = formatBytes(archiveContent[r].Size);
|
||||
aRow.appendChild(aSizeCell);
|
||||
|
||||
aBody.appendChild(aRow);
|
||||
|
||||
var hRow = document.createElement('tr');
|
||||
|
||||
var aHashCell = document.createElement('td');
|
||||
aHashCell.setAttribute('colspan', 2);
|
||||
aHashCell.style.paddingLeft = '20px';
|
||||
aHashCell.innerHTML = "MD5: " + archiveContent[r].MD5 + "<br />SHA1: " + archiveContent[r].SHA1;
|
||||
hRow.appendChild(aHashCell);
|
||||
aBody.appendChild(hRow);
|
||||
|
||||
aTable.appendChild(aBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aTable;
|
||||
|
@@ -245,8 +245,6 @@ function intToRGB(i) {
|
||||
}
|
||||
|
||||
function DropDownRenderGameOption(state) {
|
||||
console.log(state);
|
||||
|
||||
if (state.loading) {
|
||||
return state;
|
||||
}
|
||||
@@ -260,7 +258,7 @@ function DropDownRenderGameOption(state) {
|
||||
|
||||
if (state.cover) {
|
||||
response = $(
|
||||
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.cover.value.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
|
||||
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.cover.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
|
||||
);
|
||||
} else {
|
||||
response = $(
|
||||
|
Reference in New Issue
Block a user