Compare commits

...

52 Commits

Author SHA1 Message Date
Michael Green
68bd3d8408 WIP 2025-01-11 21:41:59 +11:00
Michael Green
25ce6979ad WIP 2025-01-11 12:48:49 +11:00
Michael Green
9145688d35 Merge branch 'main' into michael-j-green/issue86 2025-01-11 01:46:15 +11:00
Michael Green
d07300ecb5 WIP 2025-01-11 01:42:06 +11:00
Michael Green
86c207e389 WIP 2025-01-10 17:35:29 +11:00
Michael Green
a0a8495597 WIP 2025-01-09 16:22:08 +11:00
Michael Green
aa90b4ebc3 WIP 2025-01-09 15:31:28 +11:00
Michael Green
9e93ed1041 WIP 2025-01-09 01:54:24 +11:00
Michael Green
f8b9ca3b66 WIP 2025-01-09 01:34:48 +11:00
Michael Green
6863f65640 WIP 2025-01-09 00:48:34 +11:00
Michael Green
7c327eeb3e WIP 2025-01-07 12:40:26 +11:00
Michael Green
202c2c0b62 WIP 2025-01-07 11:58:55 +11:00
Michael Green
da98b1cd74 WIP 2025-01-07 00:48:56 +11:00
Michael Green
fc0d164a96 WIP 2025-01-06 22:55:06 +11:00
Michael Green
8153c42fe1 WIP 2025-01-04 09:38:46 +11:00
Michael Green
97645af336 WIP 2025-01-03 00:27:26 +11:00
Michael Green
42370a9699 WIP 2025-01-01 21:33:35 +11:00
Michael Green
1727a62150 WIP 2024-12-28 16:19:05 +11:00
Michael Green
c170d98a68 WIP 2024-12-27 16:35:32 +11:00
Michael Green
ae41fae54f WIP 2024-12-27 08:07:10 +11:00
Michael Green
ffd4dcbb33 WIP 2024-12-19 21:02:41 +11:00
Michael Green
c724d927bb WIP 2024-12-19 09:15:01 +11:00
Michael Green
a859d873e6 WIP 2024-12-15 12:32:27 +11:00
Michael Green
7a9827923f WIP 2024-12-15 11:17:13 +11:00
Michael Green
142f3ed561 WIP 2024-12-14 22:02:32 +11:00
Michael Green
2303ab5ffc WIP 2024-12-06 16:48:20 +11:00
Michael Green
173e49286b WIP 2024-12-06 12:25:17 +11:00
Michael Green
ca2dbfb4cd WIP 2024-11-20 11:16:10 +11:00
Michael Green
74171b50af WIP 2024-11-17 22:04:26 +11:00
Michael Green
b1fcfec14f Fix for generating launch links 2024-10-30 23:14:12 +11:00
Michael Green
e8ca8ca5ca Fix for EJS version bump 2024-10-28 23:29:23 +11:00
Michael Green
6f5b52058c Updated to the latest version of EJS (see: https://github.com/EmulatorJS/EmulatorJS/releases/tag/v4.1.1) 2024-10-28 23:18:49 +11:00
Michael Green
8b9d7c66ab Merge v1.7.7 update - #445 2024-10-28 23:14:31 +11:00
Michael Green
f19214ba6b Settings UI changes complete 2024-10-28 11:00:51 +11:00
Michael Green
2d63a1c416 Nightly build (#450) 2024-10-26 20:10:11 +11:00
Michael Green
f99eec2eec WIP - All server communications can now use Hasheous 2024-10-22 23:37:34 +11:00
Michael Green
b3ca94b217 WIP 2024-10-21 14:32:13 +11:00
Michael Green
53c79287a9 WIP - Bug hunting 2024-10-20 12:14:13 +11:00
Michael Green
48e4f17090 Fixed ratings loading in the preferences 2024-10-20 02:10:59 +11:00
Michael Green
478044a5a1 WIP - Images are now pulled via Hasheous proxy 2024-10-20 01:25:25 +11:00
Michael Green
f25b3d0eaa WIP - Can now pull via the Hasheous metadata proxy 2024-10-19 09:10:24 +11:00
Michael Green
574d3bc39e WIP 2024-10-18 10:25:48 +11:00
Michael Green
bca4dd178e Various BIOS file bug fixes 2024-10-02 15:38:06 +10:00
Michael Green
4a76436223 Hide favourite icon in similar games list + minor UI tweaks and fixes 2024-10-02 13:08:19 +10:00
Michael Green
32f6a86dc9 Migrate away from system version file to fetch 2024-10-02 12:25:08 +10:00
Michael Green
4075976ad8 Bump Hasheous-Client version 2024-10-02 11:17:32 +10:00
Michael Green
146597dd4b Minor UI tweaks (#431) 2024-09-20 15:00:44 -07:00
Michael Green
f4ba84a54f Update README.MD (#428) 2024-09-17 11:30:52 +10:00
Michael Green
64fb76484b Update to profile cards to display now-playing (#427) 2024-09-17 10:48:49 +10:00
Michael Green
bfade006bd New UI Styling Fixes (#426)
Resolve some issues with how the new UI renders.
2024-09-16 16:25:36 +10:00
Michael Green
c8140d7178 Revamp of BIOS handling (#423)
Many of the platforms are similar enough to other platforms that they'll
share the same BIOS files.

The current storage method stores BIOS files per platform, meaning that
files can be duplicated multiple times to satisfy the requirements of
each platform.

This change stores the files as their hash with a .bios extension
(example: `85ad74194e87c08904327de1a9443b7a.bios`) in a flat directory
structure. This allows BIOS files that are used by multiple platforms to
be shared without duplication.
2024-09-15 20:35:36 +10:00
Michael Green
070589f718 Fix for home path change (#422)
The migration of the docker container to a rootless one requires the
default library to move.

This change strips the library path from the path to the ROM and
replaces it with an SQL view that concatenates the library path and ROM
path.
2024-09-14 01:32:18 +10:00
93 changed files with 6273 additions and 4847 deletions

View File

@@ -1,6 +1,10 @@
FROM mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm
RUN apt-get update && apt-get install -y p7zip-full
RUN mkdir -p /workspace/gaseous-server/wwwroot/emulators/EmulatorJS
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
RUN 7z x -y -o/workspace/gaseous-server/wwwroot/emulators/EmulatorJS 4.0.12.7z
# update apt-get
RUN apt-get update
# download and unzip EmulatorJS from CDN
RUN apt-get install -y p7zip-full default-jdk nodejs wget
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z

View File

@@ -1,27 +1,25 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
{
"name": "C# (.NET)",
"name": "Gaseous C# (.NET)",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
//"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm",
"dockerComposeFile": "docker-compose.yml",
"service": "development",
"workspaceFolder": "/workspace",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [5198],
"forwardPorts": [
5198
],
"portsAttributes": {
"5198": {
"protocol": "http"
}
},
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "dotnet restore",
// Configure tool-specific properties.
"customizations": {
"vscode": {
@@ -38,11 +36,12 @@
"ms-dotnettools.vscodeintellicode-csharp",
"Zignd.html-css-class-completion",
"PWABuilder.pwa-studio",
"ms-azuretools.vscode-docker"
"ms-azuretools.vscode-docker",
"SonarSource.sonarlint-vscode",
"oderwat.indent-rainbow"
]
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

61
.github/workflows/BuildNightly.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Build Nightly Docker Image
on:
schedule:
- cron: '15 4 * * *'
workflow_dispatch:
jobs:
docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: 'true'
- name: Install dotnet tool
run: dotnet tool install -g dotnetCampus.TagToVersion
- name: Set tag to version
run: dotnet TagToVersion -t 0.0.0-nightly
- name: Sign in to Nuget
run: dotnet nuget add source --username michael-j-green --password ${{ secrets.NUGETKEY }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/gaseous-project/index.json"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Package Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push standard image
uses: docker/build-push-action@v6
with:
context: .
file: ./build/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
gaseousgames/gaseousserver:nightly
ghcr.io/gaseous-project/gaseousserver:nightly
- name: Build and push image with embedded mariadb
uses: docker/build-push-action@v6
with:
context: .
file: ./build/Dockerfile-EmbeddedDB
platforms: linux/amd64,linux/arm64
push: true
tags: |
gaseousgames/gaseousserver:nightly-embeddeddb
ghcr.io/gaseous-project/gaseousserver:nightly-embeddeddb

View File

@@ -1,17 +1,9 @@
[![.NET](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml/badge.svg)](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml) [![CodeQL](https://github.com/gaseous-project/gaseous-server/actions/workflows/codeql.yml/badge.svg)](https://github.com/gaseous-project/gaseous-server/actions/workflows/codeql.yml) [![Build Release Docker Image](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml/badge.svg)](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml)
# Gaseous Server
[![.NET](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml/badge.svg)](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml) [![Nightly](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildNightly.yml/badge.svg)](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildNightly.yml) [![Build Release Docker Image](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml/badge.svg)](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml)
# <img src="./logo.png" height="28" style="float: right;" /> Gaseous Server
This is the server for the Gaseous system. It offers ROM and title management, as well as some basic in browser emulation of those ROMs.
## Warning
Versions 1.6.1 and earlier are not suitable for being exposed to the internet, as:
1. there is no authentication support, meaning anyone could trash your library
2. the server has not been hardened for exposure to the internet - so there maybe unknown vulnerabilities
If you expose one of these earlier versions of the server to the internet, **you do so at your own risk**.
Version 1.7.0 and later contain user authentication, and can be exposed to the internet. However, it is recommended to no expose the server to the internet if you're not actively using it remotely, or if you have alternative means to access it remotely like a VPN.
Version 1.7.0 and later contain user authentication, and can be exposed to the internet. However, it is recommended to not expose the server to the internet if you're not actively using it remotely, or if you have alternative means to access it remotely like a VPN.
While we do our best to stay on top of server security, if you expose the server to the internet **you do so at your own risk**.
@@ -20,15 +12,19 @@ While we do our best to stay on top of server security, if you expose the server
![Game](./gaseous-server/wwwroot/screenshots/Game.png)
![Emulator](./gaseous-server/wwwroot/screenshots/Emulator.png)
## Requirements
* MariaDB 11.1.2 (preferred) or MySQL Server 8+
* MariaDB 11.1.2+ (preferred) or MySQL Server 8+
* These are the database versions Gaseous has been tested and developed against. Your mileage may vary with earlier versions.
* MariaDB is the preferred database server, while MySQL will continue to be supported for existing users (they should be interchangable).
* Note that due to the earlier database schema using MySQL specific features, moving to MariaDB from MySQL will require rebuilding your database from scratch. The "Library Scan" background task can be used to re-import all titles.
* Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation
If using the provided docker-compose.yml, MariaDB will be installed for you.
# Installation
See https://github.com/gaseous-project/gaseous-server/wiki/Installation for installation instructions.
# Adding Content
1. Import signatures: see https://github.com/gaseous-project/gaseous-server/wiki/Signatures
2. Add ROMs: see https://github.com/gaseous-project/gaseous-server/wiki/Adding-ROMs
## Friends of Gaseous
* [EmulatorJS](https://github.com/EmulatorJS/EmulatorJS): A fantastic (and fast) Javascript based implementation of RetroArch, supporting a wide variety of platforms. Discord: https://discord.gg/6akryGkETU
@@ -43,11 +39,4 @@ The following projects are used by Gaseous
* [EmulatorJS](https://github.com/EmulatorJS/EmulatorJS)
## Discord Server
[![Join our Discord server!](https://invite.casperiv.dev/?inviteCode=Nhu7wpT3k4&format=svg)](https://discord.gg/Nhu7wpT3k4)
# Installation
See https://github.com/gaseous-project/gaseous-server/wiki/Installation for installation instructions.
# Adding Content
1. Import signatures: see https://github.com/gaseous-project/gaseous-server/wiki/Signatures
2. Add ROMs: see https://github.com/gaseous-project/gaseous-server/wiki/Adding-ROMs
Join our Discord server: https://discord.gg/Nhu7wpT3k4

View File

@@ -16,20 +16,14 @@ RUN dotnet restore "gaseous-server/gaseous-server.csproj" -a $TARGETARCH
# Build and publish a release
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
# Build Gaseous CLI
# Restore as distinct layers
RUN dotnet restore "gaseous-cli/gaseous-cli.csproj" -a $TARGETARCH
# Build and publish a release
RUN dotnet publish "gaseous-cli/gaseous-cli.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
# update apt-get
RUN apt-get update
# # download and unzip EmulatorJS from CDN
# download and unzip EmulatorJS from CDN
RUN apt-get install -y p7zip-full
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z
# clean up apt-get
RUN apt-get clean && rm -rf /var/lib/apt/lists

View File

@@ -16,20 +16,14 @@ RUN dotnet restore "gaseous-server/gaseous-server.csproj" -a $TARGETARCH
# Build and publish a release
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
# Build Gaseous CLI
# Restore as distinct layers
RUN dotnet restore "gaseous-cli/gaseous-cli.csproj" -a $TARGETARCH
# Build and publish a release
RUN dotnet publish "gaseous-cli/gaseous-cli.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
# update apt-get
RUN apt-get update
# # download and unzip EmulatorJS from CDN
# download and unzip EmulatorJS from CDN
RUN apt-get install -y p7zip-full
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0

View File

@@ -5,6 +5,9 @@ echo "Creating user gaseous with UID ${PUID} and GID ${PGID}"
groupadd -g ${PGID} gaseous
useradd -u ${PUID} -g ${PGID} -m gaseous -d /home/gaseous -G sudo
usermod -p "*" gaseous
mkdir -p /home/gaseous/.aspnet
chown -R ${PUID} /App /home/gaseous/.aspnet
chgrp -R ${PGID} /App /home/gaseous/.aspnet
mkdir -p /home/gaseous/.gaseous-server
chown -R ${PUID} /App /home/gaseous/.gaseous-server
chgrp -R ${PGID} /App /home/gaseous/.gaseous-server

View File

@@ -5,6 +5,9 @@ echo "Creating user gaseous with UID ${PUID} and GID ${PGID}"
groupadd -g ${PGID} gaseous
useradd -u ${PUID} -g ${PGID} -m gaseous -d /home/gaseous -G sudo
usermod -p "*" gaseous
mkdir -p /home/gaseous/.aspnet
chown -R ${PUID} /App /home/gaseous/.aspnet
chgrp -R ${PGID} /App /home/gaseous/.aspnet
mkdir -p /home/gaseous/.gaseous-server
chown -R ${PUID} /App /home/gaseous/.gaseous-server
chgrp -R ${PGID} /App /home/gaseous/.gaseous-server

View File

@@ -1,39 +0,0 @@
version: '2'
services:
gaseous-server:
container_name: gaseous-server
image: gaseousgames/gaseousserver:latest
restart: unless-stopped
networks:
- gaseous
depends_on:
- gsdb
ports:
- 5198:80
volumes:
- gs:/home/gaseous/.gaseous-server
environment:
- TZ=Australia/Sydney
- dbhost=gsdb
- dbuser=root
- dbpass=gaseous
- igdbclientid=<clientid>
- igdbclientsecret=<clientsecret>
gsdb:
container_name: gsdb
image: mariadb
restart: unless-stopped
networks:
- gaseous
volumes:
- gsdb:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=gaseous
- MARIADB_USER=gaseous
- MARIADB_PASSWORD=gaseous
networks:
gaseous:
driver: bridge
volumes:
gs:
gsdb:

View File

@@ -1,6 +1,8 @@
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using gaseous_server.Classes.Metadata;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes
{
@@ -11,6 +13,64 @@ namespace gaseous_server.Classes
}
public static void ImportBiosFile(string FilePath, Common.hashObject Hash, ref Dictionary<string, object> BiosFileInfo)
{
BiosFileInfo.Add("type", "bios");
BiosFileInfo.Add("status", "notimported");
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
{
if (biosItem.Available == false)
{
if (biosItem.hash == Hash.md5hash)
{
string biosPath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, biosItem.hash + ".bios");
Logging.Log(Logging.LogType.Information, "Import BIOS File", " " + FilePath + " is a BIOS file - moving to " + biosPath);
File.Move(FilePath, biosItem.biosPath, true);
BiosFileInfo.Add("name", biosItem.filename);
BiosFileInfo.Add("platform", Platforms.GetPlatform(biosItem.platformid));
BiosFileInfo["status"] = "imported";
}
}
else
{
if (biosItem.hash == Hash.md5hash)
{
BiosFileInfo["status"] = "duplicate";
}
}
}
}
public static void MigrateToNewFolderStructure()
{
// migrate from old BIOS file structure which had each bios file inside a folder named for the platform to the new structure which has each file in a subdirectory named after the MD5 hash
if (Directory.Exists(Config.LibraryConfiguration.LibraryBIOSDirectory))
{
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{
if (platformMapping.Bios != null)
{
foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBiosItem in platformMapping.Bios)
{
string oldBiosPath = Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platformMapping.IGDBSlug.ToString(), emulatorBiosItem.filename);
string newBiosPath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, emulatorBiosItem.hash + ".bios");
if (File.Exists(oldBiosPath))
{
File.Copy(oldBiosPath, newBiosPath, true);
}
}
}
}
// remove old BIOS folder structure
Directory.Delete(Config.LibraryConfiguration.LibraryBIOSDirectory, true);
}
}
public static Models.PlatformMapping.PlatformMapItem? BiosHashSignatureLookup(string MD5)
{
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
@@ -67,7 +127,7 @@ namespace gaseous_server.Classes
{
if (platformMapping.Bios != null)
{
IGDB.Models.Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBios in platformMapping.Bios)
{
@@ -96,10 +156,11 @@ namespace gaseous_server.Classes
{
get
{
return Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platformslug, base.filename);
return Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, hash + ".bios");
}
}
public bool Available {
public bool Available
{
get
{
bool fileExists = File.Exists(biosPath);

View File

@@ -9,7 +9,7 @@ using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers;
using gaseous_server.Controllers.v1_1;
using gaseous_server.Models;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
@@ -20,7 +20,8 @@ namespace gaseous_server.Classes
{
public class Collections
{
public static List<CollectionItem> GetCollections(string userid) {
public static List<CollectionItem> GetCollections(string userid)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM RomCollections WHERE OwnedBy=@ownedby ORDER BY `Name`";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
@@ -30,14 +31,16 @@ namespace gaseous_server.Classes
List<CollectionItem> collectionItems = new List<CollectionItem>();
foreach(DataRow row in data.Rows) {
foreach (DataRow row in data.Rows)
{
collectionItems.Add(BuildCollectionItem(row));
}
return collectionItems;
}
public static CollectionItem GetCollection(long Id, string userid) {
public static CollectionItem GetCollection(long Id, string userid)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql;
if (userid == "")
@@ -208,7 +211,8 @@ namespace gaseous_server.Classes
}
}
public static CollectionContents GetCollectionContent(CollectionItem collectionItem, string userid) {
public static CollectionContents GetCollectionContent(CollectionItem collectionItem, string userid)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings for specified user
@@ -260,15 +264,20 @@ namespace gaseous_server.Classes
}
// add dynamic platforms
if (DynamicPlatforms.Count > 0) {
foreach (long PlatformId in platformids) {
if (DynamicPlatforms.Count > 0)
{
foreach (long PlatformId in platformids)
{
platforms.Add(Platforms.GetPlatform(PlatformId));
}
} else {
}
else
{
// get all platforms to pull from
Dictionary<string, List<Filters.FilterItem>> FilterDict = Filters.Filter(AgeGroups.AgeRestrictionGroupings.Adult, true);
List<Classes.Filters.FilterItem> filteredPlatforms = (List<Classes.Filters.FilterItem>)FilterDict["platforms"];
foreach (Filters.FilterItem filterItem in filteredPlatforms) {
foreach (Filters.FilterItem filterItem in filteredPlatforms)
{
platforms.Add(Platforms.GetPlatform(filterItem.Id));
}
}
@@ -280,7 +289,8 @@ namespace gaseous_server.Classes
// build collection
List<CollectionContents.CollectionPlatformItem> platformItems = new List<CollectionContents.CollectionPlatformItem>();
foreach (Platform platform in platforms) {
foreach (Platform platform in platforms)
{
long TotalRomSize = 0;
long TotalGameCount = 0;
@@ -297,7 +307,8 @@ namespace gaseous_server.Classes
Controllers.v1_1.GamesController.GameReturnPackage games = new Controllers.v1_1.GamesController.GameReturnPackage();
if (isDynamic == true)
{
Controllers.v1_1.GamesController.GameSearchModel searchModel = new Controllers.v1_1.GamesController.GameSearchModel{
Controllers.v1_1.GamesController.GameSearchModel searchModel = new Controllers.v1_1.GamesController.GameSearchModel
{
Name = "",
Platform = new List<string>{
platform.Id.ToString()
@@ -306,11 +317,13 @@ namespace gaseous_server.Classes
GameMode = collectionItem.Players.ConvertAll(s => s.ToString()),
PlayerPerspective = collectionItem.PlayerPerspectives.ConvertAll(s => s.ToString()),
Theme = collectionItem.Themes.ConvertAll(s => s.ToString()),
GameRating = new Controllers.v1_1.GamesController.GameSearchModel.GameRatingItem{
GameRating = new Controllers.v1_1.GamesController.GameSearchModel.GameRatingItem
{
MinimumRating = collectionItem.MinimumRating,
MaximumRating = collectionItem.MaximumRating
},
GameAgeRating = new Controllers.v1_1.GamesController.GameSearchModel.GameAgeRatingItem{
GameAgeRating = new Controllers.v1_1.GamesController.GameSearchModel.GameAgeRatingItem
{
AgeGroupings = UserAgeGroupings,
IncludeUnrated = UserAgeGroupIncludeUnrated
}
@@ -332,7 +345,7 @@ namespace gaseous_server.Classes
) && alwaysIncludeItem.PlatformId == platform.Id
)
{
MinimalGameItem AlwaysIncludeGame = new MinimalGameItem(Games.GetGame(alwaysIncludeItem.GameId, false, false, false));
MinimalGameItem AlwaysIncludeGame = new MinimalGameItem(Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, alwaysIncludeItem.GameId));
CollectionContents.CollectionPlatformItem.CollectionGameItem gameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(AlwaysIncludeGame);
gameItem.InclusionStatus = new CollectionItem.AlwaysIncludeItem();
gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId;
@@ -344,7 +357,8 @@ namespace gaseous_server.Classes
}
}
foreach (MinimalGameItem game in games.Games) {
foreach (MinimalGameItem game in games.Games)
{
bool gameAlreadyInList = false;
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem existingGame in collectionPlatformItem.Games)
{
@@ -364,11 +378,14 @@ namespace gaseous_server.Classes
// calculate total rom size for the game
long GameRomSize = 0;
foreach (Roms.GameRomItem gameRom in gameRoms) {
foreach (Roms.GameRomItem gameRom in gameRoms)
{
GameRomSize += (long)gameRom.Size;
}
if (collectionItem.MaximumBytesPerPlatform > 0) {
if ((TotalRomSize + GameRomSize) < collectionItem.MaximumBytesPerPlatform) {
if (collectionItem.MaximumBytesPerPlatform > 0)
{
if ((TotalRomSize + GameRomSize) < collectionItem.MaximumBytesPerPlatform)
{
AddGame = true;
}
}
@@ -377,13 +394,16 @@ namespace gaseous_server.Classes
AddGame = true;
}
if (AddGame == true) {
if (AddGame == true)
{
TotalRomSize += GameRomSize;
bool AddRoms = false;
if (collectionItem.MaximumRomsPerPlatform > 0) {
if (TotalGameCount < collectionItem.MaximumRomsPerPlatform) {
if (collectionItem.MaximumRomsPerPlatform > 0)
{
if (TotalGameCount < collectionItem.MaximumRomsPerPlatform)
{
AddRoms = true;
}
}
@@ -392,7 +412,8 @@ namespace gaseous_server.Classes
AddRoms = true;
}
if (AddRoms == true) {
if (AddRoms == true)
{
TotalGameCount += 1;
collectionGameItem.Roms = gameRoms;
collectionPlatformItem.Games.Add(collectionGameItem);
@@ -401,7 +422,8 @@ namespace gaseous_server.Classes
}
// handle age grouping
AgeGroups.AgeRestrictionGroupings CurrentAgeGroup = AgeGroups.GetAgeGroupFromAgeRatings(game.AgeRatings);
List<AgeRating> gameAgeRatings = game.AgeRatings.Select(s => (AgeRating)s).ToList();
AgeGroups.AgeRestrictionGroupings CurrentAgeGroup = AgeGroups.GetAgeGroupFromAgeRatings(gameAgeRatings);
if (CurrentAgeGroup > AgeGrouping)
{
AgeGrouping = CurrentAgeGroup;
@@ -500,7 +522,8 @@ namespace gaseous_server.Classes
if (collectionItem.IncludeBIOSFiles == true)
{
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
if (!Directory.Exists(ZipBiosPath)) {
if (!Directory.Exists(ZipBiosPath))
{
Directory.CreateDirectory(ZipBiosPath);
}
@@ -639,7 +662,8 @@ namespace gaseous_server.Classes
}
}
private static CollectionItem BuildCollectionItem(DataRow row) {
private static CollectionItem BuildCollectionItem(DataRow row)
{
string strPlatforms = (string)Common.ReturnValueIfNull(row["Platforms"], "[ ]");
string strGenres = (string)Common.ReturnValueIfNull(row["Genres"], "[ ]");
string strPlayers = (string)Common.ReturnValueIfNull(row["Players"], "[ ]");
@@ -810,7 +834,8 @@ namespace gaseous_server.Classes
}
}
public class CollectionContents {
public class CollectionContents
{
[JsonIgnore]
public List<CollectionPlatformItem> Collection { get; set; }
@@ -840,13 +865,16 @@ namespace gaseous_server.Classes
public AgeGroups.AgeRestrictionGroupings AgeGroup { get; set; }
public bool ContainsUnclassifiedAgeGroup { get; set; }
public class CollectionPlatformItem {
public CollectionPlatformItem(IGDB.Models.Platform platform) {
public class CollectionPlatformItem
{
public CollectionPlatformItem(Platform platform)
{
string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug" };
PropertyInfo[] srcProperties = typeof(IGDB.Models.Platform).GetProperties();
PropertyInfo[] srcProperties = typeof(Platform).GetProperties();
PropertyInfo[] dstProperties = typeof(CollectionPlatformItem).GetProperties();
foreach (PropertyInfo srcProperty in srcProperties) {
foreach (PropertyInfo srcProperty in srcProperties)
{
if (PropertyWhitelist.Contains<string>(srcProperty.Name))
{
foreach (PropertyInfo dstProperty in dstProperties)
@@ -866,10 +894,13 @@ namespace gaseous_server.Classes
public List<CollectionGameItem> Games { get; set; }
public int RomCount {
get {
public int RomCount
{
get
{
int Counter = 0;
foreach (CollectionGameItem Game in Games) {
foreach (CollectionGameItem Game in Games)
{
Counter += 1;
}
@@ -877,11 +908,15 @@ namespace gaseous_server.Classes
}
}
public long RomSize {
get {
public long RomSize
{
get
{
long Size = 0;
foreach (CollectionGameItem Game in Games) {
foreach (Roms.GameRomItem Rom in Game.Roms) {
foreach (CollectionGameItem Game in Games)
{
foreach (Roms.GameRomItem Rom in Game.Roms)
{
Size += (long)Rom.Size;
}
}
@@ -905,28 +940,12 @@ namespace gaseous_server.Classes
this.AgeRatings = gameObject.AgeRatings;
}
public IGDB.Models.Cover? CoverItem
{
get
{
if (Cover != null)
{
IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory, "Games", Slug), false);
return cover;
}
else
{
return null;
}
}
}
public AgeGroups.AgeRestrictionGroupings AgeGrouping
{
get
{
return AgeGroups.GetAgeGroupFromAgeRatings(this.AgeRatings);
List<AgeRating> gameAgeRatings = this.AgeRatings.Select(s => (AgeRating)s).ToList();
return AgeGroups.GetAgeGroupFromAgeRatings(gameAgeRatings);
}
}
@@ -934,10 +953,13 @@ namespace gaseous_server.Classes
public List<Roms.GameRomItem> Roms { get; set; }
public long RomSize {
get {
public long RomSize
{
get
{
long Size = 0;
foreach (Roms.GameRomItem Rom in Roms) {
foreach (Roms.GameRomItem Rom in Roms)
{
Size += (long)Rom.Size;
}

View File

@@ -1,9 +1,11 @@
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Compression;
using System.Reflection;
using System.Security.Cryptography;
using static gaseous_server.Classes.Metadata.Communications;
namespace gaseous_server.Classes
{
@@ -126,6 +128,27 @@ namespace gaseous_server.Classes
typeof(DescriptionAttribute)))?.Description ?? value.ToString();
}
public static Point GetResolution(this Enum value)
{
string width = ((ResolutionAttribute)Attribute.GetCustomAttribute(
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => x.GetValue(null).Equals(value)),
typeof(ResolutionAttribute)))?.width.ToString() ?? value.ToString();
string height = ((ResolutionAttribute)Attribute.GetCustomAttribute(
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => x.GetValue(null).Equals(value)),
typeof(ResolutionAttribute)))?.height.ToString() ?? value.ToString();
return new Point(int.Parse(width), int.Parse(height));
}
public static bool IsNullableEnum(this Type t)
{
Type u = Nullable.GetUnderlyingType(t);
return u != null && u.IsEnum;
}
// compression
public static byte[] Compress(byte[] data)
{

View File

@@ -1,7 +1,6 @@
using System;
using System.Data;
using Newtonsoft.Json;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
using NuGet.Common;
@@ -115,7 +114,12 @@ namespace gaseous_server.Classes
if (File.Exists(ConfigurationFilePath))
{
string configRaw = File.ReadAllText(ConfigurationFilePath);
ConfigFile? _tempConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<ConfigFile>(configRaw);
Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore
};
ConfigFile? _tempConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<ConfigFile>(configRaw, serializerSettings);
if (_tempConfig != null)
{
_config = _tempConfig;
@@ -131,9 +135,9 @@ namespace gaseous_server.Classes
_config.DatabaseConfiguration.Password = (string)Common.GetEnvVar("dbpass", _config.DatabaseConfiguration.Password);
_config.DatabaseConfiguration.DatabaseName = (string)Common.GetEnvVar("dbname", _config.DatabaseConfiguration.DatabaseName);
_config.DatabaseConfiguration.Port = int.Parse((string)Common.GetEnvVar("dbport", _config.DatabaseConfiguration.Port.ToString()));
_config.MetadataConfiguration.MetadataSource = (HasheousClient.Models.MetadataModel.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.MetadataSources), (string)Common.GetEnvVar("metadatasource", _config.MetadataConfiguration.MetadataSource.ToString()));
_config.MetadataConfiguration.DefaultMetadataSource = (HasheousClient.Models.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataSources), (string)Common.GetEnvVar("metadatasource", _config.MetadataConfiguration.DefaultMetadataSource.ToString()));
_config.IGDBConfiguration.UseHasheousProxy = bool.Parse((string)Common.GetEnvVar("metadatausehasheousproxy", _config.IGDBConfiguration.UseHasheousProxy.ToString()));
_config.MetadataConfiguration.SignatureSource = (HasheousClient.Models.MetadataModel.SignatureSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.SignatureSources), (string)Common.GetEnvVar("signaturesource", _config.MetadataConfiguration.SignatureSource.ToString())); ;
_config.MetadataConfiguration.MaxLibraryScanWorkers = int.Parse((string)Common.GetEnvVar("maxlibraryscanworkers", _config.MetadataConfiguration.MaxLibraryScanWorkers.ToString()));
_config.MetadataConfiguration.HasheousHost = (string)Common.GetEnvVar("hasheoushost", _config.MetadataConfiguration.HasheousHost);
_config.IGDBConfiguration.ClientId = (string)Common.GetEnvVar("igdbclientid", _config.IGDBConfiguration.ClientId);
_config.IGDBConfiguration.Secret = (string)Common.GetEnvVar("igdbclientsecret", _config.IGDBConfiguration.Secret);
@@ -531,11 +535,7 @@ namespace gaseous_server.Classes
{
get
{
return ReadSetting<string>("LibraryRootDirectory", Path.Combine(Config.ConfigurationPath, "Data"));
}
set
{
SetSetting<string>("LibraryRootDirectory", value);
return Path.Combine(Config.ConfigurationPath, "Data");
}
}
@@ -579,6 +579,14 @@ namespace gaseous_server.Classes
}
}
public string LibraryFirmwareDirectory
{
get
{
return Path.Combine(LibraryRootDirectory, "Firmware");
}
}
public string LibraryUploadDirectory
{
get
@@ -619,27 +627,34 @@ namespace gaseous_server.Classes
}
}
public string LibraryMetadataDirectory_Platform(Platform platform)
public string LibraryMetadataDirectory_Platform(HasheousClient.Models.Metadata.IGDB.Platform platform)
{
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Platforms", platform.Slug);
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
return MetadataPath;
}
public string LibraryMetadataDirectory_Game(Game game)
public string LibraryMetadataDirectory_Game(gaseous_server.Models.Game game)
{
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Games", game.Slug);
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
return MetadataPath;
}
public string LibraryMetadataDirectory_Company(Company company)
public string LibraryMetadataDirectory_Company(HasheousClient.Models.Metadata.IGDB.Company company)
{
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Companies", company.Slug);
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
return MetadataPath;
}
public string LibraryMetadataDirectory_Hasheous()
{
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Hasheous");
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
return MetadataPath;
}
public string LibrarySignaturesDirectory
{
get
@@ -660,7 +675,7 @@ namespace gaseous_server.Classes
{
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); }
if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); }
if (!Directory.Exists(LibraryFirmwareDirectory)) { Directory.CreateDirectory(LibraryFirmwareDirectory); }
if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); }
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
@@ -672,17 +687,40 @@ namespace gaseous_server.Classes
public class MetadataAPI
{
private static HasheousClient.Models.MetadataModel.MetadataSources _MetadataSource
public static string _HasheousClientAPIKey
{
get
{
return "Pna5SRcbJ6R8aasytab_6vZD0aBKDGNZKRz4WY4xArpfZ-3mdZq0hXIGyy0AD43b";
}
}
private static HasheousClient.Models.MetadataSources _MetadataSource
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
{
return (HasheousClient.Models.MetadataModel.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
return (HasheousClient.Models.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
}
else
{
return HasheousClient.Models.MetadataModel.MetadataSources.IGDB;
return HasheousClient.Models.MetadataSources.IGDB;
}
}
}
private static bool _MetadataUseHasheousProxy
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatausehasheousproxy")))
{
return bool.Parse(Environment.GetEnvironmentVariable("metadatausehasheousproxy"));
}
else
{
return true;
}
}
}
@@ -706,21 +744,6 @@ namespace gaseous_server.Classes
private static string _HasheousAPIKey { get; set; } = "";
private static int _MaxLibraryScanWorkers
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("maxlibraryscanworkers")))
{
return int.Parse(Environment.GetEnvironmentVariable("maxlibraryscanworkers"));
}
else
{
return 4;
}
}
}
private static string _HasheousHost
{
get
@@ -736,7 +759,7 @@ namespace gaseous_server.Classes
}
}
public HasheousClient.Models.MetadataModel.MetadataSources MetadataSource = _MetadataSource;
public HasheousClient.Models.MetadataSources DefaultMetadataSource = _MetadataSource;
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
@@ -744,7 +767,8 @@ namespace gaseous_server.Classes
public string HasheousAPIKey = _HasheousAPIKey;
public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers;
[JsonIgnore]
public string HasheousClientAPIKey = _HasheousClientAPIKey;
public string HasheousHost = _HasheousHost;
}
@@ -781,8 +805,24 @@ namespace gaseous_server.Classes
}
}
private static bool _MetadataUseHasheousProxy
{
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("igdbusehasheousproxy")))
{
return bool.Parse(Environment.GetEnvironmentVariable("igdbusehasheousproxy"));
}
else
{
return false;
}
}
}
public string ClientId = _DefaultIGDBClientId;
public string Secret = _DefaultIGDBSecret;
public bool UseHasheousProxy = _MetadataUseHasheousProxy;
}
public class Logging

View File

@@ -3,7 +3,6 @@ using System.Data;
using System.Reflection;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
namespace gaseous_server.Classes
{
@@ -149,9 +148,6 @@ namespace gaseous_server.Classes
db.ExecuteNonQuery(sql, dbDict);
} while (reader.EndOfStream == false);
}
// this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1023);
break;
case 1024:
@@ -181,6 +177,90 @@ namespace gaseous_server.Classes
};
db.ExecuteNonQuery(sql, dbDict);
}
// update all rom paths to use the new format
sql = "SELECT * FROM GameLibraries;";
data = db.ExecuteCMD(sql);
foreach (DataRow row in data.Rows)
{
sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid;";
dbDict = new Dictionary<string, object>
{
{ "libraryid", row["Id"] }
};
DataTable romData = db.ExecuteCMD(sql, dbDict);
string libraryRootPath = (string)row["Path"];
if (libraryRootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == false)
{
libraryRootPath += Path.DirectorySeparatorChar;
}
bool GetLastThreeElements = false;
if ((int)row["DefaultLibrary"] == 1)
{
GetLastThreeElements = true;
}
foreach (DataRow romRow in romData.Rows)
{
string existingPath = (string)romRow["RelativePath"];
string newPath = "";
if (GetLastThreeElements == true)
{
// strip all but the last 3 elements from existingPath separated by directory separator
// this mode only works for the default library
string[] pathParts = existingPath.Split(Path.DirectorySeparatorChar);
if (pathParts.Length > 3)
{
newPath = Path.Combine(pathParts[pathParts.Length - 3], pathParts[pathParts.Length - 2], pathParts[pathParts.Length - 1]);
}
else
{
newPath = existingPath;
}
}
else
{
// strip the library root path from the existing path
if (existingPath.StartsWith(libraryRootPath))
{
newPath = existingPath.Substring(libraryRootPath.Length);
}
else
{
newPath = existingPath;
}
}
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Updating ROM path from " + existingPath + " to " + newPath);
sql = "UPDATE Games_Roms SET RelativePath = @newpath WHERE Id = @id;";
dbDict = new Dictionary<string, object>
{
{ "newpath", newPath },
{ "id", romRow["Id"] }
};
db.ExecuteNonQuery(sql, dbDict);
}
}
// get all tables that have the prefix "Relation_" and drop them
sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = @dbname AND table_name LIKE 'Relation_%';";
dbDict = new Dictionary<string, object>
{
{ "dbname", Config.DatabaseConfiguration.DatabaseName }
};
data = db.ExecuteCMD(sql, dbDict);
foreach (DataRow row in data.Rows)
{
sql = "DROP TABLE " + (string)row["table_name"] + ";";
db.ExecuteNonQuery(sql);
}
// migrating metadata is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1024);
break;
}
break;
@@ -197,8 +277,8 @@ namespace gaseous_server.Classes
MySql_1002_MigrateMetadataVersion();
break;
case 1023:
MySql_1023_MigrateMetadataVersion();
case 1024:
MySql_1024_MigrateMetadataVersion();
break;
}
}
@@ -301,12 +381,12 @@ namespace gaseous_server.Classes
}
}
public static void MySql_1023_MigrateMetadataVersion()
public static void MySql_1024_MigrateMetadataVersion()
{
FileSignature fileSignature = new FileSignature();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE RomDataVersion = 1;";
string sql = "SELECT * FROM view_Games_Roms WHERE RomDataVersion = 1;";
DataTable data = db.ExecuteCMD(sql);
long count = 1;
foreach (DataRow row in data.Rows)
@@ -326,10 +406,9 @@ namespace gaseous_server.Classes
(string)row["Path"]
);
Platform platform = Platforms.GetPlatform((long)row["PlatformId"], false);
Game game = Games.GetGame((long)row["GameId"], false, false, false);
HasheousClient.Models.Metadata.IGDB.Platform platform = Platforms.GetPlatform((long)row["PlatformId"]);
ImportGame.StoreROM(library, hash, game, platform, signature, (string)row["Path"], (long)row["Id"]);
ImportGame.StoreGame(library, hash, signature, platform, (string)row["Path"], (long)row["Id"]);
count += 1;
}

View File

@@ -1,8 +1,10 @@
using System.Collections.Concurrent;
using System.Configuration;
using System.IO.Compression;
using System.Net;
using gaseous_server.Classes.Metadata;
using HasheousClient.Models;
using gaseous_server.Models;
using HasheousClient.Models.Metadata.IGDB;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NuGet.Common;
using SevenZip;
@@ -185,6 +187,22 @@ namespace gaseous_server.Classes
}
}
// get discovered platform
Platform? determinedPlatform = null;
if (library.DefaultPlatformId == null || library.DefaultPlatformId == 0)
{
determinedPlatform = Metadata.Platforms.GetPlatform((long)discoveredSignature.Flags.PlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new Platform();
}
}
else
{
determinedPlatform = Metadata.Platforms.GetPlatform((long)library.DefaultPlatformId);
discoveredSignature.MetadataSources.AddPlatform((long)determinedPlatform.Id, determinedPlatform.Name, HasheousClient.Models.MetadataSources.None);
}
return discoveredSignature;
}
@@ -237,7 +255,7 @@ namespace gaseous_server.Classes
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 + ")");
Logging.Log(Logging.LogType.Information, "Import Game", " Platform determined to be: " + discoveredSignature.Flags.PlatformName + " (" + discoveredSignature.Flags.PlatformId + ")");
return discoveredSignature;
}
@@ -291,15 +309,59 @@ namespace gaseous_server.Classes
{
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
Console.WriteLine(HasheousClient.WebApp.HttpHelper.BaseUri);
LookupItemModel? HasheousResult = null;
HasheousClient.Models.LookupItemModel? HasheousResult = null;
try
{
HasheousResult = hasheous.RetrieveFromHasheous(new HashLookupModel
// check the cache first
if (!Directory.Exists(Config.LibraryConfiguration.LibraryMetadataDirectory_Hasheous()))
{
Directory.CreateDirectory(Config.LibraryConfiguration.LibraryMetadataDirectory_Hasheous());
}
// create file name from hash object
string cacheFileName = hash.md5hash + "_" + hash.sha1hash + ".json";
string cacheFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Hasheous(), cacheFileName);
// use cache file if it exists and is less than 30 days old, otherwise fetch from hasheous. if the fetch from hasheous is successful, save it to the cache, if it fails, use the cache if it exists even if it's old
if (File.Exists(cacheFilePath))
{
FileInfo cacheFile = new FileInfo(cacheFilePath);
if (cacheFile.LastWriteTimeUtc > DateTime.UtcNow.AddDays(-30))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Using cached signature from Hasheous");
HasheousResult = Newtonsoft.Json.JsonConvert.DeserializeObject<HasheousClient.Models.LookupItemModel>(File.ReadAllText(cacheFilePath));
}
}
try
{
if (HasheousResult == null)
{
// fetch from hasheous
HasheousResult = hasheous.RetrieveFromHasheous(new HasheousClient.Models.HashLookupModel
{
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
});
if (HasheousResult != null)
{
// save to cache
File.WriteAllText(cacheFilePath, Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult));
}
}
}
catch (Exception ex)
{
if (File.Exists(cacheFilePath))
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous - using cached signature", ex);
HasheousResult = Newtonsoft.Json.JsonConvert.DeserializeObject<HasheousClient.Models.LookupItemModel>(File.ReadAllText(cacheFilePath));
}
else
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
}
}
if (HasheousResult != null)
{
if (HasheousResult.Signature != null)
@@ -317,13 +379,25 @@ namespace gaseous_server.Classes
{
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Platform.metadata)
{
if (metadataResult.Id.Length > 0)
// only IGDB metadata is supported
if (metadataResult.Source == HasheousClient.Models.MetadataSources.IGDB)
{
switch (metadataResult.Source)
if (metadataResult.ImmutableId.Length > 0)
{
case HasheousClient.Models.MetadataSources.IGDB:
signature.Flags.IGDBPlatformId = (long)Platforms.GetPlatform(metadataResult.Id, false).Id;
break;
// use immutable id
Platform hasheousPlatform = Platforms.GetPlatform(long.Parse(metadataResult.ImmutableId));
signature.MetadataSources.AddPlatform((long)hasheousPlatform.Id, hasheousPlatform.Name, metadataResult.Source);
}
else if (metadataResult.Id.Length > 0)
{
// fall back to id
Platform hasheousPlatform = Platforms.GetPlatform(metadataResult.Id);
signature.MetadataSources.AddPlatform((long)hasheousPlatform.Id, hasheousPlatform.Name, metadataResult.Source);
}
else
{
// no id or immutable id - use unknown platform
signature.MetadataSources.AddPlatform(0, "Unknown Platform", HasheousClient.Models.MetadataSources.None);
}
}
}
@@ -337,15 +411,51 @@ namespace gaseous_server.Classes
{
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Metadata)
{
if (metadataResult.Id.Length > 0)
if (metadataResult.ImmutableId.Length > 0)
{
signature.MetadataSources.AddGame(long.Parse(metadataResult.ImmutableId), HasheousResult.Name, metadataResult.Source);
}
else if (metadataResult.Id.Length > 0)
{
switch (metadataResult.Source)
{
case HasheousClient.Models.MetadataSources.IGDB:
signature.Flags.IGDBGameId = (long)Games.GetGame(metadataResult.Id, false, false, false).Id;
gaseous_server.Models.Game hasheousGame = Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, metadataResult.Id);
signature.MetadataSources.AddGame((long)hasheousGame.Id, hasheousGame.Name, metadataResult.Source);
break;
default:
if (long.TryParse(metadataResult.Id, out long id) == true)
{
signature.MetadataSources.AddGame(id, HasheousResult.Name, metadataResult.Source);
}
else
{
signature.MetadataSources.AddGame(0, "Unknown Game", HasheousClient.Models.MetadataSources.None);
}
break;
}
}
else
{
// no id or immutable id - use unknown game
signature.MetadataSources.AddGame(0, "Unknown Game", HasheousClient.Models.MetadataSources.None);
}
}
}
}
// check attributes for a user manual link
if (HasheousResult.Attributes != null)
{
if (HasheousResult.Attributes.Count > 0)
{
foreach (HasheousClient.Models.AttributeItem attribute in HasheousResult.Attributes)
{
if (attribute.attributeName == HasheousClient.Models.AttributeItem.AttributeName.VIMMManualId)
{
signature.Game.UserManual = attribute.GetType().GetProperty("Link").GetValue(attribute).ToString();
}
}
}
}
@@ -368,18 +478,20 @@ namespace gaseous_server.Classes
else
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
throw;
}
}
else
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
throw;
}
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
throw;
}
}

View File

@@ -24,7 +24,7 @@ namespace gaseous_server.Classes
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
}
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, Games_Roms.PlatformId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id , Games_Roms.PlatformId HAVING RomCount > 0) Game JOIN Platform ON Game.PlatformId = Platform.Id GROUP BY Platform.`Name`;";
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, view_Games_Roms.PlatformId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id , view_Games_Roms.PlatformId HAVING RomCount > 0) Game JOIN Platform ON Game.PlatformId = Platform.Id AND Platform.SourceId = 0 GROUP BY Platform.`Name`;";
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
@@ -82,7 +82,7 @@ namespace gaseous_server.Classes
// age groups
List<FilterItem> agegroupings = new List<FilterItem>();
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
foreach (DataRow dr in dbResponse.Rows)
@@ -112,7 +112,7 @@ namespace gaseous_server.Classes
{
//string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + AgeRestriction + ") GROUP BY Game.Id HAVING RomCount > 0) Game JOIN Relation_Game_<ITEMNAME>s ON Game.Id = Relation_Game_<ITEMNAME>s.GameId JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT view_Games_Roms.GameIdType, Game.Id, AgeGroup.AgeGroupId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + AgeRestriction + ") GROUP BY Game.Id HAVING RomCount > 0) Game JOIN Relation_Game_<ITEMNAME>s ON Game.Id = Relation_Game_<ITEMNAME>s.GameId AND Game.GameIdType = Relation_Game_<ITEMNAME>s.GameSourceId JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
sql = sql.Replace("<ITEMNAME>", Name);
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));

View File

@@ -2,7 +2,6 @@ using System;
using System.Data;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
namespace gaseous_server
@@ -60,6 +59,18 @@ namespace gaseous_server
}
}
// update default library path
public static void UpdateDefaultLibraryPath()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE GameLibraries SET Path=@path WHERE DefaultLibrary=1;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "path", Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "Library") }
};
db.ExecuteCMD(sql, dbDict);
}
public static List<LibraryItem> GetLibraries
{
get
@@ -225,7 +236,7 @@ namespace gaseous_server
{
if (_DefaultPlatformId != 0)
{
Platform platform = Platforms.GetPlatform(_DefaultPlatformId);
HasheousClient.Models.Metadata.IGDB.Platform platform = Platforms.GetPlatform(_DefaultPlatformId);
return platform.Name;
}
else

View File

@@ -7,17 +7,26 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
using NuGet.Common;
using NuGet.LibraryModel;
using static gaseous_server.Classes.Metadata.Games;
using static gaseous_server.Classes.FileSignature;
using HasheousClient.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes
{
public class ImportGame : QueueItemStatus
{
/// <summary>
/// Scan the import directory for games and process them
/// </summary>
/// <param name="ImportPath">
/// The path to the directory to scan
/// </param>
/// <exception cref="DirectoryNotFoundException">
/// Thrown when the import directory does not exist
/// </exception>
public void ProcessDirectory(string ImportPath)
{
if (Directory.Exists(ImportPath))
@@ -47,22 +56,29 @@ namespace gaseous_server.Classes
}
}
public Dictionary<string, object> ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
/// <summary>
/// Import a single game file
/// </summary>
/// <param name="GameFileImportPath">
/// The path to the game file to import
/// </param>
/// <param name="OverridePlatform">
/// The platform to use for the game file
/// </param>
/// <returns>
/// A dictionary containing the results of the import
/// </returns>
public Dictionary<string, object> ImportGameFile(string GameFileImportPath, Platform? OverridePlatform)
{
Dictionary<string, object> RetVal = new Dictionary<string, object>();
RetVal.Add("path", Path.GetFileName(GameFileImportPath));
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
if (Common.SkippableFiles.Contains<string>(Path.GetFileName(GameFileImportPath), StringComparer.OrdinalIgnoreCase))
{
Logging.Log(Logging.LogType.Debug, "Import Game", "Skipping item " + GameFileImportPath);
}
else
{
FileInfo fi = new FileInfo(GameFileImportPath);
Common.hashObject hash = new Common.hashObject(GameFileImportPath);
Models.PlatformMapping.PlatformMapItem? IsBios = Classes.Bios.BiosHashSignatureLookup(hash.md5hash);
@@ -70,127 +86,251 @@ namespace gaseous_server.Classes
if (IsBios == null)
{
// file is a rom
RetVal.Add("type", "rom");
_ImportGameFile(GameFileImportPath, hash, ref RetVal, OverridePlatform);
}
else
{
// file is a bios
Bios.ImportBiosFile(GameFileImportPath, hash, ref RetVal);
}
}
return RetVal;
}
/// <summary>
/// Import a single game file
/// </summary>
/// <param name="FilePath">
/// The path to the game file to import
/// </param>
/// <param name="Hash">
/// The hash of the game file
/// </param>
/// <param name="GameFileInfo">
/// A dictionary to store the results of the import
/// </param>
/// <param name="OverridePlatform">
/// The platform to use for the game file
/// </param>
private static void _ImportGameFile(string FilePath, Common.hashObject Hash, ref Dictionary<string, object> GameFileInfo, Platform? OverridePlatform)
{
GameFileInfo.Add("type", "rom");
// check to make sure we don't already have this file imported
sql = "SELECT COUNT(Id) AS count FROM Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
sql = "SELECT COUNT(Id) AS count FROM view_Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
dbDict.Add("md5", Hash.md5hash);
dbDict.Add("sha1", Hash.sha1hash);
DataTable importDB = db.ExecuteCMD(sql, dbDict);
if ((Int64)importDB.Rows[0]["count"] > 0)
{
// import source was the import directory
if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryImportDirectory))
if (FilePath.StartsWith(Config.LibraryConfiguration.LibraryImportDirectory))
{
Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - moving to " + Config.LibraryConfiguration.LibraryImportDuplicatesDirectory);
Logging.Log(Logging.LogType.Warning, "Import Game", " " + FilePath + " already in database - moving to " + Config.LibraryConfiguration.LibraryImportDuplicatesDirectory);
string targetPathWithFileName = GameFileImportPath.Replace(Config.LibraryConfiguration.LibraryImportDirectory, Config.LibraryConfiguration.LibraryImportDuplicatesDirectory);
string targetPathWithFileName = FilePath.Replace(Config.LibraryConfiguration.LibraryImportDirectory, Config.LibraryConfiguration.LibraryImportDuplicatesDirectory);
string targetPath = Path.GetDirectoryName(targetPathWithFileName);
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
File.Move(GameFileImportPath, targetPathWithFileName, true);
File.Move(FilePath, targetPathWithFileName, true);
}
// import source was the upload directory
if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryUploadDirectory))
if (FilePath.StartsWith(Config.LibraryConfiguration.LibraryUploadDirectory))
{
Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - skipping import");
Logging.Log(Logging.LogType.Warning, "Import Game", " " + FilePath + " already in database - skipping import");
}
RetVal.Add("status", "duplicate");
GameFileInfo.Add("status", "duplicate");
}
else
{
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing");
Logging.Log(Logging.LogType.Information, "Import Game", " " + FilePath + " not in database - processing");
FileInfo fi = new FileInfo(FilePath);
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;
if (OverridePlatform == null)
{
determinedPlatform = Metadata.Platforms.GetPlatform(discoveredSignature.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
}
else
{
determinedPlatform = OverridePlatform;
discoveredSignature.Flags.IGDBPlatformId = (long)determinedPlatform.Id;
discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name;
}
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature, discoveredSignature.Flags.IGDBPlatformId, true);
gaseous_server.Models.Signatures_Games discoveredSignature = fileSignature.GetFileSignature(GameLibrary.GetDefaultLibrary, Hash, fi, FilePath);
// add to database
long RomId = StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
Platform? determinedPlatform = Metadata.Platforms.GetPlatform((long)discoveredSignature.Flags.PlatformId);
Models.Game? determinedGame = Metadata.Games.GetGame(discoveredSignature.Flags.GameMetadataSource, discoveredSignature.Flags.GameId);
long RomId = StoreGame(GameLibrary.GetDefaultLibrary, Hash, discoveredSignature, determinedPlatform, FilePath, 0, true);
Roms.GameRomItem romItem = Roms.GetRom(RomId);
// build return value
RetVal.Add("romid", RomId);
RetVal.Add("platform", determinedPlatform);
RetVal.Add("game", determinedGame);
RetVal.Add("signature", discoveredSignature);
RetVal.Add("status", "imported");
GameFileInfo.Add("romid", RomId);
GameFileInfo.Add("platform", determinedPlatform);
GameFileInfo.Add("game", determinedGame);
GameFileInfo.Add("signature", discoveredSignature);
GameFileInfo.Add("rom", romItem);
GameFileInfo.Add("status", "imported");
}
}
/// <summary>
/// Store a game in the database and move the file to the library (if required)
/// </summary>
/// <param name="library">
/// The library to store the game in
/// </param>
/// <param name="hash">
/// The hash of the game file
/// </param>
/// <param name="signature">
/// The signature of the game file
/// </param>
/// <param name="filePath">
/// The path to the game file
/// </param>
/// <param name="romId">
/// The ID of the ROM in the database (if it already exists, 0 if it doesn't)
/// </param>
/// <param name="SourceIsExternal">
/// Whether the source of the file is external to the library
/// </param>
public static long StoreGame(GameLibrary.LibraryItem library, Common.hashObject hash, Signatures_Games signature, Platform platform, string filePath, long romId = 0, bool SourceIsExternal = false)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
// add/get the metadata map
MetadataMap? map = MetadataManagement.NewMetadataMap((long)platform.Id, signature.Game.Name);
// add any metadata attributes that may be supplied as part of the signature
if (signature.Game.UserManual != null)
{
if (signature.Game.UserManual.Length > 0)
{
MetadataManagement.SetMetadataSupportData((long)map.Id, MetadataManagement.MetadataMapSupportDataTypes.UserManualLink, signature.Game.UserManual);
}
}
// populate map with the sources from the signature if they don't already exist
foreach (MetadataSources source in Enum.GetValues(typeof(MetadataSources)))
{
bool sourceExists = false;
if (source != MetadataSources.None)
{
// get the signature that matches this source
Signatures_Games.SourceValues.SourceValueItem? signatureSource = signature.MetadataSources.Games.Find(x => x.Source == source);
if (signatureSource == null)
{
Logging.Log(Logging.LogType.Information, "Import Game", " No source found for " + source.ToString());
continue;
}
// get the metadata map for this source
MetadataMap.MetadataMapItem? mapSource = map.MetadataMapItems.Find(x => x.SourceType == source);
if (mapSource == null)
{
// add the source to the map
bool preferred = false;
if (source == Config.MetadataConfiguration.DefaultMetadataSource)
{
preferred = true;
}
MetadataManagement.AddMetadataMapItem((long)map.Id, source, signatureSource.Id, preferred);
}
else
{
// update the source in the map - do not modify the preferred status
MetadataManagement.UpdateMetadataMapItem((long)map.Id, source, signatureSource.Id, null);
}
}
}
// reload the map
map = MetadataManagement.GetMetadataMap((long)map.Id);
// add or update the rom
dbDict = new Dictionary<string, object>();
if (romId == 0)
{
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, RelativePath, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion, MetadataMapId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion, @metadatamapid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
}
else
{
sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion, RomDataVersion=@romdataversion, MetadataMapId=@metadatamapid WHERE Id=@id;";
dbDict.Add("id", romId);
}
dbDict.Add("platformid", Common.ReturnValueIfNull(platform.Id, 0));
dbDict.Add("gameid", 0); // set to 0 - no longer required as game is mapped using the MetadataMapBridge table
dbDict.Add("name", Common.ReturnValueIfNull(signature.Rom.Name, 0));
dbDict.Add("size", Common.ReturnValueIfNull(signature.Rom.Size, 0));
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
dbDict.Add("crc", Common.ReturnValueIfNull(signature.Rom.Crc, ""));
dbDict.Add("developmentstatus", Common.ReturnValueIfNull(signature.Rom.DevelopmentStatus, ""));
dbDict.Add("metadatasource", signature.Rom.SignatureSource);
dbDict.Add("metadatagamename", signature.Game.Name);
dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id);
dbDict.Add("romdataversion", 2);
dbDict.Add("metadatamapid", map.Id);
if (signature.Rom.Attributes != null)
{
if (signature.Rom.Attributes.Count > 0)
{
dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(signature.Rom.Attributes));
}
else
{
dbDict.Add("attributes", "[ ]");
}
}
else
{
// file is a bios
RetVal.Add("type", "bios");
RetVal.Add("status", "notimported");
dbDict.Add("attributes", "[ ]");
}
dbDict.Add("romtype", (int)signature.Rom.RomType);
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(signature.Rom.RomTypeMedia, ""));
dbDict.Add("medialabel", Common.ReturnValueIfNull(signature.Rom.MediaLabel, ""));
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
string libraryRootPath = library.Path;
if (libraryRootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == false)
{
if (biosItem.Available == false)
libraryRootPath += Path.DirectorySeparatorChar;
}
dbDict.Add("path", filePath.Replace(libraryRootPath, ""));
DataTable romInsert = db.ExecuteCMD(sql, dbDict);
if (romId == 0)
{
if (biosItem.hash == hash.md5hash)
{
string biosPath = biosItem.biosPath.Replace(biosItem.filename, "");
if (!Directory.Exists(biosPath))
{
Directory.CreateDirectory(biosPath);
romId = (long)romInsert.Rows[0][0];
}
File.Move(GameFileImportPath, biosItem.biosPath, true);
RetVal.Add("name", biosItem.filename);
RetVal.Add("platform", Platforms.GetPlatform(biosItem.platformid, false, false));
RetVal["status"] = "imported";
return RetVal;
}
}
else
// move to destination
if (library.IsDefaultLibrary == true)
{
if (biosItem.hash == hash.md5hash)
{
RetVal["status"] = "duplicate";
return RetVal;
}
}
}
}
MoveGameFile(romId, SourceIsExternal);
}
return RetVal;
return romId;
}
public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
public static gaseous_server.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
{
if (Signature.Flags != null)
{
if (Signature.Flags.IGDBGameId != null && Signature.Flags.IGDBGameId != 0)
if (Signature.Flags.GameId != null && Signature.Flags.GameId != 0)
{
// game was determined elsewhere - probably a Hasheous server
try
{
return Games.GetGame(Signature.Flags.IGDBGameId, false, false, FullDownload);
return Games.GetGame(MetadataSources.IGDB, Signature.Flags.GameId);
}
catch (Exception ex)
{
@@ -200,7 +340,7 @@ namespace gaseous_server.Classes
}
// search discovered game - case insensitive exact match first
IGDB.Models.Game determinedGame = new IGDB.Models.Game();
gaseous_server.Models.Game determinedGame = new gaseous_server.Models.Game();
string GameName = Signature.Game.Name;
@@ -215,11 +355,13 @@ namespace gaseous_server.Classes
foreach (Metadata.Games.SearchType searchType in Enum.GetValues(typeof(Metadata.Games.SearchType)))
{
Logging.Log(Logging.LogType.Information, "Import Game", " Search type: " + searchType.ToString());
IGDB.Models.Game[] games = Metadata.Games.SearchForGame(SearchCandidate, PlatformId, searchType);
gaseous_server.Models.Game[] games = Metadata.Games.SearchForGame(SearchCandidate, PlatformId, searchType);
if (games != null)
{
if (games.Length == 1)
{
// exact match!
determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false);
determinedGame = Metadata.Games.GetGame(MetadataSources.IGDB, (long)games[0].Id);
Logging.Log(Logging.LogType.Information, "Import Game", " IGDB game: " + determinedGame.Name);
GameFound = true;
break;
@@ -229,12 +371,12 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found");
// quite likely we've found sequels and alternate types
foreach (Game game in games)
foreach (gaseous_server.Models.Game game in games)
{
if (game.Name == SearchCandidate)
{
// found game title matches the search candidate
determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false);
determinedGame = Metadata.Games.GetGame(MetadataSources.IGDB, (long)games[0].Id);
Logging.Log(Logging.LogType.Information, "Import Game", "Found exact match!");
GameFound = true;
break;
@@ -246,11 +388,16 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Import Game", " No search results found");
}
}
else
{
Logging.Log(Logging.LogType.Information, "Import Game", " No search results found");
}
}
if (GameFound == true) { break; }
}
if (determinedGame == null)
{
determinedGame = new IGDB.Models.Game();
determinedGame = new gaseous_server.Models.Game();
}
string destSlug = "";
@@ -262,9 +409,9 @@ namespace gaseous_server.Classes
return determinedGame;
}
public static List<IGDB.Models.Game> SearchForGame_GetAll(string GameName, long PlatformId)
public static List<gaseous_server.Models.Game> SearchForGame_GetAll(string GameName, long PlatformId)
{
List<IGDB.Models.Game> searchResults = new List<IGDB.Models.Game>();
List<gaseous_server.Models.Game> searchResults = new List<gaseous_server.Models.Game>();
List<string> SearchCandidates = GetSearchCandidates(GameName);
@@ -274,11 +421,11 @@ namespace gaseous_server.Classes
{
if ((PlatformId == 0 && searchType == SearchType.searchNoPlatform) || (PlatformId != 0 && searchType != SearchType.searchNoPlatform))
{
IGDB.Models.Game[] games = Metadata.Games.SearchForGame(SearchCandidate, PlatformId, searchType);
foreach (IGDB.Models.Game foundGame in games)
gaseous_server.Models.Game[] games = Metadata.Games.SearchForGame(SearchCandidate, PlatformId, searchType);
foreach (gaseous_server.Models.Game foundGame in games)
{
bool gameInResults = false;
foreach (IGDB.Models.Game searchResult in searchResults)
foreach (gaseous_server.Models.Game searchResult in searchResults)
{
if (searchResult.Id == foundGame.Id)
{
@@ -329,84 +476,14 @@ namespace gaseous_server.Classes
return SearchCandidates;
}
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, gaseous_server.Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
if (UpdateId == 0)
{
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
}
else
{
sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion, RomDataVersion=@romdataversion WHERE Id=@id;";
dbDict.Add("id", UpdateId);
}
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
dbDict.Add("gameid", Common.ReturnValueIfNull(determinedGame.Id, 0));
dbDict.Add("name", Common.ReturnValueIfNull(discoveredSignature.Rom.Name, 0));
dbDict.Add("size", Common.ReturnValueIfNull(discoveredSignature.Rom.Size, 0));
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
dbDict.Add("crc", Common.ReturnValueIfNull(discoveredSignature.Rom.Crc, ""));
dbDict.Add("developmentstatus", Common.ReturnValueIfNull(discoveredSignature.Rom.DevelopmentStatus, ""));
dbDict.Add("metadatasource", discoveredSignature.Rom.SignatureSource);
dbDict.Add("metadatagamename", discoveredSignature.Game.Name);
dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id);
dbDict.Add("romdataversion", 2);
if (discoveredSignature.Rom.Attributes != null)
{
if (discoveredSignature.Rom.Attributes.Count > 0)
{
dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(discoveredSignature.Rom.Attributes));
}
else
{
dbDict.Add("attributes", "[ ]");
}
}
else
{
dbDict.Add("attributes", "[ ]");
}
dbDict.Add("romtype", (int)discoveredSignature.Rom.RomType);
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(discoveredSignature.Rom.RomTypeMedia, ""));
dbDict.Add("medialabel", Common.ReturnValueIfNull(discoveredSignature.Rom.MediaLabel, ""));
dbDict.Add("path", GameFileImportPath);
DataTable romInsert = db.ExecuteCMD(sql, dbDict);
long romId = 0;
if (UpdateId == 0)
{
romId = (long)romInsert.Rows[0][0];
}
else
{
romId = UpdateId;
}
// move to destination
if (library.IsDefaultLibrary == true)
{
MoveGameFile(romId);
}
return romId;
}
public static string ComputeROMPath(long RomId)
{
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
// get metadata
IGDB.Models.Platform platform = gaseous_server.Classes.Metadata.Platforms.GetPlatform(rom.PlatformId);
IGDB.Models.Game game = gaseous_server.Classes.Metadata.Games.GetGame(rom.GameId, false, false, false);
MetadataMap.MetadataMapItem metadataMap = Classes.MetadataManagement.GetMetadataMap(rom.MetadataMapId).PreferredMetadataMapItem;
Platform? platform = gaseous_server.Classes.Metadata.Platforms.GetPlatform(rom.PlatformId);
gaseous_server.Models.Game? game = Classes.Metadata.Games.GetGame(metadataMap.SourceType, metadataMap.SourceId);
// build path
string platformSlug = "Unknown Platform";
@@ -419,7 +496,7 @@ namespace gaseous_server.Classes
{
gameSlug = game.Slug;
}
string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug);
string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, platformSlug);
if (!Directory.Exists(DestinationPath))
{
Directory.CreateDirectory(DestinationPath);
@@ -430,10 +507,14 @@ namespace gaseous_server.Classes
return DestinationPathName;
}
public static bool MoveGameFile(long RomId)
public static bool MoveGameFile(long RomId, bool SourceIsExternal)
{
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
string romPath = rom.Path;
if (SourceIsExternal == true)
{
romPath = rom.RelativePath;
}
if (File.Exists(romPath))
{
@@ -458,10 +539,16 @@ namespace gaseous_server.Classes
// update the db
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
string sql = "UPDATE Games_Roms SET RelativePath=@path WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
dbDict.Add("path", DestinationPath);
string libraryRootPath = rom.Library.Path;
if (libraryRootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == false)
{
libraryRootPath += Path.DirectorySeparatorChar;
}
dbDict.Add("path", DestinationPath.Replace(libraryRootPath, ""));
db.ExecuteCMD(sql, dbDict);
return true;
@@ -483,7 +570,7 @@ namespace gaseous_server.Classes
// move rom files to their new location
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid";
string sql = "SELECT * FROM view_Games_Roms WHERE LibraryId = @libraryid";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("libraryid", library.Id);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -495,7 +582,7 @@ namespace gaseous_server.Classes
SetStatus(i, romDT.Rows.Count, "Processing file " + romDT.Rows[i]["name"]);
Logging.Log(Logging.LogType.Information, "Organise Library", "(" + i + "/" + romDT.Rows.Count + ") Processing ROM " + romDT.Rows[i]["name"]);
long RomId = (long)romDT.Rows[i]["id"];
MoveGameFile(RomId);
MoveGameFile(RomId, false);
}
}
ClearStatus();
@@ -526,7 +613,7 @@ namespace gaseous_server.Classes
public static List<GameLibrary.LibraryItem> LibrariesToScan = new List<GameLibrary.LibraryItem>();
public void LibraryScan()
{
int maxWorkers = Config.MetadataConfiguration.MaxLibraryScanWorkers;
int maxWorkers = 4;
if (LibrariesToScan.Count == 0)
{
@@ -564,8 +651,7 @@ namespace gaseous_server.Classes
1,
new List<ProcessQueue.QueueItemType>
{
ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.Rematcher
ProcessQueue.QueueItemType.OrganiseLibrary
},
false,
true)
@@ -639,7 +725,7 @@ namespace gaseous_server.Classes
dupDict.Add("libraryid", library.Id);
db.ExecuteCMD(duplicateSql, dupDict);
string sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
string sql = "SELECT * FROM view_Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("libraryid", library.Id);
DataTable dtRoms = db.ExecuteCMD(sql, dbDict);
@@ -664,7 +750,7 @@ namespace gaseous_server.Classes
}
}
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
sql = "SELECT * FROM view_Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
// search for files in the library that aren't in the database
@@ -708,9 +794,9 @@ namespace gaseous_server.Classes
{
// get discovered platform
long PlatformId;
IGDB.Models.Platform determinedPlatform;
Platform determinedPlatform;
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0)
if (sig.Flags.PlatformId == null || sig.Flags.PlatformId == 0)
{
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;
@@ -718,13 +804,13 @@ namespace gaseous_server.Classes
else
{
// use the platform discovered in the signature
PlatformId = sig.Flags.IGDBPlatformId;
PlatformId = (long)sig.Flags.PlatformId;
}
determinedPlatform = Platforms.GetPlatform(PlatformId);
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
gaseous_server.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile);
StoreGame(library, hash, sig, determinedPlatform, LibraryFile, 0, false);
}
catch (Exception ex)
{
@@ -736,7 +822,7 @@ namespace gaseous_server.Classes
}
ClearStatus();
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
sql = "SELECT * FROM view_Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
dtRoms = db.ExecuteCMD(sql, dbDict);
// check all roms to see if their local file still exists
@@ -760,7 +846,7 @@ 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);
MoveGameFile(romId, false);
}
else
{
@@ -786,86 +872,6 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
}
public void Rematcher(bool ForceExecute = false)
{
// rescan all titles with an unknown platform or title and see if we can get a match
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch on library " + library.Name);
string sql = "";
if (ForceExecute == false)
{
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) AND LibraryId = @libraryid LIMIT 100;";
}
else
{
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND LibraryId = @libraryid;";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
dbDict.Add("libraryid", library.Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
int StatusCount = -0;
foreach (DataRow row in data.Rows)
{
SetStatus(StatusCount, data.Rows.Count, "Running rematcher");
// 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);
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
string attemptSql = "UPDATE Games_Roms SET LastMatchAttemptDate=@lastmatchattemptdate WHERE Id=@id;";
Dictionary<string, object> dbLastAttemptDict = new Dictionary<string, object>();
dbLastAttemptDict.Add("id", romId);
dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow);
db.ExecuteCMD(attemptSql, dbLastAttemptDict);
StatusCount += 1;
}
ClearStatus();
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed");
ClearStatus();
}
}
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System.Reflection;
using System.Text.Json.Serialization;
using IGDB;
using IGDB.Models;
using gaseous_server.Models;
using HasheousClient.Models.Metadata.IGDB;
using Microsoft.CodeAnalysis.Classification;
namespace gaseous_server.Classes.Metadata
@@ -14,7 +14,7 @@ namespace gaseous_server.Classes.Metadata
}
public static AgeGroup? GetAgeGroup(Game? game)
public static AgeGroup? GetAgeGroup(Models.Game? game)
{
if (game == null)
{
@@ -23,7 +23,7 @@ namespace gaseous_server.Classes.Metadata
else
{
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
cacheStatus = Storage.GetCacheStatus("AgeGroup", (long)game.Id);
cacheStatus = Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.IGDB, "AgeGroup", (long)game.Id);
AgeGroup? RetVal = new AgeGroup();
@@ -31,16 +31,16 @@ namespace gaseous_server.Classes.Metadata
{
case Storage.CacheStatus.NotPresent:
RetVal = _GetAgeGroup(game);
Storage.NewCacheValue(RetVal, false);
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, RetVal, false);
break;
case Storage.CacheStatus.Expired:
RetVal = _GetAgeGroup(game);
Storage.NewCacheValue(RetVal, true);
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, RetVal, true);
break;
case Storage.CacheStatus.Current:
RetVal = Storage.GetCacheValue<AgeGroup>(RetVal, "Id", game.Id);
RetVal = Storage.GetCacheValue<AgeGroup>(HasheousClient.Models.MetadataSources.IGDB, RetVal, "Id", game.Id);
break;
default:
@@ -51,20 +51,20 @@ namespace gaseous_server.Classes.Metadata
}
}
public static AgeGroup? _GetAgeGroup(Game game)
public static AgeGroup? _GetAgeGroup(Models.Game game)
{
// compile the maximum age group for the given game
if (game != null)
{
if (game.AgeRatings != null)
{
if (game.AgeRatings.Ids != null)
if (game.AgeRatings != null)
{
// collect ratings values from metadata
List<AgeRating> ageRatings = new List<AgeRating>();
foreach (long ratingId in game.AgeRatings.Ids)
foreach (long ratingId in game.AgeRatings)
{
AgeRating? rating = AgeRatings.GetAgeRatings(ratingId);
AgeRating? rating = AgeRatings.GetAgeRating(game.MetadataSource, ratingId);
if (rating != null)
{
ageRatings.Add(rating);
@@ -262,13 +262,13 @@ namespace gaseous_server.Classes.Metadata
public class AgeGroupItem
{
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
public List<AgeRatingTitle> ACB { get; set; }
public List<AgeRatingTitle> CERO { get; set; }
public List<AgeRatingTitle> CLASS_IND { get; set; }
public List<AgeRatingTitle> ESRB { get; set; }
public List<AgeRatingTitle> GRAC { get; set; }
public List<AgeRatingTitle> PEGI { get; set; }
public List<AgeRatingTitle> USK { get; set; }
[JsonIgnore]
[Newtonsoft.Json.JsonIgnore]

View File

@@ -1,21 +1,21 @@
using System;
using System.Buffers;
using System.Reflection;
using System.Text.Json.Serialization;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
using Microsoft.CodeAnalysis.Classification;
namespace gaseous_server.Classes.Metadata
{
public class AgeRatings
{
const string fieldList = "fields category,checksum,content_descriptions,rating,rating_cover_url,synopsis;";
public const string fieldList = "fields category,checksum,content_descriptions,rating,rating_cover_url,synopsis;";
public AgeRatings()
{
}
public static AgeRating? GetAgeRatings(long? Id)
public static AgeRating? GetAgeRating(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -23,106 +23,16 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<AgeRating> RetVal = _GetAgeRatings(SearchUsing.id, Id);
return RetVal.Result;
AgeRating? RetVal = Metadata.GetMetadata<AgeRating>(SourceType, (long)Id, false);
return RetVal;
}
}
public static AgeRating GetAgeRatings(string Slug)
{
Task<AgeRating> RetVal = _GetAgeRatings(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<AgeRating> _GetAgeRatings(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("AgeRating", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("AgeRating", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
AgeRating returnValue = new AgeRating();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private static void UpdateSubClasses(AgeRating ageRating)
{
if (ageRating.ContentDescriptions != null)
{
foreach (long AgeRatingContentDescriptionId in ageRating.ContentDescriptions.Ids)
{
AgeRatingContentDescription ageRatingContentDescription = AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(AgeRatingContentDescriptionId);
}
}
}
private enum SearchUsing
{
id,
slug
}
private static async Task<AgeRating> GetObjectFromServer(string WhereClause)
{
// get AgeRatings metadata
Communications comms = new Communications();
var results = await comms.APIComm<AgeRating>(IGDBClient.Endpoints.AgeRating, fieldList, WhereClause);
var result = results.First();
return result;
}
public static GameAgeRating GetConsolidatedAgeRating(long RatingId)
public static GameAgeRating GetConsolidatedAgeRating(HasheousClient.Models.MetadataSources SourceType, long RatingId)
{
GameAgeRating gameAgeRating = new GameAgeRating();
AgeRating ageRating = GetAgeRatings(RatingId);
AgeRating ageRating = GetAgeRating(SourceType, RatingId);
gameAgeRating.Id = (long)ageRating.Id;
gameAgeRating.RatingBoard = (AgeRatingCategory)ageRating.Category;
gameAgeRating.RatingTitle = (AgeRatingTitle)ageRating.Rating;
@@ -130,9 +40,9 @@ namespace gaseous_server.Classes.Metadata
List<string> descriptions = new List<string>();
if (ageRating.ContentDescriptions != null)
{
foreach (long ContentId in ageRating.ContentDescriptions.Ids)
foreach (long ContentId in ageRating.ContentDescriptions)
{
AgeRatingContentDescription ageRatingContentDescription = AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(ContentId);
AgeRatingContentDescription ageRatingContentDescription = AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(SourceType, ContentId);
descriptions.Add(ageRatingContentDescription.Description);
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class AgeRatingContentDescriptions
{
const string fieldList = "fields category,checksum,description;";
public const string fieldList = "fields category,checksum,description;";
public AgeRatingContentDescriptions()
{
}
public static AgeRatingContentDescription? GetAgeRatingContentDescriptions(long? Id)
public static AgeRatingContentDescription? GetAgeRatingContentDescriptions(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<AgeRatingContentDescription> RetVal = _GetAgeRatingContentDescriptions(SearchUsing.id, Id);
return RetVal.Result;
AgeRatingContentDescription? RetVal = Metadata.GetMetadata<AgeRatingContentDescription>(SourceType, (long)Id, false);
return RetVal;
}
}
public static AgeRatingContentDescription GetAgeRatingContentDescriptions(string Slug)
{
Task<AgeRatingContentDescription> RetVal = _GetAgeRatingContentDescriptions(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<AgeRatingContentDescription> _GetAgeRatingContentDescriptions(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("AgeRatingContentDescription", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("AgeRatingContentDescription", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
AgeRatingContentDescription returnValue = new AgeRatingContentDescription();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<AgeRatingContentDescription> GetObjectFromServer(string WhereClause)
{
// get AgeRatingContentDescriptionContentDescriptions metadata
Communications comms = new Communications();
var results = await comms.APIComm<AgeRatingContentDescription>(IGDBClient.Endpoints.AgeRatingContentDescriptions, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class AlternativeNames
{
const string fieldList = "fields checksum,comment,game,name;";
public const string fieldList = "fields checksum,comment,game,name;";
public AlternativeNames()
{
}
public static AlternativeName? GetAlternativeNames(long? Id)
public static AlternativeName? GetAlternativeNames(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<AlternativeName> RetVal = _GetAlternativeNames(SearchUsing.id, Id);
return RetVal.Result;
AlternativeName? RetVal = Metadata.GetMetadata<AlternativeName>(SourceType, (long)Id, false);
return RetVal;
}
}
public static AlternativeName GetAlternativeNames(string Slug)
{
Task<AlternativeName> RetVal = _GetAlternativeNames(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<AlternativeName> _GetAlternativeNames(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("AlternativeName", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("AlternativeName", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
AlternativeName returnValue = new AlternativeName();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<AlternativeName> GetObjectFromServer(string WhereClause)
{
// get AlternativeNames metadata
Communications comms = new Communications();
var results = await comms.APIComm<AlternativeName>(IGDBClient.Endpoints.AlternativeNames, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,17 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Artworks
{
const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
public const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
public Artworks()
{
}
public static Artwork? GetArtwork(long? Id, string ImagePath, bool GetImages)
public static Artwork? GetArtwork(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,111 +19,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Artwork> RetVal = _GetArtwork(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result;
Artwork? RetVal = Metadata.GetMetadata<Artwork>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Artwork GetArtwork(string Slug, string ImagePath, bool GetImages)
{
Task<Artwork> RetVal = _GetArtwork(SearchUsing.slug, Slug, ImagePath, GetImages);
return RetVal.Result;
}
private static async Task<Artwork> _GetArtwork(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Artwork", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Artwork", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Artwork returnValue = new Artwork();
bool forceImageDownload = false;
ImagePath = Path.Combine(ImagePath, "Artwork");
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
// check if old value is different from the new value - only download if it's different
Artwork oldImage = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
if (oldImage.ImageId != returnValue.ImageId)
{
forceImageDownload = true;
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if (GetImages == true)
{
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Artwork download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<Artwork>(IGDBClient.Endpoints.Artworks, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,17 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Collections
{
const string fieldList = "fields as_child_relations,as_parent_relations,checksum,created_at,games,name,slug,type,updated_at,url;";
public const string fieldList = "fields as_child_relations,as_parent_relations,checksum,created_at,games,name,slug,type,updated_at,url;";
public Collections()
{
}
public static Collection? GetCollections(long? Id)
public static Collection? GetCollections(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +19,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Collection> RetVal = _GetCollections(SearchUsing.id, Id);
return RetVal.Result;
Collection? RetVal = Metadata.GetMetadata<Collection>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Collection GetCollections(string Slug)
{
Task<Collection> RetVal = _GetCollections(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<Collection> _GetCollections(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Collection", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Collection", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Collection returnValue = new Collection();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Collection> GetObjectFromServer(string WhereClause)
{
// get Collections metadata
Communications comms = new Communications();
var results = await comms.APIComm<Collection>(IGDBClient.Endpoints.Collections, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,17 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Companies
{
const string fieldList = "fields change_date,change_date_category,changed_company_id,checksum,country,created_at,description,developed,logo,name,parent,published,slug,start_date,start_date_category,updated_at,url,websites;";
public const string fieldList = "fields change_date,change_date_category,changed_company_id,checksum,country,created_at,description,developed,logo,name,parent,published,slug,start_date,start_date_category,updated_at,url,websites;";
public Companies()
{
}
public static Company? GetCompanies(long? Id)
public static Company? GetCompanies(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -20,105 +19,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Company> RetVal = _GetCompanies(SearchUsing.id, Id);
return RetVal.Result;
Company? RetVal = Metadata.GetMetadata<Company>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Company GetCompanies(string Slug)
{
Task<Company> RetVal = _GetCompanies(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<Company> _GetCompanies(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Company", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Company", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Company returnValue = new Company();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
if (returnValue != null) { Storage.NewCacheValue(returnValue); }
UpdateSubClasses(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private static void UpdateSubClasses(Company company)
{
if (company.Logo != null)
{
CompanyLogo companyLogo = CompanyLogos.GetCompanyLogo(company.Logo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Company(company));
}
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Company> GetObjectFromServer(string WhereClause)
{
// get Companies metadata
Communications comms = new Communications();
var results = await comms.APIComm<Company>(IGDBClient.Endpoints.Companies, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First();
return result;
}
else
{
return null;
}
}
}
}

View File

@@ -1,13 +1,11 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class CompanyLogos
{
const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
public const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
public CompanyLogos()
{
@@ -21,110 +19,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, ImagePath);
return RetVal.Result;
CompanyLogo? RetVal = Metadata.GetMetadata<CompanyLogo>((long)Id, false);
return RetVal;
}
}
public static CompanyLogo GetCompanyLogo(string Slug, string ImagePath)
{
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.slug, Slug, ImagePath);
return RetVal.Result;
}
private static async Task<CompanyLogo> _GetCompanyLogo(SearchUsing searchUsing, object searchValue, string ImagePath)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("CompanyLogo", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("CompanyLogo", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
CompanyLogo returnValue = new CompanyLogo();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
}
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
// check if old value is different from the new value - only download if it's different
CompanyLogo oldImage = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
if (oldImage.ImageId != returnValue.ImageId)
{
forceImageDownload = true;
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
// 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)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Company logo download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<CompanyLogo> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,21 +1,17 @@
using System;
using System.Net;
using IGDB;
using IGDB.Models;
using Microsoft.CodeAnalysis.Elfie.Model.Strings;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Covers
{
const string fieldList = "fields alpha_channel,animated,checksum,game,game_localization,height,image_id,url,width;";
public const string fieldList = "fields alpha_channel,animated,checksum,game,game_localization,height,image_id,url,width;";
public Covers()
{
}
public static Cover? GetCover(long? Id, string ImagePath, bool GetImages)
public static Cover? GetCover(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -23,124 +19,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Cover> RetVal = _GetCover(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result;
}
}
public static Cover GetCover(string Slug, string ImagePath, bool GetImages)
{
Task<Cover> RetVal = _GetCover(SearchUsing.slug, Slug, ImagePath, GetImages);
return RetVal.Result;
}
private static async Task<Cover> _GetCover(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Cover", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Cover", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Cover returnValue = new Cover();
bool forceImageDownload = false;
ImagePath = Path.Combine(ImagePath, "Covers");
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
// check if old value is different from the new value - only download if it's different
Cover oldCover = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
if (oldCover.ImageId != returnValue.ImageId)
{
forceImageDownload = true;
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if (GetImages == true)
{
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Cover download forced.");
// check for presence of image file - download if absent or force download is true
List<Communications.IGDBAPI_ImageSize> imageSizes = new List<Communications.IGDBAPI_ImageSize>{
Communications.IGDBAPI_ImageSize.cover_big,
Communications.IGDBAPI_ImageSize.cover_small,
Communications.IGDBAPI_ImageSize.original
};
Communications comms = new Communications();
foreach (Communications.IGDBAPI_ImageSize size in imageSizes)
{
localFile = Path.Combine(ImagePath, size.ToString(), returnValue.ImageId + ".jpg");
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, size, null);
Cover? RetVal = Metadata.GetMetadata<Cover>(SourceType, (long)Id, false);
return RetVal;
}
}
}
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Cover> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Cover metadata
Communications comms = new Communications();
var results = await comms.APIComm<Cover>(IGDBClient.Endpoints.Covers, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,17 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class ExternalGames
{
const string fieldList = "fields category,checksum,countries,created_at,game,media,name,platform,uid,updated_at,url,year;";
public const string fieldList = "fields category,checksum,countries,created_at,game,media,name,platform,uid,updated_at,url,year;";
public ExternalGames()
{
}
public static ExternalGame? GetExternalGames(long? Id)
public static ExternalGame? GetExternalGames(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,96 +19,8 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<ExternalGame> RetVal = _GetExternalGames(SearchUsing.id, Id);
return RetVal.Result;
}
}
public static ExternalGame GetExternalGames(string Slug)
{
Task<ExternalGame> RetVal = _GetExternalGames(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<ExternalGame> _GetExternalGames(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("ExternalGame", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("ExternalGame", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
ExternalGame returnValue = new ExternalGame();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
}
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<ExternalGame?> GetObjectFromServer(string WhereClause)
{
// get ExternalGames metadata
Communications comms = new Communications();
var results = await comms.APIComm<ExternalGame>(IGDBClient.Endpoints.ExternalGames, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First();
return result;
}
else
{
return null;
ExternalGame? RetVal = Metadata.GetMetadata<ExternalGame>(SourceType, (long)Id, false);
return RetVal;
}
}
}

View File

@@ -1,19 +1,17 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Franchises
{
const string fieldList = "fields checksum,created_at,games,name,slug,updated_at,url;";
public const string fieldList = "fields checksum,created_at,games,name,slug,updated_at,url;";
public Franchises()
{
}
public static Franchise? GetFranchises(long? Id)
public static Franchise? GetFranchises(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +19,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Franchise> RetVal = _GetFranchises(SearchUsing.id, Id);
return RetVal.Result;
Franchise? RetVal = Metadata.GetMetadata<Franchise>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Franchise GetFranchises(string Slug)
{
Task<Franchise> RetVal = _GetFranchises(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<Franchise> _GetFranchises(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Franchise", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Franchise", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Franchise returnValue = new Franchise();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Franchise> GetObjectFromServer(string WhereClause)
{
// get FranchiseContentDescriptions metadata
Communications comms = new Communications();
var results = await comms.APIComm<Franchise>(IGDBClient.Endpoints.Franchies, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class GameModes
{
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public GameModes()
{
}
public static GameMode? GetGame_Modes(long? Id)
public static GameMode? GetGame_Modes(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<GameMode> RetVal = _GetGame_Modes(SearchUsing.id, Id);
return RetVal.Result;
GameMode? RetVal = Metadata.GetMetadata<GameMode>(SourceType, (long)Id, false);
return RetVal;
}
}
public static GameMode GetGame_Modes(string Slug)
{
Task<GameMode> RetVal = _GetGame_Modes(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<GameMode> _GetGame_Modes(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("GameMode", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("GameMode", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
GameMode returnValue = new GameMode();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<GameMode> GetObjectFromServer(string WhereClause)
{
// get Game_Modes metadata
Communications comms = new Communications();
var results = await comms.APIComm<GameMode>(IGDBClient.Endpoints.GameModes, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class GamesVideos
{
const string fieldList = "fields checksum,game,name,video_id;";
public const string fieldList = "fields checksum,game,name,video_id;";
public GamesVideos()
{
}
public static GameVideo? GetGame_Videos(long? Id)
public static GameVideo? GetGame_Videos(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<GameVideo> RetVal = _GetGame_Videos(SearchUsing.id, Id);
return RetVal.Result;
GameVideo? RetVal = Metadata.GetMetadata<GameVideo>(SourceType, (long)Id, false);
return RetVal;
}
}
public static GameVideo GetGame_Videos(string Slug)
{
Task<GameVideo> RetVal = _GetGame_Videos(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<GameVideo> _GetGame_Videos(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("GameVideo", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("GameVideo", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
GameVideo returnValue = new GameVideo();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<GameVideo> GetObjectFromServer(string WhereClause)
{
// get Game_Videos metadata
Communications comms = new Communications();
var results = await comms.APIComm<GameVideo>(IGDBClient.Endpoints.GameVideos, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,14 +1,13 @@
using System;
using System.Data;
using System.Security.Cryptography.X509Certificates;
using gaseous_server.Models;
using IGDB;
using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class Games
{
const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collections,cover,created_at,dlcs,expanded_games,expansions,external_games,first_release_date,follows,forks,franchise,franchises,game_engines,game_localizations,game_modes,genres,hypes,involved_companies,keywords,language_supports,multiplayer_modes,name,parent_game,platforms,player_perspectives,ports,rating,rating_count,release_dates,remakes,remasters,screenshots,similar_games,slug,standalone_expansions,status,storyline,summary,tags,themes,total_rating,total_rating_count,updated_at,url,version_parent,version_title,videos,websites;";
public const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collections,cover,created_at,dlcs,expanded_games,expansions,external_games,first_release_date,follows,forks,franchise,franchises,game_engines,game_localizations,game_modes,genres,hypes,involved_companies,keywords,language_supports,multiplayer_modes,name,parent_game,platforms,player_perspectives,ports,rating,rating_count,release_dates,remakes,remasters,screenshots,similar_games,slug,standalone_expansions,status,storyline,summary,tags,themes,total_rating,total_rating_count,updated_at,url,version_parent,version_title,videos,websites;";
public Games()
{
@@ -21,39 +20,32 @@ namespace gaseous_server.Classes.Metadata
{ }
}
public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh)
public static Game? GetGame(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if (Id == 0)
if ((Id == 0) || (Id == null))
{
Game returnValue = new Game();
if (Storage.GetCacheStatus("Game", 0) == Storage.CacheStatus.NotPresent)
{
returnValue = new Game
{
Id = 0,
Name = "Unknown Title",
Slug = "Unknown"
};
Storage.NewCacheValue(returnValue);
return returnValue;
return null;
}
else
{
return Storage.GetCacheValue<Game>(returnValue, "id", 0);
}
}
else
Game? RetVal = Metadata.GetMetadata<Game>(SourceType, (long)Id, false);
RetVal.MetadataSource = SourceType;
long? metadataMap = MetadataManagement.GetMetadataMapIdFromSourceId(SourceType, (long)Id);
if (metadataMap != null)
{
Task<Game> RetVal = _GetGame(SearchUsing.id, Id, getAllMetadata, followSubGames, forceRefresh);
return RetVal.Result;
RetVal.MetadataMapId = (long)metadataMap;
}
RetVal = MassageResult(RetVal);
return RetVal;
}
}
public static Game GetGame(string Slug, bool getAllMetadata, bool followSubGames, bool forceRefresh)
public static Game? GetGame(HasheousClient.Models.MetadataSources SourceType, string? Slug)
{
Task<Game> RetVal = _GetGame(SearchUsing.slug, Slug, getAllMetadata, followSubGames, forceRefresh);
return RetVal.Result;
Game? RetVal = Metadata.GetMetadata<Game>(SourceType, Slug, false);
RetVal.MetadataSource = SourceType;
RetVal = MassageResult(RetVal);
return RetVal;
}
public static Game GetGame(DataRow dataRow)
@@ -61,327 +53,43 @@ namespace gaseous_server.Classes.Metadata
return Storage.BuildCacheObject<Game>(new Game(), dataRow);
}
private static async Task<Game> _GetGame(SearchUsing searchUsing, object searchValue, bool getAllMetadata = true, bool followSubGames = false, bool forceRefresh = false)
private static Game MassageResult(Game result)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Game", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Game", (string)searchValue);
}
if (forceRefresh == true)
{
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
}
string WhereClause = "";
string searchField = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
searchField = "id";
break;
case SearchUsing.slug:
WhereClause = "where slug = \"" + searchValue + "\"";
searchField = "slug";
break;
default:
throw new Exception("Invalid search type");
}
Game returnValue = new Game();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
return returnValue;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Game>(returnValue, searchField, searchValue);
}
return returnValue;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Game>(returnValue, searchField, searchValue);
UpdateSubClasses(returnValue, false, false, false);
return returnValue;
default:
throw new Exception("How did you get here?");
}
}
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames, bool forceRefresh)
{
// required metadata
// if (Game.Cover != null)
// {
// try
// {
// Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
// }
// catch (Exception ex)
// {
// Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
// }
// }
if (Game.Genres != null)
{
foreach (long GenreId in Game.Genres.Ids)
{
Genre GameGenre = Genres.GetGenres(GenreId);
}
}
if (Game.GameModes != null)
{
foreach (long gameModeId in Game.GameModes.Ids)
{
GameMode gameMode = GameModes.GetGame_Modes(gameModeId);
}
}
if (Game.MultiplayerModes != null)
{
foreach (long multiplayerModeId in Game.MultiplayerModes.Ids)
{
MultiplayerMode multiplayerMode = MultiplayerModes.GetGame_MultiplayerModes(multiplayerModeId);
}
}
if (Game.PlayerPerspectives != null)
{
foreach (long PerspectiveId in Game.PlayerPerspectives.Ids)
{
PlayerPerspective GamePlayPerspective = PlayerPerspectives.GetGame_PlayerPerspectives(PerspectiveId);
}
}
if (Game.Themes != null)
{
foreach (long ThemeId in Game.Themes.Ids)
{
Theme GameTheme = Themes.GetGame_Themes(ThemeId);
}
}
if (Game.AgeRatings != null)
{
foreach (long AgeRatingId in Game.AgeRatings.Ids)
{
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
AgeGroups.GetAgeGroup(Game);
if (Game.ReleaseDates != null)
{
foreach (long ReleaseDateId in Game.ReleaseDates.Ids)
{
ReleaseDate GameReleaseDate = ReleaseDates.GetReleaseDates(ReleaseDateId);
}
}
// optional metadata - usually downloaded as needed
if (getAllMetadata == true)
{
if (Game.AlternativeNames != null)
{
foreach (long AlternativeNameId in Game.AlternativeNames.Ids)
{
AlternativeName GameAlternativeName = AlternativeNames.GetAlternativeNames(AlternativeNameId);
}
}
if (Game.Artworks != null)
{
foreach (long ArtworkId in Game.Artworks.Ids)
{
try
{
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch artwork id: " + ArtworkId, ex);
}
}
}
if (followSubGames)
{
List<long> gamesToFetch = new List<long>();
if (Game.Bundles != null) { gamesToFetch.AddRange(Game.Bundles.Ids); }
if (Game.Dlcs != null) { gamesToFetch.AddRange(Game.Dlcs.Ids); }
if (Game.Expansions != null) { gamesToFetch.AddRange(Game.Expansions.Ids); }
if (Game.ParentGame != null) { gamesToFetch.Add((long)Game.ParentGame.Id); }
//if (Game.SimilarGames != null) { gamesToFetch.AddRange(Game.SimilarGames.Ids); }
if (Game.StandaloneExpansions != null) { gamesToFetch.AddRange(Game.StandaloneExpansions.Ids); }
if (Game.VersionParent != null) { gamesToFetch.Add((long)Game.VersionParent.Id); }
foreach (long gameId in gamesToFetch)
{
Game relatedGame = GetGame(gameId, false, true, false);
}
}
if (Game.Collection != null)
{
Collection GameCollection = Collections.GetCollections(Game.Collection.Id);
}
if (Game.ExternalGames != null)
{
foreach (long ExternalGameId in Game.ExternalGames.Ids)
{
ExternalGame GameExternalGame = ExternalGames.GetExternalGames(ExternalGameId);
}
}
if (Game.Franchise != null)
{
Franchise GameFranchise = Franchises.GetFranchises(Game.Franchise.Id);
}
if (Game.Franchises != null)
{
foreach (long FranchiseId in Game.Franchises.Ids)
{
Franchise GameFranchise = Franchises.GetFranchises(FranchiseId);
}
}
if (Game.InvolvedCompanies != null)
{
foreach (long involvedCompanyId in Game.InvolvedCompanies.Ids)
{
InvolvedCompany involvedCompany = InvolvedCompanies.GetInvolvedCompanies(involvedCompanyId);
}
}
if (Game.Platforms != null)
{
foreach (long PlatformId in Game.Platforms.Ids)
{
Platform GamePlatform = Platforms.GetPlatform(PlatformId);
}
}
if (Game.Screenshots != null)
{
foreach (long ScreenshotId in Game.Screenshots.Ids)
{
try
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch screenshot id: " + ScreenshotId, ex);
}
}
}
if (Game.Videos != null)
{
foreach (long GameVideoId in Game.Videos.Ids)
{
GameVideo gameVideo = GamesVideos.GetGame_Videos(GameVideoId);
}
}
}
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Game> GetObjectFromServer(string WhereClause)
{
// get Game metadata
Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, fieldList, WhereClause);
var result = results.First();
// add artificial unknown platform mapping
List<long> platformIds = new List<long>();
platformIds.Add(0);
if (result.Platforms != null)
{
if (result.Platforms.Ids != null)
{
platformIds.AddRange(result.Platforms.Ids.ToList());
}
}
result.Platforms = new IdentitiesOrValues<Platform>(
ids: platformIds.ToArray<long>()
);
Game? parentGame = null;
// get cover art from parent if this has no cover
if (result.Cover == null)
{
if (result.ParentGame != null)
{
if (result.ParentGame.Id != null)
{
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no cover art, fetching cover art from parent game");
Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false);
parentGame = GetGame(result.MetadataSource, (long)result.ParentGame);
result.Cover = parentGame.Cover;
}
}
}
// get missing metadata from parent if this is a port
if (result.Category == Category.Port)
if (result.Category == HasheousClient.Models.Metadata.IGDB.Category.Port)
{
if (result.Summary == null)
{
if (result.ParentGame != null)
{
if (result.ParentGame.Id != null)
{
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no summary, fetching summary from parent game");
Game parentGame = GetGame((long)result.ParentGame.Id, false, false, false);
result.Summary = parentGame.Summary;
}
}
}
// populate age group data
if (result.MetadataSource == HasheousClient.Models.MetadataSources.IGDB)
{
AgeGroups.GetAgeGroup(result);
}
return result;
}
public static void AssignAllGamesToPlatformIdZero()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Game;";
DataTable gamesTable = db.ExecuteCMD(sql);
foreach (DataRow gameRow in gamesTable.Rows)
{
sql = "DELETE FROM Relation_Game_Platforms WHERE PlatformsId = 0 AND GameId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (@Id, 0);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", (long)gameRow["Id"]);
db.ExecuteCMD(sql, dbDict);
}
}
private static bool AllowNoPlatformSearch = false;
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType)
@@ -430,7 +138,7 @@ namespace gaseous_server.Classes.Metadata
break;
}
string sql = "SELECT Game.Id, Game.`Name`, Game.Slug, Relation_Game_Platforms.PlatformsId AS PlatformsId, Game.Summary FROM gaseous.Game JOIN Relation_Game_Platforms ON Game.Id = Relation_Game_Platforms.GameId WHERE " + whereClause + ";";
string sql = "SELECT Game.Id, Game.`Name`, Game.Slug, Relation_Game_Platforms.PlatformsId AS PlatformsId, Game.Summary FROM gaseous.Game JOIN Relation_Game_Platforms ON Game.Id = Relation_Game_Platforms.GameId AND Game.SourceId = Relation_Game_Platforms.GameSourceId WHERE " + whereClause + ";";
// get Game metadata
@@ -459,6 +167,13 @@ namespace gaseous_server.Classes.Metadata
}
private static async Task<Game[]> _SearchForGameRemote(string SearchString, long PlatformId, SearchType searchType)
{
switch (Config.MetadataConfiguration.DefaultMetadataSource)
{
case HasheousClient.Models.MetadataSources.None:
return new Game[0];
case HasheousClient.Models.MetadataSources.IGDB:
if (Config.IGDB.UseHasheousProxy == false)
{
string searchBody = "";
string searchFields = "fields id,name,slug,platforms,summary; ";
@@ -492,7 +207,7 @@ namespace gaseous_server.Classes.Metadata
Game[]? results = new Game[0];
if (allowSearch == true)
{
results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
results = await comms.APIComm<Game>(IGDB.IGDBClient.Endpoints.Games, searchFields, searchBody);
Communications.SetSearchCache<Game[]?>(searchFields, searchBody, results);
}
@@ -504,20 +219,39 @@ namespace gaseous_server.Classes.Metadata
return games.ToArray();
}
}
else
{
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
HasheousClient.Models.Metadata.IGDB.Game[] hResults = hasheous.GetMetadataProxy_SearchGame<HasheousClient.Models.Metadata.IGDB.Game>(HasheousClient.Hasheous.MetadataProvider.IGDB, PlatformId.ToString(), SearchString);
public static List<AvailablePlatformItem> GetAvailablePlatforms(string UserId, long GameId)
List<Game> hGames = new List<Game>();
foreach (HasheousClient.Models.Metadata.IGDB.Game hResult in hResults)
{
hGames.Add(Communications.ConvertToIGDBModel<Game>(hResult));
}
return hGames.ToArray();
}
default:
return new Game[0];
}
}
public static List<AvailablePlatformItem> GetAvailablePlatforms(string UserId, HasheousClient.Models.MetadataSources SourceType, long GameId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = @"
SELECT DISTINCT
Games_Roms.GameId,
Games_Roms.PlatformId,
view_Games_Roms.MetadataMapId,
view_Games_Roms.GameId,
view_Games_Roms.PlatformId,
view_Games_Roms.UserManualLink,
Platform.`Name`,
User_RecentPlayedRoms.UserId AS MostRecentUserId,
User_RecentPlayedRoms.RomId AS MostRecentRomId,
CASE User_RecentPlayedRoms.IsMediaGroup
WHEN 0 THEN GMR.`Name`
WHEN 1 THEN 'Media Group'
WHEN 1 THEN view_Games_Roms.`MetadataGameName`
ELSE NULL
END AS `MostRecentRomName`,
User_RecentPlayedRoms.IsMediaGroup AS MostRecentRomIsMediaGroup,
@@ -525,31 +259,32 @@ SELECT DISTINCT
User_GameFavouriteRoms.RomId AS FavouriteRomId,
CASE User_GameFavouriteRoms.IsMediaGroup
WHEN 0 THEN GFV.`Name`
WHEN 1 THEN 'Media Group'
WHEN 1 THEN view_Games_Roms.`MetadataGameName`
ELSE NULL
END AS `FavouriteRomName`,
User_GameFavouriteRoms.IsMediaGroup AS FavouriteRomIsMediaGroup
FROM
Games_Roms
view_Games_Roms
LEFT JOIN
Platform ON Games_Roms.PlatformId = Platform.Id
Platform ON view_Games_Roms.PlatformId = Platform.Id AND Platform.SourceId = view_Games_Roms.GameIdType
LEFT JOIN
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
AND User_RecentPlayedRoms.GameId = Games_Roms.GameId
AND User_RecentPlayedRoms.PlatformId = Games_Roms.PlatformId
AND User_RecentPlayedRoms.GameId = view_Games_Roms.MetadataMapId
AND User_RecentPlayedRoms.PlatformId = view_Games_Roms.PlatformId
LEFT JOIN
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
AND User_GameFavouriteRoms.GameId = Games_Roms.GameId
AND User_GameFavouriteRoms.PlatformId = Games_Roms.PlatformId
AND User_GameFavouriteRoms.GameId = view_Games_Roms.MetadataMapId
AND User_GameFavouriteRoms.PlatformId = view_Games_Roms.PlatformId
LEFT JOIN
Games_Roms AS GMR ON GMR.Id = User_RecentPlayedRoms.RomId
view_Games_Roms AS GMR ON GMR.Id = User_RecentPlayedRoms.RomId
LEFT JOIN
Games_Roms AS GFV ON GFV.Id = User_GameFavouriteRoms.RomId
view_Games_Roms AS GFV ON GFV.Id = User_GameFavouriteRoms.RomId
WHERE
Games_Roms.GameId = @gameid
view_Games_Roms.GameIdType = @sourcetype AND view_Games_Roms.GameId = @gameid
ORDER BY Platform.`Name`;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "sourcetype", (int)SourceType },
{ "gameid", GameId },
{ "userid", UserId }
};
@@ -559,14 +294,19 @@ ORDER BY Platform.`Name`;";
List<AvailablePlatformItem> platforms = new List<AvailablePlatformItem>();
foreach (DataRow row in data.Rows)
{
IGDB.Models.Platform platform = Platforms.GetPlatform((long)row["PlatformId"]);
HasheousClient.Models.Metadata.IGDB.Platform platform = Platforms.GetPlatform((long)row["PlatformId"]);
// get the user emulator configuration
PlatformMapping.UserEmulatorConfiguration? emulatorConfiguration = platformMapping.GetUserEmulator(UserId, GameId, (long)platform.Id);
// if no user configuration, get the platform emulator configuration
if (emulatorConfiguration == null)
{
if (platform.Id != 0)
{
Models.PlatformMapping.PlatformMapItem platformMap = PlatformMapping.GetPlatformMap((long)platform.Id);
if (platformMap != null)
{
emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration
{
EmulatorType = platformMap.WebEmulator.Type,
@@ -575,6 +315,18 @@ ORDER BY Platform.`Name`;";
};
}
}
}
// if still no emulator configuration, create a blank one
if (emulatorConfiguration == null)
{
emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration
{
EmulatorType = "",
Core = "",
EnableBIOSFiles = new List<string>()
};
}
long? LastPlayedRomId = null;
bool? LastPlayedIsMediagroup = false;
@@ -583,7 +335,10 @@ ORDER BY Platform.`Name`;";
{
LastPlayedRomId = (long?)row["MostRecentRomId"];
LastPlayedIsMediagroup = (bool)row["MostRecentRomIsMediaGroup"];
LastPlayedRomName = (string)row["MostRecentRomName"];
if (row["MostRecentRomName"] != System.DBNull.Value)
{
LastPlayedRomName = string.IsNullOrEmpty((string?)row["MostRecentRomName"]) ? "" : (string)row["MostRecentRomName"];
}
}
long? FavouriteRomId = null;
@@ -593,13 +348,23 @@ ORDER BY Platform.`Name`;";
{
FavouriteRomId = (long?)row["FavouriteRomId"];
FavouriteRomIsMediagroup = (bool)row["FavouriteRomIsMediaGroup"];
FavouriteRomName = (string)row["FavouriteRomName"];
if (row["FavouriteRomName"] != System.DBNull.Value)
{
FavouriteRomName = string.IsNullOrEmpty((string?)row["FavouriteRomName"]) ? "" : (string)row["FavouriteRomName"];
}
}
string? UserManualLink = null;
if (row["UserManualLink"] != DBNull.Value)
{
UserManualLink = string.IsNullOrEmpty((string?)row["UserManualLink"]) ? "" : (string)row["UserManualLink"];
}
AvailablePlatformItem valuePair = new AvailablePlatformItem
{
Id = platform.Id,
Name = platform.Name,
MetadataMapId = (long)row["MetadataMapId"],
Category = platform.Category,
emulatorConfiguration = emulatorConfiguration,
LastPlayedRomId = LastPlayedRomId,
@@ -607,11 +372,15 @@ ORDER BY Platform.`Name`;";
LastPlayedRomName = LastPlayedRomName,
FavouriteRomId = FavouriteRomId,
FavouriteRomIsMediagroup = FavouriteRomIsMediagroup,
FavouriteRomName = FavouriteRomName
FavouriteRomName = FavouriteRomName,
UserManualLink = UserManualLink
};
platforms.Add(valuePair);
}
// sort platforms by the Name attribute
platforms.Sort((x, y) => x.Name.CompareTo(y.Name));
return platforms;
}
@@ -643,8 +412,9 @@ ORDER BY Platform.`Name`;";
db.ExecuteCMD(sql, dbDict);
}
public class AvailablePlatformItem : IGDB.Models.Platform
public class AvailablePlatformItem : HasheousClient.Models.Metadata.IGDB.Platform
{
public long MetadataMapId { get; set; }
public PlatformMapping.UserEmulatorConfiguration emulatorConfiguration { get; set; }
public long? LastPlayedRomId { get; set; }
public bool? LastPlayedRomIsMediagroup { get; set; }
@@ -652,6 +422,7 @@ ORDER BY Platform.`Name`;";
public long? FavouriteRomId { get; set; }
public bool? FavouriteRomIsMediagroup { get; set; }
public string? FavouriteRomName { get; set; }
public string? UserManualLink { get; set; }
}
public enum SearchType
@@ -672,6 +443,7 @@ ORDER BY Platform.`Name`;";
public MinimalGameItem(Game gameObject)
{
this.Id = gameObject.Id;
this.MetadataMapId = gameObject.MetadataMapId;
this.Name = gameObject.Name;
this.Slug = gameObject.Slug;
this.Summary = gameObject.Summary;
@@ -682,12 +454,12 @@ ORDER BY Platform.`Name`;";
this.FirstReleaseDate = gameObject.FirstReleaseDate;
// compile age ratings
this.AgeRatings = new List<AgeRating>();
this.AgeRatings = new List<object>();
if (gameObject.AgeRatings != null)
{
foreach (long ageRatingId in gameObject.AgeRatings.Ids)
foreach (long ageRatingId in gameObject.AgeRatings)
{
AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRatings(ageRatingId);
HasheousClient.Models.Metadata.IGDB.AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRating(gameObject.MetadataSource, ageRatingId);
if (rating != null)
{
this.AgeRatings.Add(rating);
@@ -697,6 +469,7 @@ ORDER BY Platform.`Name`;";
}
public long? Id { get; set; }
public long? MetadataMapId { get; set; }
public long Index { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
@@ -706,9 +479,9 @@ ORDER BY Platform.`Name`;";
public bool HasSavedGame { get; set; } = false;
public bool IsFavourite { get; set; } = false;
public DateTimeOffset? FirstReleaseDate { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
public List<IGDB.Models.AgeRating> AgeRatings { get; set; }
public object Cover { get; set; }
public List<long> Artworks { get; set; }
public List<object> AgeRatings { get; set; }
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Genres
{
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public Genres()
{
}
public static Genre? GetGenres(long? Id)
public static Genre? GetGenres(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Genre> RetVal = _GetGenres(SearchUsing.id, Id);
return RetVal.Result;
Genre? RetVal = Metadata.GetMetadata<Genre>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Genre GetGenres(string Slug)
{
Task<Genre> RetVal = _GetGenres(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<Genre> _GetGenres(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Genre", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Genre", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Genre returnValue = new Genre();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Genre> GetObjectFromServer(string WhereClause)
{
// get Genres metadata
Communications comms = new Communications();
var results = await comms.APIComm<Genre>(IGDBClient.Endpoints.Genres, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,12 +1,11 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class InvolvedCompanies
{
const string fieldList = "fields *;";
public const string fieldList = "fields *;";
public InvolvedCompanies()
{
@@ -20,106 +19,8 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<InvolvedCompany> RetVal = _GetInvolvedCompanies(SearchUsing.id, Id);
return RetVal.Result;
}
}
public static InvolvedCompany GetInvolvedCompanies(string Slug)
{
Task<InvolvedCompany> RetVal = _GetInvolvedCompanies(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<InvolvedCompany> _GetInvolvedCompanies(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("InvolvedCompany", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("InvolvedCompany", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
InvolvedCompany returnValue = new InvolvedCompany();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private static void UpdateSubClasses(InvolvedCompany involvedCompany)
{
if (involvedCompany.Company != null)
{
Company company = Companies.GetCompanies(involvedCompany.Company.Id);
}
}
private enum SearchUsing
{
id,
slug
}
private static async Task<InvolvedCompany> GetObjectFromServer(string WhereClause)
{
// get InvolvedCompanies metadata
try
{
Communications comms = new Communications();
var results = await comms.APIComm<InvolvedCompany>(IGDBClient.Endpoints.InvolvedCompanies, fieldList, WhereClause);
var result = results.First();
return result;
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Failure when requesting involved companies.");
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Field list: " + fieldList);
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Where clause: " + WhereClause);
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Error", ex);
throw;
InvolvedCompany? RetVal = Metadata.GetMetadata<InvolvedCompany>(HasheousClient.Models.MetadataSources.IGDB, (long)Id, false);
return RetVal;
}
}
}

View File

@@ -0,0 +1,262 @@
using System.Data;
namespace gaseous_server.Classes.Metadata
{
public class Metadata
{
#region Exception Handling
public class InvalidMetadataId : Exception
{
public InvalidMetadataId(long Id) : base("Invalid Metadata id: " + Id + " from source: " + HasheousClient.Models.MetadataSources.IGDB + " (default)")
{
}
public InvalidMetadataId(HasheousClient.Models.MetadataSources SourceType, long Id) : base("Invalid Metadata id: " + Id + " from source: " + SourceType)
{
}
public InvalidMetadataId(string Id) : base("Invalid Metadata id: " + Id + " from source: " + HasheousClient.Models.MetadataSources.IGDB + " (default)")
{
}
public InvalidMetadataId(HasheousClient.Models.MetadataSources SourceType, string Id) : base("Invalid Metadata id: " + Id + " from source: " + SourceType)
{
}
}
#endregion
#region Get Metadata
/// <summary>
/// Get metadata from the default source
/// </summary>
/// <typeparam name="T">
/// The type of metadata to get
/// </typeparam>
/// <param name="Id">
/// The id of the metadata to get
/// </param>
/// <returns>
/// The metadata object
/// </returns>
/// <exception cref="InvalidMetadataId">
/// Thrown when the id is invalid
/// </exception>
public static T? GetMetadata<T>(long Id, Boolean ForceRefresh = false) where T : class
{
if (Id < 0)
{
throw new InvalidMetadataId(Id);
}
return _GetMetadata<T>(HasheousClient.Models.MetadataSources.IGDB, Id, ForceRefresh);
}
/// <summary>
/// Get metadata from the specified source
/// </summary>
/// <typeparam name="T">
/// The type of metadata to get
/// </typeparam>
/// <param name="SourceType">
/// The source of the metadata
/// </param>
/// <param name="Id">
/// The id of the metadata to get
/// </param>
/// <returns>
/// The metadata object
/// </returns>
/// <exception cref="InvalidMetadataId">
/// Thrown when the id is invalid
/// </exception>
public static T? GetMetadata<T>(HasheousClient.Models.MetadataSources SourceType, long Id, Boolean ForceRefresh = false) where T : class
{
if (Id < 0)
{
throw new InvalidMetadataId(SourceType, Id);
}
return _GetMetadata<T>(SourceType, Id, ForceRefresh);
}
public static T? GetMetadata<T>(HasheousClient.Models.MetadataSources SourceType, string Slug, Boolean ForceRefresh = false) where T : class
{
return _GetMetadata<T>(SourceType, Slug, ForceRefresh);
}
private static T? _GetMetadata<T>(HasheousClient.Models.MetadataSources SourceType, object Id, Boolean ForceRefresh) where T : class
{
// get T type as string
string type = typeof(T).Name;
// get type of Id as string
IdType idType = Id.GetType() == typeof(long) ? IdType.Long : IdType.String;
// check cached metadata status
// if metadata is not cached or expired, get it from the source. Otherwise, return the cached metadata
Storage.CacheStatus? cacheStatus;
if (idType == IdType.Long)
{
cacheStatus = Storage.GetCacheStatus(SourceType, type, (long)Id);
}
else
{
cacheStatus = Storage.GetCacheStatus(SourceType, type, (string)Id);
}
// if ForceRefresh is true, set cache status to expired if it is current
if (ForceRefresh == true)
{
if (cacheStatus == Storage.CacheStatus.Current)
{
cacheStatus = Storage.CacheStatus.Expired;
}
}
// if the source is "none", cache status should be "current" or "not present"
if (SourceType == HasheousClient.Models.MetadataSources.None)
{
if (cacheStatus == Storage.CacheStatus.Expired)
{
cacheStatus = Storage.CacheStatus.Current;
}
}
T? metadata = (T)Activator.CreateInstance(typeof(T));
switch (cacheStatus)
{
case Storage.CacheStatus.Current:
if (idType == IdType.Long)
{
metadata = Storage.GetCacheValue<T>(SourceType, metadata, "Id", (long)Id);
}
else
{
metadata = Storage.GetCacheValue<T>(SourceType, metadata, "Slug", (string)Id);
}
break;
case Storage.CacheStatus.Expired:
try
{
if (idType == IdType.Long)
{
metadata = GetMetadataFromServer<T>(SourceType, (long)Id).Result;
}
else
{
metadata = GetMetadataFromServer<T>(SourceType, (string)Id).Result;
}
Storage.NewCacheValue<T>(SourceType, metadata, true);
}
catch (Exception e)
{
Logging.Log(Logging.LogType.Warning, "Fetch Metadata", "Failed to fetch metadata from source: " + SourceType + " for id: " + Id + " of type: " + type + ". Error: " + e.Message);
metadata = null;
}
break;
case Storage.CacheStatus.NotPresent:
try
{
if (idType == IdType.Long)
{
metadata = GetMetadataFromServer<T>(SourceType, (long)Id).Result;
}
else
{
metadata = GetMetadataFromServer<T>(SourceType, (string)Id).Result;
}
Storage.NewCacheValue<T>(SourceType, metadata, false);
}
catch (Exception e)
{
Logging.Log(Logging.LogType.Warning, "Fetch Metadata", "Failed to fetch metadata from source: " + SourceType + " for id: " + Id + " of type: " + type + ". Error: " + e.Message);
metadata = null;
}
break;
}
return metadata;
}
private enum IdType
{
Long,
String
}
private static async Task<T> GetMetadataFromServer<T>(HasheousClient.Models.MetadataSources SourceType, long Id) where T : class
{
// get T type as string
string type = typeof(T).Name;
if (SourceType == HasheousClient.Models.MetadataSources.None)
{
// generate a dummy object
var returnObject = (T)Activator.CreateInstance(typeof(T));
returnObject.GetType().GetProperty("Id").SetValue(returnObject, Id);
// if returnObject has a property called "name", query the metadatamap view for the name
if (returnObject.GetType().GetProperty("Name") != null)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM MetadataMap JOIN MetadataMapBridge ON MetadataMap.Id = MetadataMapBridge.ParentMapId WHERE MetadataSourceId = @id AND MetadataSourceType = 0;";
DataTable dataTable = db.ExecuteCMD(sql, new Dictionary<string, object>
{
{ "@id", Id }
});
if (dataTable.Rows.Count > 0)
{
returnObject.GetType().GetProperty("Name").SetValue(returnObject, dataTable.Rows[0]["SignatureGameName"].ToString());
}
}
return returnObject;
}
else
{
// get metadata from the server
Communications comms = new Communications();
var results = await comms.APIComm<T>(SourceType, (Communications.MetadataEndpoint)Enum.Parse(typeof(Communications.MetadataEndpoint), type, true), Id);
// check for errors
if (results == null)
{
throw new InvalidMetadataId(SourceType, Id);
}
return results.FirstOrDefault<T>();
}
}
private static async Task<T> GetMetadataFromServer<T>(HasheousClient.Models.MetadataSources SourceType, string Id) where T : class
{
// get T type as string
string type = typeof(T).Name;
if (SourceType == HasheousClient.Models.MetadataSources.None)
{
// generate a dummy object
return (T)Activator.CreateInstance(typeof(T));
}
else
{
// get metadata from the server
Communications comms = new Communications();
var results = await comms.APIComm<T>(SourceType, (Communications.MetadataEndpoint)Enum.Parse(typeof(Communications.MetadataEndpoint), type, true), Id);
// check for errors
if (results == null)
{
throw new InvalidMetadataId(SourceType, Id);
}
return results.FirstOrDefault<T>();
}
}
#endregion
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class MultiplayerModes
{
const string fieldList = "fields campaigncoop,checksum,dropin,game,lancoop,offlinecoop,offlinecoopmax,offlinemax,onlinecoop,onlinecoopmax,onlinemax,platform,splitscreen,splitscreenonline;";
public const string fieldList = "fields campaigncoop,checksum,dropin,game,lancoop,offlinecoop,offlinecoopmax,offlinemax,onlinecoop,onlinecoopmax,onlinemax,platform,splitscreen,splitscreenonline;";
public MultiplayerModes()
{
}
public static MultiplayerMode? GetGame_MultiplayerModes(long? Id)
public static MultiplayerMode? GetGame_MultiplayerModes(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<MultiplayerMode> RetVal = _GetGame_MultiplayerModes(SearchUsing.id, Id);
return RetVal.Result;
MultiplayerMode? RetVal = Metadata.GetMetadata<MultiplayerMode>(SourceType, (long)Id, false);
return RetVal;
}
}
public static MultiplayerMode GetGame_MultiplayerModes(string Slug)
{
Task<MultiplayerMode> RetVal = _GetGame_MultiplayerModes(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<MultiplayerMode> _GetGame_MultiplayerModes(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("MultiplayerMode", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("MultiplayerMode", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
MultiplayerMode returnValue = new MultiplayerMode();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<MultiplayerMode> GetObjectFromServer(string WhereClause)
{
// get Game_MultiplayerModes metadata
Communications comms = new Communications();
var results = await comms.APIComm<MultiplayerMode>(IGDBClient.Endpoints.MultiplayerModes, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,19 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
using static gaseous_server.Models.PlatformMapping;
namespace gaseous_server.Classes.Metadata
{
public class PlatformLogos
{
const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
public const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
public PlatformLogos()
{
}
public static PlatformLogo? GetPlatformLogo(long? Id, string ImagePath)
public static PlatformLogo? GetPlatformLogo(long? Id, HasheousClient.Models.MetadataSources SourceType = HasheousClient.Models.MetadataSources.IGDB)
{
if ((Id == 0) || (Id == null))
{
@@ -21,113 +21,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, ImagePath);
return RetVal.Result;
PlatformLogo? RetVal = Metadata.GetMetadata<PlatformLogo>(SourceType, (long)Id, false);
return RetVal;
}
}
public static PlatformLogo GetPlatformLogo(string Slug, string ImagePath)
{
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.slug, Slug, ImagePath);
return RetVal.Result;
}
private static async Task<PlatformLogo> _GetPlatformLogo(SearchUsing searchUsing, object searchValue, string ImagePath)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("PlatformLogo", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("PlatformLogo", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
PlatformLogo returnValue = new PlatformLogo();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
}
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
// check if old value is different from the new value - only download if it's different
PlatformLogo oldImage = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
if (oldImage.ImageId != returnValue.ImageId)
{
forceImageDownload = true;
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
if (returnValue != null)
{
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Platform logo download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<PlatformLogo> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Artwork metadata
Communications comms = new Communications();
var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using System.Data;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class PlatformVersions
{
const string fieldList = "fields checksum,companies,connectivity,cpu,graphics,main_manufacturer,media,memory,name,online,os,output,platform_logo,platform_version_release_dates,resolutions,slug,sound,storage,summary,url;";
public const string fieldList = "fields checksum,companies,connectivity,cpu,graphics,main_manufacturer,media,memory,name,online,os,output,platform_logo,platform_version_release_dates,resolutions,slug,sound,storage,summary,url;";
public PlatformVersions()
{
}
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform, bool GetImages = false)
public static PlatformVersion? GetPlatformVersion(HasheousClient.Models.MetadataSources SourceType, long Id)
{
if (Id == 0)
{
@@ -21,113 +20,8 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.id, Id, ParentPlatform, GetImages);
return RetVal.Result;
}
}
public static PlatformVersion GetPlatformVersion(string Slug, Platform ParentPlatform, bool GetImages = false)
{
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.slug, Slug, ParentPlatform, GetImages);
return RetVal.Result;
}
private static async Task<PlatformVersion> _GetPlatformVersion(SearchUsing searchUsing, object searchValue, Platform ParentPlatform, bool GetImages)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("PlatformVersion", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("PlatformVersion", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
PlatformVersion returnValue = new PlatformVersion();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
UpdateSubClasses(ParentPlatform, returnValue, GetImages);
}
return returnValue;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(ParentPlatform, returnValue, GetImages);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);
}
return returnValue;
case Storage.CacheStatus.Current:
return Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);
default:
throw new Exception("How did you get here?");
}
}
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion, bool GetImages)
{
if (GetImages == true)
{
if (platformVersion.PlatformLogo != null)
{
try
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
}
}
}
}
private enum SearchUsing
{
id,
slug
}
private static async Task<PlatformVersion?> GetObjectFromServer(string WhereClause)
{
// get PlatformVersion metadata
Communications comms = new Communications();
var results = await comms.APIComm<PlatformVersion>(IGDBClient.Endpoints.PlatformVersions, fieldList, WhereClause);
if (results.Length > 0)
{
var result = results.First();
return result;
}
else
{
return null;
PlatformVersion? RetVal = Metadata.GetMetadata<PlatformVersion>(SourceType, Id, false);
return RetVal;
}
}
}

View File

@@ -1,152 +1,71 @@
using System;
using System.Data;
using System.Net;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Platforms
{
const string fieldList = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites;";
public const string fieldList = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites;";
public Platforms()
{
}
public static Platform? GetPlatform(long Id, bool forceRefresh = false, bool GetImages = false)
public static Platform? GetPlatform(long Id, HasheousClient.Models.MetadataSources? SourceType = null)
{
if (Id == 0)
HasheousClient.Models.MetadataSources Source = SourceType ?? Communications.MetadataSource;
if ((Id == 0) || (Id == null))
{
Platform returnValue = new Platform();
if (Storage.GetCacheStatus("Platform", 0) == Storage.CacheStatus.NotPresent)
if (Storage.GetCacheStatus(Source, "Platform", 0) == Storage.CacheStatus.NotPresent)
{
returnValue = new Platform
{
Id = 0,
Name = "Unknown Platform",
Slug = "Unknown"
Slug = "unknown"
};
Storage.NewCacheValue(returnValue);
Storage.NewCacheValue(Source, returnValue);
return returnValue;
}
else
{
return Storage.GetCacheValue<Platform>(returnValue, "id", 0);
return Storage.GetCacheValue<Platform>(Source, returnValue, "id", 0);
}
}
else
{
try
Platform? RetVal = new Platform();
RetVal = (Platform?)Storage.GetCacheValue<Platform>(HasheousClient.Models.MetadataSources.None, RetVal, "Id", (long)Id);
if (Source != HasheousClient.Models.MetadataSources.None)
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh, GetImages);
return RetVal.Result;
}
catch (Exception ex)
if (RetVal == null)
{
Logging.Log(Logging.LogType.Warning, "Metadata", "An error occurred fetching Platform Id " + Id, ex);
return null;
RetVal = Metadata.GetMetadata<Platform>(Source, (long)Id, false);
}
}
return RetVal;
}
}
public static Platform GetPlatform(string Slug, bool forceRefresh = false, bool GetImages = false)
public static Platform GetPlatform(string Slug)
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh, GetImages);
return RetVal.Result;
// get platform id from slug - query Platform table
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string query = "SELECT Id FROM Platform WHERE slug = @slug AND SourceId = @sourceid;";
DataTable result = db.ExecuteCMD(query, new Dictionary<string, object> { { "@slug", Slug }, { "@sourceid", HasheousClient.Models.MetadataSources.IGDB } });
if (result.Rows.Count == 0)
{
throw new Metadata.InvalidMetadataId(Slug);
}
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh, bool GetImages)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Platform", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Platform", (string)searchValue);
}
if (forceRefresh == true)
{
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
}
// set up where clause
string WhereClause = "";
string searchField = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
searchField = "id";
break;
case SearchUsing.slug:
WhereClause = "where slug = \"" + searchValue + "\"";
searchField = "slug";
break;
default:
throw new Exception("Invalid search type");
}
Platform returnValue = new Platform();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue, GetImages);
AddPlatformMapping(returnValue);
return returnValue;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue, GetImages);
AddPlatformMapping(returnValue);
return returnValue;
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Platform>(returnValue, searchField, searchValue);
}
case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Platform>(returnValue, searchField, searchValue);
default:
throw new Exception("How did you get here?");
}
}
private static void UpdateSubClasses(Platform platform, bool GetImages)
{
if (platform.Versions != null)
{
foreach (long PlatformVersionId in platform.Versions.Ids)
{
PlatformVersion platformVersion = PlatformVersions.GetPlatformVersion(PlatformVersionId, platform);
}
}
if (GetImages == true)
{
if (platform.PlatformLogo != null)
{
try
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
}
}
}
long Id = (long)result.Rows[0]["Id"];
return GetPlatform(Id);
}
private static void AddPlatformMapping(Platform platform)
@@ -174,36 +93,6 @@ namespace gaseous_server.Classes.Metadata
Models.PlatformMapping.WritePlatformMap(item, false, true);
}
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Platform> GetObjectFromServer(string WhereClause)
{
// get platform metadata
Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, fieldList, WhereClause);
var result = results.First();
return result;
}
public static void AssignAllPlatformsToGameIdZero()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Platform;";
DataTable platformsTable = db.ExecuteCMD(sql);
foreach (DataRow platformRow in platformsTable.Rows)
{
sql = "DELETE FROM Relation_Game_Platforms WHERE GameId = 0 AND PlatformsId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (0, @Id);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", (long)platformRow["Id"]);
db.ExecuteCMD(sql, dbDict);
}
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class PlayerPerspectives
{
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public PlayerPerspectives()
{
}
public static PlayerPerspective? GetGame_PlayerPerspectives(long? Id)
public static PlayerPerspective? GetGame_PlayerPerspectives(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,90 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<PlayerPerspective> RetVal = _GetGame_PlayerPerspectives(SearchUsing.id, Id);
return RetVal.Result;
PlayerPerspective? RetVal = Metadata.GetMetadata<PlayerPerspective>(SourceType, (long)Id, false);
return RetVal;
}
}
public static PlayerPerspective GetGame_PlayerPerspectives(string Slug)
{
Task<PlayerPerspective> RetVal = _GetGame_PlayerPerspectives(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<PlayerPerspective> _GetGame_PlayerPerspectives(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("PlayerPerspective", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("PlayerPerspective", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
PlayerPerspective returnValue = new PlayerPerspective();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<PlayerPerspective> GetObjectFromServer(string WhereClause)
{
// get Game_PlayerPerspectives metadata
Communications comms = new Communications();
var results = await comms.APIComm<PlayerPerspective>(IGDBClient.Endpoints.PlayerPerspectives, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class ReleaseDates
{
const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
public const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
public ReleaseDates()
{
}
public static ReleaseDate? GetReleaseDates(long? Id)
public static ReleaseDate? GetReleaseDates(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.id, Id);
return RetVal.Result;
ReleaseDate? RetVal = Metadata.GetMetadata<ReleaseDate>(SourceType, (long)Id, false);
return RetVal;
}
}
public static ReleaseDate GetReleaseDates(string Slug)
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<ReleaseDate> _GetReleaseDates(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
ReleaseDate returnValue = new ReleaseDate();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<ReleaseDate> GetObjectFromServer(string WhereClause)
{
// get ReleaseDates metadata
Communications comms = new Communications();
var results = await comms.APIComm<ReleaseDate>(IGDBClient.Endpoints.ReleaseDates, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Screenshots
{
const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
public const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
public Screenshots()
{
}
public static Screenshot? GetScreenshot(long? Id, string ImagePath, bool GetImages)
public static Screenshot? GetScreenshot(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,111 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, ImagePath, GetImages);
return RetVal.Result;
Screenshot? RetVal = Metadata.GetMetadata<Screenshot>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Screenshot GetScreenshot(string Slug, string ImagePath, bool GetImages)
{
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.slug, Slug, ImagePath, GetImages);
return RetVal.Result;
}
private static async Task<Screenshot> _GetScreenshot(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Screenshot", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Screenshot", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Screenshot returnValue = new Screenshot();
bool forceImageDownload = false;
ImagePath = Path.Combine(ImagePath, "Screenshots");
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
Storage.NewCacheValue(returnValue, true);
// check if old value is different from the new value - only download if it's different
Screenshot oldImage = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
if (oldImage.ImageId != returnValue.ImageId)
{
forceImageDownload = true;
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
// check for presence of "original" quality file - download if absent or force download is true
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
if (GetImages == true)
{
if ((!File.Exists(localFile)) || forceImageDownload == true)
{
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Screenshot download forced.");
Communications comms = new Communications();
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
}
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string ImagePath)
{
// get Screenshot metadata
Communications comms = new Communications();
var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -9,23 +9,79 @@ namespace gaseous_server.Classes.Metadata
{
public class Storage
{
/// <summary>
/// Cache status of a record
/// </summary>
public enum CacheStatus
{
/// <summary>
/// The record is not present in the database
/// </summary>
NotPresent,
/// <summary>
/// The record is present in the database and is current
/// </summary>
Current,
/// <summary>
/// The record is present in the database but is expired
/// </summary>
Expired
}
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
/// <summary>
/// Get the cache status of a record in the database
/// </summary>
/// <param name="SourceType">
/// The source of the metadata (IGDB, RAWG, etc.)
/// </param>
/// <param name="Endpoint">
/// The endpoint of the metadata (games, companies, etc.)
/// </param>
/// <param name="Slug">
/// The slug of the metadata record
/// </param>
/// <returns>
/// The cache status of the record
/// </returns>
public static CacheStatus GetCacheStatus(HasheousClient.Models.MetadataSources SourceType, string Endpoint, string Slug)
{
return _GetCacheStatus(Endpoint, "slug", Slug);
return _GetCacheStatus(SourceType, Endpoint, "slug", Slug);
}
public static CacheStatus GetCacheStatus(string Endpoint, long Id)
/// <summary>
/// Get the cache status of a record in the database
/// </summary>
/// <param name="SourceType">
/// The source of the metadata (IGDB, RAWG, etc.)
/// </param>
/// <param name="Endpoint">
/// The endpoint of the metadata (games, companies, etc.)
/// </param>
/// <param name="Id">
/// The ID of the metadata record
/// </param>
/// <returns>
/// The cache status of the record
/// </returns>
public static CacheStatus GetCacheStatus(HasheousClient.Models.MetadataSources SourceType, string Endpoint, long Id)
{
return _GetCacheStatus(Endpoint, "id", Id);
return _GetCacheStatus(SourceType, Endpoint, "id", Id);
}
/// <summary>
/// Get the cache status of a record in the database
/// </summary>
/// <param name="Row">
/// The DataRow object to check
/// </param>
/// <returns>
/// The cache status of the record
/// </returns>
/// <exception cref="Exception">
/// Thrown when the DataRow object does not contain a "lastUpdated" column
/// </exception>
public static CacheStatus GetCacheStatus(DataRow Row)
{
if (Row.Table.Columns.Contains("lastUpdated"))
@@ -46,13 +102,14 @@ namespace gaseous_server.Classes.Metadata
}
}
private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue)
private static CacheStatus _GetCacheStatus(HasheousClient.Models.MetadataSources SourceType, string Endpoint, string SearchField, object SearchValue)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE SourceId = @SourceType AND " + SearchField + " = @" + SearchField;
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("SourceType", SourceType);
dbDict.Add("Endpoint", Endpoint);
dbDict.Add(SearchField, SearchValue);
@@ -76,16 +133,41 @@ namespace gaseous_server.Classes.Metadata
}
}
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
/// <summary>
/// Add a new record to the cache
/// </summary>
/// <param name="SourceType">
/// The source of the metadata (IGDB, RAWG, etc.)
/// </param>
/// <param name="ObjectToCache">
/// The object to cache
/// </param>
/// <param name="UpdateRecord">
/// Whether to update the record if it already exists
/// </param>
public static void NewCacheValue<T>(HasheousClient.Models.MetadataSources SourceType, T ObjectToCache, bool UpdateRecord = false)
{
// get the object type name
string ObjectTypeName = ObjectToCache.GetType().Name;
// build dictionary
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
objectDict.Add("dateAdded", DateTime.UtcNow);
objectDict.Add("lastUpdated", DateTime.UtcNow);
Dictionary<string, object?> objectDict = new Dictionary<string, object?>
{
{ "SourceId", SourceType },
{ "dateAdded", DateTime.UtcNow },
{ "lastUpdated", DateTime.UtcNow }
};
foreach (PropertyInfo property in ObjectToCache.GetType().GetProperties())
{
if (property.GetCustomAttribute<Models.NoDatabaseAttribute>() == null)
{
object? propertyValue = property.GetValue(ObjectToCache);
if (propertyValue != null)
{
objectDict.Add(property.Name, propertyValue);
}
}
}
// generate sql
string fieldList = "";
@@ -100,7 +182,7 @@ namespace gaseous_server.Classes.Metadata
}
fieldList = fieldList + key.Key;
valueList = valueList + "@" + key.Key;
if ((key.Key != "id") && (key.Key != "dateAdded"))
if ((key.Key != "id") && (key.Key != "dateAdded") && (key.Key != "SourceId"))
{
if (updateFieldValueList.Length > 0)
{
@@ -135,7 +217,13 @@ namespace gaseous_server.Classes.Metadata
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
objectDict[key.Key] = newObjectValue;
StoreRelations(ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
StoreRelations(SourceType, ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
break;
case "list":
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
objectDict[key.Key] = newObjectValue;
StoreRelations(SourceType, ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
break;
case "int32[]":
@@ -151,11 +239,11 @@ namespace gaseous_server.Classes.Metadata
string sql = "";
if (UpdateRecord == false)
{
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ");";
}
else
{
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id AND SourceId = @SourceId;";
}
// execute sql
@@ -163,15 +251,40 @@ namespace gaseous_server.Classes.Metadata
db.ExecuteCMD(sql, objectDict);
}
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
/// <summary>
/// Get a record from the cache
/// </summary>
/// <typeparam name="T">
/// The type of the object to return
/// </typeparam>
/// <param name="SourceType">
/// The source of the metadata (IGDB, RAWG, etc.)
/// </param>
/// <param name="EndpointType">
/// The type of the endpoint (games, companies, etc.)
/// </param>
/// <param name="SearchField">
/// The field to search for the record by
/// </param>
/// <param name="SearchValue">
/// The value to search for
/// </param>
/// <returns>
/// The object from the cache
/// </returns>
/// <exception cref="Exception">
/// Thrown when no record is found that matches the search criteria
/// </exception>
public static T GetCacheValue<T>(HasheousClient.Models.MetadataSources SourceType, T? EndpointType, string SearchField, object SearchValue)
{
string Endpoint = EndpointType.GetType().Name;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
string sql = "SELECT * FROM " + Endpoint + " WHERE SourceId = @SourceType AND " + SearchField + " = @" + SearchField;
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("SourceType", SourceType);
dbDict.Add("Endpoint", Endpoint);
dbDict.Add(SearchField, SearchValue);
@@ -192,201 +305,65 @@ namespace gaseous_server.Classes.Metadata
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
{
// copy the DataRow to EndpointType
foreach (PropertyInfo property in EndpointType.GetType().GetProperties())
{
if (dataRow.Table.Columns.Contains(property.Name))
if (property.GetCustomAttribute<Models.NoDatabaseAttribute>() == null)
{
if (dataRow[property.Name] != DBNull.Value)
// get the value from the DataRow with the same name as the property
if (dataRow.Table.Columns.Contains(property.Name) == true)
{
string objectTypeName = property.PropertyType.Name.ToLower().Split("`")[0];
string subObjectTypeName = "";
object? objectToStore = null;
if (objectTypeName == "nullable")
object? value = dataRow[property.Name];
if (value != null && value != DBNull.Value)
{
objectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[System.", "").Replace("]", "").ToLower();
// check the property type - if it's a list or array, deserialize it. Otherwise, just set the value
Type objectType = EndpointType.GetType();
if (objectType != null)
{
// fullname = System.Nullable`1[[System.DateTimeOffset,
string propertyTypeName = property.PropertyType.FullName.Split(",")[0];
bool isNullable = false;
if (propertyTypeName.StartsWith("System.Nullable"))
{
isNullable = true;
propertyTypeName = propertyTypeName.Split("[[")[1];
}
try
{
switch (objectTypeName)
{
//case "boolean":
// Boolean storedBool = Convert.ToBoolean((int)dataRow[property.Name]);
// property.SetValue(EndpointType, storedBool);
// break;
case "datetimeoffset":
DateTimeOffset? storedDate = (DateTime?)dataRow[property.Name];
property.SetValue(EndpointType, storedDate);
break;
//case "nullable":
// Console.WriteLine("Nullable: " + property.PropertyType.UnderlyingSystemType);
// break;
case "identityorvalue":
subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower();
propertyTypeName = propertyTypeName.Split("`")[0];
switch (subObjectTypeName)
switch (propertyTypeName.ToLower())
{
case "collection":
objectToStore = new IdentityOrValue<Collection>(id: (long)dataRow[property.Name]);
case "system.collections.generic.list":
var listArray = Newtonsoft.Json.JsonConvert.DeserializeObject<List<long>>(value.ToString());
property.SetValue(EndpointType, listArray);
break;
case "company":
objectToStore = new IdentityOrValue<Company>(id: (long)dataRow[property.Name]);
break;
case "cover":
objectToStore = new IdentityOrValue<Cover>(id: (long)dataRow[property.Name]);
break;
case "franchise":
objectToStore = new IdentityOrValue<Franchise>(id: (long)dataRow[property.Name]);
break;
case "game":
objectToStore = new IdentityOrValue<Game>(id: (long)dataRow[property.Name]);
break;
case "platformfamily":
objectToStore = new IdentityOrValue<PlatformFamily>(id: (long)dataRow[property.Name]);
break;
case "platformlogo":
objectToStore = new IdentityOrValue<PlatformLogo>(id: (long)dataRow[property.Name]);
break;
case "platformversioncompany":
objectToStore = new IdentityOrValue<PlatformVersionCompany>(id: (long)dataRow[property.Name]);
break;
}
if (objectToStore != null)
{
property.SetValue(EndpointType, objectToStore);
}
case "system.int32[]":
var int32array = Newtonsoft.Json.JsonConvert.DeserializeObject<int[]>(value.ToString());
property.SetValue(EndpointType, int32array);
break;
case "system.datetimeoffset":
property.SetValue(EndpointType, (DateTimeOffset)(DateTime?)value);
break;
case "identitiesorvalues":
subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower();
long[] fromJsonObject = Newtonsoft.Json.JsonConvert.DeserializeObject<long[]>((string)dataRow[property.Name]);
switch (subObjectTypeName)
{
case "agerating":
objectToStore = new IdentitiesOrValues<AgeRating>(ids: fromJsonObject);
break;
case "alternativename":
objectToStore = new IdentitiesOrValues<AlternativeName>(ids: fromJsonObject);
break;
case "artwork":
objectToStore = new IdentitiesOrValues<Artwork>(ids: fromJsonObject);
break;
case "ageratingcontentdescription":
objectToStore = new IdentitiesOrValues<AgeRatingContentDescription>(ids: fromJsonObject);
break;
case "game":
objectToStore = new IdentitiesOrValues<Game>(ids: fromJsonObject);
break;
case "externalgame":
objectToStore = new IdentitiesOrValues<ExternalGame>(ids: fromJsonObject);
break;
case "franchise":
objectToStore = new IdentitiesOrValues<Franchise>(ids: fromJsonObject);
break;
case "gameengine":
objectToStore = new IdentitiesOrValues<GameEngine>(ids: fromJsonObject);
break;
case "gamemode":
objectToStore = new IdentitiesOrValues<GameMode>(ids: fromJsonObject);
break;
case "gamevideo":
objectToStore = new IdentitiesOrValues<GameVideo>(ids: fromJsonObject);
break;
case "genre":
objectToStore = new IdentitiesOrValues<Genre>(ids: fromJsonObject);
break;
case "involvedcompany":
objectToStore = new IdentitiesOrValues<InvolvedCompany>(ids: fromJsonObject);
break;
case "multiplayermode":
objectToStore = new IdentitiesOrValues<MultiplayerMode>(ids: fromJsonObject);
break;
case "platform":
objectToStore = new IdentitiesOrValues<Platform>(ids: fromJsonObject);
break;
case "platformversion":
objectToStore = new IdentitiesOrValues<PlatformVersion>(ids: fromJsonObject);
break;
case "platformwebsite":
objectToStore = new IdentitiesOrValues<PlatformWebsite>(ids: fromJsonObject);
break;
case "platformversioncompany":
objectToStore = new IdentitiesOrValues<PlatformVersionCompany>(ids: fromJsonObject);
break;
case "platformversionreleasedate":
objectToStore = new IdentitiesOrValues<PlatformVersionReleaseDate>(ids: fromJsonObject);
break;
case "playerperspective":
objectToStore = new IdentitiesOrValues<PlayerPerspective>(ids: fromJsonObject);
break;
case "releasedate":
objectToStore = new IdentitiesOrValues<ReleaseDate>(ids: fromJsonObject);
break;
case "screenshot":
objectToStore = new IdentitiesOrValues<Screenshot>(ids: fromJsonObject);
break;
case "theme":
objectToStore = new IdentitiesOrValues<Theme>(ids: fromJsonObject);
break;
case "website":
objectToStore = new IdentitiesOrValues<Website>(ids: fromJsonObject);
break;
}
if (objectToStore != null)
{
property.SetValue(EndpointType, objectToStore);
}
break;
case "int32[]":
Int32[] fromJsonObject_int32Array = Newtonsoft.Json.JsonConvert.DeserializeObject<Int32[]>((string)dataRow[property.Name]);
if (fromJsonObject_int32Array != null)
{
property.SetValue(EndpointType, fromJsonObject_int32Array);
}
break;
case "[igdb.models.category":
property.SetValue(EndpointType, (Category)dataRow[property.Name]);
break;
case "[igdb.models.gamestatus":
property.SetValue(EndpointType, (GameStatus)dataRow[property.Name]);
break;
case "[igdb.models.ageratingcategory":
property.SetValue(EndpointType, (AgeRatingCategory)dataRow[property.Name]);
break;
case "[igdb.models.ageratingcontentdescriptioncategory":
property.SetValue(EndpointType, (AgeRatingContentDescriptionCategory)dataRow[property.Name]);
break;
case "[igdb.models.ageratingtitle":
property.SetValue(EndpointType, (AgeRatingTitle)dataRow[property.Name]);
break;
case "[igdb.models.externalcategory":
property.SetValue(EndpointType, (ExternalCategory)dataRow[property.Name]);
break;
case "[igdb.models.startdatecategory":
property.SetValue(EndpointType, (StartDateCategory)dataRow[property.Name]);
break;
case "[igdb.models.releasedateregion":
property.SetValue(EndpointType, (ReleaseDateRegion)dataRow[property.Name]);
break;
case "[igdb.models.releasedatecategory":
property.SetValue(EndpointType, (ReleaseDateCategory)dataRow[property.Name]);
break;
case "[gaseous_server.classes.metadata.agegroups+agerestrictiongroupings":
property.SetValue(EndpointType, (AgeGroups.AgeRestrictionGroupings)dataRow[property.Name]);
break;
default:
property.SetValue(EndpointType, dataRow[property.Name]);
// check if property is an enum
if (property.PropertyType.IsEnum)
{
property.SetValue(EndpointType, Enum.Parse(property.PropertyType, value.ToString()));
}
else if (Common.IsNullableEnum(property.PropertyType))
{
property.SetValue(EndpointType, Enum.Parse(Nullable.GetUnderlyingType(property.PropertyType), value.ToString()));
}
else
{
property.SetValue(EndpointType, value);
}
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error occurred in column " + property.Name);
Console.WriteLine(ex.ToString());
}
}
}
@@ -395,7 +372,7 @@ namespace gaseous_server.Classes.Metadata
return EndpointType;
}
private static void StoreRelations(string PrimaryTable, string SecondaryTable, long ObjectId, string Relations)
private static void StoreRelations(HasheousClient.Models.MetadataSources SourceType, string PrimaryTable, string SecondaryTable, long ObjectId, string Relations)
{
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -404,7 +381,13 @@ namespace gaseous_server.Classes.Metadata
if (data.Rows.Count == 0)
{
// table doesn't exist, create it
sql = "CREATE TABLE `" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + "` (`" + PrimaryTable + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
sql = @"
CREATE TABLE
`" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + @"`
(`" + PrimaryTable + @"SourceId` INT NOT NULL,
`" + PrimaryTable + @"Id` BIGINT NOT NULL,
`" + SecondaryTable + @"Id` BIGINT NOT NULL,
PRIMARY KEY (`" + PrimaryTable + "SourceId`, `" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
db.ExecuteCMD(sql);
}
else
@@ -420,10 +403,13 @@ namespace gaseous_server.Classes.Metadata
long[] RelationValues = Newtonsoft.Json.JsonConvert.DeserializeObject<long[]>(Relations);
foreach (long RelationValue in RelationValues)
{
sql = "INSERT INTO " + TableName + " (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`) VALUES (@objectid, @relationvalue);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("objectid", ObjectId);
dbDict.Add("relationvalue", RelationValue);
sql = "INSERT INTO " + TableName + " (`" + PrimaryTable + "SourceId`, `" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`) VALUES (@sourceid, @objectid, @relationvalue);";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "sourceid", SourceType },
{ "objectid", ObjectId },
{ "relationvalue", RelationValue }
};
db.ExecuteCMD(sql, dbDict);
}
}
@@ -445,7 +431,13 @@ namespace gaseous_server.Classes.Metadata
if (data.Rows.Count == 0)
{
// table doesn't exist, create it
sql = "CREATE TABLE `" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + "` (`" + PrimaryTable + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
sql = @"
CREATE TABLE
`" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + @"`
(`" + PrimaryTable + @"SourceId` INT NOT NULL,
`" + PrimaryTable + @"Id` BIGINT NOT NULL,
`" + SecondaryTable + @"Id` BIGINT NOT NULL,
PRIMARY KEY (`" + PrimaryTable + "SourceId`, `" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
db.ExecuteCMD(sql);
}
}

View File

@@ -1,19 +1,18 @@
using System;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes.Metadata
{
public class Themes
{
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
public Themes()
{
}
public static Theme? GetGame_Themes(long? Id)
public static Theme? GetGame_Themes(HasheousClient.Models.MetadataSources SourceType, long? Id)
{
if ((Id == 0) || (Id == null))
{
@@ -21,90 +20,10 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<Theme> RetVal = _GetGame_Themes(SearchUsing.id, Id);
return RetVal.Result;
Theme? RetVal = Metadata.GetMetadata<Theme>(SourceType, (long)Id, false);
return RetVal;
}
}
public static Theme GetGame_Themes(string Slug)
{
Task<Theme> RetVal = _GetGame_Themes(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<Theme> _GetGame_Themes(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("Theme", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("Theme", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
Theme returnValue = new Theme();
bool forceImageDownload = false;
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
forceImageDownload = true;
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
return returnValue;
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
}
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<Theme> GetObjectFromServer(string WhereClause)
{
// get Game_Themes metadata
Communications comms = new Communications();
var results = await comms.APIComm<Theme>(IGDBClient.Endpoints.Themes, fieldList, WhereClause);
var result = results.First();
return result;
}
}
}

View File

@@ -1,11 +1,354 @@
using System;
using System.Data;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using HasheousClient.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes
{
public class MetadataManagement : QueueItemStatus
{
private static bool Processing = false;
public enum MetadataMapSupportDataTypes
{
UserManualLink
}
/// <summary>
/// Creates a new metadata map, if one with the same platformId and name does not already exist.
/// </summary>
/// <param name="platformId">
/// The ID of the platform to which the metadata map belongs.
/// </param>
/// <param name="name">
/// The name of the metadata map.
/// </param>
/// <returns>
/// The ID of the new metadata map, or the ID of the existing metadata map if one already exists.
/// </returns>
public static MetadataMap? NewMetadataMap(long platformId, string name)
{
if (Processing == true)
{
// loop until processing = false
while (Processing == true)
{
System.Threading.Thread.Sleep(500);
}
}
Processing = true;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@platformId", platformId },
{ "@name", name }
};
DataTable dt = new DataTable();
// check if the metadata map already exists
MetadataMap? existingMetadataMap = GetMetadataMap(platformId, name);
if (existingMetadataMap != null)
{
Processing = false;
return existingMetadataMap;
}
// create the metadata map
sql = "INSERT INTO MetadataMap (PlatformId, SignatureGameName) VALUES (@platformId, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
dt = db.ExecuteCMD(sql, dbDict);
long metadataMapId = (long)dt.Rows[0][0];
// create dummy game metadata item and capture id
sql = "INSERT INTO Game (SourceId, Name, dateAdded, lastUpdated) VALUES (@sourceid, @name, @dateadded, @lastupdated); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
dbDict = new Dictionary<string, object>()
{
{ "@sourceid", HasheousClient.Models.MetadataSources.None },
{ "@name", name },
{ "@dateadded", DateTime.UtcNow },
{ "@lastupdated", DateTime.UtcNow }
};
dt = db.ExecuteCMD(sql, dbDict);
long gameId = (long)dt.Rows[0][0];
// add default metadata sources
AddMetadataMapItem(metadataMapId, HasheousClient.Models.MetadataSources.None, gameId, true);
// return the new metadata map
MetadataMap RetVal = GetMetadataMap(metadataMapId);
Processing = false;
return RetVal;
}
/// <summary>
/// Adds a metadata map item to the database.
/// </summary>
/// <param name="metadataMapId">
/// The ID of the metadata map to which the item belongs.
/// </param>
/// <param name="sourceType">
/// The type of the metadata source.
/// </param>
/// <param name="sourceId">
/// The ID of the metadata source.
/// </param>
/// <param name="preferred">
/// Whether the metadata source is preferred.
/// </param>
/// <remarks>
/// If the metadata source is preferred, all other metadata sources for the same metadata map will be set to not preferred.
/// </remarks>
public static void AddMetadataMapItem(long metadataMapId, HasheousClient.Models.MetadataSources sourceType, long sourceId, bool preferred)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@metadataMapId", metadataMapId },
{ "@sourceType", sourceType },
{ "@sourceId", sourceId },
{ "@preferred", preferred },
{ "@processedatimport", false }
};
if (preferred == true)
{
// set all other items to not preferred
sql = "UPDATE MetadataMapBridge SET Preferred = 0 WHERE ParentMapId = @metadataMapId;";
db.ExecuteCMD(sql, dbDict);
}
sql = "INSERT INTO MetadataMapBridge (ParentMapId, MetadataSourceType, MetadataSourceId, Preferred, ProcessedAtImport) VALUES (@metadataMapId, @sourceType, @sourceId, @preferred, @processedatimport);";
db.ExecuteCMD(sql, dbDict);
}
public static void UpdateMetadataMapItem(long metadataMapId, HasheousClient.Models.MetadataSources SourceType, long sourceId, bool? preferred = null)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@metadataMapId", metadataMapId },
{ "@sourceType", SourceType },
{ "@sourceId", sourceId },
{ "@preferred", preferred }
};
if (preferred == true)
{
// set all other items to not preferred
sql = "UPDATE MetadataMapBridge SET Preferred = 0 WHERE ParentMapId = @metadataMapId; UPDATE MetadataMapBridge SET MetadataSourceId = @sourceId, Preferred = @preferred WHERE ParentMapId = @metadataMapId AND MetadataSourceType = @sourceType;";
db.ExecuteCMD(sql, dbDict);
}
else
{
sql = "UPDATE MetadataMapBridge SET MetadataSourceId = @sourceId WHERE ParentMapId = @metadataMapId AND MetadataSourceType = @sourceType;";
db.ExecuteCMD(sql, dbDict);
}
}
/// <summary>
/// Gets a metadata map from the database.
/// </summary>
/// <param name="platformId">
/// The ID of the platform to which the metadata map belongs.
/// </param>
/// <param name="name">
/// The name of the metadata map.
/// </param>
/// <returns>
/// The metadata map, or null if it does not exist.
/// </returns>
/// <remarks>
/// This method will return the first metadata map found with the given platformId and name.
/// </remarks>
public static MetadataMap? GetMetadataMap(long platformId, string name)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@platformId", platformId },
{ "@name", name.Trim() }
};
DataTable dt = new DataTable();
sql = "SELECT Id FROM MetadataMap WHERE PlatformId = @platformId AND SignatureGameName = @name;";
dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count > 0)
{
return GetMetadataMap((long)dt.Rows[0]["Id"]);
}
return null;
}
/// <summary>
/// Gets a metadata map from the database.
/// </summary>
/// <param name="metadataMapId">
/// The ID of the metadata map.
/// </param>
/// <returns>
/// The metadata map, or null if it does not exist.
/// </returns>
/// <remarks>
/// This method will return the metadata map with the given ID.
/// </remarks>
public static MetadataMap? GetMetadataMap(long metadataMapId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@metadataMapId", metadataMapId }
};
DataTable dt = new DataTable();
sql = "SELECT * FROM MetadataMap WHERE Id = @metadataMapId;";
dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count > 0)
{
MetadataMap metadataMap = new MetadataMap()
{
Id = (long)dt.Rows[0]["Id"],
PlatformId = (long)dt.Rows[0]["PlatformId"],
SignatureGameName = dt.Rows[0]["SignatureGameName"].ToString(),
MetadataMapItems = new List<MetadataMap.MetadataMapItem>()
};
sql = "SELECT * FROM MetadataMapBridge WHERE ParentMapId = @metadataMapId;";
dt = db.ExecuteCMD(sql, dbDict);
foreach (DataRow dr in dt.Rows)
{
MetadataMap.MetadataMapItem metadataMapItem = new MetadataMap.MetadataMapItem()
{
SourceType = (HasheousClient.Models.MetadataSources)dr["MetadataSourceType"],
SourceId = (long)dr["MetadataSourceId"],
Preferred = (bool)dr["Preferred"]
};
metadataMap.MetadataMapItems.Add(metadataMapItem);
}
return metadataMap;
}
return null;
}
public static void SetMetadataSupportData(long metadataMapId, MetadataMapSupportDataTypes dataType, string data)
{
// verify the metadata map exists
MetadataMap? metadataMap = GetMetadataMap(metadataMapId);
if (metadataMap == null)
{
return;
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@metadataMapId", metadataMapId },
{ "@data", data }
};
switch (dataType)
{
case MetadataMapSupportDataTypes.UserManualLink:
sql = "UPDATE MetadataMap SET UserManualLink = @data WHERE Id = @metadataMapId;";
db.ExecuteCMD(sql, dbDict);
break;
}
}
/// <summary>
/// Get the MetadataMapItem for the provided metadata source, and source id
/// </summary>
/// <param name="sourceType">
/// The type of the metadata source.
/// </param>
/// <param name="sourceId">
/// The ID of the metadata source.
/// </param>
/// <returns>
/// The MetadataMapItem, or null if it does not exist.
/// </returns>
/// <remarks>
/// This method will return the MetadataMapItem with the given sourceType and sourceId.
/// </remarks>
public static MetadataMap.MetadataMapItem? GetMetadataMapFromSourceId(HasheousClient.Models.MetadataSources sourceType, long sourceId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@sourceType", sourceType },
{ "@sourceId", sourceId }
};
DataTable dt = new DataTable();
sql = "SELECT * FROM MetadataMapBridge WHERE MetadataSourceType = @sourceType AND MetadataSourceId = @sourceId;";
dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count > 0)
{
MetadataMap.MetadataMapItem metadataMapItem = new MetadataMap.MetadataMapItem()
{
SourceType = (HasheousClient.Models.MetadataSources)dt.Rows[0]["MetadataSourceType"],
SourceId = (long)dt.Rows[0]["MetadataSourceId"],
Preferred = (bool)dt.Rows[0]["Preferred"]
};
return metadataMapItem;
}
return null;
}
/// <summary>
/// Get the Id of the MetadataMap for the provided metadata source, and source id
/// </summary>
/// <param name="sourceType">
/// The type of the metadata source.
/// </param>
/// <param name="sourceId">
/// The ID of the metadata source.
/// </param>
/// <returns>
/// The ID of the MetadataMap, or null if it does not exist.
/// </returns>
public static long? GetMetadataMapIdFromSourceId(HasheousClient.Models.MetadataSources sourceType, long sourceId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>()
{
{ "@sourceType", sourceType },
{ "@sourceId", sourceId }
};
DataTable dt = new DataTable();
sql = "SELECT * FROM MetadataMapBridge WHERE MetadataSourceType = @sourceType AND MetadataSourceId = @sourceId;";
dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count > 0)
{
return (long)dt.Rows[0]["ParentMapId"];
}
return null;
}
public void RefreshMetadata(bool forceRefresh = false)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -15,7 +358,7 @@ namespace gaseous_server.Classes
// disabling forceRefresh
forceRefresh = false;
// update platforms
// update platform metadata
sql = "SELECT Id, `Name` FROM Platform;";
dt = db.ExecuteCMD(sql);
@@ -27,7 +370,23 @@ namespace gaseous_server.Classes
try
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Platforms.GetPlatform((long)dr["id"], true);
HasheousClient.Models.MetadataSources metadataSource = HasheousClient.Models.MetadataSources.None;
// fetch the platform metadata
Platform platform = Metadata.Platforms.GetPlatform((long)dr["id"], metadataSource);
// fetch the platform metadata from Hasheous
if (Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous)
{
Communications.PopulateHasheousPlatformData((long)dr["id"]);
}
// force platformLogo refresh
if (platform.PlatformLogo != null)
{
Metadata.PlatformLogos.GetPlatformLogo(platform.PlatformLogo, metadataSource);
}
}
catch (Exception ex)
{
@@ -38,7 +397,68 @@ namespace gaseous_server.Classes
}
ClearStatus();
// update games
// update rom signatures - only valid if Haseheous is enabled
if (Config.MetadataConfiguration.SignatureSource == MetadataModel.SignatureSources.Hasheous)
{
// get all ROMs in the database
sql = "SELECT * FROM view_Games_Roms;";
dt = db.ExecuteCMD(sql);
StatusCounter = 1;
foreach (DataRow dr in dt.Rows)
{
SetStatus(StatusCounter, dt.Rows.Count, "Refreshing signature for ROM " + dr["Name"]);
try
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing signature for ROM " + dr["Name"] + " (" + dr["Id"] + ")");
// get the hash of the ROM from the datarow
string? md5 = dr["MD5"] == DBNull.Value ? null : dr["MD5"].ToString();
string? sha1 = dr["SHA1"] == DBNull.Value ? null : dr["SHA1"].ToString();
Common.hashObject hash = new Common.hashObject();
if (md5 != null)
{
hash.md5hash = md5;
}
if (sha1 != null)
{
hash.sha1hash = sha1;
}
// get the library for the ROM
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)dr["LibraryId"]);
// get the signature for the ROM
FileInfo fi = new FileInfo(dr["Path"].ToString());
FileSignature fileSignature = new FileSignature();
gaseous_server.Models.Signatures_Games signature = fileSignature.GetFileSignature(library, hash, fi, fi.FullName);
// validate the signature - if it is invalid, skip the rest of the loop
// validation rules: 1) signature must not be null, 2) signature must have a platform ID
if (signature == null || signature.Flags.PlatformId == null)
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Signature for " + dr["RomName"] + " is invalid - skipping metadata refresh");
StatusCounter += 1;
continue;
}
// update the signature in the database
Platform? signaturePlatform = Metadata.Platforms.GetPlatform((long)signature.Flags.PlatformId);
ImportGame.StoreGame(library, hash, signature, signaturePlatform, fi.FullName, (long)dr["Id"], false);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["RomName"], ex);
}
StatusCounter += 1;
}
ClearStatus();
}
// update game metadata
if (forceRefresh == true)
{
// when forced, only update games with ROMs for
@@ -47,7 +467,7 @@ namespace gaseous_server.Classes
else
{
// when run normally, update all games (since this will honour cache timeouts)
sql = "SELECT Id, `Name` FROM Game;";
sql = "SELECT DISTINCT MetadataSourceId AS `Id`, MetadataSourceType AS `GameIdType`, SignatureGameName AS `Name` FROM gaseous.view_MetadataMap;";
}
dt = db.ExecuteCMD(sql);
@@ -58,8 +478,111 @@ namespace gaseous_server.Classes
try
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Games.GetGame((long)dr["id"], true, false, true);
MetadataSources metadataSource;
if (dr["GameIdType"] == DBNull.Value)
{
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Unable to refresh metadata for game " + dr["name"] + " (" + dr["id"] + ") - no source type specified");
}
else
{
metadataSource = (MetadataSources)Enum.Parse(typeof(MetadataSources), dr["GameIdType"].ToString());
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ") using source " + metadataSource.ToString());
HasheousClient.Models.Metadata.IGDB.Game game = Metadata.Games.GetGame(metadataSource, (long)dr["id"]);
// get supporting metadata
if (game != null)
{
if (game.AgeRatings != null)
{
foreach (long ageRatingId in game.AgeRatings)
{
AgeRating ageRating = Metadata.AgeRatings.GetAgeRating(metadataSource, ageRatingId);
if (ageRating.ContentDescriptions != null)
{
foreach (long ageRatingContentDescriptionId in ageRating.ContentDescriptions)
{
Metadata.AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(metadataSource, ageRatingContentDescriptionId);
}
}
}
}
if (game.AlternativeNames != null)
{
foreach (long alternateNameId in game.AlternativeNames)
{
Metadata.AlternativeNames.GetAlternativeNames(metadataSource, alternateNameId);
}
}
if (game.Artworks != null)
{
foreach (long artworkId in game.Artworks)
{
Metadata.Artworks.GetArtwork(metadataSource, artworkId);
}
}
if (game.Cover != null)
{
Metadata.Covers.GetCover(metadataSource, (long?)game.Cover);
}
if (game.GameModes != null)
{
foreach (long gameModeId in game.GameModes)
{
Metadata.GameModes.GetGame_Modes(metadataSource, gameModeId);
}
}
if (game.Genres != null)
{
foreach (long genreId in game.Genres)
{
Metadata.Genres.GetGenres(metadataSource, genreId);
}
}
if (game.Videos != null)
{
foreach (long gameVideoId in game.Videos)
{
Metadata.GamesVideos.GetGame_Videos(metadataSource, gameVideoId);
}
}
if (game.MultiplayerModes != null)
{
foreach (long multiplayerModeId in game.MultiplayerModes)
{
Metadata.MultiplayerModes.GetGame_MultiplayerModes(metadataSource, multiplayerModeId);
}
}
if (game.PlayerPerspectives != null)
{
foreach (long playerPerspectiveId in game.PlayerPerspectives)
{
Metadata.PlayerPerspectives.GetGame_PlayerPerspectives(metadataSource, playerPerspectiveId);
}
}
if (game.ReleaseDates != null)
{
foreach (long releaseDateId in game.ReleaseDates)
{
Metadata.ReleaseDates.GetReleaseDates(metadataSource, releaseDateId);
}
}
if (game.Screenshots != null)
{
foreach (long screenshotId in game.Screenshots)
{
Metadata.Screenshots.GetScreenshot(metadataSource, screenshotId);
}
}
if (game.Themes != null)
{
foreach (long themeId in game.Themes)
{
Metadata.Themes.GetGame_Themes(metadataSource, themeId);
}
}
}
}
}
catch (Exception ex)
{

View File

@@ -2,12 +2,12 @@ using System;
using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
using Microsoft.VisualBasic;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
using System.IO.Compression;
using SharpCompress.Archives;
using SharpCompress.Common;
using gaseous_server.Models;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Classes
{
@@ -193,7 +193,7 @@ namespace gaseous_server.Classes
public static void DeleteMediaGroup(long Id)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id; DELETE FROM GameState WHERE RomId=@id AND IsMediaGroup=1; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId=@id AND IsMediaGroup = 1;";
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id; DELETE FROM GameState WHERE RomId=@id AND IsMediaGroup=1; DELETE FROM User_GameFavouriteRoms WHERE RomId = @id AND IsMediaGroup = 1; DELETE FROM User_RecentPlayedRoms WHERE RomId = @id AND IsMediaGroup = 1; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId=@id AND IsMediaGroup = 1;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id);
db.ExecuteCMD(sql, dbDict);
@@ -296,8 +296,9 @@ namespace gaseous_server.Classes
GameRomMediaGroupItem mediaGroupItem = GetMediaGroup(Id);
if (mediaGroupItem.Status == GameRomMediaGroupItem.GroupBuildStatus.WaitingForBuild)
{
Game GameObject = Games.GetGame(mediaGroupItem.GameId, false, false, false);
Platform PlatformObject = Platforms.GetPlatform(mediaGroupItem.PlatformId, false);
MetadataMap.MetadataMapItem metadataMap = Classes.MetadataManagement.GetMetadataMap(mediaGroupItem.GameId).PreferredMetadataMapItem;
Models.Game GameObject = Games.GetGame(metadataMap.SourceType, metadataMap.SourceId);
Platform PlatformObject = Platforms.GetPlatform(mediaGroupItem.PlatformId);
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(mediaGroupItem.PlatformId);
Logging.Log(Logging.LogType.Information, "Media Group", "Beginning build of media group: " + GameObject.Name + " for platform " + PlatformObject.Name);
@@ -557,7 +558,7 @@ namespace gaseous_server.Classes
{
try
{
return Platforms.GetPlatform(PlatformId, false).Name;
return Platforms.GetPlatform(PlatformId).Name;
}
catch
{

View File

@@ -3,7 +3,6 @@ using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using static HasheousClient.Models.FixMatchModel;
using NuGet.Protocol.Core.Types;
using static gaseous_server.Classes.FileSignature;
@@ -51,13 +50,13 @@ namespace gaseous_server.Classes
UserJoin = @"
LEFT JOIN
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
AND User_RecentPlayedRoms.GameId = Games_Roms.GameId
AND User_RecentPlayedRoms.GameId = Games_Roms.MetadataMapId
AND User_RecentPlayedRoms.PlatformId = Games_Roms.PlatformId
AND User_RecentPlayedRoms.RomId = Games_Roms.Id
AND User_RecentPlayedRoms.IsMediaGroup = 0
LEFT JOIN
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
AND User_GameFavouriteRoms.GameId = Games_Roms.GameId
AND User_GameFavouriteRoms.GameId = Games_Roms.MetadataMapId
AND User_GameFavouriteRoms.PlatformId = Games_Roms.PlatformId
AND User_GameFavouriteRoms.RomId = Games_Roms.Id
AND User_GameFavouriteRoms.IsMediaGroup = 0
@@ -70,27 +69,53 @@ namespace gaseous_server.Classes
if (PlatformId == -1)
{
// data query
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename, GameState.RomId AS SavedStateRomId" + UserFields + " FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + " WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
sql = "SELECT DISTINCT view_Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename, GameState.RomId AS SavedStateRomId" + UserFields + " FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON view_Games_Roms.GameId = Game.Id LEFT JOIN GameState ON (view_Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + " WHERE view_Games_Roms.MetadataMapId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, view_Games_Roms.`Name`;";
// count query
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
sqlCount = "SELECT COUNT(view_Games_Roms.Id) AS RomCount FROM view_Games_Roms WHERE view_Games_Roms.MetadataMapId = @id" + NameSearchWhere + ";";
}
else
{
// data query
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename, GameState.RomId AS SavedStateRomId" + UserFields + " FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + " WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
sql = @"
SELECT DISTINCT
Games_Roms.*,
Platform.`Name` AS platformname,
view_GamesWithRoms.`Name` AS gamename,
GameState.RomId AS SavedStateRomId,
CONCAT(`GameLibraries`.`Path`,
'/',
`Games_Roms`.`RelativePath`) AS `Path`,
`GameLibraries`.`Name` AS `LibraryName`
" + UserFields + @"
FROM
Games_Roms
JOIN
GameLibraries ON Games_Roms.LibraryId = GameLibraries.Id
LEFT JOIN
Platform ON Games_Roms.PlatformId = Platform.Id AND Platform.SourceId = @platformsource
LEFT JOIN
view_GamesWithRoms ON view_GamesWithRoms.MetadataMapId = Games_Roms.MetadataMapId
LEFT JOIN
GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + @"
WHERE
Games_Roms.MetadataMapId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + @"
ORDER BY
Platform.`Name`, Games_Roms.`Name`;
";
// count query
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.MetadataMapId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
dbDict.Add("platformid", PlatformId);
dbDict.Add("platformsource", (int)HasheousClient.Models.MetadataSources.None);
}
DataTable romDT = db.ExecuteCMD(sql, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks));
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks))[0];
if (romDT.Rows.Count > 0)
{
// set count of roms
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks))[0];
GameRoms.Count = int.Parse((string)rowCount["RomCount"]);
int pageOffset = pageSize * (pageNumber - 1);
@@ -114,7 +139,7 @@ namespace gaseous_server.Classes
public static GameRomItem GetRom(long RomId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id WHERE Games_Roms.Id = @id";
string sql = "SELECT DISTINCT view_Games_Roms.*, Platform.`Name` AS platformname, view_GamesWithRoms.`Name` AS gamename FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN view_GamesWithRoms ON view_Games_Roms.MetadataMapId = view_GamesWithRoms.MetadataMapId WHERE view_Games_Roms.Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -134,7 +159,7 @@ namespace gaseous_server.Classes
public static GameRomItem GetRom(string MD5)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id WHERE Games_Roms.MD5 = @id";
string sql = "SELECT DISTINCT view_Games_Roms.*, Platform.`Name` AS platformname, view_GamesWithRoms.`Name` AS gamename FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN view_GamesWithRoms ON view_Games_Roms.MetadataMapId = view_GamesWithRoms.MetadataMapId WHERE view_Games_Roms.MD5 = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", MD5);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
@@ -154,13 +179,13 @@ namespace gaseous_server.Classes
public static GameRomItem UpdateRom(long RomId, long PlatformId, long GameId)
{
// ensure metadata for platformid is present
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
HasheousClient.Models.Metadata.IGDB.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
// ensure metadata for gameid is present
IGDB.Models.Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Models.Game game = Classes.Metadata.Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, GameId);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid WHERE Id = @id";
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, MetadataMapId=@gameid WHERE Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
dbDict.Add("platformid", PlatformId);
@@ -202,7 +227,8 @@ namespace gaseous_server.Classes
}
}
HasheousClient.WebApp.HttpHelper.AddHeader("X-API-Key", Config.MetadataConfiguration.HasheousAPIKey);
HasheousClient.WebApp.HttpHelper.APIKey = Config.MetadataConfiguration.HasheousAPIKey;
HasheousClient.WebApp.HttpHelper.ClientKey = Config.MetadataConfiguration.HasheousClientAPIKey;
HasheousClient.Hasheous hasheousClient = new HasheousClient.Hasheous();
List<MetadataMatch> metadataMatchList = new List<MetadataMatch>();
metadataMatchList.Add(new MetadataMatch(HasheousClient.Models.MetadataSources.IGDB, platform.Slug, game.Slug));
@@ -230,7 +256,7 @@ namespace gaseous_server.Classes
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId = @id AND IsMediaGroup = 0;";
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id; DELETE FROM User_GameFavouriteRoms WHERE RomId = @id AND IsMediaGroup = 0; DELETE FROM User_RecentPlayedRoms WHERE RomId = @id AND IsMediaGroup = 0; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId = @id AND IsMediaGroup = 0;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
db.ExecuteCMD(sql, dbDict);
@@ -269,8 +295,10 @@ namespace gaseous_server.Classes
Id = (long)romDR["id"],
PlatformId = (long)romDR["platformid"],
Platform = (string)romDR["platformname"],
MetadataMapId = (long)romDR["metadatamapid"],
MetadataSource = (HasheousClient.Models.MetadataSources)(int)romDR["metadatasource"],
GameId = (long)romDR["gameid"],
Game = (string)romDR["gamename"],
Game = (string)Common.ReturnValueIfNull(romDR["gamename"], ""),
Name = (string)romDR["name"],
Size = (long)romDR["size"],
Crc = ((string)romDR["crc"]).ToLower(),
@@ -282,6 +310,7 @@ namespace gaseous_server.Classes
RomTypeMedia = (string)romDR["romtypemedia"],
MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"],
RelativePath = (string)romDR["relativepath"],
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
HasSaveStates = hasSaveStates,
@@ -319,9 +348,12 @@ namespace gaseous_server.Classes
{
public long PlatformId { get; set; }
public string Platform { get; set; }
public long MetadataMapId { get; set; }
public HasheousClient.Models.MetadataSources MetadataSource { get; set; }
public long GameId { get; set; }
public string Game { get; set; }
public string? Path { get; set; }
public string? RelativePath { get; set; }
public string? SignatureSourceGameTitle { get; set; }
public bool HasSaveStates { get; set; } = false;
public GameLibrary.LibraryItem Library { get; set; }

View File

@@ -1,4 +1,7 @@
using System.Data;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using HasheousClient.Models;
namespace gaseous_server.Classes
{
@@ -15,8 +18,9 @@ namespace gaseous_server.Classes
public Models.UserProfile? GetUserProfile(string UserId)
{
// build the user profile object
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Id, DisplayName, Quip, AvatarExtension, ProfileBackgroundExtension, UnstructuredData FROM UserProfiles WHERE Id = @userid;";
string sql = "SELECT Id, UserId, DisplayName, Quip, AvatarExtension, ProfileBackgroundExtension, UnstructuredData FROM UserProfiles WHERE Id = @userid;";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "userid", UserId }
};
@@ -48,6 +52,32 @@ namespace gaseous_server.Classes
};
}
// get now playing game - if available
Models.UserProfile.NowPlayingItem? NowPlaying = null;
sql = "SELECT * FROM `view_UserTimeTracking` WHERE UserId = @userid AND UTC_TIMESTAMP() BETWEEN SessionTime AND DATE_ADD(SessionEnd, INTERVAL 2 MINUTE) ORDER BY SessionEnd DESC LIMIT 1;";
dbDict = new Dictionary<string, object>{
{ "userid", data.Rows[0]["UserId"].ToString() }
};
DataTable nowPlayingData = db.ExecuteCMD(sql, dbDict);
if (nowPlayingData.Rows.Count > 0)
{
try
{
gaseous_server.Models.MetadataMap.MetadataMapItem metadataMap = Classes.MetadataManagement.GetMetadataMap((long)nowPlayingData.Rows[0]["GameId"]).PreferredMetadataMapItem;
NowPlaying = new Models.UserProfile.NowPlayingItem
{
Game = Games.GetGame(metadataMap.SourceType, metadataMap.SourceId),
Platform = Platforms.GetPlatform((long)nowPlayingData.Rows[0]["PlatformId"]),
Duration = Convert.ToInt64(nowPlayingData.Rows[0]["SessionLength"])
};
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
// return the user profile object
return new Models.UserProfile
{
UserId = Guid.Parse(data.Rows[0]["Id"].ToString()),
@@ -55,6 +85,7 @@ namespace gaseous_server.Classes
Quip = data.Rows[0]["Quip"].ToString(),
Avatar = Avatar,
ProfileBackground = ProfileBackground,
NowPlaying = NowPlaying,
Data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(data.Rows[0]["UnstructuredData"].ToString())
};
}

View File

@@ -10,6 +10,8 @@ using Asp.Versioning;
using Authentication;
using Microsoft.AspNetCore.Identity;
using gaseous_server.Models;
using gaseous_server.Classes.Metadata;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Controllers
{
@@ -62,25 +64,15 @@ namespace gaseous_server.Controllers
{
try
{
Platform platform = Platforms.GetPlatform(PlatformId);
PlatformMapping.PlatformMapItem platformMap = PlatformMapping.GetPlatformMap(PlatformId);
List<string> biosHashes = new List<string>();
if (GameId == -1 || filtered == false)
{
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
string biosPath = Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platform.Slug);
string tempFile = Path.GetTempFileName();
using (FileStream zipFile = System.IO.File.Create(tempFile))
using (var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
{
foreach (string file in Directory.GetFiles(biosPath))
{
zipArchive.CreateEntryFromFile(file, Path.GetFileName(file));
}
}
var stream = new FileStream(tempFile, FileMode.Open);
return File(stream, "application/zip", platform.Slug + ".zip");
// get all bios files for selected platform
biosHashes.AddRange(platformMap.EnabledBIOSHashes);
}
else
{
@@ -90,24 +82,35 @@ namespace gaseous_server.Controllers
PlatformMapping platformMapping = new PlatformMapping();
PlatformMapping.PlatformMapItem userPlatformMap = platformMapping.GetUserPlatformMap(user.Id, PlatformId, GameId);
biosHashes.AddRange(userPlatformMap.EnabledBIOSHashes);
}
// build zip file
string tempFile = Path.GetTempFileName();
using (FileStream zipFile = System.IO.File.Create(tempFile))
using (var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
{
foreach (Bios.BiosItem bios in GetBios(PlatformId, true))
foreach (string hash in biosHashes)
{
if (userPlatformMap.EnabledBIOSHashes.Contains(bios.hash))
// get the bios data for the hash
foreach (PlatformMapping.PlatformMapItem.EmulatorBiosItem bios in platformMap.Bios)
{
zipArchive.CreateEntryFromFile(bios.biosPath, bios.filename);
if (bios.hash == hash)
{
// add the bios file to the zip
string biosFilePath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, hash + ".bios");
if (System.IO.File.Exists(biosFilePath))
{
zipArchive.CreateEntryFromFile(biosFilePath, bios.filename);
}
}
}
}
}
var stream = new FileStream(tempFile, FileMode.Open);
return File(stream, "application/zip", userPlatformMap.IGDBSlug + ".zip");
}
return File(stream, "application/zip", platform.Slug + ".zip");
}
catch
{

File diff suppressed because it is too large Load Diff

View File

@@ -8,12 +8,12 @@ using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using Asp.Versioning;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Controllers
{
@@ -37,7 +37,7 @@ namespace gaseous_server.Controllers
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM Games_Roms) ORDER BY `Name` ASC;";
string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM view_Games_Roms) ORDER BY `Name` ASC;";
List<Platform> RetVal = new List<Platform>();
@@ -60,7 +60,7 @@ namespace gaseous_server.Controllers
{
try
{
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
if (platformObject != null)
{
@@ -77,43 +77,45 @@ namespace gaseous_server.Controllers
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}/platformlogo")]
[ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult PlatformLogo(long PlatformId)
{
try
{
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
if (platformObject != null)
{
IGDB.Models.PlatformLogo logoObject = PlatformLogos.GetPlatformLogo(platformObject.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
if (logoObject != null)
{
return Ok(logoObject);
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
// [MapToApiVersion("1.0")]
// [MapToApiVersion("1.1")]
// [HttpGet]
// [Route("{PlatformId}/platformlogo")]
// [ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
// [ProducesResponseType(StatusCodes.Status404NotFound)]
// public ActionResult PlatformLogo(long PlatformId)
// {
// try
// {
// Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
// if (platformObject != null)
// {
// PlatformLogo logoObjectParent = (PlatformLogo)platformObject.PlatformLogo;
// PlatformLogo logoObject = PlatformLogos.GetPlatformLogo(logoObjectParent.Id);
// if (logoObject != null)
// {
// return Ok(logoObject);
// }
// else
// {
// return NotFound();
// }
// }
// else
// {
// return NotFound();
// }
// }
// catch
// {
// return NotFound();
// }
// }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}/platformlogo/{size}/")]
[Route("{PlatformId}/platformlogo/{size}/logo.png")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -121,40 +123,41 @@ namespace gaseous_server.Controllers
{
try
{
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
IGDB.Models.PlatformLogo? logoObject = null;
try
{
logoObject = PlatformLogos.GetPlatformLogo(platformObject.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
}
catch
HasheousClient.Models.MetadataSources metadataSources = HasheousClient.Models.MetadataSources.None;
Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId, metadataSources);
PlatformLogo? logoObject = null;
logoObject = PlatformLogos.GetPlatformLogo((long)platformObject.PlatformLogo, metadataSources);
if (logoObject == null)
{
// getting the logo failed, so we'll try a platform variant if available
if (platformObject.Versions != null)
{
if (platformObject.Versions.Ids.Length > 0)
if (platformObject.Versions.Count > 0)
{
IGDB.Models.PlatformVersion platformVersion = Classes.Metadata.PlatformVersions.GetPlatformVersion(platformObject.Versions.Ids[0], platformObject);
logoObject = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
PlatformVersion platformVersion = Classes.Metadata.PlatformVersions.GetPlatformVersion(metadataSources, (long)platformObject.Versions[0]);
logoObject = PlatformLogos.GetPlatformLogo((long)platformVersion.PlatformLogo);
}
else
{
return NotFound();
return GetDummyImage();
}
}
else
{
return NotFound();
return GetDummyImage();
}
}
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
string imagePath = Path.Combine(basePath, size.ToString(), logoObject.ImageId + ".jpg");
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject), metadataSources.ToString());
string imagePath = Path.Combine(basePath, size.ToString(), logoObject.ImageId);
if (!System.IO.File.Exists(imagePath))
{
Communications comms = new Communications();
Task<string> ImgFetch = comms.GetSpecificImageFromServer(Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject)), logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
Task<string> ImgFetch = comms.GetSpecificImageFromServer(metadataSources, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject)), logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
imagePath = ImgFetch.Result;
}
@@ -162,16 +165,78 @@ namespace gaseous_server.Controllers
if (!System.IO.File.Exists(imagePath))
{
Communications comms = new Communications();
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
Task<string> ImgFetch = comms.GetSpecificImageFromServer(metadataSources, basePath, logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
imagePath = ImgFetch.Result;
}
if (System.IO.File.Exists(imagePath))
{
string filename = logoObject.ImageId + ".jpg";
// get image info
var info = new ImageMagick.MagickImageInfo(imagePath);
string extension = ".jpg";
string mimeType = "image/jpg";
switch (info.Format)
{
case ImageMagick.MagickFormat.Jpeg:
extension = ".jpg";
mimeType = "image/jpg";
break;
case ImageMagick.MagickFormat.Png:
extension = ".png";
mimeType = "image/png";
break;
case ImageMagick.MagickFormat.Gif:
extension = ".gif";
mimeType = "image/gif";
break;
case ImageMagick.MagickFormat.Bmp:
extension = ".bmp";
mimeType = "image/bmp";
break;
case ImageMagick.MagickFormat.Tiff:
extension = ".tiff";
mimeType = "image/tiff";
break;
case ImageMagick.MagickFormat.Unknown:
extension = ".jpg";
mimeType = "image/jpg";
break;
case ImageMagick.MagickFormat.WebP:
extension = ".webp";
mimeType = "image/webp";
break;
case ImageMagick.MagickFormat.Heic:
extension = ".heic";
mimeType = "image/heic";
break;
case ImageMagick.MagickFormat.Heif:
extension = ".heif";
mimeType = "image/heif";
break;
case ImageMagick.MagickFormat.Svg:
extension = ".svg";
mimeType = "image/svg+xml";
break;
default:
extension = ".jpg";
mimeType = "image/jpg";
break;
}
string filename = logoObject.ImageId + extension;
string filepath = imagePath;
string contentType = "image/jpg";
string contentType = mimeType;
var cd = new System.Net.Mime.ContentDisposition
{
@@ -193,14 +258,60 @@ namespace gaseous_server.Controllers
return File(filedata, contentType);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
private ActionResult GetDummyImage()
{
// return resource named DefaultPlatformLogo.svg
var assembly = Assembly.GetExecutingAssembly();
string resourceName = "gaseous_server.Support.DefaultPlatformLogo.svg";
string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
if (resources.Contains(resourceName))
{
string svgData = "";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
svgData = reader.ReadToEnd();
}
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "DefaultPlatformLogo.svg",
Inline = true,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
byte[] filedata = null;
using (MemoryStream ms = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(ms))
{
writer.Write(svgData);
writer.Flush();
ms.Position = 0;
filedata = ms.ToArray();
}
}
return File(filedata, "image/svg+xml");
}
else
{
return NotFound();
}
}
}
}

View File

@@ -8,13 +8,13 @@ using System.Reflection;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings;
using Asp.Versioning;
using HasheousClient.Models.Metadata.IGDB;
namespace gaseous_server.Controllers
{
@@ -62,7 +62,7 @@ namespace gaseous_server.Controllers
}
// get override platform if specified
IGDB.Models.Platform? OverridePlatform = null;
Platform? OverridePlatform = null;
if (OverridePlatformId != null)
{
OverridePlatform = Platforms.GetPlatform((long)OverridePlatformId);
@@ -76,10 +76,10 @@ namespace gaseous_server.Controllers
case "rom":
if (RetVal["status"] == "imported")
{
IGDB.Models.Game? game = (IGDB.Models.Game)RetVal["game"];
if (game.Id == null)
gaseous_server.Models.Game? game = (gaseous_server.Models.Game)RetVal["game"];
if (game == null || game.Id == null)
{
RetVal["game"] = Games.GetGame(0, false, false, false);
RetVal["game"] = Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, 0);
}
}
break;

View File

@@ -6,8 +6,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NuGet.Common;
@@ -36,41 +35,39 @@ namespace gaseous_server.Controllers
private static async Task<List<Platform>> _SearchForPlatform(string SearchString)
{
string searchBody = "";
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 + "\"*;";
// search the database for the requested platforms
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string query = "SELECT `Id` FROM Platform WHERE `Name` LIKE '%" + SearchString + "%';";
DataTable data = db.ExecuteCMD(query);
List<Platform>? searchCache = Communications.GetSearchCache<List<Platform>>(searchFields, searchBody);
if (searchCache == null)
List<Platform> platforms = new List<Platform>();
foreach (DataRow row in data.Rows)
{
// cache miss
// get Platform metadata from data source
Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
Platform platform = Platforms.GetPlatform((long)row["Id"]);
Communications.SetSearchCache<List<Platform>>(searchFields, searchBody, results.ToList());
platforms.Add(platform);
}
return results.ToList();
}
else
{
return searchCache;
}
return platforms;
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Game")]
[ProducesResponseType(typeof(List<GaseousGame>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<gaseous_server.Models.Game>), StatusCodes.Status200OK)]
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
{
List<GaseousGame> RetVal = await _SearchForGame(PlatformId, SearchString);
List<gaseous_server.Models.Game> RetVal = await _SearchForGame(PlatformId, SearchString);
return Ok(RetVal);
}
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
private static async Task<List<gaseous_server.Models.Game>> _SearchForGame(long PlatformId, string SearchString)
{
switch (Config.MetadataConfiguration.DefaultMetadataSource)
{
case HasheousClient.Models.MetadataSources.IGDB:
if (Config.IGDB.UseHasheousProxy == false)
{
string searchBody = "";
string searchFields = "fields *; ";
@@ -78,35 +75,35 @@ namespace gaseous_server.Controllers
searchBody += "where platforms = (" + PlatformId + ");";
searchBody += "limit 100;";
List<GaseousGame>? searchCache = Communications.GetSearchCache<List<GaseousGame>>(searchFields, searchBody);
List<gaseous_server.Models.Game>? searchCache = Communications.GetSearchCache<List<gaseous_server.Models.Game>>(searchFields, searchBody);
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);
var results = await comms.APIComm<gaseous_server.Models.Game>("Game", searchFields, searchBody);
List<GaseousGame> games = new List<GaseousGame>();
foreach (Game game in results.ToList())
List<gaseous_server.Models.Game> games = new List<gaseous_server.Models.Game>();
foreach (gaseous_server.Models.Game game in results.ToList())
{
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.IGDB, "Game", (long)game.Id);
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
Storage.NewCacheValue(game, false);
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, game, false);
break;
case Storage.CacheStatus.Expired:
Storage.NewCacheValue(game, true);
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, game, true);
break;
}
games.Add(new GaseousGame(game));
games.Add(game);
}
Communications.SetSearchCache<List<GaseousGame>>(searchFields, searchBody, games);
Communications.SetSearchCache<List<gaseous_server.Models.Game>>(searchFields, searchBody, games);
return games;
}
@@ -114,16 +111,38 @@ namespace gaseous_server.Controllers
{
// 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)
List<gaseous_server.Models.Game> gamesToReturn = new List<gaseous_server.Models.Game>();
foreach (gaseous_server.Models.Game game in searchCache)
{
Game tempGame = Games.GetGame((long)game.Id, false, false, false);
gamesToReturn.Add(new GaseousGame(tempGame));
gaseous_server.Models.Game? tempGame = Games.GetGame(Communications.MetadataSource, (long)game.Id);
if (tempGame != null)
{
gamesToReturn.Add(tempGame);
}
}
return gamesToReturn;
}
}
else
{
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
Communications.ConfigureHasheousClient(ref hasheous);
List<gaseous_server.Models.Game> hSearch = hasheous.GetMetadataProxy_SearchGame<gaseous_server.Models.Game>(HasheousClient.Hasheous.MetadataProvider.IGDB, PlatformId.ToString(), SearchString).ToList<gaseous_server.Models.Game>();
List<gaseous_server.Models.Game> hGamesToReturn = new List<gaseous_server.Models.Game>();
foreach (gaseous_server.Models.Game game in hSearch)
{
hGamesToReturn.Add(game);
}
return hGamesToReturn;
}
default:
return new List<gaseous_server.Models.Game>();
}
}
}
}

View File

@@ -51,7 +51,19 @@ namespace gaseous_server.Controllers
ReturnValue.DatabaseSize = (long)(System.Decimal)dbResponse.Rows[0][1];
// platform statistics
sql = "SELECT Platform.`name`, grc.Count, grs.Size FROM Platform INNER JOIN (SELECT Platform.`name` AS `Name`, SUM(grs.Size) AS Size FROM Platform JOIN Games_Roms AS grs ON (grs.PlatformId = Platform.Id) GROUP BY Platform.`name`) grs ON (grs.`Name` = Platform.`name`) INNER JOIN (SELECT Platform.`name` AS `Name`, COUNT(grc.Size) AS Count FROM Platform JOIN Games_Roms AS grc ON (grc.PlatformId = Platform.Id) GROUP BY Platform.`name`) grc ON (grc.`Name` = Platform.`name`) ORDER BY Platform.`name`;";
sql = @"
SELECT
view_Games_Roms.PlatformId,
Platform.`Name`,
SUM(view_Games_Roms.Size) AS Size,
COUNT(view_Games_Roms.`Id`) AS Count
FROM
view_Games_Roms
LEFT JOIN
Platform ON view_Games_Roms.PlatformId = Platform.`Id`
AND Platform.SourceId = 0
GROUP BY Platform.`Name`
ORDER BY Platform.`Name`; ";
dbResponse = db.ExecuteCMD(sql);
ReturnValue.PlatformStatistics = new List<SystemInfo.PlatformStatisticsItem>();
foreach (DataRow dr in dbResponse.Rows)
@@ -72,10 +84,51 @@ namespace gaseous_server.Controllers
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Version")]
[ProducesResponseType(StatusCodes.Status200OK)]
public Version GetSystemVersion()
[ProducesResponseType(typeof(Dictionary<string, object>), StatusCodes.Status200OK)]
[AllowAnonymous]
public ActionResult GetSystemVersion()
{
return Assembly.GetExecutingAssembly().GetName().Version;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings dictionary
Dictionary<int, string> ClassificationBoardsStrings = new Dictionary<int, string>();
foreach (IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)))
{
ClassificationBoardsStrings.Add((int)ageRatingCategory, ageRatingCategory.ToString());
}
Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>();
foreach (IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)))
{
AgeRatingsStrings.Add((int)ageRatingTitle, ageRatingTitle.ToString());
}
Dictionary<string, object> retVal = new Dictionary<string, object>
{
{
"AppVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString()
},
{
"DBSchemaVersion", db.GetDatabaseSchemaVersion().ToString()
},
{
"FirstRunStatus", Config.ReadSetting<string>("FirstRunStatus", "0")
},
{
"AgeRatingBoardsStrings", ClassificationBoardsStrings
},
{
"AgeRatingStrings", AgeRatingsStrings
},
{
"AgeRatingGroups", AgeGroups.AgeGroupingsFlat
},
{
"emulatorDebugMode", Config.ReadSetting<string>("emulatorDebugMode", false.ToString()).ToLower()
}
};
return Ok(retVal);
}
[MapToApiVersion("1.0")]
@@ -270,6 +323,11 @@ namespace gaseous_server.Controllers
HasheousHost = Config.MetadataConfiguration.HasheousHost,
HasheousSubmitFixes = (bool)Config.MetadataConfiguration.HasheousSubmitFixes,
HasheousAPIKey = Config.MetadataConfiguration.HasheousAPIKey
},
MetadataSources = new List<SystemSettingsModel.MetadataSourceItem>
{
new SystemSettingsModel.MetadataSourceItem(HasheousClient.Models.MetadataSources.None, false, "", "", Config.MetadataConfiguration.DefaultMetadataSource),
new SystemSettingsModel.MetadataSourceItem(HasheousClient.Models.MetadataSources.IGDB, Config.IGDB.UseHasheousProxy, Config.IGDB.ClientId, Config.IGDB.Secret, Config.MetadataConfiguration.DefaultMetadataSource)
}
};
@@ -293,6 +351,34 @@ namespace gaseous_server.Controllers
Config.MetadataConfiguration.HasheousHost = model.SignatureSource.HasheousHost;
Config.MetadataConfiguration.HasheousAPIKey = model.SignatureSource.HasheousAPIKey;
Config.MetadataConfiguration.HasheousSubmitFixes = model.SignatureSource.HasheousSubmitFixes;
foreach (SystemSettingsModel.MetadataSourceItem metadataSourceItem in model.MetadataSources)
{
// configure the default metadata source
if (metadataSourceItem.Default == true)
{
Config.MetadataConfiguration.DefaultMetadataSource = metadataSourceItem.Source;
}
else
{
Config.MetadataConfiguration.DefaultMetadataSource = HasheousClient.Models.MetadataSources.None;
}
// configure the metadata source
switch (metadataSourceItem.Source)
{
case HasheousClient.Models.MetadataSources.None:
break;
case HasheousClient.Models.MetadataSources.IGDB:
Config.IGDB.UseHasheousProxy = metadataSourceItem.UseHasheousProxy;
Config.IGDB.ClientId = metadataSourceItem.ClientId;
Config.IGDB.Secret = metadataSourceItem.Secret;
break;
case HasheousClient.Models.MetadataSources.TheGamesDb:
break;
default:
break;
}
}
Config.UpdateConfig();
}
@@ -439,8 +525,7 @@ namespace gaseous_server.Controllers
this._Blocks = new List<ProcessQueue.QueueItemType>{
ProcessQueue.QueueItemType.LibraryScan,
ProcessQueue.QueueItemType.LibraryScanWorker,
ProcessQueue.QueueItemType.TitleIngestor,
ProcessQueue.QueueItemType.Rematcher
ProcessQueue.QueueItemType.TitleIngestor
};
break;
@@ -462,32 +547,7 @@ namespace gaseous_server.Controllers
this.DefaultAllowedEndHours = 23;
this.DefaultAllowedEndMinutes = 59;
this._Blocks = new List<ProcessQueue.QueueItemType>{
ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.Rematcher
};
break;
case ProcessQueue.QueueItemType.Rematcher:
this._UserManageable = true;
this.DefaultInterval = 1440;
this.MinimumAllowedInterval = 360;
this.DefaultAllowedDays = new List<DayOfWeek>{
DayOfWeek.Sunday,
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday,
DayOfWeek.Friday,
DayOfWeek.Saturday
};
this.DefaultAllowedStartHours = 0;
this.DefaultAllowedStartMinutes = 0;
this.DefaultAllowedEndHours = 23;
this.DefaultAllowedEndMinutes = 59;
this._Blocks = new List<ProcessQueue.QueueItemType>{
ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.LibraryScan,
ProcessQueue.QueueItemType.LibraryScanWorker
ProcessQueue.QueueItemType.OrganiseLibrary
};
break;
@@ -735,6 +795,7 @@ namespace gaseous_server.Controllers
public int MinimumLogRetentionPeriod { get; set; }
public bool EmulatorDebugMode { get; set; }
public SignatureSourceItem SignatureSource { get; set; }
public List<MetadataSourceItem> MetadataSources { get; set; }
public class SignatureSourceItem
{
@@ -743,5 +804,100 @@ namespace gaseous_server.Controllers
public string HasheousAPIKey { get; set; }
public bool HasheousSubmitFixes { get; set; }
}
public class MetadataSourceItem
{
public MetadataSourceItem()
{
}
public MetadataSourceItem(HasheousClient.Models.MetadataSources source, bool useHasheousProxy, string clientId, string secret, HasheousClient.Models.MetadataSources defaultSource)
{
Source = source;
UseHasheousProxy = useHasheousProxy;
ClientId = clientId;
Secret = secret;
if (Source == defaultSource)
{
Default = true;
}
else
{
Default = false;
}
}
public HasheousClient.Models.MetadataSources Source { get; set; }
public bool UseHasheousProxy { get; set; }
public string ClientId { get; set; }
public string Secret { get; set; }
public bool Default { get; set; }
public bool? Configured
{
get
{
switch (Source)
{
case HasheousClient.Models.MetadataSources.None:
return true;
case HasheousClient.Models.MetadataSources.IGDB:
if ((!String.IsNullOrEmpty(ClientId) && !String.IsNullOrEmpty(Secret)) || UseHasheousProxy == true)
{
return true;
}
else
{
return false;
}
case HasheousClient.Models.MetadataSources.TheGamesDb:
if ((!String.IsNullOrEmpty(ClientId) && !String.IsNullOrEmpty(Secret)) || UseHasheousProxy == true)
{
return true;
}
else
{
return false;
}
default:
return false;
}
}
}
public bool? UsesProxy
{
get
{
switch (Source)
{
case HasheousClient.Models.MetadataSources.None:
return false;
case HasheousClient.Models.MetadataSources.IGDB:
return true;
case HasheousClient.Models.MetadataSources.TheGamesDb:
return true;
default:
return false;
}
}
}
public bool? UsesClientIdAndSecret
{
get
{
switch (Source)
{
case HasheousClient.Models.MetadataSources.None:
return false;
case HasheousClient.Models.MetadataSources.IGDB:
return true;
case HasheousClient.Models.MetadataSources.TheGamesDb:
return true;
default:
return false;
}
}
}
}
}
}

View File

@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
@@ -19,6 +18,8 @@ using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings;
using Asp.Versioning;
using Humanizer;
using HasheousClient.Models.Metadata.IGDB;
using gaseous_server.Models;
namespace gaseous_server.Controllers.v1_1
{
@@ -42,7 +43,7 @@ namespace gaseous_server.Controllers.v1_1
[MapToApiVersion("1.1")]
[HttpPost]
[ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> Game_v1_1(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
public async Task<IActionResult> Game_v1_1(GameSearchModel model, int pageNumber = 0, int pageSize = 0, bool returnSummary = true, bool returnGames = true)
{
var user = await _userManager.GetUserAsync(User);
@@ -87,7 +88,7 @@ namespace gaseous_server.Controllers.v1_1
model.GameAgeRating.IncludeUnrated = false;
}
return Ok(GetGames(model, user.Id, pageNumber, pageSize));
return Ok(GetGames(model, user.Id, pageNumber, pageSize, returnSummary, returnGames));
}
else
{
@@ -97,9 +98,9 @@ namespace gaseous_server.Controllers.v1_1
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/Related")]
[Route("{MetadataMapId}/Related")]
[ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> GameRelated(long GameId)
public async Task<IActionResult> GameRelated(long MetadataMapId)
{
var user = await _userManager.GetUserAsync(User);
@@ -112,18 +113,19 @@ namespace gaseous_server.Controllers.v1_1
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Games.Id, view_Games.AgeGroupId, Relation_Game_SimilarGames.SimilarGamesId FROM view_Games JOIN Relation_Game_SimilarGames ON view_Games.Id = Relation_Game_SimilarGames.GameId AND Relation_Game_SimilarGames.SimilarGamesId IN (SELECT Id FROM view_Games) WHERE view_Games.Id = @id AND (view_Games.AgeGroupId <= @agegroupid" + IncludeUnrated + ")";
string sql = "SELECT view_Games.Id, view_Games.AgeGroupId, Relation_Game_SimilarGames.SimilarGamesId FROM view_Games JOIN Relation_Game_SimilarGames ON view_Games.Id = Relation_Game_SimilarGames.GameId AND view_Games.GameIdType = Relation_Game_SimilarGames.GameSourceId AND Relation_Game_SimilarGames.SimilarGamesId IN (SELECT Id FROM view_Games) WHERE view_Games.Id = @id AND (view_Games.AgeGroupId <= @agegroupid" + IncludeUnrated + ")";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId);
dbDict.Add("id", MetadataMapId);
dbDict.Add("agegroupid", (int)user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction);
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
List<Models.Game> RetVal = new List<Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, dbDict);
foreach (DataRow dr in dbResponse.Rows)
{
RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["SimilarGamesId"], false, false, false));
MetadataMap.MetadataMapItem metadataMap = Classes.MetadataManagement.GetMetadataMap(MetadataMapId).PreferredMetadataMapItem;
RetVal.Add(Classes.Metadata.Games.GetGame(metadataMap.SourceType, (long)dr["SimilarGamesId"]));
}
GameReturnPackage gameReturn = new GameReturnPackage(RetVal.Count, RetVal);
@@ -188,7 +190,7 @@ namespace gaseous_server.Controllers.v1_1
}
}
public static GameReturnPackage GetGames(GameSearchModel model, string userid, int pageNumber = 0, int pageSize = 0)
public static GameReturnPackage GetGames(GameSearchModel model, string userid, int pageNumber = 0, int pageSize = 0, bool returnSummary = true, bool returnGames = true)
{
string whereClause = "";
string havingClause = "";
@@ -303,7 +305,7 @@ namespace gaseous_server.Controllers.v1_1
string platformWhereClause = "";
if (model.Platform.Count > 0)
{
tempVal = " AND Games_Roms.PlatformId IN (";
tempVal = " AND view_Games_Roms.PlatformId IN (";
for (int i = 0; i < model.Platform.Count; i++)
{
if (i > 0)
@@ -483,6 +485,8 @@ namespace gaseous_server.Controllers.v1_1
SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
SELECT DISTINCT
Game.Id,
Game.MetadataMapId,
Game.GameIdType,
Game.`Name`,
Game.NameThe,
Game.Slug,
@@ -507,30 +511,26 @@ SELECT DISTINCT
FROM
(SELECT DISTINCT
Game.*,
CASE
WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The')
ELSE Game.`Name`
END AS NameThe,
Games_Roms.PlatformId,
view_Games_Roms.PlatformId,
AgeGroup.AgeGroupId,
COUNT(Games_Roms.Id) AS RomCount
COUNT(view_Games_Roms.Id) AS RomCount
FROM
Game
view_GamesWithRoms AS Game
LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId
LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + @"
LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId" + platformWhereClause + @"
LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + @"
GROUP BY Game.Id
HAVING RomCount > 0) Game
LEFT JOIN
(SELECT
Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
view_Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
FROM
GameState
JOIN Games_Roms ON GameState.RomId = Games_Roms.Id
JOIN view_Games_Roms ON GameState.RomId = view_Games_Roms.Id
WHERE
GameState.IsMediaGroup = 0
AND GameState.UserId = @userid
GROUP BY Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
GROUP BY view_Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
LEFT JOIN
(SELECT
RomMediaGroup.GameId,
@@ -540,37 +540,45 @@ FROM
JOIN GameState ON RomMediaGroup.Id = GameState.RomId
AND GameState.IsMediaGroup = 1
AND GameState.UserId = @userid
GROUP BY RomMediaGroup.GameId) RomGroupSavedStates ON Game.Id = RomGroupSavedStates.GameId
GROUP BY RomMediaGroup.GameId) RomGroupSavedStates ON Game.MetadataMapId = RomGroupSavedStates.GameId
LEFT JOIN
Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId
Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId AND Relation_Game_Genres.GameSourceId = Game.GameIdType
LEFT JOIN
Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId
Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId AND Relation_Game_GameModes.GameSourceId = Game.GameIdType
LEFT JOIN
Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId
Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId AND Relation_Game_PlayerPerspectives.GameSourceId = Game.GameIdType
LEFT JOIN
Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId
Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId AND Relation_Game_Themes.GameSourceId = Game.GameIdType
LEFT JOIN
Favourites ON Game.Id = Favourites.GameId AND Favourites.UserId = @userid " + whereClause + " " + havingClause + " " + orderByClause;
List<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>();
Favourites ON Game.MetadataMapId = Favourites.GameId AND Favourites.UserId = @userid " + whereClause + " " + havingClause + " " + orderByClause;
// if (returnGames == true)
// {
// sql += " LIMIT @pageOffset, @pageSize";
// whereParams.Add("pageOffset", pageSize * (pageNumber - 1));
// whereParams.Add("pageSize", pageSize);
// }
DataTable dbResponse = db.ExecuteCMD(sql, whereParams, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 60));
// get count
int RecordCount = dbResponse.Rows.Count;
int? RecordCount = null;
if (returnSummary == true)
{
RecordCount = dbResponse.Rows.Count;
}
// compile data for return
int pageOffset = pageSize * (pageNumber - 1);
for (int i = pageOffset; i < dbResponse.Rows.Count; i++)
List<Games.MinimalGameItem>? RetVal = null;
if (returnGames == true)
{
if (pageNumber != 0 && pageSize != 0)
RetVal = new List<Games.MinimalGameItem>();
foreach (int i in Enumerable.Range(0, dbResponse.Rows.Count))
{
if (i >= (pageOffset + pageSize))
{
break;
}
}
Models.Game retGame = Storage.BuildCacheObject<Models.Game>(new Models.Game(), dbResponse.Rows[i]);
retGame.MetadataMapId = (long)dbResponse.Rows[i]["MetadataMapId"];
retGame.MetadataSource = (HasheousClient.Models.MetadataSources)dbResponse.Rows[i]["GameIdType"];
Game retGame = Storage.BuildCacheObject<Game>(new Game(), dbResponse.Rows[i]);
Games.MinimalGameItem retMinGame = new Games.MinimalGameItem(retGame);
retMinGame.Index = i;
if (dbResponse.Rows[i]["RomSaveCount"] != DBNull.Value || dbResponse.Rows[i]["MediaGroupSaveCount"] != DBNull.Value)
@@ -592,9 +600,14 @@ FROM
RetVal.Add(retMinGame);
}
}
Dictionary<string, int>? AlphaList = null;
if (returnSummary == true)
{
AlphaList = new Dictionary<string, int>();
// build alpha list
Dictionary<string, int> AlphaList = new Dictionary<string, int>();
if (orderByField == "NameThe" || orderByField == "Name")
{
int CurrentPage = 1;
@@ -635,6 +648,7 @@ FROM
}
}
}
}
GameReturnPackage gameReturn = new GameReturnPackage
{
@@ -653,12 +667,12 @@ FROM
}
public GameReturnPackage(int Count, List<Game> Games)
public GameReturnPackage(int Count, List<Models.Game> Games)
{
this.Count = Count;
List<Games.MinimalGameItem> minimalGames = new List<Games.MinimalGameItem>();
foreach (Game game in Games)
foreach (Models.Game game in Games)
{
minimalGames.Add(new Classes.Metadata.Games.MinimalGameItem(game));
}
@@ -666,9 +680,9 @@ FROM
this.Games = minimalGames;
}
public int Count { get; set; }
public List<Games.MinimalGameItem> Games { get; set; } = new List<Games.MinimalGameItem>();
public Dictionary<string, int> AlphaList { get; set; }
public int? Count { get; set; }
public List<Games.MinimalGameItem>? Games { get; set; } = new List<Games.MinimalGameItem>();
public Dictionary<string, int>? AlphaList { get; set; }
}
}
}

View File

@@ -270,7 +270,7 @@ namespace gaseous_server.Controllers.v1_1
else
{
RomMediaGroup.GameRomMediaGroupItem mediaGroupItem = RomMediaGroup.GetMediaGroup(RomId);
IGDB.Models.Game game = Games.GetGame(mediaGroupItem.GameId, false, false, false);
Models.Game game = Games.GetGame(Communications.MetadataSource, mediaGroupItem.GameId);
Classes.Common.hashObject hashObject = new Classes.Common.hashObject(Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, mediaGroupItem.Id.ToString() + ".zip"));
romName = game.Name;
romMd5 = hashObject.md5hash;

View File

@@ -31,7 +31,7 @@ namespace gaseous_server.Controllers
[Route("{UserId}")]
[ProducesResponseType(typeof(Models.UserProfile), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ResponseCache(CacheProfileName = "5Minute")]
[ResponseCache(CacheProfileName = "Default30")]
public ActionResult GetUserProfile(string UserId)
{
Classes.UserProfile profile = new Classes.UserProfile();

View File

@@ -5,50 +5,22 @@ using Swashbuckle.AspNetCore.SwaggerGen;
namespace gaseous_server.Models
{
public class GaseousGame : IGDB.Models.Game
{
public GaseousGame()
public class Game : HasheousClient.Models.Metadata.IGDB.Game
{
[NoDatabase]
public bool IsFavourite { get; set; } = false;
}
public GaseousGame(IGDB.Models.Game game)
{
var targetType = this.GetType();
var sourceType = game.GetType();
foreach (var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty))
{
// check whether source object has the the property
var sp = sourceType.GetProperty(prop.Name);
if (sp != null)
{
// if yes, copy the value to the matching property
var value = sp.GetValue(game, null);
prop.SetValue(this, value, null);
}
}
}
[NoDatabase]
public bool HasSavedGame { get; set; } = false;
public IGDB.Models.Cover? CoverItem
{
get
{
if (this.Cover != null)
{
if (this.Cover.Id != null)
{
// IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
IGDB.Models.Cover cover = new IGDB.Models.Cover()
{
Id = this.Cover.Id
};
}
[NoDatabase]
public long MetadataMapId { get; set; }
[NoDatabase]
public HasheousClient.Models.MetadataSources MetadataSource { get; set; }
}
return null;
}
}
internal class NoDatabaseAttribute : Attribute
{
}
}

View File

@@ -0,0 +1,77 @@
using gaseous_server.Classes.Metadata;
using HasheousClient.Models;
namespace gaseous_server.Models
{
public class MetadataMap
{
public long Id { get; set; }
public long PlatformId { get; set; }
public string SignatureGameName { get; set; }
public List<MetadataMapItem> MetadataMapItems { get; set; }
public MetadataMapItem? PreferredMetadataMapItem
{
get
{
if (MetadataMapItems == null || MetadataMapItems.Count == 0)
{
return null;
}
return MetadataMapItems.FirstOrDefault(mmi => mmi.Preferred);
}
}
public class MetadataMapItem
{
public HasheousClient.Models.MetadataSources SourceType { get; set; }
public long SourceId { get; set; }
public bool Preferred { get; set; }
public string SourceSlug
{
get
{
string slug = "";
switch (SourceType)
{
case MetadataSources.IGDB:
Game game = Games.GetGame(SourceType, (long)SourceId);
if (game != null)
{
slug = game.Slug;
}
break;
default:
slug = SourceId.ToString();
break;
}
return slug;
}
}
public string link
{
get
{
string link = "";
switch (SourceType)
{
case MetadataSources.IGDB:
link = $"https://www.igdb.com/games/{SourceSlug}";
break;
case MetadataSources.TheGamesDb:
link = $"https://thegamesdb.net/game.php?id={SourceId}";
break;
default:
link = "";
break;
}
return link;
}
}
}
}
}

View File

@@ -8,7 +8,7 @@ using System.Web;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers;
using IGDB.Models;
using HasheousClient.Models.Metadata.IGDB;
using Newtonsoft.Json;
namespace gaseous_server.Models
@@ -46,7 +46,7 @@ namespace gaseous_server.Models
}
else
{
WritePlatformMap(mapItem, true, true);
WritePlatformMap(mapItem, true, true, true);
Logging.Log(Logging.LogType.Information, "Platform Map", "Overwriting " + mapItem.IGDBName + " with default values.");
}
}
@@ -54,7 +54,7 @@ namespace gaseous_server.Models
{
Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from predefined data.");
// doesn't exist - add it
WritePlatformMap(mapItem, false, true);
WritePlatformMap(mapItem, false, true, true);
}
}
}
@@ -92,9 +92,9 @@ namespace gaseous_server.Models
}
}
private static IGDB.Models.Platform CreateDummyPlatform(PlatformMapItem mapItem)
private static Platform CreateDummyPlatform(PlatformMapItem mapItem)
{
IGDB.Models.Platform platform = new IGDB.Models.Platform
Platform platform = new Platform
{
Id = mapItem.IGDBId,
Name = mapItem.IGDBName,
@@ -102,9 +102,14 @@ namespace gaseous_server.Models
AlternativeName = mapItem.AlternateNames.FirstOrDefault()
};
if (Storage.GetCacheStatus("Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent)
if (Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.None, "Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent)
{
Storage.NewCacheValue(platform);
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.None, platform);
}
if (Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.IGDB, "Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent)
{
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, platform);
}
return platform;
@@ -164,7 +169,7 @@ namespace gaseous_server.Models
}
}
public static void WritePlatformMap(PlatformMapItem item, bool Update, bool AllowAvailableEmulatorOverwrite)
public static void WritePlatformMap(PlatformMapItem item, bool Update, bool AllowAvailableEmulatorOverwrite, bool overwriteBios = false)
{
CreateDummyPlatform(item);
@@ -247,6 +252,12 @@ namespace gaseous_server.Models
foreach (PlatformMapItem.EmulatorBiosItem biosItem in item.Bios)
{
bool isEnabled = false;
if (overwriteBios == true)
{
isEnabled = true;
}
else
{
if (item.EnabledBIOSHashes == null)
{
item.EnabledBIOSHashes = new List<string>();
@@ -255,6 +266,7 @@ namespace gaseous_server.Models
{
isEnabled = true;
}
}
sql = "INSERT INTO PlatformMap_Bios (Id, Filename, Description, Hash, Enabled) VALUES (@Id, @Filename, @Description, @Hash, @Enabled);";
dbDict.Clear();
@@ -303,15 +315,15 @@ namespace gaseous_server.Models
string sql = "";
// get platform data
// IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId, false);
IGDB.Models.Platform? platform = null;
if (Storage.GetCacheStatus("Platform", IGDBId) == Storage.CacheStatus.NotPresent)
Platform? platform = null;
if (Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.None, "Platform", IGDBId) == Storage.CacheStatus.NotPresent)
{
//platform = Platforms.GetPlatform(IGDBId, false);
}
else
{
platform = (IGDB.Models.Platform)Storage.GetCacheValue<IGDB.Models.Platform>(new Platform(), "id", IGDBId);
// platform = (Platform)Storage.GetCacheValue<Platform>(HasheousClient.Models.MetadataSources.None, new Platform(), "id", IGDBId);
platform = Platforms.GetPlatform(IGDBId, HasheousClient.Models.MetadataSources.None);
}
if (platform != null)
@@ -440,8 +452,8 @@ namespace gaseous_server.Models
{
if (Signature.Game != null) { Signature.Game.System = PlatformMapping.IGDBName; }
}
Signature.Flags.IGDBPlatformId = PlatformMapping.IGDBId;
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
Signature.Flags.PlatformId = PlatformMapping.IGDBId;
Signature.Flags.PlatformName = PlatformMapping.IGDBName;
PlatformFound = true;
@@ -464,8 +476,8 @@ namespace gaseous_server.Models
{
if (Signature.Game != null) { Signature.Game.System = PlatformMapping.IGDBName; }
}
Signature.Flags.IGDBPlatformId = PlatformMapping.IGDBId;
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
Signature.Flags.PlatformId = PlatformMapping.IGDBId;
Signature.Flags.PlatformName = PlatformMapping.IGDBName;
PlatformFound = true;

View File

@@ -1,6 +1,9 @@
using System;
using System.Text.Json.Serialization;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_signature_parser.models.RomSignatureObject;
using HasheousClient.Models;
namespace gaseous_server.Models
{
@@ -34,13 +37,119 @@ namespace gaseous_server.Models
public GameItem Game = new GameItem();
public RomItem Rom = new RomItem();
public SignatureFlags Flags = new SignatureFlags();
public SignatureFlags Flags
{
get
{
SignatureFlags _flags = new SignatureFlags();
foreach (SourceValues.SourceValueItem source in MetadataSources.Platforms)
{
if (source.Source == Config.MetadataConfiguration.DefaultMetadataSource)
{
_flags.PlatformId = source.Id;
_flags.PlatformName = source.Name;
_flags.PlatformMetadataSource = source.Source;
break;
}
}
if (_flags.PlatformId == 0)
{
// fall back to the IGDB source if present
foreach (SourceValues.SourceValueItem source in MetadataSources.Platforms)
{
if (source.Source == HasheousClient.Models.MetadataSources.IGDB)
{
_flags.PlatformId = source.Id;
_flags.PlatformName = source.Name;
_flags.PlatformMetadataSource = source.Source;
break;
}
}
}
foreach (SourceValues.SourceValueItem source in MetadataSources.Games)
{
if (source.Source == Config.MetadataConfiguration.DefaultMetadataSource)
{
_flags.GameId = source.Id;
_flags.GameName = source.Name;
_flags.GameMetadataSource = source.Source;
break;
}
}
if (_flags.GameId == null || _flags.GameId == 0)
{
_flags.GameId = 0;
_flags.GameName = "Unknown Game";
_flags.GameMetadataSource = HasheousClient.Models.MetadataSources.None;
}
return _flags;
}
}
public SourceValues MetadataSources = new SourceValues();
public class SourceValues
{
public List<SourceValueItem> Platforms = new List<SourceValueItem>();
public List<SourceValueItem> Games = new List<SourceValueItem>();
public class SourceValueItem
{
public long Id { get; set; }
public string Name { get; set; }
public HasheousClient.Models.MetadataSources Source { get; set; }
}
public void AddPlatform(long Id, string Name, HasheousClient.Models.MetadataSources Source)
{
// check that the platform doesn't already exist
foreach (SourceValueItem item in Platforms)
{
if (item.Id == Id)
{
return;
}
}
SourceValueItem newItem = new SourceValueItem();
newItem.Id = Id;
newItem.Name = Name;
newItem.Source = Source;
Platforms.Add(newItem);
}
public void AddGame(long Id, string Name, HasheousClient.Models.MetadataSources Source)
{
// check that the game doesn't already exist
foreach (SourceValueItem item in Games)
{
if (item.Id == Id)
{
return;
}
}
SourceValueItem newItem = new SourceValueItem();
newItem.Id = Id;
newItem.Name = Name;
newItem.Source = Source;
Games.Add(newItem);
}
}
public class SignatureFlags
{
public long IGDBPlatformId { get; set; }
public string IGDBPlatformName { get; set; }
public long IGDBGameId { get; set; }
public long PlatformId { get; set; }
public string PlatformName { get; set; }
public long GameId { get; set; }
public string GameName { get; set; }
public HasheousClient.Models.MetadataSources PlatformMetadataSource { get; set; }
public HasheousClient.Models.MetadataSources GameMetadataSource { get; set; }
}
public class GameItem : HasheousClient.Models.SignatureModel.GameItem
@@ -50,6 +159,8 @@ namespace gaseous_server.Models
}
public string UserManual { get; set; }
[JsonIgnore]
public int Score
{
@@ -211,7 +322,8 @@ namespace gaseous_server.Models
break;
}
}
else {
else
{
switch (inType.ToLower())
{
case "disk":

View File

@@ -5,6 +5,13 @@ namespace gaseous_server.Models
public Guid UserId { get; set; }
public string DisplayName { get; set; }
public string Quip { get; set; }
public NowPlayingItem? NowPlaying { get; set; }
public class NowPlayingItem
{
public Game Game { get; set; }
public HasheousClient.Models.Metadata.IGDB.Platform Platform { get; set; }
public long Duration { get; set; }
}
public ProfileImageItem? Avatar { get; set; }
public ProfileImageItem? ProfileBackground { get; set; }
public Dictionary<string, object> Data { get; set; }

View File

@@ -338,18 +338,6 @@ namespace gaseous_server
break;
case QueueItemType.Rematcher:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Rematch");
Classes.ImportGame importRematch = new ImportGame
{
CallingQueueItem = this
};
importRematch.Rematcher(_ForceExecute);
_SaveLastRunTime = true;
break;
case QueueItemType.CollectionCompiler:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler");
Dictionary<string, object> collectionOptions = (Dictionary<string, object>)Options;
@@ -368,7 +356,8 @@ namespace gaseous_server
case QueueItemType.DailyMaintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Daily Maintenance");
Classes.Maintenance maintenance = new Maintenance{
Classes.Maintenance maintenance = new Maintenance
{
CallingQueueItem = this
};
maintenance.RunDailyMaintenance();
@@ -379,7 +368,8 @@ namespace gaseous_server
case QueueItemType.WeeklyMaintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Weekly Maintenance");
Classes.Maintenance weeklyMaintenance = new Maintenance{
Classes.Maintenance weeklyMaintenance = new Maintenance
{
CallingQueueItem = this
};
weeklyMaintenance.RunWeeklyMaintenance();
@@ -572,11 +562,6 @@ namespace gaseous_server
/// </summary>
LibraryScanWorker,
/// <summary>
/// Looks for roms in the library that have an unknown platform or game match
/// </summary>
Rematcher,
/// <summary>
/// Builds collections - set the options attribute to the id of the collection to build
/// </summary>

View File

@@ -11,6 +11,7 @@ using Authentication;
using Microsoft.AspNetCore.Identity;
using gaseous_server.Classes.Metadata;
using Asp.Versioning;
using HasheousClient.Models.Metadata.IGDB;
Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
@@ -48,8 +49,11 @@ Config.InitSettings();
// write updated settings back to the config file
Config.UpdateConfig();
// update default library path
GameLibrary.UpdateDefaultLibraryPath();
// set api metadata source from config
Communications.MetadataSource = Config.MetadataConfiguration.MetadataSource;
Communications.MetadataSource = Config.MetadataConfiguration.DefaultMetadataSource;
// set up hasheous client
HasheousClient.WebApp.HttpHelper.BaseUri = Config.MetadataConfiguration.HasheousHost;
@@ -326,15 +330,17 @@ app.Use(async (context, next) =>
// setup library directories
Config.LibraryConfiguration.InitLibrary();
// insert unknown platform and game if not present
gaseous_server.Classes.Metadata.Games.GetGame(0, false, false, false);
gaseous_server.Classes.Metadata.Games.AssignAllGamesToPlatformIdZero();
gaseous_server.Classes.Metadata.Platforms.GetPlatform(0);
gaseous_server.Classes.Metadata.Platforms.AssignAllPlatformsToGameIdZero();
// create unknown platform
Platforms.GetPlatform(0, HasheousClient.Models.MetadataSources.None);
Platforms.GetPlatform(0, HasheousClient.Models.MetadataSources.IGDB);
Platforms.GetPlatform(0, HasheousClient.Models.MetadataSources.TheGamesDb);
// extract platform map if not present
PlatformMapping.ExtractPlatformMap();
// migrate old firmware directory structure to new style
Bios.MigrateToNewFolderStructure();
// add background tasks
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.SignatureIngestor)
@@ -351,9 +357,6 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScan)
);
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Rematcher)
);
// maintenance tasks
ProcessQueue.QueueItem dailyMaintenance = new ProcessQueue.QueueItem(

View File

@@ -77,3 +77,404 @@ CREATE TABLE `User_GameFavouriteRoms` (
),
CONSTRAINT `GameFavouriteRoms_Users` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
);
ALTER TABLE `Games_Roms`
CHANGE `Path` `RelativePath` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL;
ALTER TABLE `Games_Roms`
ADD CONSTRAINT Games_Roms_LibraryId FOREIGN KEY (`LibraryId`) REFERENCES `GameLibraries` (`Id`) ON DELETE CASCADE;
CREATE VIEW view_Games_Roms AS
SELECT `Games_Roms`.*, CONCAT(
`GameLibraries`.`Path`, '/', `Games_Roms`.`RelativePath`
) AS `Path`, `GameLibraries`.`Name` AS `LibraryName`
FROM
`Games_Roms`
JOIN `GameLibraries` ON `Games_Roms`.`LibraryId` = `GameLibraries`.`Id`;
CREATE VIEW view_UserTimeTracking AS
SELECT *, DATE_ADD(
SessionTime, INTERVAL SessionLength MINUTE
) AS SessionEnd
FROM UserTimeTracking;
CREATE INDEX idx_game_name ON Game (`Name`);
CREATE INDEX idx_game_totalratingcount ON Game (TotalRatingCount);
CREATE INDEX idx_alternativename_game ON AlternativeName (Game);
CREATE INDEX idx_gamestate_romid ON GameState (RomId);
CREATE INDEX idx_gamestate_ismediagroup_userid ON GameState (IsMediaGroup, UserId);
CREATE INDEX idx_rommediagroup_gameid ON RomMediaGroup (GameId);
CREATE INDEX idx_favourites_userid_gameid ON Favourites (UserId, GameId);
CREATE TABLE `MetadataMap` (
`Id` bigint(20) NOT NULL AUTO_INCREMENT,
`PlatformId` bigint(20) NOT NULL,
`SignatureGameName` varchar(255) NOT NULL,
`UserManualLink` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `idx_gamename` (
`SignatureGameName`,
`PlatformId`
)
);
CREATE TABLE `MetadataMapBridge` (
`ParentMapId` bigint(20) NOT NULL,
`MetadataSourceType` int(11) NOT NULL DEFAULT 0,
`MetadataSourceId` bigint(20) NOT NULL,
`Preferred` BOOLEAN NOT NULL DEFAULT 0,
`ProcessedAtImport` BOOLEAN NOT NULL DEFAULT 0,
PRIMARY KEY (
`ParentMapId`,
`MetadataSourceType`,
`MetadataSourceId`
),
CONSTRAINT `MetadataMapBridge_MetadataMap` FOREIGN KEY (`ParentMapId`) REFERENCES `MetadataMap` (`Id`) ON DELETE CASCADE
);
CREATE OR REPLACE VIEW `view_MetadataMap` AS
SELECT `MetadataMap`.*, `MetadataMapBridge`.`MetadataSourceType`, `MetadataMapBridge`.`MetadataSourceId`
FROM
`MetadataMap`
LEFT JOIN `MetadataMapBridge` ON (
`MetadataMap`.`Id` = `MetadataMapBridge`.`ParentMapId`
AND `MetadataMapBridge`.`Preferred` = 1
);
ALTER TABLE `Games_Roms`
ADD COLUMN `MetadataMapId` BIGINT NOT NULL DEFAULT 0;
ALTER TABLE `Games_Roms`
ADD CONSTRAINT metadataMapId FOREIGN KEY (`MetadataMapId`) REFERENCES `MetadataMap` (`Id`) ON DELETE CASCADE;
ALTER TABLE `AgeGroup`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `AgeRating`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `AgeRatingContentDescription`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `AlternativeName`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Artwork`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Collection`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Company`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `CompanyLogo`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Cover`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `ExternalGame`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Franchise`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Game`
CHANGE `Id` `Id` bigint(20) NOT NULL AUTO_INCREMENT,
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP INDEX IF EXISTS `Id_UNIQUE`,
DROP INDEX IF EXISTS `PRIMARY`,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `GameMode`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `GameVideo`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Genre`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `InvolvedCompany`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `MultiplayerMode`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Platform`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP INDEX IF EXISTS `Id_UNIQUE`,
DROP INDEX IF EXISTS `PRIMARY`,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `PlatformLogo`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `PlatformVersion`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `PlayerPerspective`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `ReleaseDate`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Screenshot`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `Theme`
ADD COLUMN `SourceId` INT NOT NULL DEFAULT 1 AFTER `Id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`Id`, `SourceId`);
ALTER TABLE `ReleaseDate`
CHANGE `m` `Month` int(11) DEFAULT NULL,
CHANGE `y` `Year` int(11) DEFAULT NULL;
CREATE OR REPLACE VIEW `view_Games_Roms` AS
SELECT
`Games_Roms`.`Id` AS `Id`,
`Games_Roms`.`PlatformId` AS `PlatformId`,
`view_MetadataMap`.`Id` AS `MetadataMapId`,
`view_MetadataMap`.`MetadataSourceType` AS `GameIdType`,
`view_MetadataMap`.`MetadataSourceId` AS `GameId`,
`view_MetadataMap`.`UserManualLink` AS `UserManualLink`,
`Games_Roms`.`Name` AS `Name`,
`Games_Roms`.`Size` AS `Size`,
`Games_Roms`.`CRC` AS `CRC`,
`Games_Roms`.`MD5` AS `MD5`,
`Games_Roms`.`SHA1` AS `SHA1`,
`Games_Roms`.`DevelopmentStatus` AS `DevelopmentStatus`,
`Games_Roms`.`Flags` AS `Flags`,
`Games_Roms`.`Attributes` AS `Attributes`,
`Games_Roms`.`RomType` AS `RomType`,
`Games_Roms`.`RomTypeMedia` AS `RomTypeMedia`,
`Games_Roms`.`MediaLabel` AS `MediaLabel`,
`Games_Roms`.`RelativePath` AS `RelativePath`,
`Games_Roms`.`MetadataSource` AS `MetadataSource`,
`Games_Roms`.`MetadataGameName` AS `MetadataGameName`,
`Games_Roms`.`MetadataVersion` AS `MetadataVersion`,
`Games_Roms`.`LibraryId` AS `LibraryId`,
`Games_Roms`.`LastMatchAttemptDate` AS `LastMatchAttemptDate`,
`Games_Roms`.`RomDataVersion` AS `RomDataVersion`,
CONCAT(
`GameLibraries`.`Path`,
'/',
`Games_Roms`.`RelativePath`
) AS `Path`,
`GameLibraries`.`Name` AS `LibraryName`
FROM (
`Games_Roms`
JOIN `GameLibraries` ON (
`Games_Roms`.`LibraryId` = `GameLibraries`.`Id`
)
LEFT JOIN `view_MetadataMap` ON (
`Games_Roms`.`MetadataMapId` = `view_MetadataMap`.`Id`
)
);
CREATE OR REPLACE VIEW `view_GamesWithRoms` AS
SELECT DISTINCT
`Games_Roms`.`GameId` AS `ROMGameId`,
`view_MetadataMap`.`Id` AS `MetadataMapId`,
`view_MetadataMap`.`MetadataSourceType` AS `GameIdType`,
CASE
WHEN `Game`.`Id` IS NULL THEN 0
ELSE `Game`.`Id`
END AS `Id`,
`Game`.`AgeRatings` AS `AgeRatings`,
`Game`.`AggregatedRating` AS `AggregatedRating`,
`Game`.`AggregatedRatingCount` AS `AggregatedRatingCount`,
`Game`.`AlternativeNames` AS `AlternativeNames`,
`Game`.`Artworks` AS `Artworks`,
`Game`.`Bundles` AS `Bundles`,
`Game`.`Category` AS `Category`,
`Game`.`Checksum` AS `Checksum`,
`Game`.`Collection` AS `Collection`,
`Game`.`Cover` AS `Cover`,
`Game`.`CreatedAt` AS `CreatedAt`,
`Game`.`Dlcs` AS `Dlcs`,
`Game`.`Expansions` AS `Expansions`,
`Game`.`ExternalGames` AS `ExternalGames`,
`Game`.`FirstReleaseDate` AS `FirstReleaseDate`,
`Game`.`Follows` AS `Follows`,
`Game`.`Franchise` AS `Franchise`,
`Game`.`Franchises` AS `Franchises`,
`Game`.`GameEngines` AS `GameEngines`,
`Game`.`GameModes` AS `GameModes`,
`Game`.`Genres` AS `Genres`,
`Game`.`Hypes` AS `Hypes`,
`Game`.`InvolvedCompanies` AS `InvolvedCompanies`,
`Game`.`Keywords` AS `Keywords`,
`Game`.`MultiplayerModes` AS `MultiplayerModes`,
CASE
WHEN `Game`.`Name` IS NULL THEN `view_MetadataMap`.`SignatureGameName`
ELSE `Game`.`Name`
END AS `Name`,
CASE
WHEN `Game`.`Name` IS NULL THEN CASE
WHEN `view_MetadataMap`.`SignatureGameName` LIKE 'The %' THEN CONCAT(
TRIM(
SUBSTR(
`view_MetadataMap`.`SignatureGameName`,
4
)
),
', The'
)
ELSE `view_MetadataMap`.`SignatureGameName`
END
WHEN `Game`.`Name` LIKE 'The %' THEN CONCAT(
TRIM(SUBSTR(`Game`.`Name`, 4)),
', The'
)
ELSE `Game`.`Name`
END AS `NameThe`,
`Game`.`ParentGame` AS `ParentGame`,
`Game`.`Platforms` AS `Platforms`,
`Game`.`PlayerPerspectives` AS `PlayerPerspectives`,
`Game`.`Rating` AS `Rating`,
`Game`.`RatingCount` AS `RatingCount`,
`Game`.`ReleaseDates` AS `ReleaseDates`,
`Game`.`Screenshots` AS `Screenshots`,
`Game`.`SimilarGames` AS `SimilarGames`,
`Game`.`Slug` AS `Slug`,
`Game`.`StandaloneExpansions` AS `StandaloneExpansions`,
`Game`.`Status` AS `Status`,
`Game`.`StoryLine` AS `StoryLine`,
`Game`.`Summary` AS `Summary`,
`Game`.`Tags` AS `Tags`,
`Game`.`Themes` AS `Themes`,
`Game`.`TotalRating` AS `TotalRating`,
`Game`.`TotalRatingCount` AS `TotalRatingCount`,
`Game`.`UpdatedAt` AS `UpdatedAt`,
`Game`.`Url` AS `Url`,
`Game`.`VersionParent` AS `VersionParent`,
`Game`.`VersionTitle` AS `VersionTitle`,
`Game`.`Videos` AS `Videos`,
`Game`.`Websites` AS `Websites`,
`Game`.`dateAdded` AS `dateAdded`,
`Game`.`lastUpdated` AS `lastUpdated`
FROM (
`Games_Roms`
JOIN `view_MetadataMap` ON (
`view_MetadataMap`.`Id` = `Games_Roms`.`MetadataMapId`
)
LEFT JOIN `Game` ON (
`Game`.`SourceId` = `view_MetadataMap`.`MetadataSourceType`
AND `Game`.`Id` = `view_MetadataMap`.`MetadataSourceId`
)
);
CREATE OR REPLACE VIEW `view_Games` AS
SELECT
`a`.`ROMGameId` AS `ROMGameId`,
`a`.`Id` AS `Id`,
`a`.`GameIdType` AS `GameIdType`,
`a`.`AgeRatings` AS `AgeRatings`,
`a`.`AggregatedRating` AS `AggregatedRating`,
`a`.`AggregatedRatingCount` AS `AggregatedRatingCount`,
`a`.`AlternativeNames` AS `AlternativeNames`,
`a`.`Artworks` AS `Artworks`,
`a`.`Bundles` AS `Bundles`,
`a`.`Category` AS `Category`,
`a`.`Checksum` AS `Checksum`,
`a`.`Collection` AS `Collection`,
`a`.`Cover` AS `Cover`,
`a`.`CreatedAt` AS `CreatedAt`,
`a`.`Dlcs` AS `Dlcs`,
`a`.`Expansions` AS `Expansions`,
`a`.`ExternalGames` AS `ExternalGames`,
`a`.`FirstReleaseDate` AS `FirstReleaseDate`,
`a`.`Follows` AS `Follows`,
`a`.`Franchise` AS `Franchise`,
`a`.`Franchises` AS `Franchises`,
`a`.`GameEngines` AS `GameEngines`,
`a`.`GameModes` AS `GameModes`,
`a`.`Genres` AS `Genres`,
`a`.`Hypes` AS `Hypes`,
`a`.`InvolvedCompanies` AS `InvolvedCompanies`,
`a`.`Keywords` AS `Keywords`,
`a`.`MultiplayerModes` AS `MultiplayerModes`,
`a`.`Name` AS `Name`,
`a`.`ParentGame` AS `ParentGame`,
`a`.`Platforms` AS `Platforms`,
`a`.`PlayerPerspectives` AS `PlayerPerspectives`,
`a`.`Rating` AS `Rating`,
`a`.`RatingCount` AS `RatingCount`,
`a`.`ReleaseDates` AS `ReleaseDates`,
`a`.`Screenshots` AS `Screenshots`,
`a`.`SimilarGames` AS `SimilarGames`,
`a`.`Slug` AS `Slug`,
`a`.`StandaloneExpansions` AS `StandaloneExpansions`,
`a`.`Status` AS `Status`,
`a`.`StoryLine` AS `StoryLine`,
`a`.`Summary` AS `Summary`,
`a`.`Tags` AS `Tags`,
`a`.`Themes` AS `Themes`,
`a`.`TotalRating` AS `TotalRating`,
`a`.`TotalRatingCount` AS `TotalRatingCount`,
`a`.`UpdatedAt` AS `UpdatedAt`,
`a`.`Url` AS `Url`,
`a`.`VersionParent` AS `VersionParent`,
`a`.`VersionTitle` AS `VersionTitle`,
`a`.`Videos` AS `Videos`,
`a`.`Websites` AS `Websites`,
`a`.`dateAdded` AS `dateAdded`,
`a`.`lastUpdated` AS `lastUpdated`,
`a`.`NameThe` AS `NameThe`,
`b`.`AgeGroupId` AS `AgeGroupId`
FROM (
`view_GamesWithRoms` `a`
LEFT JOIN `AgeGroup` `b` ON (`b`.`GameId` = `a`.`Id`)
)
ORDER BY `a`.`NameThe`;

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 1024 1024" class="icon" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M403.2 803.2c-0.8 0-8.8 64-9.6 65.6-1.6 13.6-4 21.6-11.2 28-2.4 2.4-4.8 3.2-7.2 4.8-5.6 4-10.4 6.4-16.8 10.4-4.8 2.4-10.4 12-4.8 14.4h316.8c5.6-2.4 0-11.2-4.8-13.6-6.4-4-11.2-7.2-16.8-12-2.4-1.6-4.8-4-7.2-6.4-6.4-6.4-9.6-12-11.2-25.6 0-1.6-9.6-71.2-9.6-65.6" fill="#D2D5D6" /><path d="M992 704.8V124c0-14.4-11.2-26.4-25.6-26.4H57.6c-14.4 0-25.6 12-25.6 26.4v581.6" fill="#938993" /><path d="M84 149.6h856v503.2h-856z" fill="#E2D3E2" /><path d="M376 137.6l576 528v-528z" fill="#FAFBFA" /><path d="M32 704v72.8c0 14.4 11.2 26.4 25.6 26.4h908c14.4 0 25.6-12 25.6-26.4V704" fill="#D2D5D6" /><path d="M511.2 754.4m-24 0a24 24 0 1 0 48 0 24 24 0 1 0-48 0Z" fill="#414343" /><path d="M623.2 827.2c-0.8-24-1.6-24-1.6-24H404s-0.8 0-1.6 24h220.8z" fill="#0D1014" /><path d="M449.6 568.8l-1.6 4z" fill="#99D9E6" /><path d="M353.6 934.4c-0.8 0-2.4 0-3.2-0.8-4-1.6-7.2-5.6-7.2-10.4 0-8 6.4-16 11.2-18.4l3.2-1.6c4.8-2.4 8.8-4.8 12-8 1.6-0.8 2.4-1.6 4-2.4 0.8-0.8 1.6-0.8 2.4-1.6 4.8-4 6.4-10.4 8-22.4 8-67.2 8-67.2 16-67.2 4.8 0 8 3.2 8 8v2.4c-0.8 5.6-7.2 54.4-8 58.4-1.6 14.4-4.8 24.8-13.6 32.8-1.6 1.6-4 3.2-5.6 4-0.8 0.8-1.6 0.8-2.4 1.6-4.8 4-9.6 6.4-14.4 8.8l-3.2 1.6H656c-5.6-3.2-10.4-7.2-15.2-10.4-1.6-1.6-3.2-3.2-5.6-4.8l-2.4-1.6c-8.8-8-12-16.8-13.6-30.4-0.8-4-6.4-48.8-8-58.4 0-0.8-0.8-1.6-0.8-3.2 0-4 3.2-8 7.2-8.8 8.8-0.8 8.8-0.8 16.8 67.2 1.6 12.8 4 16.8 8 20.8l2.4 2.4c1.6 1.6 3.2 2.4 4 4 5.6 4 9.6 7.2 16 11.2 4.8 2.4 12 10.4 11.2 17.6 0 4.8-3.2 8-7.2 10.4-0.8 0.8-2.4 0.8-3.2 0.8l-312-1.6zM992 712.8c-4.8 0-8-3.2-8-8V124c0-10.4-8-18.4-17.6-18.4H57.6c-9.6 0-17.6 8-17.6 18.4v581.6c0 4.8-3.2 8-8 8s-8-3.2-8-8V124c0-19.2 15.2-34.4 33.6-34.4h908c18.4 0 33.6 15.2 33.6 34.4v581.6c0.8 4-2.4 7.2-7.2 7.2z" fill="#6A576D" /><path d="M940 660.8h-856c-4.8 0-8-3.2-8-8V149.6c0-4.8 3.2-8 8-8h856c4.8 0 8 3.2 8 8v503.2c0 4.8-4 8-8 8z m-848-16h840V157.6h-840v487.2zM966.4 811.2H57.6c-18.4 0-33.6-15.2-33.6-34.4V704c0-4.8 3.2-8 8-8h960c4.8 0 8 3.2 8 8v72.8c0 19.2-15.2 34.4-33.6 34.4zM40 712v64.8c0 10.4 8 18.4 17.6 18.4h908c9.6 0 17.6-8 17.6-18.4V712H40z" fill="#6A576D" /></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -18,9 +18,9 @@
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="gaseous-signature-parser" Version="2.2.1" />
<PackageReference Include="gaseous-signature-parser" Version="2.3.3" />
<PackageReference Include="gaseous.IGDB" Version="1.0.2" />
<PackageReference Include="hasheous-client" Version="1.0.2" />
<PackageReference Include="hasheous-client" Version="1.2.4" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.8.0" />
<PackageReference Include="sharpcompress" Version="0.37.2" />
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.2.24" />
@@ -92,6 +92,7 @@
</EmbeddedResource>
<EmbeddedResource Include="Support\Country.txt" />
<EmbeddedResource Include="Support\Language.txt" />
<EmbeddedResource Include="Support\DefaultPlatformLogo.svg" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1000.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />

View File

@@ -43,8 +43,6 @@
EJS_threads = false;
EJS_Buttons = {
saveSavFiles: false,
loadSavFiles: false,
exitEmulation: false
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by Pixelmator Pro 3.6.14 -->
<svg width="800" height="800" viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg">
<path id="Path" fill="none" stroke="#000000" stroke-width="33.333332" stroke-linejoin="round" d="M 400 230.302979 C 363.330017 183.630981 306.802002 136.958984 166.706345 133.533997 C 157.504333 133.309326 150 140.78363 150 149.988342 C 150 218.270996 150 476.779999 150 553.233337 C 150 562.436646 157.505005 569.666687 166.704666 569.966675 C 306.801666 574.546692 363.330017 636.660034 400 683.333313 M 400 230.302979 C 436.669983 183.630981 493.196686 136.958984 633.293335 133.533997 C 642.496704 133.309326 650 140.615662 650 149.820313 C 650 226.148987 650 476.880005 650 553.210022 C 650 562.41333 642.496704 569.666687 633.296631 569.966675 C 493.199982 574.546692 436.669983 636.660034 400 683.333313 M 400 230.302979 L 400 683.333313"/>
<path id="path1" fill="none" stroke="#000000" stroke-width="33.333332" stroke-linejoin="round" d="M 641.176697 200 L 716.666687 200 C 725.869995 200 733.333313 207.461975 733.333313 216.666687 L 733.333313 651.299988 C 733.333313 664.786621 717.443359 673.746704 705.116638 668.27002 C 678.613342 656.493286 634.383301 642.106689 576.469971 642.106689 C 478.429993 642.106689 400 700 400 700 C 400 700 321.568665 642.106689 223.529343 642.106689 C 165.615005 642.106689 121.385338 656.493286 94.882339 668.27002 C 82.556 673.746704 66.666664 664.786621 66.666664 651.299988 L 66.666664 216.666687 C 66.666664 207.461975 74.12867 200 83.333336 200 L 158.823669 200"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -3,7 +3,11 @@
<head>
<meta charset="utf-8" />
<script src="/api/v1.1/System/VersionFile"></script>
<script type="text/javascript" src="/scripts/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="/scripts/jquery.lazy.min.js"></script>
<script type="text/javascript" src="/scripts/jquery.lazy.plugins.min.js"></script>
<script type="text/javascript" src="/scripts/moment-with-locales.min.js"></script>
<script type="text/javascript" src="/scripts/select2.min.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
@@ -21,47 +25,52 @@
];
let scriptLinks = [
"/scripts/jquery-3.6.0.min.js",
"/scripts/jquery.lazy.min.js",
"/scripts/jquery.lazy.plugins.min.js",
"/scripts/moment-with-locales.min.js",
"/scripts/select2.min.js",
"/scripts/filterformating.js",
"/scripts/gamesformating.js",
"/scripts/main.js",
"/scripts/modals.js",
"/scripts/preferences.js",
"/scripts/account.js",
"/scripts/libraries.js",
"/scripts/notifications.js",
"/scripts/rominfo.js",
"/scripts/uploadrom.js",
"/scripts/filterformating.js",
"/scripts/gamesformating.js",
"/scripts/main.js"
"/scripts/uploadrom.js"
];
let head = document.getElementsByTagName('head')[0];
// placeholder for global userProfile variable
var userProfile;
</script>
</head>
// update script links
for (let i = 0; i < scriptLinks.length; i++) {
let newScript = document.createElement('script');
newScript.src = scriptLinks[i] + '?v=' + AppVersion;
newScript.type = "text/javascript";
newScript.async = false;
<body>
<!-- Background Images -->
<div id="bgImages"></div>
<div id="bgImage_Opacity"></div>
head.appendChild(newScript);
}
<!-- Notifications -->
<div id="notifications_target"></div>
// update stylesheet links
for (let i = 0; i < styleSheets.length; i++) {
let newLink = document.createElement('link');
newLink.rel = "stylesheet";
newLink.href = styleSheets[i] + '?v=' + AppVersion;
newLink.type = "text/css";
<!-- Page Banner -->
<div id="banner_target"></div>
head.appendChild(newLink);
}
<!-- Page Content -->
<div id="content"></div>
<script type="text/javascript">
// Gaseous Games - Main Application Script
// start the application
let AppVersion = "0.0.0";
let DBSchemaVersion = "1000";
let FirstRunStatus = "0";
let AgeRatingBoardStrings = {};
let AgeRatingStrings = {};
let AgeRatingGroups = {};
let emulatorDebugMode = "false";
let backgroundImageHandler = undefined;
async function LoadPageContent(page, targetDiv) {
if (targetDiv == undefined || targetDiv == null || targetDiv == '') {
@@ -83,40 +92,67 @@
backgroundImageHandler = new BackgroundImageRotator();
}
</script>
</head>
<body>
<!-- Background Images -->
<div id="bgImages"></div>
<div id="bgImage_Opacity"></div>
async function startApp() {
// load /api/v1.1/System/Version
await fetch('/api/v1.1/System/Version')
.then(async response => {
if (response.ok) {
// get version information
let versionFile = await response.json();
AppVersion = versionFile.AppVersion;
DBSchemaVersion = versionFile.DBSchemaVersion;
FirstRunStatus = versionFile.FirstRunStatus;
AgeRatingBoardStrings = versionFile.AgeRatingBoardStrings;
AgeRatingStrings = versionFile.AgeRatingStrings;
AgeRatingGroups = versionFile.AgeRatingGroups;
emulatorDebugMode = versionFile.emulatorDebugMode;
<!-- Notifications -->
<div id="notifications_target"></div>
// load scripts and style files
// update script links
for (let i = 0; i < scriptLinks.length; i++) {
let newScript = document.createElement('script');
newScript.src = scriptLinks[i] + '?v=' + AppVersion;
newScript.type = "text/javascript";
newScript.async = false;
<!-- Page Banner -->
<div id="banner_target"></div>
head.appendChild(newScript);
}
<!-- Page Content -->
<div id="content"></div>
// update stylesheet links
for (let i = 0; i < styleSheets.length; i++) {
let newLink = document.createElement('link');
newLink.rel = "stylesheet";
newLink.href = styleSheets[i] + '?v=' + AppVersion;
newLink.type = "text/css";
head.appendChild(newLink);
}
// wait for all scripts to load
do {
if (typeof getQueryString === "function") {
break;
}
console.log("Waiting for scripts to load...");
await new Promise(r => setTimeout(r, 100));
} while (typeof getQueryString !== "function");
<script type="text/javascript">
// start the application
let backgroundImageHandler = undefined;
console.log("Starting Gaseous Games");
console.log("Starting Gaseous");
console.log("App Version: " + AppVersion);
console.log("First Run Status: " + FirstRunStatus);
switch (FirstRunStatus) {
case 0:
case "0":
// first run - load first run wizard
LoadPageContent('first', 'content');
await LoadPageContent('first', 'content');
break;
default:
// first run - load login page or redirect if user already logged in
fetch('/api/v1.1/Account/Profile/Basic')
await fetch('/api/v1.1/Account/Profile/Basic')
.then(async response => {
if (response.ok) {
// user is signed in - start setting up the application
@@ -124,25 +160,34 @@
userProfile = await response.json();
// load page banner
LoadPageContent('banner', 'banner_target');
await LoadPageContent('banner', 'banner_target');
// load page content
let pageSelection = getQueryString('page', 'string');
if (!pageSelection) {
pageSelection = GetPreference("DefaultHomePage", 'home');
pageSelection = GetPreference("DefaultHomePage", "home");
}
LoadPageContent(pageSelection, 'content');
console.log("Loading page: " + pageSelection);
await LoadPageContent(pageSelection, 'content');
} else {
// user is not signed in - load login page
LoadPageContent('login');
await LoadPageContent('login');
}
})
.catch((error) => {
.catch(async (error) => {
console.log(error);
LoadPageContent('login');
await LoadPageContent('login');
});
break;
}
}
})
.catch(async (error) => {
console.log(error);
});
}
window.document.addEventListener('DOMContentLoaded', startApp);
</script>
</body>

View File

@@ -44,7 +44,7 @@
</div>
</div>
<div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous Games</div>
<div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous</div>
</div>
<!-- page code -->

View File

@@ -29,7 +29,7 @@ function SetupPage() {
gameData = result;
if (result.cover) {
emuBackground = '/api/v1.1/Games/' + gameId + '/cover/' + result.cover.id + '/image/original/' + result.cover.imageId + '.jpg';
emuBackground = '/api/v1.1/Games/' + gameId + '/cover/' + result.cover + '/image/original/' + result.cover + '.jpg';
}
emuGameTitle = gameData.name;

View File

@@ -8,7 +8,7 @@
<!-- <img src="/images/logo.png" style="display: block; margin: 20px auto; width: 100px;" /> -->
<div class="loginwindow-logospace-logo">&nbsp;</div>
<div id="loginwindow_header_label" class="loginwindow-logospace-label">Gaseous Games</div>
<div id="loginwindow_header_label" class="loginwindow-logospace-label">Gaseous</div>
<div style="height: 20px;">&nbsp;</div>
@@ -29,7 +29,7 @@
<div class="loginwindow-logospace-logo">&nbsp;</div>
<div class="loginwindow-logospace-label">
<div>
Gaseous Games
Gaseous
</div>
</div>
</div>

View File

@@ -104,6 +104,20 @@
<div class="section-header">Similar Games</div>
<div id="gamesummarysimilarcontent" class="section-body"></div>
</div>
<div id="gamesmetadataprovider" class="section" style="display: none;">
<div id="gamesmetadataprovidercontent" class="section-body">
<table style="width: 100%;">
<tr>
<td style="width: 40px;">
<img src="/images/IGDB_logo.svg" class="metadata-attribution-icon"
id="metadata-attribution-icon" />
</td>
<td>
<span id="metadata-attribution-text">Game data provided by IGDB</span>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>

View File

@@ -12,28 +12,47 @@ function SetupPage() {
mappingScript.src = '/pages/settings/mapping.js';
document.head.appendChild(mappingScript);
ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) {
let nonce = (Math.random() + 1).toString(36).substring(7);
ajaxCall('/api/v1.1/Games/' + gameId + '?nonce=' + nonce, 'GET', function (result) {
// populate games page
gameData = result;
console.log(gameData);
switch (gameData.metadataSource) {
case "IGDB":
let attributionSection = document.getElementById('gamesmetadataprovider');
attributionSection.style.display = 'block';
let attributionIcon = document.getElementById('metadata-attribution-icon');
attributionIcon.setAttribute('src', '/images/IGDB_Logo.svg');
let attributionText = document.getElementById('metadata-attribution-text');
attributionText.innerHTML = 'This game\'s metadata is provided by IGDB. <a href="https://www.igdb.com/games/' + gameData.slug + '" class="romlink" target="_blank" rel="noopener noreferrer">Source Page</a>';
break;
default:
break;
}
// get name
var gameTitleLabel = document.getElementById('gametitle_label');
gameTitleLabel.innerHTML = result.name;
// get critic rating
if (gameData.totalRating) {
if (gameData.total_rating) {
var criticscoreval = document.getElementById('gametitle_criticrating_value');
criticscoreval.innerHTML = Math.floor(gameData.totalRating) + '%';
criticscoreval.innerHTML = Math.floor(gameData.total_rating) + '%';
if (gameData.totalRatingCount) {
if (gameData.total_rating_count) {
var criticscorelabel = document.getElementById('gametitle_criticrating_label');
criticscorelabel.innerHTML = '<img src="/images/IGDB_logo.svg" style="filter: invert(100%); height: 13px; margin-bottom: -5px;" /><span style="font-size: 10px;"> User Rating<br />' + "based on " + gameData.totalRatingCount + " votes</span>"
criticscorelabel.innerHTML = '<span style="font-size: 10px;"> User rating<br />based on ' + gameData.total_rating_count + ' votes</span>'
}
}
// get alt name
var gameTitleAltLabel = document.getElementById('gametitle_alts');
if (result.alternativeNames) {
if (result.alternative_names) {
ajaxCall('/api/v1.1/Games/' + gameId + '/alternativename', 'GET', function (result) {
var altNames = '';
for (var i = 0; i < result.length; i++) {
@@ -50,8 +69,9 @@ function SetupPage() {
}
// get summary
var gameSummaryLabel = document.getElementById('gamesummarytext_label');
let gameSummaryBox = document.getElementById('gamesummarytext');
if (result.summary || result.storyline) {
let gameSummaryLabel = document.getElementById('gamesummarytext_label');
if (result.summary) {
gameSummaryLabel.innerHTML = result.summary.replaceAll("\n", "<br />");
} else {
@@ -67,7 +87,7 @@ function SetupPage() {
// your element doesn't overflow (not truncated)
}
} else {
gameSummaryLabel.setAttribute('style', 'display: none;');
gameSummaryBox.setAttribute('style', 'display: none;');
}
// load cover
@@ -103,7 +123,7 @@ function SetupPage() {
var gamePublisherContent = document.getElementById('gamesummary_publishers_content');
var gameDeveloperLoaded = false;
var gamePublisherLoaded = false;
if (result.involvedCompanies) {
if (result.involved_companies) {
ajaxCall('/api/v1.1/Games/' + gameId + '/companies', 'GET', function (result) {
var lstDevelopers = [];
var lstPublishers = [];
@@ -199,9 +219,9 @@ function SetupPage() {
// load release date
var gameSummaryRelease = document.getElementById('gamesummary_firstrelease');
var gameSummaryReleaseContent = document.getElementById('gamesummary_firstrelease_content');
if (result.firstReleaseDate) {
if (result.first_release_date) {
var firstRelease = document.createElement('span');
firstRelease.innerHTML = moment(result.firstReleaseDate).format('LL') + ' (' + moment(result.firstReleaseDate).fromNow() + ')';
firstRelease.innerHTML = moment(result.first_release_date).format('LL') + ' (' + moment(result.first_release_date).fromNow() + ')';
gameSummaryReleaseContent.appendChild(firstRelease);
} else {
gameSummaryRelease.setAttribute('style', 'display: none;');
@@ -210,7 +230,7 @@ function SetupPage() {
// load ratings
let gameSummaryRatings = document.getElementById('gamesummary_ratings');
let gameSummaryRatingsContent = document.getElementById('gamesummary_ratings_content');
if (result.ageRatings) {
if (result.age_ratings) {
ajaxCall('/api/v1.1/Games/' + gameId + '/agerating', 'GET', function (result) {
let classTable = document.createElement('table');
@@ -305,7 +325,7 @@ function SetupPage() {
var gameScreenshots_Gallery = document.getElementById('gamescreenshots_gallery_panel');
var imageIndex = 0;
if (result.videos) {
imageIndex = result.videos.ids.length;
imageIndex = result.videos.length;
}
if (result.screenshots) {
ajaxCall('/api/v1.1/Games/' + gameId + '/screenshots', 'GET', function (screenshotsItem) {
@@ -335,10 +355,10 @@ function SetupPage() {
var vScreenshotItem = document.createElement('li');
vScreenshotItem.id = 'gamescreenshots_gallery_' + i;
vScreenshotItem.setAttribute('name', 'gamescreenshots_gallery_item');
vScreenshotItem.setAttribute('style', 'background-image: url("https://i.ytimg.com/vi/' + result[i].videoId + '/hqdefault.jpg"); background-position: center; background-repeat: no-repeat; background-size: contain;)');
vScreenshotItem.setAttribute('style', 'background-image: url("https://i.ytimg.com/vi/' + result[i].video_id + '/hqdefault.jpg"); background-position: center; background-repeat: no-repeat; background-size: contain;)');
vScreenshotItem.setAttribute('imageid', i);
vScreenshotItem.setAttribute('imagetype', 1);
vScreenshotItem.setAttribute('imageref', result[i].videoId);
vScreenshotItem.setAttribute('imageref', result[i].video_id);
vScreenshotItem.className = 'gamescreenshots_gallery_item';
vScreenshotItem.setAttribute('onclick', 'selectScreenshot(' + i + ');');
@@ -388,7 +408,7 @@ function SetupPage() {
var gameSummarySimilarContent = document.getElementById('gamesummarysimilarcontent');
for (var i = 0; i < result.games.length; i++) {
var similarObject = renderGameIcon(result.games[i], true, true, true, GetRatingsBoards(), false, true);
var similarObject = renderGameIcon(result.games[i], true, true, true, GetRatingsBoards(), false, true, false);
gameSummarySimilarContent.appendChild(similarObject);
}
@@ -406,11 +426,11 @@ function SetupPage() {
function LoadGamePlatforms() {
// get platforms
ajaxCall('/api/v1.1/Games/' + gameId + '/platforms', 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/platforms', 'GET', async function (result) {
let platformContainer = document.getElementById('gamesummaryplatformscontent');
platformContainer.innerHTML = '';
for (let i = 0; i < result.length; i++) {
let logoUrl = '/api/v1/Platforms/' + result[i].id + '/platformlogo/original/logo.png';
let logoUrl = '/api/v1.1/Platforms/' + result[i].id + '/platformlogo/original/';
// create platform container
let platformItem = document.createElement('div');
@@ -429,6 +449,7 @@ function LoadGamePlatforms() {
// 1. if FavouriteRomId is not null, load the rom, otherwise
// 2. if LastPlayedRomId is null, load the rom, otherwise
// 3. load the rom management dialog
console.log(result[i]);
if (result[i].emulatorConfiguration.emulatorType.length > 0 && result[i].emulatorConfiguration.core.length > 0 && result[i].favouriteRomId) {
showSaveState = true;
romId = result[i].favouriteRomId;
@@ -437,7 +458,8 @@ function LoadGamePlatforms() {
platformItem.setAttribute('isFavourite', true);
platformItem.classList.add('platform_item_green');
let launchLink = BuildLaunchLink(platformData.emulatorConfiguration.emulatorType, platformData.emulatorConfiguration.core, platformData.id, Number(gameId), platformData.favouriteRomId, platformData.favouriteRomIsMediagroup, platformData.favouriteRomName);
let launchLink = await BuildLaunchLink(platformData.emulatorConfiguration.emulatorType, platformData.emulatorConfiguration.core, platformData.id, Number(gameId), platformData.favouriteRomId, platformData.favouriteRomIsMediagroup, platformData.favouriteRomName);
console.log(launchLink);
platformItem.addEventListener('click', () => {
window.location.href = launchLink;
@@ -450,7 +472,8 @@ function LoadGamePlatforms() {
platformItem.setAttribute('isLastUsed', true);
platformItem.classList.add('platform_item_green');
let launchLink = BuildLaunchLink(platformData.emulatorConfiguration.emulatorType, platformData.emulatorConfiguration.core, platformData.id, Number(gameId), platformData.lastPlayedRomId, platformData.lastPlayedRomIsMediagroup, platformData.lastPlayedRomName);
let launchLink = await BuildLaunchLink(platformData.emulatorConfiguration.emulatorType, platformData.emulatorConfiguration.core, platformData.id, Number(gameId), platformData.lastPlayedRomId, platformData.lastPlayedRomIsMediagroup, platformData.lastPlayedRomName);
console.log(launchLink);
platformItem.addEventListener('click', () => {
window.location.href = launchLink;
@@ -485,6 +508,16 @@ function LoadGamePlatforms() {
let platformEditButtonContainer = document.createElement('div');
platformEditButtonContainer.className = 'platform_edit_button_container';
// create user manual button
let userManualButton = document.createElement('div');
userManualButton.className = 'platform_edit_button';
userManualButton.innerHTML = '<img src="/images/manual.svg" class="banner_button_image" />';
userManualButton.addEventListener('click', (e) => {
e.stopPropagation();
let guideUrl = window.open(result[i].userManualLink, '_blank');
guideUrl.opener = null;
});
// create platform state manager button
let platformStateManagerButton = document.createElement('div');
if (showSaveState === true) {
@@ -514,10 +547,15 @@ function LoadGamePlatforms() {
platformNameContainer.appendChild(platformName);
platformItem.appendChild(platformNameContainer);
platformItem.appendChild(platformEditButtonContainer);
platformItem.appendChild(platformEditButton);
if (showSaveState === true) {
platformItem.appendChild(platformStateManagerButton);
platformEditButtonContainer.appendChild(platformStateManagerButton);
}
if (result[i].userManualLink) {
platformEditButtonContainer.appendChild(userManualButton);
}
platformEditButtonContainer.appendChild(platformEditButton);
platformContainer.appendChild(platformItem);
}
});
@@ -575,7 +613,135 @@ class RomManagement {
this.#SetupFixPlatformDropDown();
// add buttons
let platformEditButton = new ModalButton('Edit Platform', 0, this, async function (callingObject) {
if (userProfile.roles.includes("Admin")) {
let platformMappingButton = new ModalButton('Metadata Mapping', 0, this, async function (callingObject) {
let metadataModal = await new Modal('messagebox');
await metadataModal.BuildModal();
// override the dialog size
metadataModal.modalElement.style = 'width: 600px; height: 400px; min-width: unset; min-height: 400px; max-width: unset; max-height: 400px;';
// set the title
metadataModal.modalElement.querySelector('#modal-header-text').innerHTML = callingObject.Platform.name + ' Metadata Mapping';
// set the content
let metadataContent = metadataModal.modalElement.querySelector('#modal-body');
// fetch the metadata map
let metadataMap = await fetch('/api/v1.1/Games/' + callingObject.Platform.metadataMapId + '/metadata', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}).then(response => response.json());
console.log(metadataMap);
metadataMap.metadataMapItems.forEach(element => {
let itemSection = document.createElement('div');
itemSection.className = 'section';
// header
let itemSectionHeader = document.createElement('div');
itemSectionHeader.className = 'section-header';
let itemSectionHeaderRadio = document.createElement('input');
itemSectionHeaderRadio.id = 'platformMappingSource_' + element.sourceType;
itemSectionHeaderRadio.type = 'radio';
itemSectionHeaderRadio.name = 'platformMappingSource';
itemSectionHeaderRadio.value = element.sourceType;
itemSectionHeaderRadio.style.margin = '0px';
itemSectionHeaderRadio.style.height = 'unset';
itemSectionHeaderRadio.addEventListener('change', () => {
metadataMap.metadataMapItems.forEach(element => {
element.preferred = false;
});
element.preferred = true;
console.log('Selected: ' + element.sourceType);
console.log(metadataMap);
});
if (element.preferred == true) {
itemSectionHeaderRadio.checked = true;
}
itemSectionHeader.appendChild(itemSectionHeaderRadio);
let itemSectionHeaderLabel = document.createElement('label');
itemSectionHeaderLabel.htmlFor = 'platformMappingSource_' + element.sourceType;
itemSectionHeaderLabel.style.marginLeft = '10px';
itemSectionHeaderLabel.innerHTML = element.sourceType;
itemSectionHeader.appendChild(itemSectionHeaderLabel);
itemSection.appendChild(itemSectionHeader);
// content
let itemSectionContent = document.createElement('div');
itemSectionContent.className = 'section-body';
switch (element.sourceType) {
case 'None':
let noneContent = document.createElement('div');
noneContent.className = 'section-body-content';
let noneContentLabel = document.createElement('label');
noneContentLabel.innerHTML = 'No Metadata Source';
noneContent.appendChild(noneContentLabel);
itemSectionContent.appendChild(noneContent);
break;
default:
let contentLabel2 = document.createElement('div');
contentLabel2.innerHTML = 'ID: ' + element.sourceId;
itemSectionContent.appendChild(contentLabel2);
let contentLabel3 = document.createElement('div');
contentLabel3.innerHTML = 'Slug: ' + element.sourceSlug;
itemSectionContent.appendChild(contentLabel3);
if (element.link) {
if (element.link.length > 0) {
let contentLabel4 = document.createElement('div');
contentLabel4.innerHTML = 'Link: <a href="' + element.link + '" target="_blank" rel="noopener noreferrer" class="romlink">' + element.link + '</a>';
itemSectionContent.appendChild(contentLabel4);
}
}
break;
}
itemSection.appendChild(itemSectionContent);
metadataContent.appendChild(itemSection);
});
// setup the buttons
let okButton = new ModalButton('OK', 1, callingObject, async function (callingObject) {
let model = metadataMap.metadataMapItems;
await fetch('/api/v1.1/Games/' + callingObject.Platform.metadataMapId + '/metadata', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(model)
}).then(response => response.json()).then(result => {
location.reload(true);
});
});
metadataModal.addButton(okButton);
let cancelButton = new ModalButton('Cancel', 0, metadataModal, async function (callingObject) {
metadataModal.close();
});
metadataModal.addButton(cancelButton);
// show the dialog
await metadataModal.open();
});
this.romsModal.addButton(platformMappingButton);
}
if (this.Platform.id != 0) {
let platformEditButton = new ModalButton('Configure Emulator', 0, this, async function (callingObject) {
let mappingModal = await new Modal('messagebox');
await mappingModal.BuildModal();
@@ -592,7 +758,7 @@ class RomManagement {
let defaultPlatformMap = platformMap;
// get the user emulation configuration
let userEmuConfig = await fetch('/api/v1.1/Games/' + gameId + '/emulatorconfiguration/' + callingObject.Platform.id, {
let userEmuConfig = await fetch('/api/v1.1/Games/' + callingObject.Platform.metadataMapId + '/emulatorconfiguration/' + callingObject.Platform.id, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
@@ -620,7 +786,7 @@ class RomManagement {
// setup the buttons
let resetButton = new ModalButton('Reset to Default', 0, callingObject, async function (callingObject) {
await fetch('/api/v1.1/Games/' + gameId + '/emulatorconfiguration/' + callingObject.Platform.id, {
await fetch('/api/v1.1/Games/' + callingObject.Platform.metadataMapId + '/emulatorconfiguration/' + callingObject.Platform.id, {
method: 'DELETE'
});
callingObject.Platform.emulatorConfiguration.emulatorType = defaultPlatformMap.webEmulator.type;
@@ -639,7 +805,7 @@ class RomManagement {
EnableBIOSFiles: emuConfig.PlatformMap.enabledBIOSHashes
}
await fetch('/api/v1.1/Games/' + gameId + '/emulatorconfiguration/' + callingObject.Platform.id, {
await fetch('/api/v1.1/Games/' + callingObject.Platform.metadataMapId + '/emulatorconfiguration/' + callingObject.Platform.id, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -665,6 +831,7 @@ class RomManagement {
await mappingModal.open();
});
this.romsModal.addButton(platformEditButton);
}
let closeButton = new ModalButton('Close', 0, this, function (callingObject) {
callingObject.romsModal.close();
@@ -680,12 +847,13 @@ class RomManagement {
async #loadMediaGroups() {
this.MediaGroupCount = 0;
fetch('/api/v1.1/Games/' + gameId + '/romgroup?platformid=' + this.Platform.id, {
fetch('/api/v1.1/Games/' + this.Platform.metadataMapId + '/romgroup?platformid=' + this.Platform.id, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}).then(response => response.json()).then(result => {
console.log(result);
// display media groups
if (result.length == 0) {
this.MediaGroups.style.display = 'none';
@@ -708,7 +876,7 @@ class RomManagement {
let saveStatesButton = '';
if (this.Platform.emulatorConfiguration) {
if ((this.Platform.emulatorConfiguration.emulatorType.length > 0) && (this.Platform.emulatorConfiguration.core.length > 0)) {
let romPath = encodeURIComponent('/api/v1.1/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '.zip');
let romPath = encodeURIComponent('/api/v1.1/Games/' + this.Platform.metadataMapId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '.zip');
if (mediaGroup.hasSaveStates == true) {
let modalVariables = {
@@ -723,13 +891,13 @@ class RomManagement {
};
saveStatesButton = document.createElement('div');
saveStatesButton.addEventListener('click', () => {
let stateManager = new EmulatorStateManager(mediaGroup.id, true, this.Platform.emulatorConfiguration.emulatorType, this.Platform.emulatorConfiguration.core, mediaGroup.platformId, gameId, romPath);
let stateManager = new EmulatorStateManager(mediaGroup.id, true, this.Platform.emulatorConfiguration.emulatorType, this.Platform.emulatorConfiguration.core, mediaGroup.platformId, this.Platform.metadataMapId, romPath);
stateManager.open();
});
saveStatesButton.innerHTML = '<img src="/images/SaveStates.png" class="savedstateicon" />';
}
launchButton = '<a href="/index.html?page=emulator&engine=' + this.Platform.emulatorConfiguration.emulatorType + '&core=' + this.Platform.emulatorConfiguration.core + '&platformid=' + mediaGroup.platformId + '&gameid=' + gameId + '&romid=' + mediaGroup.id + '&mediagroup=1&rompath=' + romPath + '" class="romstart">Launch</a>';
launchButton = '<a href="/index.html?page=emulator&engine=' + this.Platform.emulatorConfiguration.emulatorType + '&core=' + this.Platform.emulatorConfiguration.core + '&platformid=' + mediaGroup.platformId + '&gameid=' + this.Platform.metadataMapId + '&romid=' + mediaGroup.id + '&mediagroup=1&rompath=' + romPath + '" class="romstart">Launch</a>';
}
}
@@ -745,7 +913,7 @@ class RomManagement {
favouriteRom.src = '/images/favourite-empty.svg';
}
favouriteRom.addEventListener('click', async () => {
await fetch('/api/v1.1/Games/' + gameId + '/roms/' + mediaGroup.id + '/' + mediaGroup.platformId + '/favourite?IsMediaGroup=true&favourite=true', {
await fetch('/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms/' + mediaGroup.id + '/' + mediaGroup.platformId + '/favourite?IsMediaGroup=true&favourite=true', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -790,7 +958,7 @@ class RomManagement {
break;
case "Completed":
statusText = 'Available';
downloadLink = '<a href="/api/v1.1/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '.zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
downloadLink = '<a href="/api/v1.1/Games/' + this.Platform.metadataMapId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '.zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
packageSize = formatBytes(mediaGroup.size);
launchButtonContent = launchButton;
break;
@@ -820,7 +988,7 @@ class RomManagement {
let deleteButton = new ModalButton("Delete", 2, deleteWindow, function (callingObject) {
ajaxCall(
'/api/v1.1/Games/' + gameData.id + '/romgroup/' + mediaGroup.id,
'/api/v1.1/Games/' + this.Platform.metadataMapId + '/romgroup/' + mediaGroup.id,
'DELETE',
function (result) {
thisObject.#loadRoms();
@@ -924,7 +1092,7 @@ class RomManagement {
let gameRomsSection = this.Roms;
let gameRoms = this.RomsContent;
let pageSize = 200;
await fetch('/api/v1.1/Games/' + gameId + '/roms?pageNumber=' + pageNumber + '&pageSize=' + pageSize + '&platformId=' + selectedPlatform + nameSearchQuery, {
await fetch('/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms?pageNumber=' + pageNumber + '&pageSize=' + pageSize + '&platformId=' + selectedPlatform + nameSearchQuery, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
@@ -932,6 +1100,7 @@ class RomManagement {
}).then(async response => {
if (response.ok) {
let result = await response.json();
console.log(result);
let romCount = this.romsModal.modalElement.querySelector('#games_roms_count');
this.RomCount = result.count;
if (result.count != 1) {
@@ -988,7 +1157,7 @@ class RomManagement {
if (this.Platform.emulatorConfiguration) {
if (this.Platform.emulatorConfiguration.emulatorType) {
if (this.Platform.emulatorConfiguration.emulatorType.length > 0) {
let romPath = encodeURIComponent('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + gameRomItems[i].name);
let romPath = encodeURIComponent('/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms/' + gameRomItems[i].id + '/' + gameRomItems[i].name);
if (gameRomItems[i].hasSaveStates == true) {
let modalVariables = {
"romId": gameRomItems[i].id,
@@ -996,28 +1165,27 @@ class RomManagement {
"engine": this.Platform.emulatorConfiguration.emulatorType,
"core": this.Platform.emulatorConfiguration.core,
"platformid": gameRomItems[i].platformId,
"gameid": gameId,
"gameid": this.Platform.metadataMapId,
"mediagroup": 0,
"rompath": romPath
};
saveStatesButton = document.createElement('div');
saveStatesButton.addEventListener('click', () => {
let stateManager = new EmulatorStateManager(gameRomItems[i].id, false, this.Platform.emulatorConfiguration.emulatorType, this.Platform.emulatorConfiguration.core, gameRomItems[i].platformId, gameId, gameRomItems[i].name);
let stateManager = new EmulatorStateManager(gameRomItems[i].id, false, this.Platform.emulatorConfiguration.emulatorType, this.Platform.emulatorConfiguration.core, gameRomItems[i].platformId, this.Platform.metadataMapId, gameRomItems[i].name);
stateManager.open();
});
saveStatesButton.innerHTML = '<img src="/images/SaveStates.png" class="savedstateicon" />';
}
launchButton = '<a href="/index.html?page=emulator&engine=' + this.Platform.emulatorConfiguration.emulatorType + '&core=' + this.Platform.emulatorConfiguration.core + '&platformid=' + gameRomItems[i].platformId + '&gameid=' + gameId + '&romid=' + gameRomItems[i].id + '&mediagroup=0&rompath=' + romPath + '" class="romstart">Launch</a>';
launchButton = '<a href="/index.html?page=emulator&engine=' + this.Platform.emulatorConfiguration.emulatorType + '&core=' + this.Platform.emulatorConfiguration.core + '&platformid=' + gameRomItems[i].platformId + '&gameid=' + this.Platform.metadataMapId + '&romid=' + gameRomItems[i].id + '&mediagroup=0&rompath=' + romPath + '" class="romstart">Launch</a>';
}
}
}
let romInfoButton = document.createElement('div');
romInfoButton.className = 'properties_button';
//romInfoButton.setAttribute('onclick', 'showDialog(\'rominfo\', ' + gameRomItems[i].id + ');');
romInfoButton.setAttribute('data-romid', gameRomItems[i].id);
romInfoButton.addEventListener('click', function () {
const romInfoDialog = new rominfodialog(gameId, this.getAttribute('data-romid'));
romInfoButton.addEventListener('click', () => {
const romInfoDialog = new rominfodialog(this.Platform.metadataMapId, gameRomItems[i].id);
romInfoDialog.open();
});
romInfoButton.innerHTML = 'i';
@@ -1044,7 +1212,7 @@ class RomManagement {
favouriteRom.src = '/images/favourite-empty.svg';
}
favouriteRom.addEventListener('click', async () => {
await fetch('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + gameRomItems[i].platformId + '/favourite?IsMediaGroup=false&favourite=true', {
await fetch('/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms/' + gameRomItems[i].id + '/' + gameRomItems[i].platformId + '/favourite?IsMediaGroup=false&favourite=true', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -1071,7 +1239,7 @@ class RomManagement {
}
let romLink = document.createElement('a');
romLink.href = '/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name);
romLink.href = '/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name);
romLink.className = 'romlink';
romLink.innerHTML = gameRomItems[i].name;
@@ -1100,51 +1268,51 @@ class RomManagement {
this.#DisplayROMCheckboxes(true);
}
if (result.count > pageSize) {
// draw pagination
let numOfPages = Math.ceil(result.count / pageSize);
// if (result.count > pageSize) {
// // draw pagination
// let numOfPages = Math.ceil(result.count / pageSize);
let romPaginator = document.createElement('div');
romPaginator.id = 'romPaginator';
romPaginator.className = 'rom_pager';
// let romPaginator = document.createElement('div');
// romPaginator.id = 'romPaginator';
// romPaginator.className = 'rom_pager';
// draw previous page button
let prevPage = document.createElement('span');
prevPage.className = 'rom_pager_number_disabled';
prevPage.innerHTML = '&lt;';
if (pageNumber != 1) {
prevPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (pageNumber - 1) + ', ' + selectedPlatform + ');');
prevPage.className = 'rom_pager_number';
}
romPaginator.appendChild(prevPage);
// // draw previous page button
// let prevPage = document.createElement('span');
// prevPage.className = 'rom_pager_number_disabled';
// prevPage.innerHTML = '&lt;';
// if (pageNumber != 1) {
// prevPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (pageNumber - 1) + ', ' + selectedPlatform + ');');
// prevPage.className = 'rom_pager_number';
// }
// romPaginator.appendChild(prevPage);
// draw page numbers
for (let i = 0; i < numOfPages; i++) {
let romPaginatorPage = document.createElement('span');
romPaginatorPage.className = 'rom_pager_number_disabled';
romPaginatorPage.innerHTML = (i + 1);
if ((i + 1) != pageNumber) {
romPaginatorPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (i + 1) + ', ' + selectedPlatform + ');');
romPaginatorPage.className = 'rom_pager_number';
}
// // draw page numbers
// for (let i = 0; i < numOfPages; i++) {
// let romPaginatorPage = document.createElement('span');
// romPaginatorPage.className = 'rom_pager_number_disabled';
// romPaginatorPage.innerHTML = (i + 1);
// if ((i + 1) != pageNumber) {
// romPaginatorPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (i + 1) + ', ' + selectedPlatform + ');');
// romPaginatorPage.className = 'rom_pager_number';
// }
romPaginator.appendChild(romPaginatorPage);
}
// romPaginator.appendChild(romPaginatorPage);
// }
// draw next page button
let nextPage = document.createElement('span');
nextPage.className = 'rom_pager_number_disabled';
nextPage.innerHTML = '&gt;';
if (pageNumber != numOfPages) {
nextPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (pageNumber + 1) + ', ' + selectedPlatform + ');');
nextPage.className = 'rom_pager_number';
}
romPaginator.appendChild(nextPage);
// // draw next page button
// let nextPage = document.createElement('span');
// nextPage.className = 'rom_pager_number_disabled';
// nextPage.innerHTML = '&gt;';
// if (pageNumber != numOfPages) {
// nextPage.setAttribute('onclick', 'loadRoms(' + undefined + ', ' + (pageNumber + 1) + ', ' + selectedPlatform + ');');
// nextPage.className = 'rom_pager_number';
// }
// romPaginator.appendChild(nextPage);
gameRoms.appendChild(romPaginator);
// gameRoms.appendChild(romPaginator);
gameRomsSection.appendChild(gameRoms);
}
// gameRomsSection.appendChild(gameRoms);
// }
} else {
gameRomsSection.setAttribute('style', 'display: none;');
}
@@ -1278,7 +1446,7 @@ class RomManagement {
if (rom_checks[i].checked == true) {
let romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1;
let deletePath = '/api/v1.1/Games/' + gameId + '/roms/' + romId;
let deletePath = '/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms/' + romId;
let parentObject = this;
ajaxCall(deletePath, 'DELETE', function (result) {
parentObject.#remapTitlesCallback();
@@ -1307,7 +1475,7 @@ class RomManagement {
if (rom_checks[i].checked == true) {
let romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1;
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + romId + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
ajaxCall('/api/v1.1/Games/' + this.Platform.metadataMapId + '/roms/' + romId + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
thisObject.#remapTitlesCallback();
}, function (result) {
thisObject.#remapTitlesCallback();
@@ -1361,7 +1529,7 @@ class RomManagement {
let currentObject = this;
ajaxCall(
'/api/v1.1/Games/' + gameId + '/romgroup?PlatformId=' + platformId,
'/api/v1.1/Games/' + this.Platform.metadataMapId + '/romgroup?PlatformId=' + platformId,
'POST',
function (result) {
currentObject.#DisplayROMCheckboxes(false);
@@ -1448,7 +1616,7 @@ class RomManagement {
id: data[i].id,
text: data[i].name,
cover: data[i].cover,
releaseDate: data[i].firstReleaseDate
releaseDate: data[i].first_release_date
});
}
@@ -1466,8 +1634,8 @@ function loadArtwork(game, cover) {
// default background should be the artworks
if (game.artworks) {
for (let i = 0; i < game.artworks.ids.length; i++) {
URLList.push("/api/v1.1/Games/" + gameId + "/artwork/" + game.artworks.ids[i] + "/image/original/" + game.artworks.ids[i] + ".jpg");
for (let i = 0; i < game.artworks.length; i++) {
URLList.push("/api/v1.1/Games/" + gameId + "/artwork/" + game.artworks[i] + "/image/original/" + game.artworks[i] + ".jpg");
}
} else if (game.cover) {
// backup background is the cover artwork

View File

@@ -1,6 +1,7 @@
<div id="gamepage">
<div style="padding-top: 20px;">
<div id="gamehome" style="display: inline-block; max-width: 820px; margin-right: 20px; vertical-align: top;">
<div id="gamehome"
style="display: inline-block; max-width: 820px; margin-right: 20px; vertical-align: top; width: 100%;">
</div>
<div id="gameprofile" style="display: inline-block; width: 250px;">

View File

@@ -55,7 +55,7 @@ class HomePageGameRow {
let gameItem = document.createElement("li");
gameItem.classList.add("homegame-item");
let gameIcon = renderGameIcon(game, true, showRatings, showClassification, classificationDisplayOrder, true, false);
let gameIcon = renderGameIcon(game, true, showRatings, showClassification, classificationDisplayOrder, true, false, true);
gameItem.appendChild(gameIcon);
scroller.appendChild(gameItem);
}

View File

@@ -7,7 +7,7 @@
<div class="loginwindow-logospace-logo">&nbsp;</div>
<div class="loginwindow-logospace-label">
<div>
Gaseous Games
Gaseous
</div>
</div>
</div>

View File

@@ -3,8 +3,11 @@
<div id="gamesummarymediagroupscontent" class="section-body"></div>
</div>
<div id="gamesummaryroms" class="section">
<div class="section-header">
<span id="rom_edit" class="romlink">Edit</span>
<div class="section-header">ROMs/Images</div>
<span>ROMs/Images</span>
</div>
<div class="section-body">
<div id="rom_edit_panel" style="display: none;">
<div id="rom_edit_panel_center">

View File

@@ -3,7 +3,7 @@
<div class="section" style="flex-grow:1;">
<div class="section-header">General</div>
<div class="section-body">
<table class="romtable">
<table class="romtable" cellspacing="0">
<tr class="romrow">
<th class="romcell">User Id</th>
<td id="user-id" class="romcell"></td>

View File

@@ -17,9 +17,9 @@
<p>This list is pre-populated with some of the more common platforms. New platforms will appear in this list as
titles are added.</p>
<p id="settings_mapping_import" style="display: none;"><button value="Export to JSON"
onclick="DownloadJSON();">Export to JSON</button><button id="importjson" value="Import JSON">Import
JSON</button><button value="Reset to Default" onclick="loadPlatformMapping(true);">Reset to
<p id="settings_mapping_import" style="display: none;"><button id="exportjson" value="Export to JSON">Export to
JSON</button><button id="importjson" value="Import JSON">Import
JSON</button><button id="resetmapping" value="Reset to Default">Reset to
Default</button></p>
<input id='uploadjson' type='file' name='files' hidden />

View File

@@ -15,6 +15,7 @@ function loadPlatformMapping(Overwrite) {
createTableRow(
true,
[
'',
'Platform',
'Supported File Extensions',
'Unique File Extensions',
@@ -27,6 +28,17 @@ function loadPlatformMapping(Overwrite) {
);
for (let i = 0; i < result.length; i++) {
let logoBox = document.createElement('div');
logoBox.classList.add('platform_image_container');
let logo = document.createElement('img');
logo.src = '/api/v1.1/Platforms/' + result[i].igdbId + '/platformlogo/original/';
logo.alt = result[i].igdbName;
logo.title = result[i].igdbName;
logo.classList.add('platform_image');
logoBox.appendChild(logo);
let hasWebEmulator = '';
if (result[i].webEmulator.type.length > 0) {
hasWebEmulator = 'Yes';
@@ -49,6 +61,7 @@ function loadPlatformMapping(Overwrite) {
}
let newRow = [
logoBox,
result[i].igdbName,
result[i].extensions.supportedFileExtensions.join(', '),
result[i].extensions.uniqueFileExtensions.join(', '),
@@ -70,6 +83,7 @@ function SetupButtons() {
if (userProfile.roles.includes("Admin")) {
document.getElementById('settings_mapping_import').style.display = '';
// Setup the JSON import button
document.getElementById('uploadjson').addEventListener('change', function () {
$(this).simpleUpload("/api/v1.1/PlatformMaps", {
start: function (file) {
@@ -84,6 +98,24 @@ function SetupButtons() {
});
document.getElementById('importjson').addEventListener('click', openDialog);
// Setup the JSON export button
document.getElementById('exportjson').addEventListener('click', DownloadJSON);
// Setup the reset to defaults button
document.getElementById('resetmapping').addEventListener('click', function () {
let warningDialog = new MessageBox("Platform Mapping Reset", "This will reset the platform mappings to the default values. Are you sure you want to continue?");
warningDialog.addButton(new ModalButton("OK", 2, warningDialog, async (callingObject) => {
loadPlatformMapping(true);
callingObject.msgDialog.close();
let completedDialog = new MessageBox("Platform Mapping Reset", "All platform mappings have been reset to default values.");
completedDialog.open();
}));
warningDialog.addButton(new ModalButton("Cancel", 0, warningDialog, async (callingObject) => {
callingObject.msgDialog.close();
}));
warningDialog.open();
});
}
}

View File

@@ -3,17 +3,16 @@
</div>
<div class="section">
<div class="section-header">Metadata Sources</div>
<div class="section-header">Signature Sources</div>
<div class="section-body">
<table>
<table style="width: 100%;">
<tr>
<td style="width: 25%;">
<td style="width: 30%;">
Signature Source
</td>
<td>
<input type="radio" name="settings_signaturesource" id="settings_signaturesource_local"
value="LocalOnly"
onclick="document.getElementById('settings_hasheoushost_row').style.display = 'none';">
value="LocalOnly">
<label for="settings_signaturesource_local">Local Only</label>
</td>
</tr>
@@ -21,13 +20,27 @@
<td></td>
<td>
<input type="radio" name="settings_signaturesource" id="settings_signaturesource_hasheous"
value="Hasheous"
onclick="document.getElementById('settings_hasheoushost_row').style.display = '';">
value="Hasheous">
<label for="settings_signaturesource_hasheous">Hasheous</label>
</td>
</tr>
<tr id="settings_hasheoushost_row" style="display: none;">
<td>
</table>
</div>
</div>
<div class="section">
<div class="section-header">Default Metadata Source</div>
<div class="section-body" id="settings_metadata">
</div>
</div>
<div class="section">
<div class="section-header">Hasheous Configuration</div>
<div class="section-body">
<table style="width: 100%;">
<tr id="settings_hasheoushost_row">
<td style="width: 30%;">
Hasheous Host
</td>
<td>
@@ -36,14 +49,15 @@
</tr>
<tr>
<td>
<label for="settings_hasheoussubmit">Submit updates to Hasheous when fixing ROM matches</label>
<label for="settings_hasheoussubmit">Submit updates to Hasheous when fixing ROM
matches</label>
</td>
<td>
<input type="checkbox" id="settings_hasheoussubmit" onchange="toggleHasheousAPIKey(this);">
</td>
</tr>
<tr id="settings_hasheousapikey_row" style="display: none;">
<td>
<tr id="settings_hasheousapikey_row">
<td style="width: 30%;">
Hasheous API key
</td>
<td>
@@ -52,6 +66,9 @@
</tr>
</table>
</div>
</div>
<div class="section">
<div class="section-header">Logging</div>
<div class="section-body">
<table>
@@ -85,6 +102,8 @@
</tr>
</table>
</div>
</div>
<div class="section">
<div class="section-header">Emulator</div>
<div class="section-body">
<table>
@@ -95,10 +114,11 @@
</tr>
</table>
</div>
</div>
<div style="text-align: right;">
<button id="settings_tasktimers_new" onclick="setSystemSettings();">Save</button>
</div>
</div>
<script>
// kick of page data load

View File

@@ -1,6 +1,6 @@
function getSystemSettings() {
ajaxCall(
'/api/v1/System/Settings/System',
'/api/v1.1/System/Settings/System',
'GET',
function (result) {
console.log(result);
@@ -21,11 +21,184 @@ function getSystemSettings() {
case "Hasheous":
document.getElementById('settings_signaturesource_hasheous').checked = true;
document.getElementById('settings_hasheoushost_row').style.display = '';
break;
}
let metadataSettingsContainer = document.getElementById('settings_metadata');
metadataSettingsContainer.innerHTML = '';
result.metadataSources.forEach(element => {
// section
let sourceSection = document.createElement('div');
sourceSection.classList.add('section');
sourceSection.setAttribute('id', 'settings_metadatasource_' + element.source);
// section header
let sourceHeader = document.createElement('div');
sourceHeader.classList.add('section-header');
let sourceRadio = document.createElement('input');
sourceRadio.setAttribute('type', 'radio');
sourceRadio.setAttribute('name', 'settings_metadatasource');
sourceRadio.setAttribute('value', element.source);
sourceRadio.setAttribute('id', 'settings_metadatasource_' + element.source + '_radio');
sourceRadio.style.margin = '0px';
sourceRadio.style.height = 'unset';
if (element.default) {
sourceRadio.checked = true;
}
let sourceLabel = document.createElement('label');
sourceLabel.setAttribute('for', 'settings_metadatasource_' + element.source + '_radio');
let sourceName = document.createElement('span');
switch (element.source) {
case "IGDB":
sourceName.innerText = 'Internet Game Database (IGDB)';
break;
default:
sourceName.innerText = element.source;
break;
}
sourceName.style.marginLeft = '10px';
sourceLabel.appendChild(sourceName);
let sourceConfigured = document.createElement('span');
sourceConfigured.style.float = 'right';
sourceConfigured.classList.add(element.configured ? 'greentext' : 'redtext');
sourceConfigured.innerText = element.configured ? 'Configured' : 'Not Configured';
sourceHeader.appendChild(sourceRadio);
sourceHeader.appendChild(sourceLabel);
sourceHeader.appendChild(sourceConfigured);
sourceSection.appendChild(sourceHeader);
// section body
let sourceContent = document.createElement('div');
sourceContent.classList.add('section-body');
if (element.usesProxy === false && element.usesClientIdAndSecret === false) {
sourceContent.innerText = 'No options to configure';
} else {
// render controls
let controlsTable = document.createElement('table');
controlsTable.style.width = '100%';
// hasheous proxy row
if (element.usesProxy === true) {
let proxyRow = document.createElement('tr');
let proxyLabel = document.createElement('td');
if (element.usesClientIdAndSecret === true) {
let proxyRadio = document.createElement('input');
proxyRadio.id = 'settings_metadatasource_proxy_' + element.source;
proxyRadio.setAttribute('type', 'radio');
proxyRadio.setAttribute('name', 'settings_metadatasource_proxy_' + element.source);
proxyRadio.style.marginRight = '10px';
if (element.useHasheousProxy === true) {
proxyRadio.checked = true;
}
proxyLabel.appendChild(proxyRadio);
let proxyLabelLabel = document.createElement('label');
proxyLabelLabel.setAttribute('for', 'settings_metadatasource_proxy_' + element.source);
let proxyLabelSpan = document.createElement('span');
proxyLabelSpan.innerText = 'Use Hasheous Proxy';
proxyLabelLabel.appendChild(proxyLabelSpan);
proxyLabel.appendChild(proxyLabelLabel);
proxyRow.appendChild(proxyLabel);
} else {
proxyLabel.innerHTML = 'Uses Hasheous Proxy';
proxyRow.appendChild(proxyLabel);
}
controlsTable.appendChild(proxyRow);
}
// client id and secret row
if (element.usesClientIdAndSecret === true) {
if (element.usesProxy === true) {
let clientRadioRow = document.createElement('tr');
let clientRadioLabel = document.createElement('td');
let clientRadio = document.createElement('input');
clientRadio.id = 'settings_metadatasource_client_' + element.source;
clientRadio.setAttribute('type', 'radio');
clientRadio.setAttribute('name', 'settings_metadatasource_proxy_' + element.source);
clientRadio.style.marginRight = '10px';
if (element.useHasheousProxy === false) {
clientRadio.checked = true;
}
clientRadioLabel.appendChild(clientRadio);
let clientRadioLabelLabel = document.createElement('label');
clientRadioLabelLabel.setAttribute('for', 'settings_metadatasource_client_' + element.source);
let clientRadioLabelSpan = document.createElement('span');
clientRadioLabelSpan.innerText = 'Direct connection';
clientRadioLabelLabel.appendChild(clientRadioLabelSpan);
clientRadioLabel.appendChild(clientRadioLabelLabel);
clientRadioRow.appendChild(clientRadioLabel);
controlsTable.appendChild(clientRadioRow);
}
let clientIdTable = document.createElement('table');
clientIdTable.style.width = '100%';
if (element.usesProxy === true) {
clientIdTable.style.marginLeft = '30px';
}
let clientIdRow = document.createElement('tr');
let clientIdLabel = document.createElement('td');
clientIdLabel.style.width = '15%';
clientIdLabel.innerText = 'Client ID';
clientIdRow.appendChild(clientIdLabel);
let clientIdInput = document.createElement('td');
let clientIdInputField = document.createElement('input');
clientIdInputField.style.width = '90%';
clientIdInputField.setAttribute('type', 'text');
clientIdInputField.setAttribute('id', 'settings_metadatasource_' + element.source + '_clientid');
clientIdInputField.value = element.clientId;
clientIdInput.appendChild(clientIdInputField);
clientIdRow.appendChild(clientIdInput);
clientIdTable.appendChild(clientIdRow);
let clientSecretRow = document.createElement('tr');
let clientSecretLabel = document.createElement('td');
clientSecretLabel.style.width = '15%';
clientSecretLabel.innerText = 'Client Secret';
clientSecretRow.appendChild(clientSecretLabel);
let clientSecretInput = document.createElement('td');
let clientSecretInputField = document.createElement('input');
clientSecretInputField.style.width = '90%';
clientSecretInputField.setAttribute('type', 'text');
clientSecretInputField.setAttribute('id', 'settings_metadatasource_' + element.source + '_clientsecret');
clientSecretInputField.value = element.secret;
clientSecretInput.appendChild(clientSecretInputField);
clientSecretRow.appendChild(clientSecretInput);
clientIdTable.appendChild(clientSecretRow);
controlsTable.appendChild(clientIdTable);
}
sourceContent.appendChild(controlsTable);
}
sourceSection.appendChild(sourceContent);
metadataSettingsContainer.appendChild(sourceSection);
});
document.getElementById('settings_signaturesource_hasheoushost').value = result.signatureSource.hasheousHost;
let hasheousSubmitCheck = document.getElementById('settings_hasheoussubmit');
@@ -52,10 +225,44 @@ function setSystemSettings() {
retentionValue = 7;
}
let metadataSources = [];
let metadataSourceRadios = $("input[type='radio'][name='settings_metadatasource']");
metadataSourceRadios.each(function (index, element) {
let source = $(element).val();
let useHasheousProxy = false;
let clientId = '';
let secret = '';
if (source == "IGDB") {
let igdbClientId = document.getElementById('settings_metadatasource_' + source + '_clientid').value;
let igdbClientSecret = document.getElementById('settings_metadatasource_' + source + '_clientsecret').value;
if (igdbClientId && igdbClientSecret) {
clientId = igdbClientId;
secret = igdbClientSecret;
}
}
let useHasheousProxyRadio = $("input[type='radio'][id='settings_metadatasource_proxy_" + source + "']:checked");
if (useHasheousProxyRadio.length > 0) {
useHasheousProxy = true;
} else {
useHasheousProxy = false;
}
let metadataSource = {
"Source": source,
"UseHasheousProxy": useHasheousProxy,
"ClientId": clientId,
"Secret": secret,
"Default": $(element).is(':checked')
};
metadataSources.push(metadataSource);
});
let model = {
"alwaysLogToDisk": alwaysLogToDisk,
"minimumLogRetentionPeriod": Number(retentionValue),
"emulatorDebugMode": document.getElementById('settings_emulator_debug').checked,
"metadataSources": metadataSources,
"signatureSource": {
"Source": $("input[type='radio'][name='settings_signaturesource']:checked").val(),
"HasheousHost": document.getElementById('settings_signaturesource_hasheoushost').value,
@@ -66,7 +273,7 @@ function setSystemSettings() {
console.log(model);
ajaxCall(
'/api/v1/System/Settings/System',
'/api/v1.1/System/Settings/System',
'POST',
function (result) {
getSystemSettings();

View File

@@ -366,6 +366,20 @@ class ProfileCard {
this.Quip.classList.add('profile-card-quip');
this.ProfileBody = document.createElement('div');
this.ProfileBody.classList.add('profile-card-body');
this.ProfileNowPlaying = document.createElement('div');
this.ProfileNowPlaying.classList.add('profile-card-now-playing-body');
this.ProfileNowPlayingBg = document.createElement('div');
this.ProfileNowPlayingBg.classList.add('profile-card-now-playing-body-bg');
this.ProfileNowPlayingLabel = document.createElement('div');
this.ProfileNowPlayingLabel.classList.add('profile-card-now-playing-label');
this.ProfileNowPlayingCover = document.createElement('div');
this.ProfileNowPlayingCover.classList.add('profile-card-now-playing-cover');
this.ProfileNowPlayingTitle = document.createElement('div');
this.ProfileNowPlayingTitle.classList.add('profile-card-now-playing-title');
this.ProfileNowPlayingPlatform = document.createElement('div');
this.ProfileNowPlayingPlatform.classList.add('profile-card-now-playing-platform');
this.ProfileNowPlayingDuration = document.createElement('div');
this.ProfileNowPlayingDuration.classList.add('profile-card-now-playing-duration');
this.Avatar = document.createElement('div');
this.Avatar.classList.add('profile-card-avatar');
@@ -375,12 +389,29 @@ class ProfileCard {
this.ProfileBody.appendChild(this.DisplayName);
this.ProfileBody.appendChild(this.Quip);
// now playing
this.ProfileNowPlayingBg.appendChild(this.ProfileNowPlaying);
this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingLabel);
this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingCover);
this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingTitle);
this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingPlatform);
this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingDuration);
// assemble card
this.Card.appendChild(this.BackgroundImage);
this.Card.appendChild(this.ProfileBody);
this.Card.appendChild(this.ProfileNowPlayingBg);
this.Card.appendChild(this.Avatar);
this.ProfileData = null;
const response = this.#FetchProfile(this);
// set timeout to refresh the profile card every 30 seconds
let callingObject = this;
setInterval(function () {
callingObject.#FetchProfile(callingObject);
}, 30000);
return this.Card;
}
@@ -392,12 +423,60 @@ class ProfileCard {
} else {
const profile = await response.json();
if (profile) {
this.Avatar.appendChild(new Avatar(callingObject.ProfileId, 50, 50));
if (profile.profileBackground) {
this.BackgroundImage.style = "background-image: url('/api/v1.1/UserProfile/" + callingObject.ProfileId + "/Background');";
let stillUpdateAnyway = false;
if (callingObject.ProfileData === null) {
callingObject.ProfileData = profile;
stillUpdateAnyway = true;
}
this.DisplayName.innerHTML = profile.displayName;
this.Quip.innerHTML = profile.quip;
// update avatar if different
if (callingObject.ProfileData.avatar !== profile.avatar || stillUpdateAnyway === true) {
callingObject.Avatar.innerHTML = "";
callingObject.Avatar.appendChild(new Avatar(callingObject.ProfileId, 50, 50));
}
// update profile background if different
if (callingObject.ProfileData.profileBackground !== profile.profileBackground || stillUpdateAnyway === true) {
if (profile.profileBackground) {
callingObject.BackgroundImage.style = "background-image: url('/api/v1.1/UserProfile/" + callingObject.ProfileId + "/Background');";
} else {
callingObject.BackgroundImage.style = "";
}
}
// update display name if different
if (callingObject.ProfileData.displayName !== profile.displayName || stillUpdateAnyway === true) {
callingObject.DisplayName.innerHTML = profile.displayName;
}
// update quip if different
if (callingObject.ProfileData.quip !== profile.quip || stillUpdateAnyway === true) {
callingObject.Quip.innerHTML = profile.quip;
}
if (profile.nowPlaying) {
callingObject.ProfileNowPlayingLabel.innerHTML = "Now Playing";
let cardImage = '';
if (profile.nowPlaying.game.cover) {
cardImage = "/api/v1.1/Games/" + profile.nowPlaying.game.metadataMapId + "/cover/" + profile.nowPlaying.game.cover + "/image/cover_big/" + profile.nowPlaying.game.cover + ".jpg";
} else {
cardImage = "/images/unknowngame.png";
}
callingObject.ProfileNowPlayingCover.style = "background-image: url(\"" + cardImage + "\");";
callingObject.ProfileNowPlayingBg.style = "background-image: url(\"" + cardImage + "\");";
callingObject.ProfileNowPlayingTitle.innerHTML = profile.nowPlaying.game.name;
callingObject.ProfileNowPlayingPlatform.innerHTML = profile.nowPlaying.platform.name;
if (profile.nowPlaying.duration === 1) {
callingObject.ProfileNowPlayingDuration.innerHTML = profile.nowPlaying.duration + " minute";
} else {
callingObject.ProfileNowPlayingDuration.innerHTML = profile.nowPlaying.duration + " minutes";
}
callingObject.ProfileNowPlayingBg.style.display = "";
} else {
callingObject.ProfileNowPlayingBg.style.display = "none";
}
callingObject.ProfileData = profile;
}
}
});

View File

@@ -181,7 +181,7 @@ function formatGamesPanel(targetElement, result, pageNumber, pageSize, forceScro
}
for (let i = 0; i < result.games.length; i++) {
let game = renderGameIcon(result.games[i], showTitle, showRatings, showClassification, classificationDisplayOrder, false, listView);
let game = renderGameIcon(result.games[i], showTitle, showRatings, showClassification, classificationDisplayOrder, false, listView, true);
switch (pageMode) {
case "paged":
targetElement.appendChild(game);
@@ -396,15 +396,19 @@ function IsInView() {
}
}
function renderGameIcon(gameObject, showTitle, showRatings, showClassification, classificationDisplayOrder, useSmallCover, listView) {
function renderGameIcon(gameObject, showTitle, showRatings, showClassification, classificationDisplayOrder, useSmallCover, listView, showFavourite) {
if (listView == undefined) {
listView = false;
}
if (showFavourite == undefined) {
showFavourite = true;
}
let classes = getViewModeClasses(listView);
let gameBox = document.createElement('div');
gameBox.id = "game_tile_" + gameObject.id;
gameBox.metadataMapId = "game_tile_" + gameObject.metadataMapId;
if (useSmallCover == true) {
gameBox.classList.add(...classes['game_tile game_tile_small']);
} else {
@@ -415,14 +419,14 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
let gameImageBox = document.createElement('div');
gameImageBox.classList.add(...classes['game_tile_box']);
if (listView == true) {
gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";');
gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.metadataMapId + '";');
} else {
gameImageBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";');
gameImageBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.metadataMapId + '";');
}
let gameImage = document.createElement('img');
gameImage.id = 'game_tile_cover_' + gameObject.id;
gameImage.setAttribute('data-id', gameObject.id);
gameImage.id = 'game_tile_cover_' + gameObject.metadataMapId;
gameImage.setAttribute('data-id', gameObject.metadataMapId);
if (useSmallCover == true) {
gameImage.classList.add(...classes['game_tile_image game_tile_image_small lazy']);
} else {
@@ -430,7 +434,7 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
}
// gameImage.src = '/images/unknowngame.png';
if (gameObject.cover) {
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.id + '/cover/' + gameObject.cover.id + '/image/cover_big/' + gameObject.cover.id + '.jpg');
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.metadataMapId + '/cover/' + gameObject.cover + '/image/cover_big/' + gameObject.cover + '.jpg');
} else {
gameImage.classList.add(...classes['game_tile_image unknown']);
gameImage.setAttribute('data-src', '/images/unknowngame.png');
@@ -464,49 +468,6 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
gameImageBox.appendChild(gameSaveIcon);
}
// add favourite game icon
let gameFavIconBox = document.createElement('div');
gameFavIconBox.classList.add(...classes['game_tile_box_favouritegame']);
let gameFavIcon = document.createElement('img');
gameFavIcon.classList.add(...classes['favouriteicon']);
if (gameObject.isFavourite == true) {
gameFavIcon.src = '/images/favourite-filled.svg';
gameFavIconBox.classList.add('favourite-filled');
} else {
gameFavIcon.src = '/images/favourite-empty.svg';
gameFavIconBox.classList.add('favourite-empty');
}
gameFavIconBox.appendChild(gameFavIcon);
gameFavIconBox.addEventListener('click', (e) => {
e.stopPropagation();
if (gameFavIconBox.classList.contains('favourite-filled')) {
gameFavIcon.src = '/images/favourite-empty.svg';
gameFavIconBox.classList.remove('favourite-filled');
gameFavIconBox.classList.add('favourite-empty');
gameObject.isFavourite = false;
} else {
gameFavIcon.src = '/images/favourite-filled.svg';
gameFavIconBox.classList.remove('favourite-empty');
gameFavIconBox.classList.add('favourite-filled');
gameObject.isFavourite = true;
}
fetch('/api/v1.1/Games/' + gameObject.id + '/favourite?favourite=' + gameObject.isFavourite, {
method: 'POST'
}).then(response => {
if (response.ok) {
// console.log('Favourite status updated');
} else {
// console.log('Failed to update favourite status');
}
});
});
gameImageBox.appendChild(gameFavIconBox);
// add ratings banner
if (gameObject.totalRating || displayClassification == true) {
let gameImageRatingBanner = document.createElement('div');
@@ -538,6 +499,51 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
}
gameBox.appendChild(gameImageBox);
// add favourite game icon
if (showFavourite == true) {
let gameFavIconBox = document.createElement('div');
gameFavIconBox.classList.add(...classes['game_tile_box_favouritegame']);
let gameFavIcon = document.createElement('img');
gameFavIcon.classList.add(...classes['favouriteicon']);
if (gameObject.isFavourite == true) {
gameFavIcon.src = '/images/favourite-filled.svg';
gameFavIconBox.classList.add('favourite-filled');
} else {
gameFavIcon.src = '/images/favourite-empty.svg';
gameFavIconBox.classList.add('favourite-empty');
}
gameFavIconBox.appendChild(gameFavIcon);
gameFavIconBox.addEventListener('click', (e) => {
e.stopPropagation();
if (gameFavIconBox.classList.contains('favourite-filled')) {
gameFavIcon.src = '/images/favourite-empty.svg';
gameFavIconBox.classList.remove('favourite-filled');
gameFavIconBox.classList.add('favourite-empty');
gameObject.isFavourite = false;
} else {
gameFavIcon.src = '/images/favourite-filled.svg';
gameFavIconBox.classList.remove('favourite-empty');
gameFavIconBox.classList.add('favourite-filled');
gameObject.isFavourite = true;
}
fetch('/api/v1.1/Games/' + gameObject.metadataMapId + '/favourite?favourite=' + gameObject.isFavourite, {
method: 'POST'
}).then(response => {
if (response.ok) {
// console.log('Favourite status updated');
} else {
// console.log('Failed to update favourite status');
}
});
});
gameImageBox.appendChild(gameFavIconBox);
}
if (showTitle == true) {
let gameBoxTitle = document.createElement('div');
gameBoxTitle.classList.add(...classes['game_tile_label']);

View File

@@ -699,13 +699,104 @@ class BackgroundImageRotator {
}
}
function BuildLaunchLink(engine, core, platformId, gameId, romId, isMediaGroup, filename) {
async function BuildLaunchLink(engine, core, platformId, gameId, romId, isMediaGroup, filename) {
let launchLink = '/index.html?page=emulator&engine=<ENGINE>&core=<CORE>&platformid=<PLATFORMID>&gameid=<GAMEID>&romid=<ROMID>&mediagroup=<ISMEDIAGROUP>&rompath=<FILENAME>';
// http://localhost:5198/index.html?page=emulator&engine=EmulatorJS&core=amiga&platformid=16&gameid=5519&romid=19&mediagroup=1&rompath=%2Fapi%2Fv1.1%2FGames%2F5519%2Fromgroup%2F19%2FCannon%20Fodder.zip
let isValid = true;
// http://localhost:5198/index.html?page=emulator&engine=EmulatorJS&core=amiga&platformid=16&gameid=5519&romid=102&mediagroup=0&rompath=%2Fapi%2Fv1.1%2FGames%2F5519%2Froms%2F102%2FCannon%20Fodder%20(1993)(Virgin)(Disk%201%20of%203)%5Bcr%20CSL%5D.adf
console.log('Validating launch link: ' + engine + ' ' + core + ' ' + platformId + ' ' + gameId + ' ' + romId + ' ' + isMediaGroup + ' ' + filename);
let returnLink = '/index.html';
// check if engine is valid
let validEngines = ['EmulatorJS'];
if (!validEngines.includes(engine)) {
isValid = false;
console.log('Engine is invalid!');
}
// fetch valid cores from json file /emulators/EmulatorJS/data/cores.json
let validCores = [];
await fetch('/api/v1.1/PlatformMaps', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
validCores = data;
for (let i = 0; i < validCores.length; i++) {
isValid = false;
if (validCores[i].webEmulator) {
if (validCores[i].webEmulator.availableWebEmulators) {
for (let y = 0; y < validCores[i].webEmulator.availableWebEmulators.length; y++) {
if (validCores[i].webEmulator.availableWebEmulators[y].emulatorType == engine) {
for (let x = 0; x < validCores[i].webEmulator.availableWebEmulators[y].availableWebEmulatorCores.length; x++) {
if (validCores[i].webEmulator.availableWebEmulators[y].availableWebEmulatorCores[x].core == core ||
validCores[i].webEmulator.availableWebEmulators[y].availableWebEmulatorCores[x].alternateCoreName == core
) {
isValid = true;
break;
}
}
}
}
}
}
if (isValid == true) {
break;
}
}
if (isValid == false) {
console.log('Core is invalid!');
}
// check if platformId is an int64
if (!Number(platformId)) {
isValid = false;
console.log('PlatformId is invalid!');
}
// check if gameId is a an int64
if (!Number(gameId)) {
isValid = false;
console.log('GameId is invalid!');
}
// check if romId is a an int64
if (!Number(romId)) {
isValid = false;
console.log('RomId is invalid!');
}
// check if isMediaGroup is a boolean in a number format - if not, verify it is a boolean
if (isMediaGroup == 0 || isMediaGroup == 1) {
// value is a number, and is valid
} else {
if (isMediaGroup == true || isMediaGroup == false) {
// value is a boolean, and is valid
} else {
isValid = false;
console.log('IsMediaGroup is invalid!');
}
}
// check if filename is a string
if (typeof (filename) != 'string') {
isValid = false;
console.log('Filename is invalid!');
}
if (isValid == false) {
console.log('Link is invalid!');
returnLink = '/index.html';
return;
}
// generate the launch link
launchLink = launchLink.replace('<ENGINE>', engine);
launchLink = launchLink.replace('<CORE>', core);
launchLink = launchLink.replace('<PLATFORMID>', platformId);
@@ -719,5 +810,18 @@ function BuildLaunchLink(engine, core, platformId, gameId, romId, isMediaGroup,
launchLink = launchLink.replace('<FILENAME>', '/api/v1.1/Games/' + encodeURI(gameId) + '/roms/' + encodeURI(romId) + '/' + encodeURI(filename));
}
return launchLink;
console.log('Validated link: ' + launchLink);
returnLink = launchLink;
return;
})
.catch(error => {
console.error('Error:', error);
isValid = false;
console.log('Link is invalid!');
returnLink = '/index.html';
});
return returnLink;
}

View File

@@ -503,20 +503,20 @@ class EmulatorStateManager {
let stateControlsLaunch = document.createElement('span');
stateControlsLaunch.id = 'stateControlsLaunch_' + result[i].id;
stateControlsLaunch.className = 'romstart';
let emulatorTarget;// = '/index.html?page=emulator&engine=@engine&core=@core&platformid=@platformid&gameid=@gameid&romid=@romid&mediagroup=@mediagroup&rompath=@rompath&stateid=' + result[i].id;
let emulatorTarget;
let mediagroupint = 0;
if (thisObject.IsMediaGroup == true) {
mediagroupint = 1;
}
switch (getQueryString('page', 'string')) {
case 'emulator':
emulatorTarget = BuildLaunchLink(getQueryString('engine', 'string'), getQueryString('core', 'string'), getQueryString('platformid', 'string'), getQueryString('gameid', 'string'), getQueryString('romid', 'string'), mediagroupint, thisObject.rompath, result[i].id) + '&stateid=' + result[i].id;
emulatorTarget = await BuildLaunchLink(getQueryString('engine', 'string'), getQueryString('core', 'string'), getQueryString('platformid', 'string'), getQueryString('gameid', 'string'), getQueryString('romid', 'string'), mediagroupint, thisObject.rompath, result[i].id) + '&stateid=' + result[i].id;
stateControlsLaunch.addEventListener('click', () => {
window.location.replace(emulatorTarget);
});
break;
case 'game':
emulatorTarget = BuildLaunchLink(thisObject.engine, thisObject.core, thisObject.platformid, thisObject.gameid, thisObject.RomId, mediagroupint, thisObject.rompath, result[i].id) + '&stateid=' + result[i].id;
emulatorTarget = await BuildLaunchLink(thisObject.engine, thisObject.core, thisObject.platformid, thisObject.gameid, thisObject.RomId, mediagroupint, thisObject.rompath, result[i].id) + '&stateid=' + result[i].id;
stateControlsLaunch.addEventListener('click', () => {
window.location.href = emulatorTarget;
});

View File

@@ -181,17 +181,42 @@ class PreferencesWindow {
"Adult"
];
for (let j = 0; j < ratingGroupsOrder.length; j++) {
let ageGroupValue = AgeRatingGroups[ratingGroupsOrder[j]];
let iconIdList = ageGroupValue[key];
let ratingGroup = ratingGroupsOrder[j];
let ageGroupValue = AgeRatingGroups[ratingGroup];
let ageGroupValueLower = {};
for (const [key, value] of Object.entries(ageGroupValue)) {
ageGroupValueLower[key.toLowerCase()] = value;
}
let iconIdList = ageGroupValueLower[key.toLowerCase()];
console.log(key.toLowerCase());
if (key == 'clasS_IND' || key == 'CLASS_IND') {
console.log("here");
}
// loop the age rating icons
for (let i = 0; i < iconIdList.length; i++) {
if (iconIdList) {
for (const [i, value] of Object.entries(iconIdList)) {
console.log(" " + iconIdList[i]);
let icon = document.createElement('img');
icon.src = "/images/Ratings/" + key + "/" + AgeRatingStrings[iconIdList[i]] + ".svg";
icon.title = AgeRatingStrings[iconIdList[i]];
// get age rating strings
let iconId = iconIdList[i];
let ageRatingString;
for (const [x, y] of Object.entries(AgeRatingStrings)) {
if (AgeRatingStrings[x] == iconId) {
ageRatingString = AgeRatingStrings[x];
break;
}
}
icon.src = "/images/Ratings/" + key + "/" + ageRatingString + ".svg";
icon.title = ageRatingString;
icon.alt = ageRatingString;
icon.classList.add("rating_image_mini");
classificationIcons.appendChild(icon);
}
}
}
classificationItemBox.appendChild(classificationIcons);
this.classificationSelector.appendChild(classificationItemBox);

View File

@@ -109,6 +109,7 @@ class UploadRom {
if (xhr.status === 200) {
// process the results
let response = JSON.parse(xhr.responseText);
console.log(response);
switch (response.type) {
case 'rom':
switch (response.status) {
@@ -121,16 +122,21 @@ class UploadRom {
uploadedItem.platformName = 'Unknown Platform';
uploadedItem.gameId = 0;
uploadedItem.gameName = 'Unknown Game';
uploadedItem.gameData = response.game;
uploadedItem.romId = response.romid;
if (response.game) {
uploadedItem.gameId = response.game.id;
// game data was returned
uploadedItem.gameId = response.rom.metadataMapId;
uploadedItem.gameName = response.game.name;
if (response.game.cover != null) {
if (response.game.cover.id != null) {
uploadedItem.coverId = response.game.cover.id;
if (response.game.cover != null) {
uploadedItem.coverId = response.game.cover;
}
}
} else {
// game has been deemed to be unknown
uploadedItem.gameId = response.rom.metadataMapId;
}
if (response.platform) {
@@ -197,12 +203,12 @@ class UploadItem {
// file name label
this.filenameLabel = document.createElement('div');
this.filenameLabel.classList.add('uploadItem-Label');
this.filenameLabel.innerHTML = this.Filename;
this.filenameLabel.textContent = this.Filename;
// status label
this.statusLabel = document.createElement('div');
this.statusLabel.classList.add('uploadItem-Status');
this.statusLabel.innerHTML = UploadItem.StatusValues[this.Status];
this.statusLabel.textContent = UploadItem.StatusValues[this.Status];
// game name label
this.gameNameLabel = document.createElement('div');
@@ -273,6 +279,7 @@ class UploadItem {
platformName = null;
gameId = null;
gameName = null;
gameData = null;
coverId = null;
romId = null;
@@ -319,7 +326,7 @@ class UploadItem {
case 'rom':
this.infoButton.style.display = 'block';
if (this.gameId === null || this.gameId === 0) {
if (this.gameId === null || this.gameId === 0 || this.gameData === null) {
this.coverArt.src = '/images/unknowngame.png';
} else {
this.coverArt.src = '/api/v1.1/Games/' + this.gameId + '/cover/' + this.coverId + '/image/cover_big/cover.jpg';

View File

@@ -134,7 +134,8 @@ h3 {
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
/* transform: translate(-50%, -50%); */
transform: translateX(-50%) translateY(calc(-50% - .5px));
min-width: 700px;
max-width: 1000px;
width: 60%;
@@ -185,8 +186,8 @@ h3 {
.modal-window-tabs {
background-color: var(--modal-tabs-background-color);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
/* backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px); */
display: block;
flex-grow: 0;
flex-shrink: 1;
@@ -237,8 +238,8 @@ h3 {
text-align: top;
overflow-y: auto;
overflow-x: hidden;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
/* backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px); */
}
.modal-window-header {
@@ -313,7 +314,7 @@ h3 {
#banner_icon {
/* background-color: white; */
background-color: rgba(0, 22, 56, 0.8);
background-color: rgba(0, 22, 56, 0.2);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
position: fixed;
@@ -336,7 +337,7 @@ h3 {
#banner_icon_image {
width: 30px;
height: 30px;
filter: drop-shadow(1px 1px 0 rgb(90, 90, 90)) drop-shadow(-1px 1px 0 rgb(90, 90, 90)) drop-shadow(1px -1px 0 rgb(90, 90, 90)) drop-shadow(-1px -1px 0 rgb(90, 90, 90));
filter: drop-shadow(1px 1px 0 rgb(180, 180, 180)) drop-shadow(-1px 1px 0 rgb(180, 180, 180)) drop-shadow(1px -1px 0 rgb(180, 180, 180)) drop-shadow(-1px -1px 0 rgb(180, 180, 180));
}
.banner_button {
@@ -416,7 +417,7 @@ h3 {
#banner_header {
background: rgba(0, 22, 56, 0.8);
background: linear-gradient(90deg, rgba(0, 22, 56, 0.8) 0%, rgba(0, 0, 0, 0.8) 100%);
background: linear-gradient(90deg, rgba(0, 22, 56, 0.2) 0%, rgba(0, 0, 0, 0.5) 100%);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
position: fixed;
@@ -435,7 +436,7 @@ h3 {
font-size: 16pt;
vertical-align: top;
/*color: #edeffa;*/
color: #7c70da;
color: #FFFFFF;
}
#banner_header_label {
@@ -1108,6 +1109,7 @@ input[name='filter_panel_range_max'] {
position: absolute;
display: inline-block;
top: 30%;
padding-top: 7px;
right: 10px;
}
@@ -1137,6 +1139,28 @@ input[name='filter_panel_range_max'] {
display: inline-block;
top: 30%;
right: 50px;
width: 30px;
height: 30px;
text-align: center;
padding-top: 5px;
padding-left: 4px;
padding-right: 3px;
padding-bottom: 5px;
border-color: transparent;
border-width: 1px;
border-style: solid;
border-radius: var(--standard-radius);
z-index: +1;
}
.game_tile_box_row:hover>.game_tile_box_favouritegame_row {
border-color: var(--input-border);
background-color: var(--fancybutton-background);
}
.game_tile_box_row:hover>.game_tile_box_favouritegame_row:hover {
border-color: var(--input-border-hover);
background-color: var(--fancybutton-background-hover);
}
.game_tile_image {
@@ -1191,8 +1215,8 @@ input[name='filter_panel_range_max'] {
background-repeat: no-repeat;
background-size: cover;
background-color: white;
filter: blur(32px);
-webkit-filter: blur(32px);
filter: blur(64px);
-webkit-filter: blur(64px);
}
.bgImage_LessBlur {
@@ -1877,8 +1901,9 @@ div[name="properties_profile_toc_item"]:hover {
button:not(.select2-selection__choice__remove):not(.select2-selection__clear):not(.ejs_menu_button):not(.bigbutton) {
background-color: var(--button-background);
background: var(--button-background-gradient);
color: var(--button-font-colour);
border-width: 1px;
border-width: 0px;
border-color: var(--button-border);
border-style: solid;
border-radius: var(--standard-radius);
@@ -1894,6 +1919,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
button:not(.select2-selection__choice__remove):not(.select2-selection__clear):not(.ejs_menu_button):hover {
background-color: var(--button-background-hover);
background: var(--button-background-hover);
border-color: var(--button-border-hover);
color: var(--button-font-colour-hover);
cursor: pointer;
@@ -1923,36 +1949,42 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
.redbutton {
background-color: var(--button-red-background) !important;
background: var(--button-red-background-gradient) !important;
border-color: var(--button-red-border) !important;
color: var(--button-red-font-colour) !important;
}
.redbutton:hover {
background-color: var(--button-red-background-hover) !important;
background: var(--button-red-background-hover) !important;
border-color: var(--button-red-border-hover) !important;
color: var(--button-red-font-colour-hover) !important;
}
.redbutton:disabled {
background-color: var(--button-red-background-disabled) !important;
background: var(--button-red-background-disabled) !important;
border-color: var(--button-red-border-disabled) !important;
color: var(--button-red-font-colour-disabled) !important;
}
.bluebutton {
background-color: var(--button-blue-background) !important;
background: var(--button-blue-background-gradient) !important;
border-color: var(--button-blue-border) !important;
color: var(--button-blue-font-colour) !important;
}
.bluebutton:hover {
background-color: var(--button-blue-background-hover) !important;
background: var(--button-blue-background-hover) !important;
border-color: var(--button-blue-border-hover) !important;
color: var(--button-blue-font-colour-hover) !important;
}
.bluebutton:disabled {
background-color: var(--button-blue-background-disabled) !important;
background: var(--button-blue-background-disabled) !important;
border-color: var(--button-blue-border-disabled) !important;
color: var(--button-blue-font-colour-disabled) !important;
}
@@ -2137,9 +2169,9 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
float: right;
}
#rom_edit {
/* #rom_edit {
padding: 10px;
}
} */
#rom_edit_panel {
background-color: rgba(56, 56, 56, 0.3);
@@ -2365,18 +2397,17 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
flex-direction: row;
flex-wrap: nowrap;
gap: 1px;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
}
.loginwindow-logospace {
background-color: var(--modal-tabs-background-color);
background-color: var(--login-logospace-background);
width: 315px;
position: relative;
}
.loginwindow_loginspace {
background-color: var(--modal-window-background-color);
background-color: var(--login-loginspace-background);
padding: 20px;
flex-grow: 1;
}
@@ -2425,7 +2456,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
font-size: 18pt;
vertical-align: top;
/*color: #edeffa;*/
color: white;
color: var(--login-logospace-label);
margin-top: 8px;
}
@@ -2451,9 +2482,9 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
.dropdown-content {
display: none;
position: absolute;
background-color: var(--modal-background-color);
background-color: var(--dropdown-background);
border-radius: var(--standard-radius);
border-color: var(--modal-border-color);
border-color: var(--dropdown-border);
border-width: 1px;
border-style: solid;
overflow: hidden;
@@ -2465,15 +2496,13 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
filter: drop-shadow(5px 5px 10px #000);
-webkit-filter: drop-shadow(5px 5px 10px #000);
padding: 3px;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
cursor: default;
}
/* Links inside the dropdown */
.dropdown-content a,
.dropdown-content span {
color: white;
color: var(--dropdown-menu-text-color);
padding: 12px 16px;
text-decoration: none;
display: block;
@@ -2488,7 +2517,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
/* Change color of dropdown links on hover */
.dropdown-content a:hover {
background-color: var(--modal-border-color);
background-color: var(--dropdown-border);
}
/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
@@ -2573,6 +2602,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
}
.section-header {
position: relative;
background-color: var(--section-header-background);
padding: 10px;
font-weight: bold;
@@ -2658,7 +2688,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
border-radius: var(--standard-radius);
border-style: solid;
border-width: 1px;
border-color: rgba(173, 216, 230, 0.3);
border-color: var(--profile-card-border);
overflow: hidden;
position: relative;
}
@@ -2681,7 +2711,7 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
aspect-ratio: 4.17/1;
background-size: cover;
background-position: center;
background-color: rgb(0, 0, 130);
background-color: var(--profile-card-background-image);
}
.profile-card-body {
@@ -2690,15 +2720,13 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
padding-left: 10px;
padding-right: 10px;
padding-bottom: 20px;
background-color: rgba(173, 216, 230, 0.3);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
background-color: var(--profile-card-body-background);
}
.profile-card-display-name {
font-weight: bold;
font-size: 16px;
color: white;
color: var(--profile-card-display-name-text-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -2707,7 +2735,57 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
.profile-card-quip {
margin-top: 3px;
font-style: italic;
color: white;
color: var(--profile-card-quip-text-color);
}
.profile-card-now-playing-body-bg {
position: relative;
min-height: 70px;
background-position: center;
background-size: cover;
}
.profile-card-now-playing-body {
position: relative;
min-height: 70px;
/* background-color: var(--profile-card-body-background); */
background-color: var(--profile-card-now-playing-background-color);
padding-top: 10px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px;
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
}
.profile-card-now-playing-label {
font-style: italic;
margin-bottom: 5px;
}
.profile-card-now-playing-cover {
position: absolute;
left: 10px;
top: 35px;
width: 40px;
height: 40px;
background-position: center center;
background-repeat: no-repeat;
background-size: contain;
}
.profile-card-now-playing-title {
margin-left: 50px;
font-weight: bold;
}
.profile-card-now-playing-platform {
margin-left: 50px;
}
.profile-card-now-playing-duration {
margin-top: 10px;
margin-left: 50px;
}
.password-rules {
@@ -2806,8 +2884,6 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
display: block;
background: var(--section-body-background);
background: linear-gradient(0deg, var(--section-body-background-solid) 25%, rgba(255, 255, 255, 0) 100%);
backdrop-filter: blur(8px);
--webkit-backdrop-filter: blur(8px);
padding: 5px;
vertical-align: bottom;
content: "";
@@ -2847,13 +2923,13 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 5px;
gap: 10px;
}
.platform_item {
padding: 10px;
flex-grow: 1;
flex-basis: 45%;
/* flex-basis: 45%; */
border-radius: var(--standard-radius);
/* background-color: var(--section-header-background); */
background: linear-gradient(90deg, rgba(200, 200, 200, 0.7469362745098039) 0%, rgba(133, 133, 133, 1) 50%);
@@ -2862,9 +2938,13 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
cursor: pointer;
display: flex;
gap: 20px;
padding-left: 0;
align-items: center;
position: relative;
overflow: hidden;
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
.platform_item:hover {
@@ -2878,15 +2958,30 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
.platform_item_background:hover {}
.platform_image_container {
margin-left: 10px;
margin-right: 10px;
max-width: 50px;
max-height: 50px;
width: 70px;
height: 70px;
flex-grow: 0;
/* text-align: center;
vertical-align: middle;
line-height: 75px; */
display: flex;
justify-content: center;
align-items: center;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
background-color: white;
border-right-style: solid;
border-right-width: 1px;
border-right-color: #000000;
}
.platform_image {
width: 40px;
max-width: 90px;
max-height: 60px;
}
.platform_name_container {
@@ -2895,19 +2990,27 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
.platform_name {
display: block;
/* max-width: 75px; */
width: 25%;
height: auto;
}
.platform_edit_button_container {
width: 90px;
/* width: 90px; */
flex-grow: 0;
text-align: right;
padding-right: 5px;
}
.platform_edit_button {
position: absolute;
/* position: absolute;
right: 14px;
top: 14px;
top: 14px;*/
width: 20px;
height: 20px;
display: inline-block;
margin-left: 10px;
background-color: var(--fancybutton-background);
border-radius: var(--standard-radius);
border-width: 1px;
@@ -2951,3 +3054,11 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no
width: 160px;
white-space: normal;
}
.metadata-attribution-icon {
width: 30px;
height: 30px;
margin-right: 13px;
margin-left: 10px;
filter: invert(1);
}

View File

@@ -42,7 +42,8 @@
--input-font-colour: var(--page-font-colour);
/* button */
--button-background: rgb(85, 85, 85);
--button-background: rgb(110, 109, 109);
--button-background-gradient: linear-gradient(140deg, rgba(110, 109, 109, 1) 0%, rgba(85, 85, 85, 1) 100%);
--button-background-hover: rgb(136, 136, 136);
--button-background-disabled: var(--button-background-hover);
--button-border: rgb(85, 85, 85);
@@ -51,19 +52,21 @@
--button-font-colour: rgb(255, 255, 255);
--button-font-colour-hover: var(--button-font-colour);
--button-font-colour-disabled: var(--button-border-hover);
--button-red-background: darkred;
--button-red-background-hover: red;
--button-red-background: rgb(139, 0, 0);
--button-red-background-gradient: linear-gradient(140deg, rgba(255, 0, 0, 1) 0%, rgba(139, 0, 0, 1) 100%);
--button-red-background-hover: rgb(255, 0, 0);
--button-red-background-disabled: rgb(85, 85, 85);
--button-red-border: darkred;
--button-red-border-hover: red;
--button-red-border: rgb(139, 0, 0);
--button-red-border-hover: rgb(255, 0, 0);
--button-red-border-disabled: rgb(85, 85, 85);
--button-red-font-colour: rgb(255, 255, 255);
--button-red-font-colour-hover: var(--button-red-font-colour);
--button-red-font-colour-disabled: var(--button-red-font-colour);
--button-blue-background: rgb(0, 0, 170);
--button-blue-background-gradient: linear-gradient(140deg, rgba(0, 0, 255, 1) 0%, rgba(0, 0, 150, 1) 100%);
--button-blue-background-hover: rgb(0, 0, 255);
--button-blue-background-disabled: rgb(85, 85, 85);
--button-blue-border: rgb(0, 0, 170);
--button-blue-border: rgb(0, 0, 255);
--button-blue-border-hover: rgb(0, 0, 255);
--button-blue-border-disabled: rgb(85, 85, 85);
--button-blue-font-colour: rgb(255, 255, 255);
@@ -71,7 +74,7 @@
--button-blue-font-colour-disabled: var(--button-blue-font-colour);
/* select2 */
--select2-background: var(--input-background);
--select2-background: rgb(43, 43, 43);
--select2-background-disabled: var(--input-background-disabled);
--select2-border: var(--input-border);
--select2-border-hover: var(--input-border-hover);
@@ -106,8 +109,29 @@
/* ------------------------------------------- */
--modal-background-color: rgba(0, 0, 0, 0.4);
--modal-border-color: rgba(255, 255, 255, 0.414);
--modal-tabs-background-color: rgba(255, 255, 255, 0.2);
--modal-window-background-color: rgba(70, 70, 70, 0.6);
--modal-tabs-background-color: rgb(100, 100, 100);
--modal-window-background-color: rgb(79, 79, 79);
--modal-window-header-text-color: rgb(255, 255, 255);
--modal-window-header-background-color: rgb(56, 56, 56);
/* dropdown */
/* ------------------------------------------- */
--dropdown-background: rgb(0, 0, 0);
--dropdown-border: var(--modal-border-color);
--dropdown-menu-text-color: rgb(255, 255, 255);
/* profile card */
/* ------------------------------------------- */
--profile-card-border: rgba(173, 216, 230, 0.3);
--profile-card-background-image: rgb(0, 0, 130);
--profile-card-body-background: rgb(133, 156, 163);
--profile-card-display-name-text-color: rgb(255, 255, 255);
--profile-card-quip-text-color: var(--profile-card-display-name-text-color);
--profile-card-now-playing-background-color: rgba(0, 0, 0, 0.7);
/* login */
/* ------------------------------------------- */
--login-logospace-background: rgba(100, 100, 100, 0.7);
--login-loginspace-background: rgba(79, 79, 79, 0.7);
--login-logospace-label: rgb(255, 255, 255);
}