Compare commits
38 Commits
v1.7.0-pre
...
v1.7.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
07c973cd75 | ||
![]() |
b94950fed0 | ||
![]() |
b5511a3c51 | ||
![]() |
73174a30f0 | ||
![]() |
a2d863dc7a | ||
![]() |
3c451f5558 | ||
![]() |
645327bdd1 | ||
![]() |
7754fd5dda | ||
![]() |
5e45fc1aa9 | ||
![]() |
36938ed2f8 | ||
![]() |
344d37c96a | ||
![]() |
60da78fa7d | ||
![]() |
cb3c7fa901 | ||
![]() |
b0c8075865 | ||
![]() |
0d8d2707cb | ||
![]() |
9fce071af6 | ||
![]() |
da0813b76f | ||
![]() |
5f6a71e065 | ||
![]() |
f65408a64d | ||
![]() |
3a9d3df013 | ||
![]() |
69863f8b61 | ||
![]() |
10be6c53ba | ||
![]() |
163aa7a446 | ||
![]() |
ec115b33de | ||
![]() |
9b8874902a | ||
![]() |
127eab683b | ||
![]() |
1efc47f9cd | ||
![]() |
7f2e186d06 | ||
![]() |
7d5419d33c | ||
![]() |
ce9ab91e5b | ||
![]() |
eac35ee8a3 | ||
![]() |
49f36a2b99 | ||
![]() |
47c2fc8069 | ||
![]() |
9a215123f6 | ||
![]() |
40597b4386 | ||
![]() |
eb9c1ce1a4 | ||
![]() |
7be1ec7080 | ||
![]() |
311c7733fa |
4
.devcontainer/.env
Normal file
4
.devcontainer/.env
Normal file
@@ -0,0 +1,4 @@
|
||||
DATABASE_HOST=mariadb
|
||||
DATABASE_USER=root
|
||||
DATABASE_PASSWORD=gaseous
|
||||
DATABASE_DB=gaseous
|
6
.devcontainer/Dockerfile
Normal file
6
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
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.11.7z
|
||||
RUN 7z x -y -o/workspace/gaseous-server/wwwroot/emulators/EmulatorJS 4.0.11.7z
|
46
.devcontainer/devcontainer.json
Normal file
46
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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)",
|
||||
// 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],
|
||||
"portsAttributes": {
|
||||
"5198": {
|
||||
"protocol": "http"
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "dotnet restore",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"cweijan.vscode-mysql-client2",
|
||||
"ms-dotnettools.csdevkit",
|
||||
"ms-dotnettools.csharp",
|
||||
"ms-dotnettools.vscode-dotnet-runtime",
|
||||
"ecmel.vscode-html-css",
|
||||
"github.vscode-github-actions",
|
||||
"GitHub.vscode-pull-request-github",
|
||||
"AndersEAndersen.html-class-suggestions",
|
||||
"george-alisson.html-preview-vscode",
|
||||
"ms-dotnettools.vscodeintellicode-csharp",
|
||||
"Zignd.html-css-class-completion"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
23
.devcontainer/docker-compose.yml
Normal file
23
.devcontainer/docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
development:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ..:/workspace
|
||||
stdin_open: true
|
||||
environment:
|
||||
- TZ=Australia/Sydney
|
||||
- dbhost=${DATABASE_HOST}
|
||||
- dbuser=${DATABASE_USER}
|
||||
- dbpass=${DATABASE_PASSWORD}
|
||||
- igdbclientid=<clientid>
|
||||
- igdbclientsecret=<clientsecret>
|
||||
mariadb:
|
||||
hostname: mariadb
|
||||
image: mariadb:latest
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=${DATABASE_PASSWORD}
|
||||
- MARIADB_DATABASE=${DATABASE_DB}
|
||||
- MARIADB_USER=${DATABASE_USER}
|
||||
- MARIADB_PASSWORD=${DATABASE_PASSWORD}
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -9,9 +9,7 @@ updates:
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "gitsubmodule"
|
||||
- package-ecosystem: "devcontainers"
|
||||
directory: "/"
|
||||
allow:
|
||||
- dependency-name: "gaseous-server/wwwroot/emulators/EmulatorJS"
|
||||
schedule:
|
||||
interval: "weekly"
|
4
.github/release.yml
vendored
4
.github/release.yml
vendored
@@ -5,8 +5,12 @@ changelog:
|
||||
- '*'
|
||||
exclude:
|
||||
labels:
|
||||
- note
|
||||
- bug
|
||||
- dependencies
|
||||
- title: Notes
|
||||
labels:
|
||||
- note
|
||||
- title: Bug Fixes
|
||||
labels:
|
||||
- bug
|
||||
|
@@ -33,6 +33,6 @@ jobs:
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64 #,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: gaseousgames/gaseousserver:${{ github.ref_name}}
|
||||
|
@@ -32,6 +32,6 @@ jobs:
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64 #,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: gaseousgames/gaseousserver:latest,gaseousgames/gaseousserver:${{ github.ref_name}}
|
||||
|
36
.github/workflows/BuildOnTestBranch.yml
vendored
Normal file
36
.github/workflows/BuildOnTestBranch.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Build test branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [test]
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
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.1
|
||||
- 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: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: gaseousgames/test:latest
|
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- 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: Restore dependencies
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -403,3 +403,5 @@ ASALocalRun/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
gaseous-server/.DS_Store
|
||||
gaseous-server/wwwroot/emulators/EmulatorJS
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "gaseous-server/wwwroot/emulators/EmulatorJS"]
|
||||
path = gaseous-server/wwwroot/emulators/EmulatorJS
|
||||
url = https://github.com/EmulatorJS/EmulatorJS.git
|
||||
|
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -10,14 +10,14 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/gaseous-server/bin/Debug/net7.0/gaseous-server.dll",
|
||||
"program": "${workspaceFolder}/gaseous-server/bin/Debug/net8.0/gaseous-server.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/gaseous-server",
|
||||
"stopAtEntry": false,
|
||||
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
"pattern": "\\bNow listening on:\\s+(http?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
|
20
Dockerfile
20
Dockerfile
@@ -1,16 +1,28 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
|
||||
ARG TARGETARCH
|
||||
ARG BUILDPLATFORM
|
||||
WORKDIR /App
|
||||
EXPOSE 80
|
||||
|
||||
RUN echo "Target: $TARGETARCH"
|
||||
RUN echo "Build: $BUILDPLATFORM"
|
||||
|
||||
# Copy everything
|
||||
COPY . ./
|
||||
# Restore as distinct layers
|
||||
RUN dotnet restore "gaseous-server/gaseous-server.csproj"
|
||||
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 false -c Release -o out
|
||||
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
|
||||
|
||||
# download and unzip EmulatorJS from CDN
|
||||
RUN apt-get update && apt-get install -y p7zip-full
|
||||
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||
RUN wget https://cdn.emulatorjs.org/releases/4.0.11.7z
|
||||
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.11.7z
|
||||
|
||||
# Build runtime image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:7.0
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
ENV INDOCKER=1
|
||||
WORKDIR /App
|
||||
COPY --from=build-env /App/out .
|
||||
ENTRYPOINT ["dotnet", "gaseous-server.dll"]
|
||||
|
@@ -1,9 +1,9 @@
|
||||
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 25.0.1704.4
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaseous-server", "gaseous-server\gaseous-server.csproj", "{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "gaseous-server", "gaseous-server\gaseous-server.csproj", "{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{17FA6F12-8532-420C-9489-CB8FDE42137C}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
193
README.MD
193
README.MD
@@ -1,194 +1,53 @@
|
||||
[](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml) [](https://github.com/gaseous-project/gaseous-server/actions/workflows/codeql.yml) [](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml)
|
||||
# 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
|
||||
|
||||
This project is currently not suitable for being exposed to the internet.
|
||||
1. there is currently no authentication support, meaning anyone could trash your library
|
||||
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 the server to the internet, **you do so at your own risk**.
|
||||
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.
|
||||
|
||||
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**.
|
||||
|
||||
## Screenshots
|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
## Requirements
|
||||
* MariaDB 11.1.2 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.
|
||||
* Currently MariaDB is the preferred database server, while MySQL will continue to be supported for existing users (they should be interchangable).
|
||||
* 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.
|
||||
|
||||
## 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
|
||||
* [RomM](https://github.com/zurdi15/romm): Another self hosted ROM manager. Discord: https://discord.gg/P5HtHnhUDH
|
||||
|
||||
## Third Party Projects
|
||||
The following projects are used by Gaseous
|
||||
* https://dotnet.microsoft.com/en-us/apps/aspnet
|
||||
* https://github.com/JamesNK/Newtonsoft.Json
|
||||
* https://www.nuget.org/packages/MySql.Data/8.0.32.1
|
||||
* https://github.com/kamranayub/igdb-dotnet
|
||||
* https://github.com/EmulatorJS/EmulatorJS
|
||||
* [ASP.NET](https://dotnet.microsoft.com/en-us/apps/aspnet)
|
||||
* [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
|
||||
* [MySQLConnector](https://mysqlconnector.net)
|
||||
* [IGDB-DOTNET](https://github.com/kamranayub/igdb-dotnet)
|
||||
* [EmulatorJS](https://github.com/EmulatorJS/EmulatorJS)
|
||||
|
||||
## Discord Server
|
||||
[](https://discord.gg/Nhu7wpT3k4)
|
||||
|
||||
# Setup
|
||||
|
||||
## Configuration File
|
||||
When Gaseous-Server is started for the first time, it creates a configuration file at ~/.gaseous-server/config.json if it doesn't exist. Some values can be filled in using environment variables (such as in the case of using docker).
|
||||
|
||||
### DatabaseConfiguration
|
||||
| Attribute | Environment Variable |
|
||||
| --------- | -------------------- |
|
||||
| HostName | dbhost |
|
||||
| UserName | dbuser |
|
||||
| Password | dbpass |
|
||||
|
||||
### IGDBConfiguration
|
||||
| Attribute | Environment Variable |
|
||||
| --------- | -------------------- |
|
||||
| ClientId | igdbclientid |
|
||||
| Secret. | igdbclientsecret |
|
||||
|
||||
### config.json
|
||||
```json
|
||||
{
|
||||
"DatabaseConfiguration": {
|
||||
"HostName": "localhost",
|
||||
"UserName": "gaseous",
|
||||
"Password": "gaseous",
|
||||
"DatabaseName": "gaseous",
|
||||
"Port": 3306
|
||||
},
|
||||
"IGDBConfiguration": {
|
||||
"ClientId": "<clientid>",
|
||||
"Secret": "<secret>"
|
||||
},
|
||||
"LoggingConfiguration": {
|
||||
"DebugLogging": false,
|
||||
"LogRetention": 7
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Docker
|
||||
### Deploy with the prebuilt Docker image
|
||||
Dockerfile and docker-compose.yml files have been provided to make deployment of the server as easy as possible.
|
||||
1. Download the docker-compose-{database}.yml file for the database type you would like to use.
|
||||
2. Open the docker-compose.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
|
||||
3. Run the command ```docker-compose up -d```
|
||||
4. Connect to the host on port 5198
|
||||
|
||||
### Build and deploy a Docker image from source
|
||||
Dockerfile and docker-compose-build.yml files have been provided to make deployment of the server as easy as possible.
|
||||
1. Clone the repo with ```git clone https://github.com/gaseous-project/gaseous-server.git```
|
||||
2. Change into the gaseous-server directory
|
||||
3. Clone the submodules with the command ```git submodule update --init```
|
||||
4. Open the docker-compose-{database}-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
|
||||
5. Run the command ```docker-compose --file docker-compose-{database}-build.yml up -d```
|
||||
6. Connect to the host on port 5198
|
||||
|
||||
## Source
|
||||
### Build and deploy
|
||||
1. Install and configure a MariaDB or MySQL instance - this is beyond the scope of this document
|
||||
2. Install the dotnet 7.0 packages appropriate for your operating system
|
||||
* See: https://learn.microsoft.com/en-us/dotnet/core/install/linux
|
||||
3. Create a database user with permission to create a databse. Gaseous will create the new database and apply the database schema on it's first startup.
|
||||
4. Clone the repo with ```git clone https://github.com/gaseous-project/gaseous-server.git```
|
||||
5. Change into the gaseous-server directory
|
||||
6. As the main branch is the development branch, you might want to change to a stable version - these are tagged with a version number. For example to change to the 1.5.0 release, use the command ```git checkout v1.5.0```
|
||||
* Check the releases page for the version you would like to run: https://github.com/gaseous-project/gaseous-server/releases
|
||||
7. Clone the submodules with the command ```git submodule update --init --recursive```
|
||||
* This command will clone the code that the server uses from other projects (currently only EmulatorJS)
|
||||
8. Create a directory in the home directory of the user that will run the server. For example, if running as the user ```gaseous```, create the directory ```/home/gaseous/.gaseous-server```
|
||||
9. Change into the ```.gaseous-server``` directory created in the previous step
|
||||
10. Copy the JSON from the config file above into a new file named ```config.json```
|
||||
11. Update the database section with the database server hostname, username, password, and port
|
||||
12. Compile the server by changing back to the repo cloned earlier and executing:
|
||||
* ```dotnet restore "gaseous-server/gaseous-server.csproj"```
|
||||
* ```dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained false -c Release -o <output directory>```
|
||||
* replace ```<output directory>``` with the directory of your choosing. The compiled application will be copied there. For this example we'll use ```/opt/gaseous-server```
|
||||
13. The server can then be started by executing ```dotnet /opt/gaseous-server/gaseous-server.dll```
|
||||
* If you would like the server to run on a different ip address and port (for example 0.0.0.0:8080), add the --urls argument: ```dotnet /opt/gaseous-server/gaseous-server.dll --urls http://0.0.0.0:8080```
|
||||
|
||||
**Note**: The above instructions were tested on macOS Ventura, and Ubuntu 22.04.3. There was a report that Debian 11 had an issue with the git submodule commands (see: https://github.com/gaseous-project/gaseous-server/issues/71). This was possibly due to an older git package.
|
||||
|
||||
If the git submodule commands aren't working, you can:
|
||||
1. change to the ```gaseous-server/wwwroot/emulators``` directory
|
||||
2. delete the ```EmulatorJS``` directory
|
||||
3. clone the EmulatorJS repository with ```git clone https://github.com/EmulatorJS/EmulatorJS.git```
|
||||
|
||||
### Updating from source
|
||||
1. Stop the server
|
||||
2. Switch to the source directory
|
||||
3. Update your repo:
|
||||
* If running from the main branch, run ```git pull``` to update the repo
|
||||
* If running from another branch or tag, run:
|
||||
* ```git fetch```
|
||||
* ```git checkout <branch or tag name>```
|
||||
4. Update the submodules with ```git submodule update --recursive```
|
||||
5. Run steps 12 and 13 from the above Build guide
|
||||
# Installation
|
||||
See https://github.com/gaseous-project/gaseous-server/wiki/Installation for installation instructions.
|
||||
|
||||
# Adding Content
|
||||
While games can be added to the server without them, it is recommended adding some signature DAT files beforehand to allow for better matching of ROMs to games.
|
||||
|
||||
These signature DAT files contain a list of titles with hashes for many of the ROM images that have been found by the community.
|
||||
|
||||
Currently supported DAT's:
|
||||
* TOSEC: https://www.tosecdev.org/downloads/category/56-2023-01-23
|
||||
* MAME Arcade and MAME Mess: https://www.progettosnaps.net/dats/MAME
|
||||
|
||||
If there are other DAT's you'd like to see support for, please raise an issue with a link to the DAT's.
|
||||
|
||||
## Adding signature DAT files
|
||||
### TOSEC
|
||||
1. Download the DAT files from the source website. For example; from https://www.tosecdev.org/downloads/category/56-2023-01-23
|
||||
2. Extract the archive
|
||||
3. Copy the DAT files to ~/.gaseous-server/Data/Signatures/TOSEC/
|
||||
|
||||
### MAME Arcade
|
||||
1. Download the DAT files from the source website. For example; from https://www.progettosnaps.net/dats/MAME
|
||||
2. Extract the archive
|
||||
3. Copy the file name "MAME 0.257 (arcade).dat" files to ~/.gaseous-server/Data/Signatures/MAME Arcade/
|
||||
|
||||
### MAME MESS
|
||||
1. Download the DAT files from the source website. For example; from https://www.progettosnaps.net/dats/MAME
|
||||
2. Extract the archive
|
||||
3. Copy the file name "MAME 0.257 (mess).dat" files to ~/.gaseous-server/Data/Signatures/MAME MESS/
|
||||
|
||||
# Adding Game Images
|
||||
1. Files can be presented as either stand alone files, or as zip files - currently 7z is unsupported.
|
||||
2. Name the file appropriately.
|
||||
* Attempting a search for the game name on https://www.igdb.com can help with file naming. If a hash search is unsuccessful, Gaseous will fall back to attempting to search by the file name.
|
||||
3. Add the file to the server:
|
||||
* Click the Upload button in the top right of the main Gaseous web page, and drag the files into the modal. The files will be uploaded and analyzed.
|
||||
* Copy the file to ~/.gaseous-server/Data/Import
|
||||
|
||||
# Game Image Title Matching
|
||||
Image to game matching follows the following order of operations, stopping the process at the first match:
|
||||
### Get the file signature
|
||||
1. Attempt a hash search
|
||||
2. Attempt to search the signature database for a rom matching the file name - sometimes the hash can not be matched as a highscore table for example was saved to the image
|
||||
3. Attempt to parse the file name - clues such as the extension being used to define which platform the file belongs to are used to create a search criteria
|
||||
|
||||
**Note**: If the file being scanned is a zip, the file will be extracted and searched. The first file whose signature can be found will be used to match the entire zip archive - be sure that the zip only contains files related to one game.
|
||||
|
||||
### Create a list of search candidates
|
||||
Before beginning, remove any version numbers, and anything in the search string that is between ()
|
||||
1. Add the full name of the image
|
||||
2. Add the name of the image with any " - " replaced by ": "
|
||||
3. Add the name of the image with text after a " - " removed
|
||||
4. Add the name of the image with text after a ": " removed
|
||||
|
||||
### Search IGDB for a game match
|
||||
Loop through each of the search candidates searching using:
|
||||
1. "where" - exact match as the search candidate
|
||||
2. "wherefuzzy" - partial match using wildcards
|
||||
3. "search" - uses a more flexible search method
|
||||
4. "searchNoPlatform" - uses the "search" method, but does not constrain the search to the determined platform
|
||||
|
||||
**Note**: If more than one result is found, the seach will loop through the returned results:
|
||||
* If an exact (case-insensitive) match is found, that result is used for the match
|
||||
* If still no match, the image will be set as "Unknown" as there is no way for Gaseous to know which title is the correct one.
|
||||
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
|
||||
|
@@ -14,6 +14,7 @@ services:
|
||||
volumes:
|
||||
- gs:/root/.gaseous-server
|
||||
environment:
|
||||
- TZ=Australia/Sydney
|
||||
- dbhost=gsdb
|
||||
- dbuser=root
|
||||
- dbpass=gaseous
|
@@ -1,39 +0,0 @@
|
||||
version: '2'
|
||||
services:
|
||||
gaseous-server:
|
||||
container_name: gaseous-server
|
||||
build:
|
||||
context: ./
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gaseous
|
||||
depends_on:
|
||||
- gsdb
|
||||
ports:
|
||||
- 5198:80
|
||||
volumes:
|
||||
- gs:/root/.gaseous-server
|
||||
environment:
|
||||
- dbhost=gsdb
|
||||
- dbuser=root
|
||||
- dbpass=gaseous
|
||||
- igdbclientid=<clientid>
|
||||
- igdbclientsecret=<clientsecret>
|
||||
gsdb:
|
||||
container_name: gsdb
|
||||
image: mysql:8
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gaseous
|
||||
volumes:
|
||||
- gsdb:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=gaseous
|
||||
- MYSQL_USER=gaseous
|
||||
- MYSQL_PASSWORD=gaseous
|
||||
networks:
|
||||
gaseous:
|
||||
driver: bridge
|
||||
volumes:
|
||||
gs:
|
||||
gsdb:
|
@@ -1,38 +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:/root/.gaseous-server
|
||||
environment:
|
||||
- dbhost=gsdb
|
||||
- dbuser=root
|
||||
- dbpass=gaseous
|
||||
- igdbclientid=<clientid>
|
||||
- igdbclientsecret=<clientsecret>
|
||||
gsdb:
|
||||
container_name: gsdb
|
||||
image: mysql:8
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gaseous
|
||||
volumes:
|
||||
- gsdb:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=gaseous
|
||||
- MYSQL_USER=gaseous
|
||||
- MYSQL_PASSWORD=gaseous
|
||||
networks:
|
||||
gaseous:
|
||||
driver: bridge
|
||||
volumes:
|
||||
gs:
|
||||
gsdb:
|
@@ -13,6 +13,7 @@ services:
|
||||
volumes:
|
||||
- gs:/root/.gaseous-server
|
||||
environment:
|
||||
- TZ=Australia/Sydney
|
||||
- dbhost=gsdb
|
||||
- dbuser=root
|
||||
- dbpass=gaseous
|
BIN
gaseous-server/.DS_Store
vendored
BIN
gaseous-server/.DS_Store
vendored
Binary file not shown.
BIN
gaseous-server/Assets/.DS_Store
vendored
BIN
gaseous-server/Assets/.DS_Store
vendored
Binary file not shown.
BIN
gaseous-server/Assets/Ratings/.DS_Store
vendored
BIN
gaseous-server/Assets/Ratings/.DS_Store
vendored
Binary file not shown.
@@ -12,5 +12,6 @@ namespace Authentication
|
||||
{
|
||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
||||
public Guid Avatar { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ namespace Authentication
|
||||
public TUser GetUserById(string userId)
|
||||
{
|
||||
TUser user = null;
|
||||
string commandText = "Select * from Users where Id = @id";
|
||||
string commandText = "Select * from Users LEFT JOIN (SELECT UserId, Id AS AvatarId FROM UserAvatars) UserAvatars ON Users.Id = UserAvatars.UserId where Id = @id";
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
|
||||
|
||||
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
||||
@@ -100,6 +100,7 @@ namespace Authentication
|
||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
||||
user.SecurityProfile = GetSecurityProfile(user);
|
||||
user.UserPreferences = GetPreferences(user);
|
||||
user.Avatar = string.IsNullOrEmpty((string?)row["AvatarId"]) ? Guid.Empty : Guid.Parse((string?)row["AvatarId"]);
|
||||
}
|
||||
|
||||
return user;
|
||||
@@ -113,7 +114,7 @@ namespace Authentication
|
||||
public List<TUser> GetUserByName(string normalizedUserName)
|
||||
{
|
||||
List<TUser> users = new List<TUser>();
|
||||
string commandText = "Select * from Users where NormalizedEmail = @name";
|
||||
string commandText = "Select * from Users LEFT JOIN (SELECT UserId, Id AS AvatarId FROM UserAvatars) UserAvatars ON Users.Id = UserAvatars.UserId where NormalizedEmail = @name";
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
|
||||
|
||||
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
||||
@@ -137,6 +138,7 @@ namespace Authentication
|
||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
||||
user.SecurityProfile = GetSecurityProfile(user);
|
||||
user.UserPreferences = GetPreferences(user);
|
||||
user.Avatar = string.IsNullOrEmpty((string?)row["AvatarId"]) ? Guid.Empty : Guid.Parse((string?)row["AvatarId"]);
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
@@ -146,7 +148,7 @@ namespace Authentication
|
||||
public List<TUser> GetUsers()
|
||||
{
|
||||
List<TUser> users = new List<TUser>();
|
||||
string commandText = "Select * from Users order by NormalizedUserName";
|
||||
string commandText = "Select * from Users LEFT JOIN (SELECT UserId, Id AS AvatarId FROM UserAvatars) UserAvatars ON Users.Id = UserAvatars.UserId order by NormalizedUserName";
|
||||
|
||||
var rows = _database.ExecuteCMDDict(commandText);
|
||||
foreach(Dictionary<string, object> row in rows)
|
||||
@@ -169,6 +171,7 @@ namespace Authentication
|
||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
||||
user.SecurityProfile = GetSecurityProfile(user);
|
||||
user.UserPreferences = GetPreferences(user);
|
||||
user.Avatar = string.IsNullOrEmpty((string?)row["AvatarId"]) ? Guid.Empty : Guid.Parse((string?)row["AvatarId"]);
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
@@ -289,7 +292,7 @@ namespace Authentication
|
||||
/// <returns></returns>
|
||||
private int Delete(string userId)
|
||||
{
|
||||
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;";
|
||||
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId; Delete from GameState where UserId = @userId;";
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
parameters.Add("@userId", userId);
|
||||
|
||||
@@ -437,5 +440,30 @@ namespace Authentication
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid SetAvatar(TUser user, byte[] bytes)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql;
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "userid", user.Id }
|
||||
};
|
||||
|
||||
if (bytes.Length == 0)
|
||||
{
|
||||
sql = "DELETE FROM UserAvatars WHERE UserId = @userid";
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
return Guid.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "DELETE FROM UserAvatars WHERE UserId = @userid; INSERT INTO UserAvatars (UserId, Id, Avatar) VALUES (@userid, @id, @avatar);";
|
||||
dbDict.Add("id", Guid.NewGuid());
|
||||
dbDict.Add("avatar", bytes);
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
return (Guid)dbDict["id"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ namespace Authentication
|
||||
public List<String> Roles { get; set; }
|
||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
||||
public Guid Avatar { get; set; }
|
||||
public string HighestRole {
|
||||
get
|
||||
{
|
||||
|
@@ -8,6 +8,7 @@ namespace Authentication
|
||||
public DateTimeOffset? LockoutEnd { get; set; }
|
||||
public List<string> Roles { get; set; }
|
||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||
public Guid Avatar { get; set; }
|
||||
public string HighestRole {
|
||||
get
|
||||
{
|
||||
|
@@ -7,32 +7,26 @@ using System.Security.Cryptography;
|
||||
using Authentication;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using gaseous_server.Controllers;
|
||||
using gaseous_server.Controllers.v1_1;
|
||||
using gaseous_server.Models;
|
||||
using IGDB.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Common;
|
||||
using static gaseous_server.Classes.Metadata.Games;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Collections
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public Collections(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
public static List<CollectionItem> GetCollections() {
|
||||
public static List<CollectionItem> GetCollections(string userid) {
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM RomCollections ORDER BY `Name`";
|
||||
|
||||
DataTable data = db.ExecuteCMD(sql);
|
||||
string sql = "SELECT * FROM RomCollections WHERE OwnedBy=@ownedby ORDER BY `Name`";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||
{ "ownedby", userid }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<CollectionItem> collectionItems = new List<CollectionItem>();
|
||||
|
||||
@@ -43,11 +37,24 @@ namespace gaseous_server.Classes
|
||||
return collectionItems;
|
||||
}
|
||||
|
||||
public static CollectionItem GetCollection(long Id) {
|
||||
public static CollectionItem GetCollection(long Id, string userid) {
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", Id);
|
||||
string sql;
|
||||
if (userid == "")
|
||||
{
|
||||
// reserved for internal operations
|
||||
sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`";
|
||||
}
|
||||
else
|
||||
{
|
||||
// instigated by a user
|
||||
sql = "SELECT * FROM RomCollections WHERE Id = @id AND OwnedBy = @ownedby ORDER BY `Name`";
|
||||
}
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", Id },
|
||||
{ "ownedby", userid }
|
||||
};
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (romDT.Rows.Count > 0)
|
||||
@@ -63,60 +70,68 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static CollectionItem NewCollection(CollectionItem item)
|
||||
public static CollectionItem NewCollection(CollectionItem item, string userid)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("name", item.Name);
|
||||
dbDict.Add("description", item.Description);
|
||||
dbDict.Add("platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())));
|
||||
dbDict.Add("genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())));
|
||||
dbDict.Add("players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())));
|
||||
dbDict.Add("playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())));
|
||||
dbDict.Add("themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())));
|
||||
dbDict.Add("minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1));
|
||||
dbDict.Add("maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1));
|
||||
dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1));
|
||||
dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1));
|
||||
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
|
||||
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
|
||||
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
|
||||
dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())));
|
||||
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
|
||||
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, ArchiveType, AlwaysInclude, BuiltStatus, OwnedBy) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @archivetype, @alwaysinclude, @builtstatus, @ownedby); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "name", item.Name },
|
||||
{ "description", item.Description },
|
||||
{ "platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())) },
|
||||
{ "genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())) },
|
||||
{ "players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())) },
|
||||
{ "playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())) },
|
||||
{ "themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())) },
|
||||
{ "minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1) },
|
||||
{ "maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1) },
|
||||
{ "maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1) },
|
||||
{ "maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1) },
|
||||
{ "maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1) },
|
||||
{ "folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous) },
|
||||
{ "includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0) },
|
||||
{ "archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip) },
|
||||
{ "alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())) },
|
||||
{ "builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild },
|
||||
{ "ownedby", userid }
|
||||
};
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
long CollectionId = (long)romDT.Rows[0][0];
|
||||
|
||||
CollectionItem collectionItem = GetCollection(CollectionId);
|
||||
CollectionItem collectionItem = GetCollection(CollectionId, userid);
|
||||
|
||||
StartCollectionItemBuild(CollectionId);
|
||||
StartCollectionItemBuild(CollectionId, userid);
|
||||
|
||||
return collectionItem;
|
||||
}
|
||||
|
||||
public static CollectionItem EditCollection(long Id, CollectionItem item, bool ForceRebuild = true)
|
||||
public static CollectionItem EditCollection(long Id, CollectionItem item, string userid, bool ForceRebuild = true)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", Id);
|
||||
dbDict.Add("name", item.Name);
|
||||
dbDict.Add("description", item.Description);
|
||||
dbDict.Add("platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())));
|
||||
dbDict.Add("genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())));
|
||||
dbDict.Add("players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())));
|
||||
dbDict.Add("playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())));
|
||||
dbDict.Add("themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())));
|
||||
dbDict.Add("minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1));
|
||||
dbDict.Add("maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1));
|
||||
dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1));
|
||||
dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1));
|
||||
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
|
||||
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
|
||||
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
|
||||
dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())));
|
||||
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, ArchiveType=@archivetype, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id AND OwnedBy=@ownedby";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", Id },
|
||||
{ "name", item.Name },
|
||||
{ "description", item.Description },
|
||||
{ "platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())) },
|
||||
{ "genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())) },
|
||||
{ "players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())) },
|
||||
{ "playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())) },
|
||||
{ "themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())) },
|
||||
{ "minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1) },
|
||||
{ "maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1) },
|
||||
{ "maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1) },
|
||||
{ "maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1) },
|
||||
{ "maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1) },
|
||||
{ "folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous) },
|
||||
{ "includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0) },
|
||||
{ "alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())) },
|
||||
{ "archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip) },
|
||||
{ "ownedby", userid }
|
||||
};
|
||||
|
||||
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
|
||||
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + item.ArchiveExtension);
|
||||
if (ForceRebuild == true)
|
||||
{
|
||||
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
|
||||
@@ -139,22 +154,25 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
CollectionItem collectionItem = GetCollection(Id);
|
||||
CollectionItem collectionItem = GetCollection(Id, userid);
|
||||
|
||||
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
|
||||
{
|
||||
StartCollectionItemBuild(Id);
|
||||
StartCollectionItemBuild(Id, userid);
|
||||
}
|
||||
|
||||
return collectionItem;
|
||||
}
|
||||
|
||||
public static void DeleteCollection(long Id)
|
||||
public static void DeleteCollection(long Id, string userid)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM RomCollections WHERE Id=@id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", Id);
|
||||
string sql = "DELETE FROM RomCollections WHERE Id=@id AND OwnedBy=@ownedby";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", Id },
|
||||
{ "ownedby", userid }
|
||||
};
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
|
||||
@@ -164,9 +182,10 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static void StartCollectionItemBuild(long Id)
|
||||
public static void StartCollectionItemBuild(long Id, string userid)
|
||||
{
|
||||
CollectionItem collectionItem = GetCollection(Id);
|
||||
// send blank user id to getcollection as this is not a user initiated process
|
||||
CollectionItem collectionItem = GetCollection(Id, userid);
|
||||
|
||||
if (collectionItem.BuildStatus != CollectionItem.CollectionBuildStatus.Building)
|
||||
{
|
||||
@@ -180,13 +199,40 @@ namespace gaseous_server.Classes
|
||||
|
||||
// start background task
|
||||
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 1, false, true);
|
||||
queueItem.Options = Id;
|
||||
queueItem.Options = new Dictionary<string, object>{
|
||||
{ "Id", Id },
|
||||
{ "UserId", userid }
|
||||
};
|
||||
queueItem.ForceExecute();
|
||||
ProcessQueue.QueueItems.Add(queueItem);
|
||||
}
|
||||
}
|
||||
|
||||
public static CollectionContents GetCollectionContent(CollectionItem collectionItem) {
|
||||
public static CollectionContents GetCollectionContent(CollectionItem collectionItem, string userid) {
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
// get age ratings for specified user
|
||||
List<AgeGroups.AgeRestrictionGroupings> UserAgeGroupings = new List<AgeGroups.AgeRestrictionGroupings>();
|
||||
bool UserAgeGroupIncludeUnrated = true;
|
||||
if (userid != "")
|
||||
{
|
||||
Authentication.UserTable<Authentication.ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||
var user = userTable.GetUserById(userid);
|
||||
|
||||
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == false)
|
||||
{
|
||||
UserAgeGroupIncludeUnrated = false;
|
||||
}
|
||||
|
||||
foreach (AgeGroups.AgeRestrictionGroupings ageGrouping in Enum.GetValues(typeof(AgeGroups.AgeRestrictionGroupings)))
|
||||
{
|
||||
if (ageGrouping <= user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction && ageGrouping != AgeGroups.AgeRestrictionGroupings.Unclassified)
|
||||
{
|
||||
UserAgeGroupings.Add(ageGrouping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = new List<CollectionContents.CollectionPlatformItem>();
|
||||
|
||||
// get platforms
|
||||
@@ -227,6 +273,10 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
// age ratings
|
||||
AgeGroups.AgeRestrictionGroupings AgeGrouping = AgeGroups.AgeRestrictionGroupings.Unclassified;
|
||||
bool ContainsUnclassifiedAgeGroup = false;
|
||||
|
||||
// build collection
|
||||
List<CollectionContents.CollectionPlatformItem> platformItems = new List<CollectionContents.CollectionPlatformItem>();
|
||||
|
||||
@@ -244,18 +294,29 @@ namespace gaseous_server.Classes
|
||||
isDynamic = true;
|
||||
}
|
||||
|
||||
List<Game> games = new List<Game>();
|
||||
Controllers.v1_1.GamesController.GameReturnPackage games = new Controllers.v1_1.GamesController.GameReturnPackage();
|
||||
if (isDynamic == true)
|
||||
{
|
||||
games = GamesController.GetGames("",
|
||||
platform.Id.ToString(),
|
||||
string.Join(",", collectionItem.Genres),
|
||||
string.Join(",", collectionItem.Players),
|
||||
string.Join(",", collectionItem.PlayerPerspectives),
|
||||
string.Join(",", collectionItem.Themes),
|
||||
collectionItem.MinimumRating,
|
||||
collectionItem.MaximumRating
|
||||
);
|
||||
Controllers.v1_1.GamesController.GameSearchModel searchModel = new Controllers.v1_1.GamesController.GameSearchModel{
|
||||
Name = "",
|
||||
Platform = new List<string>{
|
||||
platform.Id.ToString()
|
||||
},
|
||||
Genre = collectionItem.Genres.ConvertAll(s => s.ToString()),
|
||||
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{
|
||||
MinimumRating = collectionItem.MinimumRating,
|
||||
MaximumRating = collectionItem.MaximumRating
|
||||
},
|
||||
GameAgeRating = new Controllers.v1_1.GamesController.GameSearchModel.GameAgeRatingItem{
|
||||
AgeGroupings = UserAgeGroupings,
|
||||
IncludeUnrated = UserAgeGroupIncludeUnrated
|
||||
}
|
||||
};
|
||||
games = Controllers.v1_1.GamesController.GetGames(searchModel, userid);
|
||||
|
||||
}
|
||||
|
||||
CollectionContents.CollectionPlatformItem collectionPlatformItem = new CollectionContents.CollectionPlatformItem(platform);
|
||||
@@ -271,7 +332,7 @@ namespace gaseous_server.Classes
|
||||
) && alwaysIncludeItem.PlatformId == platform.Id
|
||||
)
|
||||
{
|
||||
Game AlwaysIncludeGame = Games.GetGame(alwaysIncludeItem.GameId, false, false, false);
|
||||
MinimalGameItem AlwaysIncludeGame = new MinimalGameItem(Games.GetGame(alwaysIncludeItem.GameId, false, false, false));
|
||||
CollectionContents.CollectionPlatformItem.CollectionGameItem gameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(AlwaysIncludeGame);
|
||||
gameItem.InclusionStatus = new CollectionItem.AlwaysIncludeItem();
|
||||
gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId;
|
||||
@@ -283,7 +344,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Game game in games) {
|
||||
foreach (MinimalGameItem game in games.Games) {
|
||||
bool gameAlreadyInList = false;
|
||||
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem existingGame in collectionPlatformItem.Games)
|
||||
{
|
||||
@@ -304,7 +365,7 @@ namespace gaseous_server.Classes
|
||||
// calculate total rom size for the game
|
||||
long GameRomSize = 0;
|
||||
foreach (Roms.GameRomItem gameRom in gameRoms) {
|
||||
GameRomSize += gameRom.Size;
|
||||
GameRomSize += (long)gameRom.Size;
|
||||
}
|
||||
if (collectionItem.MaximumBytesPerPlatform > 0) {
|
||||
if ((TotalRomSize + GameRomSize) < collectionItem.MaximumBytesPerPlatform) {
|
||||
@@ -338,6 +399,17 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle age grouping
|
||||
AgeGroups.AgeRestrictionGroupings CurrentAgeGroup = AgeGroups.GetAgeGroupFromAgeRatings(game.AgeRatings);
|
||||
if (CurrentAgeGroup > AgeGrouping)
|
||||
{
|
||||
AgeGrouping = CurrentAgeGroup;
|
||||
}
|
||||
if (CurrentAgeGroup == AgeGroups.AgeRestrictionGroupings.Unclassified)
|
||||
{
|
||||
ContainsUnclassifiedAgeGroup = true;
|
||||
}
|
||||
}
|
||||
|
||||
collectionPlatformItem.Games.Sort((x, y) => x.Name.CompareTo(y.Name));
|
||||
@@ -366,30 +438,40 @@ namespace gaseous_server.Classes
|
||||
|
||||
collectionPlatformItems.Sort((x, y) => x.Name.CompareTo(y.Name));
|
||||
|
||||
CollectionContents collectionContents = new CollectionContents();
|
||||
collectionContents.Collection = collectionPlatformItems;
|
||||
CollectionContents collectionContents = new CollectionContents
|
||||
{
|
||||
Collection = collectionPlatformItems,
|
||||
AgeGroup = AgeGrouping,
|
||||
ContainsUnclassifiedAgeGroup = ContainsUnclassifiedAgeGroup
|
||||
};
|
||||
|
||||
return collectionContents;
|
||||
}
|
||||
|
||||
public static void CompileCollections(long CollectionId)
|
||||
public static void CompileCollections(long CollectionId, string userid)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
CollectionItem collectionItem = GetCollection(CollectionId);
|
||||
CollectionItem collectionItem = GetCollection(CollectionId, userid);
|
||||
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Collections", "Beginning build of collection: " + collectionItem.Name);
|
||||
|
||||
CollectionContents collectionContents = GetCollectionContent(collectionItem, userid);
|
||||
|
||||
// set starting
|
||||
string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", collectionItem.Id);
|
||||
dbDict.Add("bs", CollectionItem.CollectionBuildStatus.Building);
|
||||
string sql = "UPDATE RomCollections SET BuiltStatus=@bs, AgeGroup=@ag, AgeGroupUnclassified=@agu WHERE Id=@id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", collectionItem.Id },
|
||||
{ "bs", CollectionItem.CollectionBuildStatus.Building },
|
||||
{ "ag", collectionContents.AgeGroup },
|
||||
{ "agu", collectionContents.ContainsUnclassifiedAgeGroup }
|
||||
};
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = GetCollectionContent(collectionItem).Collection;
|
||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + ".zip");
|
||||
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = collectionContents.Collection;
|
||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + collectionItem.ArchiveExtension);
|
||||
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString());
|
||||
|
||||
try
|
||||
@@ -508,7 +590,21 @@ namespace gaseous_server.Classes
|
||||
|
||||
// compress to zip
|
||||
Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection");
|
||||
switch(collectionItem.ArchiveType)
|
||||
{
|
||||
case CollectionItem.ArchiveTypes.Zip:
|
||||
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
|
||||
break;
|
||||
|
||||
case CollectionItem.ArchiveTypes.RAR:
|
||||
|
||||
break;
|
||||
|
||||
case CollectionItem.ArchiveTypes.SevenZip:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// clean up
|
||||
if (Directory.Exists(ZipFileTempPath))
|
||||
@@ -567,6 +663,7 @@ namespace gaseous_server.Classes
|
||||
item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1);
|
||||
item.FolderStructure = (CollectionItem.FolderStructures)(int)Common.ReturnValueIfNull(row["FolderStructure"], 0);
|
||||
item.IncludeBIOSFiles = (bool)row["IncludeBIOSFiles"];
|
||||
item.ArchiveType = (CollectionItem.ArchiveTypes)(int)Common.ReturnValueIfNull(row["ArchiveType"], 0);
|
||||
item.AlwaysInclude = Newtonsoft.Json.JsonConvert.DeserializeObject<List<CollectionItem.AlwaysIncludeItem>>(strAlwaysInclude);
|
||||
item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0);
|
||||
|
||||
@@ -595,6 +692,32 @@ namespace gaseous_server.Classes
|
||||
public long? MaximumCollectionSizeInBytes { get; set; }
|
||||
public FolderStructures FolderStructure { get; set; } = FolderStructures.Gaseous;
|
||||
public bool IncludeBIOSFiles { get; set; } = true;
|
||||
public ArchiveTypes ArchiveType { get; set; } = CollectionItem.ArchiveTypes.Zip;
|
||||
public string ArchiveExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ArchiveType != null)
|
||||
{
|
||||
switch (ArchiveType)
|
||||
{
|
||||
case ArchiveTypes.Zip:
|
||||
default:
|
||||
return ".zip";
|
||||
|
||||
case ArchiveTypes.RAR:
|
||||
return ".rar";
|
||||
|
||||
case ArchiveTypes.SevenZip:
|
||||
return ".7z";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ".zip";
|
||||
}
|
||||
}
|
||||
}
|
||||
public List<AlwaysIncludeItem> AlwaysInclude { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
@@ -604,7 +727,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
if (_BuildStatus == CollectionBuildStatus.Completed)
|
||||
{
|
||||
if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip")))
|
||||
if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension)))
|
||||
{
|
||||
return CollectionBuildStatus.Completed;
|
||||
}
|
||||
@@ -632,7 +755,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
if (BuildStatus == CollectionBuildStatus.Completed)
|
||||
{
|
||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
|
||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension);
|
||||
if (File.Exists(ZipFilePath))
|
||||
{
|
||||
FileInfo fi = new FileInfo(ZipFilePath);
|
||||
@@ -665,6 +788,13 @@ namespace gaseous_server.Classes
|
||||
RetroPie = 1
|
||||
}
|
||||
|
||||
public enum ArchiveTypes
|
||||
{
|
||||
Zip = 0,
|
||||
RAR = 1,
|
||||
SevenZip = 2
|
||||
}
|
||||
|
||||
public class AlwaysIncludeItem
|
||||
{
|
||||
public long PlatformId { get; set; }
|
||||
@@ -707,6 +837,9 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public AgeGroups.AgeRestrictionGroupings AgeGroup { get; set; }
|
||||
public bool ContainsUnclassifiedAgeGroup { get; set; }
|
||||
|
||||
public class CollectionPlatformItem {
|
||||
public CollectionPlatformItem(IGDB.Models.Platform platform) {
|
||||
string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug" };
|
||||
@@ -749,7 +882,7 @@ namespace gaseous_server.Classes
|
||||
long Size = 0;
|
||||
foreach (CollectionGameItem Game in Games) {
|
||||
foreach (Roms.GameRomItem Rom in Game.Roms) {
|
||||
Size += Rom.Size;
|
||||
Size += (long)Rom.Size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,41 +890,45 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public class CollectionGameItem {
|
||||
public CollectionGameItem(IGDB.Models.Game game) {
|
||||
string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug", "Cover" };
|
||||
PropertyInfo[] srcProperties = typeof(IGDB.Models.Game).GetProperties();
|
||||
PropertyInfo[] dstProperties = typeof(CollectionPlatformItem.CollectionGameItem).GetProperties();
|
||||
foreach (PropertyInfo srcProperty in srcProperties) {
|
||||
if (PropertyWhitelist.Contains<string>(srcProperty.Name))
|
||||
public class CollectionGameItem : MinimalGameItem
|
||||
{
|
||||
foreach (PropertyInfo dstProperty in dstProperties)
|
||||
public CollectionGameItem(MinimalGameItem gameObject)
|
||||
{
|
||||
if (srcProperty.Name == dstProperty.Name)
|
||||
this.Id = gameObject.Id;
|
||||
this.Name = gameObject.Name;
|
||||
this.Slug = gameObject.Slug;
|
||||
this.TotalRating = gameObject.TotalRating;
|
||||
this.TotalRatingCount = gameObject.TotalRatingCount;
|
||||
this.Cover = gameObject.Cover;
|
||||
this.Artworks = gameObject.Artworks;
|
||||
this.FirstReleaseDate = gameObject.FirstReleaseDate;
|
||||
this.AgeRatings = gameObject.AgeRatings;
|
||||
}
|
||||
|
||||
public IGDB.Models.Cover? CoverItem
|
||||
{
|
||||
if (srcProperty.GetValue(game) != null) {
|
||||
string compareName = srcProperty.PropertyType.Name.ToLower().Split("`")[0];
|
||||
switch(compareName) {
|
||||
case "identityorvalue":
|
||||
string newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(srcProperty.GetValue(game));
|
||||
Dictionary<string, object> newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
dstProperty.SetValue(this, newDict["Id"]);
|
||||
break;
|
||||
default:
|
||||
dstProperty.SetValue(this, srcProperty.GetValue(game));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public long Cover { get; set;}
|
||||
public AgeGroups.AgeRestrictionGroupings AgeGrouping
|
||||
{
|
||||
get
|
||||
{
|
||||
return AgeGroups.GetAgeGroupFromAgeRatings(this.AgeRatings);
|
||||
}
|
||||
}
|
||||
|
||||
public CollectionItem.AlwaysIncludeItem InclusionStatus { get; set; }
|
||||
|
||||
@@ -801,7 +938,7 @@ namespace gaseous_server.Classes
|
||||
get {
|
||||
long Size = 0;
|
||||
foreach (Roms.GameRomItem Rom in Roms) {
|
||||
Size += Rom.Size;
|
||||
Size += (long)Rom.Size;
|
||||
}
|
||||
|
||||
return Size;
|
||||
|
@@ -1,9 +1,12 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.IO.Compression;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Common
|
||||
public static class Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns IfNullValue if the ObjectToCheck is null
|
||||
@@ -110,6 +113,48 @@ namespace gaseous_server.Classes
|
||||
return Path.GetFullPath(new Uri(path).LocalPath)
|
||||
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
}
|
||||
|
||||
public static string GetDescription(this Enum value)
|
||||
{
|
||||
return ((DescriptionAttribute)Attribute.GetCustomAttribute(
|
||||
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Single(x => x.GetValue(null).Equals(value)),
|
||||
typeof(DescriptionAttribute)))?.Description ?? value.ToString();
|
||||
}
|
||||
|
||||
// compression
|
||||
public static byte[] Compress(byte[] data)
|
||||
{
|
||||
MemoryStream output = new MemoryStream();
|
||||
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
|
||||
{
|
||||
dstream.Write(data, 0, data.Length);
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
public static byte[] Decompress(byte[] data)
|
||||
{
|
||||
MemoryStream input = new MemoryStream(data);
|
||||
MemoryStream output = new MemoryStream();
|
||||
using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
|
||||
{
|
||||
dstream.CopyTo(output);
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
public static object GetEnvVar(string envName, string defaultValue)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
|
||||
{
|
||||
return Environment.GetEnvironmentVariable(envName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -3,6 +3,7 @@ using System.Data;
|
||||
using Newtonsoft.Json;
|
||||
using IGDB.Models;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using NuGet.Common;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
@@ -117,11 +118,33 @@ namespace gaseous_server.Classes
|
||||
if (_tempConfig != null)
|
||||
{
|
||||
_config = _tempConfig;
|
||||
} else
|
||||
|
||||
// load environment variables if we're in a docker container
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("INDOCKER")))
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("INDOCKER") == "1")
|
||||
{
|
||||
Console.WriteLine("Running in Docker - setting configuration from variables");
|
||||
_config.DatabaseConfiguration.HostName = (string)Common.GetEnvVar("dbhost", _config.DatabaseConfiguration.HostName);
|
||||
_config.DatabaseConfiguration.UserName = (string)Common.GetEnvVar("dbuser", _config.DatabaseConfiguration.UserName);
|
||||
_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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("There was an error reading the config file: Json returned null");
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
// no config file!
|
||||
// use defaults and save
|
||||
@@ -161,7 +184,7 @@ namespace gaseous_server.Classes
|
||||
File.WriteAllText(ConfigurationFilePath, configRaw);
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> AppSettings = new Dictionary<string, string>();
|
||||
private static Dictionary<string, object> AppSettings = new Dictionary<string, object>();
|
||||
|
||||
public static void InitSettings()
|
||||
{
|
||||
@@ -171,45 +194,91 @@ namespace gaseous_server.Classes
|
||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
||||
foreach (DataRow dataRow in dbResponse.Rows)
|
||||
{
|
||||
if (AppSettings.ContainsKey((string)dataRow["Setting"]))
|
||||
string SettingName = (string)dataRow["Setting"];
|
||||
|
||||
if (AppSettings.ContainsKey(SettingName))
|
||||
{
|
||||
AppSettings[(string)dataRow["Setting"]] = (string)dataRow["Value"];
|
||||
AppSettings.Remove(SettingName);
|
||||
}
|
||||
else
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database");
|
||||
|
||||
try
|
||||
{
|
||||
AppSettings.Add((string)dataRow["Setting"], (string)dataRow["Value"]);
|
||||
switch ((int)dataRow["ValueType"])
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
// value is a string
|
||||
AppSettings.Add(SettingName, dataRow["Value"]);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// value is a datetime
|
||||
AppSettings.Add(SettingName, dataRow["ValueDate"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException castEx)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Settings", "Exception when reading server setting " + SettingName + ". Resetting to default.", castEx);
|
||||
|
||||
// delete broken setting and return the default
|
||||
// this error is probably generated during an upgrade
|
||||
sql = "DELETE FROM Settings WHERE Setting = @SettingName";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "SettingName", SettingName }
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Settings", "Exception when reading server setting " + SettingName + ".", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadSetting(string SettingName, string DefaultValue)
|
||||
public static T ReadSetting<T>(string SettingName, T DefaultValue)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
try
|
||||
{
|
||||
if (AppSettings.ContainsKey(SettingName))
|
||||
{
|
||||
return AppSettings[SettingName];
|
||||
return (T)AppSettings[SettingName];
|
||||
}
|
||||
else
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Value FROM Settings WHERE Setting = @SettingName";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("SettingName", SettingName);
|
||||
dbDict.Add("Value", DefaultValue);
|
||||
|
||||
string sql = "SELECT Value, ValueDate FROM Settings WHERE Setting = @SettingName";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "SettingName", SettingName }
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Reading setting '" + SettingName + "'");
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, dbDict);
|
||||
Type type = typeof(T);
|
||||
if (dbResponse.Rows.Count == 0)
|
||||
{
|
||||
// no value with that name stored - respond with the default value
|
||||
SetSetting(SettingName, DefaultValue);
|
||||
SetSetting<T>(SettingName, DefaultValue);
|
||||
return DefaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppSettings.Add(SettingName, (string)dbResponse.Rows[0][0]);
|
||||
return (string)dbResponse.Rows[0][0];
|
||||
if (type.ToString() == "System.DateTime")
|
||||
{
|
||||
AppSettings.Add(SettingName, dbResponse.Rows[0]["ValueDate"]);
|
||||
return (T)dbResponse.Rows[0]["ValueDate"];
|
||||
}
|
||||
else
|
||||
{
|
||||
AppSettings.Add(SettingName, dbResponse.Rows[0]["Value"]);
|
||||
return (T)dbResponse.Rows[0]["Value"];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -219,14 +288,58 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException castEx)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Settings", "Exception when reading server setting " + SettingName + ". Resetting to default.", castEx);
|
||||
|
||||
public static void SetSetting(string SettingName, string Value)
|
||||
// delete broken setting and return the default
|
||||
// this error is probably generated during an upgrade
|
||||
if (AppSettings.ContainsKey(SettingName))
|
||||
{
|
||||
AppSettings.Remove(SettingName);
|
||||
}
|
||||
|
||||
string sql = "DELETE FROM Settings WHERE Setting = @SettingName";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "SettingName", SettingName }
|
||||
};
|
||||
|
||||
return DefaultValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Settings", "Exception when reading server setting " + SettingName + ".", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetSetting<T>(string SettingName, T Value)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "REPLACE INTO Settings (Setting, Value) VALUES (@SettingName, @Value)";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("SettingName", SettingName);
|
||||
dbDict.Add("Value", Value);
|
||||
string sql = "REPLACE INTO Settings (Setting, ValueType, Value, ValueDate) VALUES (@SettingName, @ValueType, @Value, @ValueDate)";
|
||||
Dictionary<string, object> dbDict;
|
||||
Type type = typeof(T);
|
||||
if (type.ToString() == "System.DateTime")
|
||||
{
|
||||
dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "SettingName", SettingName },
|
||||
{ "ValueType", 1 },
|
||||
{ "Value", null },
|
||||
{ "ValueDate", Value }
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "SettingName", SettingName },
|
||||
{ "ValueType", 0 },
|
||||
{ "Value", Value },
|
||||
{ "ValueDate", null }
|
||||
};
|
||||
}
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Storing setting '" + SettingName + "' to value: '" + Value + "'");
|
||||
try
|
||||
@@ -308,11 +421,41 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
private static string _DefaultDatabaseName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbname")))
|
||||
{
|
||||
return Environment.GetEnvironmentVariable("dbname");
|
||||
}
|
||||
else
|
||||
{
|
||||
return "gaseous";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int _DefaultDatabasePort
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbport")))
|
||||
{
|
||||
return int.Parse(Environment.GetEnvironmentVariable("dbport"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 3306;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string HostName = _DefaultHostName;
|
||||
public string UserName = _DefaultUserName;
|
||||
public string Password = _DefaultPassword;
|
||||
public string DatabaseName = "gaseous";
|
||||
public int Port = 3306;
|
||||
public string DatabaseName = _DefaultDatabaseName;
|
||||
public int Port = _DefaultDatabasePort;
|
||||
|
||||
[JsonIgnore]
|
||||
public string ConnectionString
|
||||
@@ -341,11 +484,11 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadSetting("LibraryRootDirectory", Path.Combine(Config.ConfigurationPath, "Data"));
|
||||
return ReadSetting<string>("LibraryRootDirectory", Path.Combine(Config.ConfigurationPath, "Data"));
|
||||
}
|
||||
set
|
||||
{
|
||||
SetSetting("LibraryRootDirectory", value);
|
||||
SetSetting<string>("LibraryRootDirectory", value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,22 +616,73 @@ namespace gaseous_server.Classes
|
||||
|
||||
public class MetadataAPI
|
||||
{
|
||||
private static Communications.MetadataSources _Source
|
||||
private static HasheousClient.Models.MetadataModel.MetadataSources _MetadataSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
|
||||
{
|
||||
return (Communications.MetadataSources)Enum.Parse(typeof(Communications.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
|
||||
return (HasheousClient.Models.MetadataModel.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Communications.MetadataSources.IGDB;
|
||||
return HasheousClient.Models.MetadataModel.MetadataSources.IGDB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Communications.MetadataSources Source = _Source;
|
||||
private static HasheousClient.Models.MetadataModel.SignatureSources _SignatureSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("signaturesource")))
|
||||
{
|
||||
return (HasheousClient.Models.MetadataModel.SignatureSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.SignatureSources), Environment.GetEnvironmentVariable("signaturesource"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return HasheousClient.Models.MetadataModel.SignatureSources.LocalOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int _MaxLibraryScanWorkers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("maxlibraryscanworkers")))
|
||||
{
|
||||
return int.Parse(Environment.GetEnvironmentVariable("maxlibraryscanworkers"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string _HasheousHost
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("hasheoushost")))
|
||||
{
|
||||
return Environment.GetEnvironmentVariable("hasheoushost");
|
||||
}
|
||||
else
|
||||
{
|
||||
return "https://hasheous.org/";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HasheousClient.Models.MetadataModel.MetadataSources MetadataSource = _MetadataSource;
|
||||
|
||||
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
|
||||
|
||||
public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers;
|
||||
|
||||
public string HasheousHost = _HasheousHost;
|
||||
}
|
||||
|
||||
public class IGDB
|
||||
|
@@ -235,7 +235,8 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||
return (int)conn.ExecNonQuery(Command, Parameters, Timeout);
|
||||
int retVal = conn.ExecNonQuery(Command, Parameters, Timeout);
|
||||
return retVal;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -329,7 +330,8 @@ namespace gaseous_server.Classes
|
||||
DataTable RetTable = new DataTable();
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||
MySqlConnection conn = new MySqlConnection(DBConn);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand
|
||||
@@ -361,6 +363,7 @@ namespace gaseous_server.Classes
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
|
||||
conn.Close();
|
||||
}
|
||||
|
||||
return RetTable;
|
||||
}
|
||||
@@ -370,7 +373,8 @@ namespace gaseous_server.Classes
|
||||
int result = 0;
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||
MySqlConnection conn = new MySqlConnection(DBConn);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand
|
||||
@@ -402,13 +406,15 @@ namespace gaseous_server.Classes
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
|
||||
conn.Close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
|
||||
{
|
||||
var conn = new MySqlConnection(DBConn);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
conn.Open();
|
||||
var command = conn.CreateCommand();
|
||||
MySqlTransaction transaction;
|
||||
@@ -425,6 +431,7 @@ namespace gaseous_server.Classes
|
||||
transaction.Commit();
|
||||
conn.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
|
||||
{
|
||||
@@ -449,7 +456,8 @@ namespace gaseous_server.Classes
|
||||
|
||||
public bool TestConnection()
|
||||
{
|
||||
MySqlConnection conn = new MySqlConnection(DBConn);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
@@ -464,4 +472,5 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
@@ -9,13 +10,64 @@ namespace gaseous_server.Classes
|
||||
|
||||
public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
|
||||
{
|
||||
// load resources
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
DataTable data;
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Checking for pre-upgrade for schema version " + TargetSchemaVersion);
|
||||
|
||||
switch(DatabaseType)
|
||||
{
|
||||
case Database.databaseType.MySql:
|
||||
switch (TargetSchemaVersion)
|
||||
{
|
||||
case 1005:
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Running pre-upgrade for schema version " + TargetSchemaVersion);
|
||||
|
||||
// there was a mistake at dbschema version 1004-1005
|
||||
// the first preview release of v1.7 reused dbschema version 1004
|
||||
// if table "Relation_Game_AgeRatings" exists - then we need to apply the gaseous-fix-1005.sql script before applying the standard 1005 script
|
||||
sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = @dbname AND table_name = @tablename;";
|
||||
dbDict.Add("dbname", Config.DatabaseConfiguration.DatabaseName);
|
||||
dbDict.Add("tablename", "Relation_Game_AgeRatings");
|
||||
data = db.ExecuteCMD(sql, dbDict);
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Schema version " + TargetSchemaVersion + " requires a table which is missing.");
|
||||
|
||||
string resourceName = "gaseous_server.Support.Database.MySQL.gaseous-fix-1005.sql";
|
||||
string dbScript = "";
|
||||
|
||||
string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
||||
if (resources.Contains(resourceName))
|
||||
{
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
dbScript = reader.ReadToEnd();
|
||||
|
||||
// apply schema!
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Applying schema version fix prior to version 1005");
|
||||
db.ExecuteCMD(dbScript, dbDict, 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
DataTable data;
|
||||
|
||||
switch(DatabaseType)
|
||||
{
|
||||
@@ -32,12 +84,12 @@ namespace gaseous_server.Classes
|
||||
|
||||
// copy root path to new libraries format
|
||||
string oldRoot = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "Library");
|
||||
string sql = "INSERT INTO GameLibraries (Name, Path, DefaultLibrary, DefaultPlatform) VALUES (@name, @path, @defaultlibrary, @defaultplatform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||
sql = "INSERT INTO GameLibraries (Name, Path, DefaultLibrary, DefaultPlatform) VALUES (@name, @path, @defaultlibrary, @defaultplatform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||
dbDict.Add("name", "Default");
|
||||
dbDict.Add("path", oldRoot);
|
||||
dbDict.Add("defaultlibrary", 1);
|
||||
dbDict.Add("defaultplatform", 0);
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
// apply the new library id to the existing roms
|
||||
sql = "UPDATE Games_Roms SET LibraryId=@libraryid;";
|
||||
@@ -46,6 +98,11 @@ namespace gaseous_server.Classes
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
break;
|
||||
|
||||
case 1016:
|
||||
// delete old format LastRun_* settings from settings table
|
||||
sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';";
|
||||
db.ExecuteNonQuery(sql);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
58
gaseous-server/Classes/Favourites.cs
Normal file
58
gaseous-server/Classes/Favourites.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using IGDB.Models;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Favourites
|
||||
{
|
||||
public bool GetFavourite(string userid, long GameId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM Favourites WHERE UserId=@userid AND GameId=@gameid";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||
{ "userid", userid },
|
||||
{ "gameid", GameId}
|
||||
};
|
||||
|
||||
if (db.ExecuteCMD(sql, dbDict).Rows.Count > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetFavourite(string userid, long GameId, bool Favourite)
|
||||
{
|
||||
bool CurrentFavourite = GetFavourite(userid, GameId);
|
||||
if (CurrentFavourite == Favourite)
|
||||
{
|
||||
return Favourite;
|
||||
}
|
||||
else
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql;
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||
{ "userid", userid },
|
||||
{ "gameid", GameId}
|
||||
};
|
||||
|
||||
if (CurrentFavourite == true)
|
||||
{
|
||||
// delete existing value
|
||||
sql = "DELETE FROM Favourites WHERE UserId=@userid AND GameId=@gameid";
|
||||
}
|
||||
else
|
||||
{
|
||||
// insert new value
|
||||
sql = "INSERT INTO Favourites (UserId, GameId) VALUES (@userid, @gameid)";
|
||||
}
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
return Favourite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
373
gaseous-server/Classes/FileSignature.cs
Normal file
373
gaseous-server/Classes/FileSignature.cs
Normal file
@@ -0,0 +1,373 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO.Compression;
|
||||
using HasheousClient.Models;
|
||||
using NuGet.Common;
|
||||
using SevenZip;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class FileSignature
|
||||
{
|
||||
public gaseous_server.Models.Signatures_Games GetFileSignature(GameLibrary.LibraryItem library, Common.hashObject hash, FileInfo fi, string GameFileImportPath)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Getting signature for file: " + GameFileImportPath);
|
||||
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
|
||||
discoveredSignature = _GetFileSignature(hash, fi.Name, fi.Extension, fi.Length, GameFileImportPath, false);
|
||||
|
||||
string[] CompressionExts = { ".zip", ".rar", ".7z" };
|
||||
string ImportedFileExtension = Path.GetExtension(GameFileImportPath);
|
||||
|
||||
if (CompressionExts.Contains(ImportedFileExtension) && (fi.Length < 1073741824))
|
||||
{
|
||||
// file is a zip and less than 1 GiB
|
||||
// extract the zip file and search the contents
|
||||
|
||||
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, library.Id.ToString(), Path.GetRandomFileName());
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing " + GameFileImportPath + " to " + ExtractPath + " examine contents");
|
||||
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
|
||||
try
|
||||
{
|
||||
switch(ImportedFileExtension)
|
||||
{
|
||||
case ".zip":
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip");
|
||||
try
|
||||
{
|
||||
using (var archive = SharpCompress.Archives.Zip.ZipArchive.Open(GameFileImportPath))
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
|
||||
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception zipEx)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Get Signature", "Unzip error", zipEx);
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
|
||||
case ".rar":
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using rar");
|
||||
try
|
||||
{
|
||||
using (var archive = RarArchive.Open(GameFileImportPath))
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
|
||||
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception zipEx)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Get Signature", "Unrar error", zipEx);
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
|
||||
case ".7z":
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using 7z");
|
||||
try
|
||||
{
|
||||
using (var archive = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(GameFileImportPath))
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
|
||||
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception zipEx)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Get Signature", "7z error", zipEx);
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches");
|
||||
// loop through contents until we find the first signature match
|
||||
List<ArchiveData> archiveFiles = new List<ArchiveData>();
|
||||
bool signatureFound = false;
|
||||
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
FileInfo zfi = new FileInfo(file);
|
||||
Common.hashObject zhash = new Common.hashObject(file);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file);
|
||||
|
||||
if (zfi != null)
|
||||
{
|
||||
ArchiveData archiveData = new ArchiveData{
|
||||
FileName = Path.GetFileName(file),
|
||||
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
|
||||
Size = zfi.Length,
|
||||
MD5 = hash.md5hash,
|
||||
SHA1 = hash.sha1hash
|
||||
};
|
||||
archiveFiles.Add(archiveData);
|
||||
|
||||
if (signatureFound == false)
|
||||
{
|
||||
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi.Name, zfi.Extension, zfi.Length, file, true);
|
||||
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ImportedFileExtension);
|
||||
|
||||
if (zDiscoveredSignature.Score > discoveredSignature.Score)
|
||||
{
|
||||
if (
|
||||
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEArcade ||
|
||||
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEMess
|
||||
)
|
||||
{
|
||||
zDiscoveredSignature.Rom.Name = zDiscoveredSignature.Game.Description + ImportedFileExtension;
|
||||
}
|
||||
zDiscoveredSignature.Rom.Crc = discoveredSignature.Rom.Crc;
|
||||
zDiscoveredSignature.Rom.Md5 = discoveredSignature.Rom.Md5;
|
||||
zDiscoveredSignature.Rom.Sha1 = discoveredSignature.Rom.Sha1;
|
||||
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
|
||||
discoveredSignature = zDiscoveredSignature;
|
||||
|
||||
signatureFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
discoveredSignature.Rom.Attributes.Add(new KeyValuePair<string, object>(
|
||||
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
|
||||
));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing compressed file: " + GameFileImportPath, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
|
||||
private gaseous_server.Models.Signatures_Games _GetFileSignature(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath, bool IsInZip)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Checking signature for file: " + GameFileImportPath + "\nMD5 hash: " + hash.md5hash + "\nSHA1 hash: " + hash.sha1hash);
|
||||
|
||||
|
||||
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
|
||||
|
||||
// do database search first
|
||||
gaseous_server.Models.Signatures_Games? dbSignature = _GetFileSignatureFromDatabase(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
|
||||
if (dbSignature != null)
|
||||
{
|
||||
// local signature found
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature found in local database for game: " + dbSignature.Game.Name);
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no local signature attempt to pull from Hasheous
|
||||
dbSignature = _GetFileSignatureFromHasheous(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
|
||||
if (dbSignature != null)
|
||||
{
|
||||
// signature retrieved from Hasheous
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
else
|
||||
{
|
||||
// construct a signature from file data
|
||||
dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
}
|
||||
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, ImageExtension, false);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", " Determined import file as: " + discoveredSignature.Game.Name + " (" + discoveredSignature.Game.Year + ") " + discoveredSignature.Game.System);
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", " Platform determined to be: " + discoveredSignature.Flags.IGDBPlatformName + " (" + discoveredSignature.Flags.IGDBPlatformId + ")");
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
|
||||
private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for MD5: " + hash.md5hash);
|
||||
|
||||
// check 1: do we have a signature for it?
|
||||
gaseous_server.Classes.SignatureManagement sc = new SignatureManagement();
|
||||
List<gaseous_server.Models.Signatures_Games> signatures = sc.GetSignature(hash.md5hash);
|
||||
if (signatures == null || signatures.Count == 0)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for SHA1: " + hash.sha1hash);
|
||||
|
||||
// no md5 signature found - try sha1
|
||||
signatures = sc.GetSignature("", hash.sha1hash);
|
||||
}
|
||||
|
||||
gaseous_server.Models.Signatures_Games? discoveredSignature = null;
|
||||
if (signatures.Count == 1)
|
||||
{
|
||||
// only 1 signature found!
|
||||
discoveredSignature = signatures.ElementAt(0);
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
else if (signatures.Count > 1)
|
||||
{
|
||||
// more than one signature found - find one with highest score
|
||||
// start with first returned element
|
||||
discoveredSignature = signatures.First();
|
||||
foreach (gaseous_server.Models.Signatures_Games Sig in signatures)
|
||||
{
|
||||
if (Sig.Score > discoveredSignature.Score)
|
||||
{
|
||||
discoveredSignature = Sig;
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromHasheous(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
|
||||
{
|
||||
// check if hasheous is enabled, and if so use it's signature database
|
||||
if (Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous)
|
||||
{
|
||||
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
|
||||
SignatureLookupItem? HasheousResult = hasheous.RetrieveFromHasheousAsync(new HashLookupModel{
|
||||
MD5 = hash.md5hash,
|
||||
SHA1 = hash.sha1hash
|
||||
});
|
||||
|
||||
if (HasheousResult != null)
|
||||
{
|
||||
if (HasheousResult.Signature != null)
|
||||
{
|
||||
gaseous_server.Models.Signatures_Games signature = new Models.Signatures_Games();
|
||||
signature.Game = HasheousResult.Signature.Game;
|
||||
signature.Rom = HasheousResult.Signature.Rom;
|
||||
|
||||
if (HasheousResult.MetadataResults != null)
|
||||
{
|
||||
if (HasheousResult.MetadataResults.Count > 0)
|
||||
{
|
||||
foreach (SignatureLookupItem.MetadataResult metadataResult in HasheousResult.MetadataResults)
|
||||
{
|
||||
if (metadataResult.Source == MetadataModel.MetadataSources.IGDB)
|
||||
{
|
||||
signature.Flags.IGDBPlatformId = (long)metadataResult.PlatformId;
|
||||
signature.Flags.IGDBGameId = (long)metadataResult.GameId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private gaseous_server.Models.Signatures_Games _GetFileSignatureFromFileData(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
|
||||
{
|
||||
SignatureManagement signatureManagement = new SignatureManagement();
|
||||
|
||||
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
|
||||
|
||||
// no signature match found - try name search
|
||||
List<gaseous_server.Models.Signatures_Games> signatures = signatureManagement.GetByTosecName(ImageName);
|
||||
|
||||
if (signatures.Count == 1)
|
||||
{
|
||||
// only 1 signature found!
|
||||
discoveredSignature = signatures.ElementAt(0);
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
else if (signatures.Count > 1)
|
||||
{
|
||||
// more than one signature found - find one with highest score
|
||||
foreach (gaseous_server.Models.Signatures_Games Sig in signatures)
|
||||
{
|
||||
if (Sig.Score > discoveredSignature.Score)
|
||||
{
|
||||
discoveredSignature = Sig;
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
else
|
||||
{
|
||||
// still no search - try alternate method
|
||||
gaseous_server.Models.Signatures_Games.GameItem gi = new gaseous_server.Models.Signatures_Games.GameItem();
|
||||
gaseous_server.Models.Signatures_Games.RomItem ri = new gaseous_server.Models.Signatures_Games.RomItem();
|
||||
|
||||
discoveredSignature.Game = gi;
|
||||
discoveredSignature.Rom = ri;
|
||||
|
||||
// game title is the file name without the extension or path
|
||||
gi.Name = Path.GetFileNameWithoutExtension(GameFileImportPath);
|
||||
|
||||
// remove everything after brackets - leaving (hopefully) only the name
|
||||
if (gi.Name.Contains("("))
|
||||
{
|
||||
gi.Name = gi.Name.Substring(0, gi.Name.IndexOf("(")).Trim();
|
||||
}
|
||||
|
||||
// remove special characters like dashes
|
||||
gi.Name = gi.Name.Replace("-", "").Trim();
|
||||
|
||||
// get rom data
|
||||
ri.Name = Path.GetFileName(GameFileImportPath);
|
||||
ri.Md5 = hash.md5hash;
|
||||
ri.Sha1 = hash.sha1hash;
|
||||
ri.Size = ImageSize;
|
||||
ri.SignatureSource = gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.None;
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
}
|
||||
|
||||
public class ArchiveData
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public long Size { get; set; }
|
||||
public string MD5 { get; set; }
|
||||
public string SHA1 { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -34,6 +34,12 @@ namespace gaseous_server
|
||||
{}
|
||||
}
|
||||
|
||||
public class CannotDeleteLibraryWhileScanIsActive : Exception
|
||||
{
|
||||
public CannotDeleteLibraryWhileScanIsActive() : base("Unable to delete library while a library scan is active. Wait for all scans to complete and try again")
|
||||
{}
|
||||
}
|
||||
|
||||
// code
|
||||
public static LibraryItem GetDefaultLibrary
|
||||
{
|
||||
@@ -110,21 +116,42 @@ namespace gaseous_server
|
||||
|
||||
int newLibraryId = (int)(long)data.Rows[0][0];
|
||||
|
||||
return GetLibrary(newLibraryId);
|
||||
Logging.Log(Logging.LogType.Information, "Library Management", "Created library " + Name + " at directory " + PathName);
|
||||
|
||||
LibraryItem library = GetLibrary(newLibraryId);
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
public static void DeleteLibrary(int LibraryId)
|
||||
{
|
||||
if (GetLibrary(LibraryId).IsDefaultLibrary == false)
|
||||
LibraryItem library = GetLibrary(LibraryId);
|
||||
if (library.IsDefaultLibrary == false)
|
||||
{
|
||||
// check for active library scans
|
||||
foreach(ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
{
|
||||
if (
|
||||
(item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState == ProcessQueue.QueueItemState.Running) ||
|
||||
(item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker && item.ItemState == ProcessQueue.QueueItemState.Running)
|
||||
)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete libraries while a library scan is running. Wait until the the library scan is completed and try again.");
|
||||
throw new CannotDeleteLibraryWhileScanIsActive();
|
||||
}
|
||||
}
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", LibraryId);
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Management", "Deleted library " + library.Name + " at path " + library.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete the default library.");
|
||||
throw new CannotDeleteDefaultLibrary();
|
||||
}
|
||||
}
|
||||
|
@@ -6,37 +6,38 @@ using System.Security.Policy;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using gaseous_server.Models;
|
||||
using IGDB.Models;
|
||||
using NuGet.Common;
|
||||
using NuGet.LibraryModel;
|
||||
using static gaseous_server.Classes.Metadata.Games;
|
||||
using static gaseous_server.Classes.FileSignature;
|
||||
using HasheousClient.Models;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class ImportGames : QueueItemStatus
|
||||
public class ImportGame : QueueItemStatus
|
||||
{
|
||||
public ImportGames(string ImportPath)
|
||||
public void ProcessDirectory(string ImportPath)
|
||||
{
|
||||
if (Directory.Exists(ImportPath))
|
||||
{
|
||||
string[] importContents_Files = Directory.GetFiles(ImportPath);
|
||||
string[] importContents_Directories = Directory.GetDirectories(ImportPath);
|
||||
string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
|
||||
|
||||
// import files first
|
||||
int importCount = 1;
|
||||
foreach (string importContent in importContents_Files) {
|
||||
SetStatus(importCount, importContents_Files.Length, "Importing file: " + importContent);
|
||||
foreach (string importContent in importContents) {
|
||||
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
|
||||
|
||||
ImportGame.ImportGameFile(importContent, null);
|
||||
ImportGameFile(importContent, null);
|
||||
|
||||
importCount += 1;
|
||||
}
|
||||
ClearStatus();
|
||||
|
||||
// import sub directories
|
||||
foreach (string importDir in importContents_Directories) {
|
||||
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
|
||||
}
|
||||
DeleteOrphanedDirectories(ImportPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -45,12 +46,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class ImportGame : QueueItemStatus
|
||||
{
|
||||
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
|
||||
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
@@ -102,7 +98,8 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing");
|
||||
|
||||
Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath);
|
||||
FileSignature fileSignature = new FileSignature();
|
||||
gaseous_server.Models.Signatures_Games discoveredSignature = fileSignature.GetFileSignature(GameLibrary.GetDefaultLibrary, hash, fi, GameFileImportPath);
|
||||
|
||||
// get discovered platform
|
||||
IGDB.Models.Platform? determinedPlatform = null;
|
||||
@@ -121,7 +118,7 @@ namespace gaseous_server.Classes
|
||||
discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name;
|
||||
}
|
||||
|
||||
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
|
||||
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature, discoveredSignature.Flags.IGDBPlatformId, true);
|
||||
|
||||
// add to database
|
||||
StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
|
||||
@@ -152,155 +149,29 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
|
||||
public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
|
||||
{
|
||||
Models.Signatures_Games discoveredSignature = _GetFileSignature(hash, fi, GameFileImportPath);
|
||||
|
||||
if ((Path.GetExtension(GameFileImportPath) == ".zip") && (fi.Length < 1073741824))
|
||||
if (Signature.Flags != null)
|
||||
{
|
||||
// file is a zip and less than 1 GiB
|
||||
// extract the zip file and search the contents
|
||||
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName());
|
||||
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
|
||||
if (Signature.Flags.IGDBGameId != null && Signature.Flags.IGDBGameId != 0)
|
||||
{
|
||||
// game was determined elsewhere - probably a Hasheous server
|
||||
try
|
||||
{
|
||||
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath);
|
||||
|
||||
// loop through contents until we find the first signature match
|
||||
foreach (string file in Directory.GetFiles(ExtractPath))
|
||||
{
|
||||
FileInfo zfi = new FileInfo(file);
|
||||
Common.hashObject zhash = new Common.hashObject(file);
|
||||
|
||||
Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file);
|
||||
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ".zip");
|
||||
|
||||
if (zDiscoveredSignature.Score > discoveredSignature.Score)
|
||||
{
|
||||
if (
|
||||
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEArcade ||
|
||||
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEMess
|
||||
)
|
||||
{
|
||||
zDiscoveredSignature.Rom.Name = zDiscoveredSignature.Game.Description + ".zip";
|
||||
}
|
||||
zDiscoveredSignature.Rom.Crc = discoveredSignature.Rom.Crc;
|
||||
zDiscoveredSignature.Rom.Md5 = discoveredSignature.Rom.Md5;
|
||||
zDiscoveredSignature.Rom.Sha1 = discoveredSignature.Rom.Sha1;
|
||||
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
|
||||
discoveredSignature = zDiscoveredSignature;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Games.GetGame(Signature.Flags.IGDBGameId, false, false, FullDownload);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing zip file: " + GameFileImportPath, ex);
|
||||
Logging.Log(Logging.LogType.Warning, "Import Game", "Provided game id resulted in a failed game lookup", ex);
|
||||
}
|
||||
|
||||
if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); }
|
||||
}
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
|
||||
private static Models.Signatures_Games _GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
|
||||
{
|
||||
// check 1: do we have a signature for it?
|
||||
gaseous_server.Controllers.SignaturesController sc = new Controllers.SignaturesController();
|
||||
List<Models.Signatures_Games> signatures = sc.GetSignature(hash.md5hash);
|
||||
if (signatures.Count == 0)
|
||||
{
|
||||
// no md5 signature found - try sha1
|
||||
signatures = sc.GetSignature("", hash.sha1hash);
|
||||
}
|
||||
|
||||
Models.Signatures_Games discoveredSignature = new Models.Signatures_Games();
|
||||
if (signatures.Count == 1)
|
||||
{
|
||||
// only 1 signature found!
|
||||
discoveredSignature = signatures.ElementAt(0);
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
|
||||
}
|
||||
else if (signatures.Count > 1)
|
||||
{
|
||||
// more than one signature found - find one with highest score
|
||||
foreach (Models.Signatures_Games Sig in signatures)
|
||||
{
|
||||
if (Sig.Score > discoveredSignature.Score)
|
||||
{
|
||||
discoveredSignature = Sig;
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no signature match found - try name search
|
||||
signatures = sc.GetByTosecName(fi.Name);
|
||||
|
||||
if (signatures.Count == 1)
|
||||
{
|
||||
// only 1 signature found!
|
||||
discoveredSignature = signatures.ElementAt(0);
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
|
||||
}
|
||||
else if (signatures.Count > 1)
|
||||
{
|
||||
// more than one signature found - find one with highest score
|
||||
foreach (Models.Signatures_Games Sig in signatures)
|
||||
{
|
||||
if (Sig.Score > discoveredSignature.Score)
|
||||
{
|
||||
discoveredSignature = Sig;
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// still no search - try alternate method
|
||||
Models.Signatures_Games.GameItem gi = new Models.Signatures_Games.GameItem();
|
||||
Models.Signatures_Games.RomItem ri = new Models.Signatures_Games.RomItem();
|
||||
|
||||
discoveredSignature.Game = gi;
|
||||
discoveredSignature.Rom = ri;
|
||||
|
||||
// game title is the file name without the extension or path
|
||||
gi.Name = Path.GetFileNameWithoutExtension(GameFileImportPath);
|
||||
|
||||
// remove everything after brackets - leaving (hopefully) only the name
|
||||
if (gi.Name.Contains("("))
|
||||
{
|
||||
gi.Name = gi.Name.Substring(0, gi.Name.IndexOf("("));
|
||||
}
|
||||
|
||||
// remove special characters like dashes
|
||||
gi.Name = gi.Name.Replace("-", "");
|
||||
|
||||
// guess platform
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, fi, true);
|
||||
|
||||
// get rom data
|
||||
ri.Name = Path.GetFileName(GameFileImportPath);
|
||||
ri.Md5 = hash.md5hash;
|
||||
ri.Sha1 = hash.sha1hash;
|
||||
ri.Size = fi.Length;
|
||||
ri.SignatureSource = gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", " Determined import file as: " + discoveredSignature.Game.Name + " (" + discoveredSignature.Game.Year + ") " + discoveredSignature.Game.System);
|
||||
|
||||
return discoveredSignature;
|
||||
}
|
||||
|
||||
public static IGDB.Models.Game SearchForGame(string GameName, long PlatformId)
|
||||
{
|
||||
// search discovered game - case insensitive exact match first
|
||||
IGDB.Models.Game determinedGame = new IGDB.Models.Game();
|
||||
|
||||
string GameName = Signature.Game.Name;
|
||||
|
||||
List<string> SearchCandidates = GetSearchCandidates(GameName);
|
||||
|
||||
foreach (string SearchCandidate in SearchCandidates)
|
||||
@@ -423,7 +294,7 @@ namespace gaseous_server.Classes
|
||||
return SearchCandidates;
|
||||
}
|
||||
|
||||
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
|
||||
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, gaseous_server.Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
@@ -566,7 +437,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static void OrganiseLibrary()
|
||||
public void OrganiseLibrary()
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
|
||||
|
||||
@@ -581,13 +452,15 @@ namespace gaseous_server.Classes
|
||||
|
||||
if (romDT.Rows.Count > 0)
|
||||
{
|
||||
foreach (DataRow dr in romDT.Rows)
|
||||
for (int i = 0; i < romDT.Rows.Count; i++)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Organise Library", "Processing ROM " + dr["name"]);
|
||||
long RomId = (long)dr["id"];
|
||||
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);
|
||||
}
|
||||
}
|
||||
ClearStatus();
|
||||
|
||||
// clean up empty directories
|
||||
DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
|
||||
@@ -600,19 +473,98 @@ namespace gaseous_server.Classes
|
||||
foreach (var directory in Directory.GetDirectories(startLocation))
|
||||
{
|
||||
DeleteOrphanedDirectories(directory);
|
||||
if (Directory.GetFiles(directory).Length == 0 &&
|
||||
Directory.GetDirectories(directory).Length == 0)
|
||||
|
||||
string[] files = Directory.GetFiles(directory);
|
||||
string[] directories = Directory.GetDirectories(directory);
|
||||
|
||||
if (files.Length == 0 &&
|
||||
directories.Length == 0)
|
||||
{
|
||||
Directory.Delete(directory, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LibraryScan()
|
||||
public void LibraryScan(GameLibrary.LibraryItem? singleLibrary = null)
|
||||
{
|
||||
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
|
||||
int maxWorkers = Config.MetadataConfiguration.MaxLibraryScanWorkers;
|
||||
|
||||
List<GameLibrary.LibraryItem> libraries = new List<GameLibrary.LibraryItem>();
|
||||
if (singleLibrary == null)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting library scan. Library " + library.Name);
|
||||
libraries.AddRange(GameLibrary.GetLibraries);
|
||||
}
|
||||
else
|
||||
{
|
||||
libraries.Add(singleLibrary);
|
||||
}
|
||||
|
||||
// setup background tasks for each library
|
||||
foreach (GameLibrary.LibraryItem library in libraries)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting worker process for library " + library.Name);
|
||||
ProcessQueue.QueueItem queue = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.LibraryScanWorker,
|
||||
1,
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
ProcessQueue.QueueItemType.Rematcher
|
||||
},
|
||||
false,
|
||||
true);
|
||||
queue.Options = library;
|
||||
queue.ForceExecute();
|
||||
|
||||
ProcessQueue.QueueItems.Add(queue);
|
||||
|
||||
// check number of running tasks is less than maxWorkers
|
||||
bool allowContinue;
|
||||
do
|
||||
{
|
||||
allowContinue = true;
|
||||
int currentWorkerCount = 0;
|
||||
List<ProcessQueue.QueueItem> queueItems = new List<ProcessQueue.QueueItem>();
|
||||
queueItems.AddRange(ProcessQueue.QueueItems);
|
||||
foreach (ProcessQueue.QueueItem item in queueItems)
|
||||
{
|
||||
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
|
||||
{
|
||||
currentWorkerCount += 1;
|
||||
}
|
||||
}
|
||||
if (currentWorkerCount >= maxWorkers)
|
||||
{
|
||||
allowContinue = false;
|
||||
Thread.Sleep(60000);
|
||||
}
|
||||
} while (allowContinue == false);
|
||||
}
|
||||
|
||||
bool WorkersStillWorking;
|
||||
do
|
||||
{
|
||||
WorkersStillWorking = false;
|
||||
List<ProcessQueue.QueueItem> queueItems = new List<ProcessQueue.QueueItem>();
|
||||
queueItems.AddRange(ProcessQueue.QueueItems);
|
||||
foreach (ProcessQueue.QueueItem item in queueItems)
|
||||
{
|
||||
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
|
||||
{
|
||||
// workers are still running - sleep and keep looping
|
||||
WorkersStillWorking = true;
|
||||
Thread.Sleep(30000);
|
||||
}
|
||||
}
|
||||
} while (WorkersStillWorking == true);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan complete. All workers stopped");
|
||||
}
|
||||
|
||||
public void LibrarySpecificScan(GameLibrary.LibraryItem library)
|
||||
{
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting scan of library: " + library.Name);
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
@@ -647,7 +599,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
sql = "SELECT * FROM Games_Roms ORDER BY `name`";
|
||||
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
dtRoms = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
// search for files in the library that aren't in the database
|
||||
@@ -679,36 +631,33 @@ namespace gaseous_server.Classes
|
||||
if (romFound == false)
|
||||
{
|
||||
// file is not in database - process it
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
|
||||
|
||||
Common.hashObject hash = new Common.hashObject(LibraryFile);
|
||||
FileInfo fi = new FileInfo(LibraryFile);
|
||||
|
||||
Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile);
|
||||
FileSignature fileSignature = new FileSignature();
|
||||
gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, LibraryFile);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", " Orphaned file found in library: " + LibraryFile);
|
||||
|
||||
// get discovered platform
|
||||
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
|
||||
|
||||
IGDB.Models.Game determinedGame = new Game();
|
||||
try
|
||||
{
|
||||
if (determinedPlatform == null)
|
||||
// get discovered platform
|
||||
long PlatformId;
|
||||
IGDB.Models.Platform determinedPlatform;
|
||||
|
||||
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
|
||||
{
|
||||
if (library.DefaultPlatformId == 0)
|
||||
{
|
||||
determinedPlatform = new IGDB.Models.Platform();
|
||||
determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
|
||||
// no platform discovered in the signature
|
||||
PlatformId = library.DefaultPlatformId;
|
||||
}
|
||||
else
|
||||
{
|
||||
determinedPlatform = Platforms.GetPlatform(library.DefaultPlatformId);
|
||||
determinedGame = SearchForGame(sig.Game.Name, library.DefaultPlatformId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
determinedGame = SearchForGame(sig.Game.Name, (long)determinedPlatform.Id);
|
||||
// use the platform discovered in the signature
|
||||
PlatformId = sig.Flags.IGDBPlatformId;
|
||||
}
|
||||
determinedPlatform = Platforms.GetPlatform(PlatformId);
|
||||
|
||||
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
|
||||
|
||||
StoreROM(library, hash, determinedGame, determinedPlatform, sig, LibraryFile);
|
||||
}
|
||||
@@ -734,7 +683,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
long romId = (long)dtRoms.Rows[i]["Id"];
|
||||
string romPath = (string)dtRoms.Rows[i]["Path"];
|
||||
gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType romMetadataSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(int)dtRoms.Rows[i]["MetadataSource"];
|
||||
gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType romMetadataSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(int)dtRoms.Rows[i]["MetadataSource"];
|
||||
|
||||
SetStatus(StatusCount, dtRoms.Rows.Count, "Processing file " + romPath);
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Processing ROM at path " + romPath);
|
||||
@@ -745,8 +694,13 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
if (romPath != ComputeROMPath(romId))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found, but needs to be moved");
|
||||
MoveGameFile(romId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -767,7 +721,6 @@ namespace gaseous_server.Classes
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
|
||||
}
|
||||
}
|
||||
|
||||
public void Rematcher(bool ForceExecute = false)
|
||||
{
|
||||
@@ -775,26 +728,29 @@ namespace gaseous_server.Classes
|
||||
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 OR GameId = 0) AND MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;";
|
||||
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) AND LibraryId = @libraryid LIMIT 100;";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);";
|
||||
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND LibraryId = @libraryid;";
|
||||
}
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
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 library
|
||||
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
|
||||
|
||||
// get rom info
|
||||
long romId = (long)row["Id"];
|
||||
string romPath = (string)row["Path"];
|
||||
@@ -808,16 +764,26 @@ namespace gaseous_server.Classes
|
||||
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath);
|
||||
|
||||
// determine rom signature
|
||||
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath);
|
||||
FileSignature fileSignature = new FileSignature();
|
||||
gaseous_server.Models.Signatures_Games sig = fileSignature.GetFileSignature(library, hash, fi, romPath);
|
||||
|
||||
// determine rom platform
|
||||
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
|
||||
if (determinedPlatform == null)
|
||||
// get discovered platform
|
||||
long PlatformId;
|
||||
IGDB.Models.Platform determinedPlatform;
|
||||
|
||||
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
|
||||
{
|
||||
determinedPlatform = new IGDB.Models.Platform();
|
||||
// no platform discovered in the signature
|
||||
PlatformId = library.DefaultPlatformId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the platform discovered in the signature
|
||||
PlatformId = sig.Flags.IGDBPlatformId;
|
||||
}
|
||||
determinedPlatform = Platforms.GetPlatform(PlatformId);
|
||||
|
||||
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
|
||||
IGDB.Models.Game determinedGame = SearchForGame(sig, PlatformId, true);
|
||||
|
||||
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
|
||||
|
||||
@@ -836,4 +802,5 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -127,9 +127,8 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);";
|
||||
string sql = "INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
|
||||
dbDict.Add("EventTime", logItem.EventTime);
|
||||
dbDict.Add("EventType", logItem.EventType);
|
||||
dbDict.Add("Process", logItem.Process);
|
||||
|
@@ -9,8 +9,34 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
const int MaxFileAge = 30;
|
||||
|
||||
public void RunMaintenance()
|
||||
public void RunDailyMaintenance()
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
|
||||
// remove any entries from the library that have an invalid id
|
||||
string LibraryWhereClause = "";
|
||||
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
|
||||
{
|
||||
if (LibraryWhereClause.Length > 0)
|
||||
{
|
||||
LibraryWhereClause += ", ";
|
||||
}
|
||||
LibraryWhereClause += library.Id;
|
||||
}
|
||||
string sqlLibraryWhereClause = "";
|
||||
if (LibraryWhereClause.Length > 0)
|
||||
{
|
||||
sqlLibraryWhereClause = "DELETE FROM Games_Roms WHERE LibraryId NOT IN ( " + LibraryWhereClause + " );";
|
||||
db.ExecuteCMD(sqlLibraryWhereClause);
|
||||
}
|
||||
|
||||
// delete old logs
|
||||
sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRetentionDate;";
|
||||
dbDict.Add("EventRetentionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
// delete files and directories older than 7 days in PathsToClean
|
||||
List<string> PathsToClean = new List<string>();
|
||||
PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory);
|
||||
@@ -43,10 +69,16 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWeeklyMaintenance()
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables");
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SHOW TABLES;";
|
||||
sql = "SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE';";
|
||||
DataTable tables = db.ExecuteCMD(sql);
|
||||
|
||||
int StatusCounter = 1;
|
||||
|
@@ -72,32 +72,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
// compile the ratings values into the ratings groups
|
||||
AgeRestrictionGroupings highestAgeGroup = AgeRestrictionGroupings.Unclassified;
|
||||
foreach (AgeRating ageRating in ageRatings)
|
||||
{
|
||||
foreach (KeyValuePair<AgeRestrictionGroupings, AgeGroupItem> ageGroupItem in AgeGroupingsFlat)
|
||||
{
|
||||
|
||||
PropertyInfo[] groupProps = typeof(AgeGroupItem).GetProperties();
|
||||
foreach (PropertyInfo property in groupProps)
|
||||
{
|
||||
if (RatingsBoards.Contains(property.Name))
|
||||
{
|
||||
List<AgeRatingTitle> ratingBoard = (List<AgeRatingTitle>)property.GetValue(ageGroupItem.Value);
|
||||
foreach (AgeRatingTitle ratingTitle in ratingBoard)
|
||||
{
|
||||
if (ageRating.Rating == ratingTitle)
|
||||
{
|
||||
if (highestAgeGroup < ageGroupItem.Key)
|
||||
{
|
||||
highestAgeGroup = ageGroupItem.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AgeRestrictionGroupings highestAgeGroup = GetAgeGroupFromAgeRatings(ageRatings);
|
||||
|
||||
// return the compiled ratings group
|
||||
AgeGroup ageGroup = new AgeGroup();
|
||||
@@ -138,6 +113,39 @@ namespace gaseous_server.Classes.Metadata
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AgeRestrictionGroupings GetAgeGroupFromAgeRatings(List<AgeRating> ageRatings)
|
||||
{
|
||||
// compile the ratings values into the ratings groups
|
||||
AgeRestrictionGroupings highestAgeGroup = AgeRestrictionGroupings.Unclassified;
|
||||
foreach (AgeRating ageRating in ageRatings)
|
||||
{
|
||||
foreach (KeyValuePair<AgeRestrictionGroupings, AgeGroupItem> ageGroupItem in AgeGroupingsFlat)
|
||||
{
|
||||
|
||||
PropertyInfo[] groupProps = typeof(AgeGroupItem).GetProperties();
|
||||
foreach (PropertyInfo property in groupProps)
|
||||
{
|
||||
if (RatingsBoards.Contains(property.Name))
|
||||
{
|
||||
List<AgeRatingTitle> ratingBoard = (List<AgeRatingTitle>)property.GetValue(ageGroupItem.Value);
|
||||
foreach (AgeRatingTitle ratingTitle in ratingBoard)
|
||||
{
|
||||
if (ageRating.Rating == ratingTitle)
|
||||
{
|
||||
if (highestAgeGroup < ageGroupItem.Key)
|
||||
{
|
||||
highestAgeGroup = ageGroupItem.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return highestAgeGroup;
|
||||
}
|
||||
|
||||
public class AgeGroup
|
||||
{
|
||||
public long? Id { get; set; }
|
||||
|
@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
}
|
||||
|
||||
public static Artwork? GetArtwork(long? Id, string LogoPath)
|
||||
public static Artwork? GetArtwork(long? Id, string ImagePath, bool GetImages)
|
||||
{
|
||||
if ((Id == 0) || (Id == null))
|
||||
{
|
||||
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
else
|
||||
{
|
||||
Task<Artwork> RetVal = _GetArtwork(SearchUsing.id, Id, LogoPath);
|
||||
Task<Artwork> RetVal = _GetArtwork(SearchUsing.id, Id, ImagePath, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static Artwork GetArtwork(string Slug, string LogoPath)
|
||||
public static Artwork GetArtwork(string Slug, string ImagePath, bool GetImages)
|
||||
{
|
||||
Task<Artwork> RetVal = _GetArtwork(SearchUsing.slug, Slug, LogoPath);
|
||||
Task<Artwork> RetVal = _GetArtwork(SearchUsing.slug, Slug, ImagePath, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<Artwork> _GetArtwork(SearchUsing searchUsing, object searchValue, string LogoPath)
|
||||
private static async Task<Artwork> _GetArtwork(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -61,19 +61,20 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
Artwork returnValue = new Artwork();
|
||||
bool forceImageDownload = false;
|
||||
LogoPath = Path.Combine(LogoPath, "Artwork");
|
||||
ImagePath = Path.Combine(ImagePath, "Artwork");
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -88,11 +89,17 @@ namespace gaseous_server.Classes.Metadata
|
||||
throw new Exception("How did you get here?");
|
||||
}
|
||||
|
||||
if ((!File.Exists(Path.Combine(LogoPath, returnValue.ImageId + ".jpg"))) || forceImageDownload == true)
|
||||
// check for presence of "original" quality file - download if absent or force download is true
|
||||
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
|
||||
if (GetImages == true)
|
||||
{
|
||||
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb, returnValue.ImageId);
|
||||
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId);
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId);
|
||||
if ((!File.Exists(localFile)) || forceImageDownload == true)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Artwork download forced.");
|
||||
|
||||
Communications comms = new Communications();
|
||||
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
@@ -104,65 +111,15 @@ namespace gaseous_server.Classes.Metadata
|
||||
slug
|
||||
}
|
||||
|
||||
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string LogoPath)
|
||||
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string ImagePath)
|
||||
{
|
||||
// get Artwork metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Artwork>(IGDBClient.Endpoints.Artworks, fieldList, WhereClause);
|
||||
var result = results.First();
|
||||
|
||||
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);
|
||||
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med, result.ImageId);
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_original, result.ImageId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize, string ImageId)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
string fileName = "Artwork.jpg";
|
||||
string extension = "jpg";
|
||||
switch (logoSize)
|
||||
{
|
||||
case LogoSize.t_thumb:
|
||||
fileName = "_Thumb";
|
||||
extension = "jpg";
|
||||
break;
|
||||
case LogoSize.t_logo_med:
|
||||
fileName = "_Medium";
|
||||
extension = "png";
|
||||
break;
|
||||
case LogoSize.t_original:
|
||||
fileName = "";
|
||||
extension = "png";
|
||||
break;
|
||||
default:
|
||||
fileName = "Artwork";
|
||||
extension = "jpg";
|
||||
break;
|
||||
}
|
||||
fileName = ImageId + fileName;
|
||||
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
|
||||
|
||||
using (var s = client.GetStreamAsync("https:" + imageUrl))
|
||||
{
|
||||
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
|
||||
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
|
||||
{
|
||||
s.Result.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum LogoSize
|
||||
{
|
||||
t_thumb,
|
||||
t_logo_med,
|
||||
t_original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Collections
|
||||
{
|
||||
const string fieldList = "fields checksum,created_at,games,name,slug,updated_at,url;";
|
||||
const string fieldList = "fields as_child_relations,as_parent_relations,checksum,created_at,games,name,slug,type,updated_at,url;";
|
||||
|
||||
public Collections()
|
||||
{
|
||||
|
@@ -1,5 +1,10 @@
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using Humanizer;
|
||||
using IGDB;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using RestEase;
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
@@ -9,16 +14,28 @@ namespace gaseous_server.Classes.Metadata
|
||||
/// </summary>
|
||||
public class Communications
|
||||
{
|
||||
static Communications()
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||
client = new HttpClient(handler);
|
||||
|
||||
client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip");
|
||||
client.DefaultRequestHeaders.Add("Accept-Encoding", "deflate");
|
||||
}
|
||||
|
||||
private static IGDBClient igdb = new IGDBClient(
|
||||
// Found in Twitch Developer portal for your app
|
||||
Config.IGDB.ClientId,
|
||||
Config.IGDB.Secret
|
||||
);
|
||||
|
||||
private static HttpClient client = new HttpClient();
|
||||
|
||||
/// <summary>
|
||||
/// Configure metadata API communications
|
||||
/// </summary>
|
||||
public static MetadataSources MetadataSource
|
||||
public static HasheousClient.Models.MetadataModel.MetadataSources MetadataSource
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -30,7 +47,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case MetadataSources.IGDB:
|
||||
case HasheousClient.Models.MetadataModel.MetadataSources.IGDB:
|
||||
// set rate limiter avoidance values
|
||||
RateLimitAvoidanceWait = 1500;
|
||||
RateLimitAvoidanceThreshold = 3;
|
||||
@@ -46,7 +63,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
private static MetadataSources _MetadataSource = MetadataSources.None;
|
||||
private static HasheousClient.Models.MetadataModel.MetadataSources _MetadataSource = HasheousClient.Models.MetadataModel.MetadataSources.None;
|
||||
|
||||
// rate limit avoidance - what can we do to ensure that rate limiting is avoided?
|
||||
// these values affect all communications
|
||||
@@ -142,22 +159,6 @@ namespace gaseous_server.Classes.Metadata
|
||||
private int RetryAttempts = 0;
|
||||
private int RetryAttemptsMax = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Supported metadata sources
|
||||
/// </summary>
|
||||
public enum MetadataSources
|
||||
{
|
||||
/// <summary>
|
||||
/// None - always returns null for metadata requests - should not really be using this source
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// IGDB - queries the IGDB service for metadata
|
||||
/// </summary>
|
||||
IGDB
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request data from the metadata API
|
||||
/// </summary>
|
||||
@@ -170,9 +171,9 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
switch (_MetadataSource)
|
||||
{
|
||||
case MetadataSources.None:
|
||||
case HasheousClient.Models.MetadataModel.MetadataSources.None:
|
||||
return null;
|
||||
case MetadataSources.IGDB:
|
||||
case HasheousClient.Models.MetadataModel.MetadataSources.IGDB:
|
||||
return await IGDBAPI<T>(Endpoint, Fields, Query);
|
||||
default:
|
||||
return null;
|
||||
@@ -223,6 +224,22 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
return await IGDBAPI<T>(Endpoint, Fields, Query);
|
||||
}
|
||||
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB API unauthorised error while accessing endpoint " + Endpoint + ". Waiting " + RateLimitAvoidanceWait + " milliseconds and resetting IGDB client.", apiEx);
|
||||
|
||||
Thread.Sleep(RateLimitAvoidanceWait);
|
||||
|
||||
igdb = new IGDBClient(
|
||||
// Found in Twitch Developer portal for your app
|
||||
Config.IGDB.ClientId,
|
||||
Config.IGDB.Secret
|
||||
);
|
||||
|
||||
RetryAttempts += 1;
|
||||
|
||||
return await IGDBAPI<T>(Endpoint, Fields, Query);
|
||||
|
||||
default:
|
||||
Logging.Log(Logging.LogType.Warning, "API Connection", "Exception when accessing endpoint " + Endpoint, apiEx);
|
||||
throw;
|
||||
@@ -234,5 +251,343 @@ namespace gaseous_server.Classes.Metadata
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download from the specified uri
|
||||
/// </summary>
|
||||
/// <param name="uri">The uri to download from</param>
|
||||
/// <param name="DestinationFile">The file name and path the download should be stored as</param>
|
||||
public Task<bool?> DownloadFile(Uri uri, string DestinationFile)
|
||||
{
|
||||
var result = _DownloadFile(uri, DestinationFile);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool?> _DownloadFile(Uri uri, string DestinationFile)
|
||||
{
|
||||
string DestinationDirectory = new FileInfo(DestinationFile).Directory.FullName;
|
||||
if (!Directory.Exists(DestinationDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(DestinationDirectory);
|
||||
}
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Communications", "Downloading from " + uri.ToString() + " to " + DestinationFile);
|
||||
|
||||
try
|
||||
{
|
||||
using (HttpResponseMessage response = client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).Result)
|
||||
{
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using (Stream contentStream = await response.Content.ReadAsStreamAsync(), fileStream = new FileStream(DestinationFile, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
|
||||
{
|
||||
var totalRead = 0L;
|
||||
var totalReads = 0L;
|
||||
var buffer = new byte[8192];
|
||||
var isMoreToRead = true;
|
||||
|
||||
do
|
||||
{
|
||||
var read = await contentStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
if (read == 0)
|
||||
{
|
||||
isMoreToRead = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
await fileStream.WriteAsync(buffer, 0, read);
|
||||
|
||||
totalRead += read;
|
||||
totalReads += 1;
|
||||
|
||||
if (totalReads % 2000 == 0)
|
||||
{
|
||||
Console.WriteLine(string.Format("total bytes downloaded so far: {0:n0}", totalRead));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (isMoreToRead);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
if (ex.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
if (File.Exists(DestinationFile))
|
||||
{
|
||||
FileInfo fi = new FileInfo(DestinationFile);
|
||||
if (fi.Length == 0)
|
||||
{
|
||||
File.Delete(DestinationFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Log(Logging.LogType.Warning, "Download Images", "Error downloading file: ", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<string> GetSpecificImageFromServer(string ImagePath, string ImageId, IGDBAPI_ImageSize size, List<IGDBAPI_ImageSize>? FallbackSizes = null)
|
||||
{
|
||||
string returnPath = "";
|
||||
|
||||
// check for artificial sizes first
|
||||
switch (size)
|
||||
{
|
||||
case IGDBAPI_ImageSize.screenshot_small:
|
||||
case IGDBAPI_ImageSize.screenshot_thumb:
|
||||
string BasePath = Path.Combine(ImagePath, size.ToString());
|
||||
if (!Directory.Exists(BasePath))
|
||||
{
|
||||
Directory.CreateDirectory(BasePath);
|
||||
}
|
||||
returnPath = Path.Combine(BasePath, ImageId + ".jpg");
|
||||
if (!File.Exists(returnPath))
|
||||
{
|
||||
// get original size image and resize
|
||||
string originalSizePath = await GetSpecificImageFromServer(ImagePath, ImageId, IGDBAPI_ImageSize.original, null);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case IGDBAPI_ImageSize.screenshot_small:
|
||||
// 235x128
|
||||
width = 235;
|
||||
height = 128;
|
||||
break;
|
||||
|
||||
case IGDBAPI_ImageSize.screenshot_thumb:
|
||||
// 165x90
|
||||
width = 165;
|
||||
height = 90;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
using (var image = new ImageMagick.MagickImage(originalSizePath))
|
||||
{
|
||||
image.Resize(width, height);
|
||||
image.Strip();
|
||||
image.Write(returnPath);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// these sizes are IGDB native
|
||||
if (RateLimitResumeTime > DateTime.UtcNow)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB rate limit hit. Pausing API communications until " + RateLimitResumeTime.ToString() + ". Attempt " + RetryAttempts + " of " + RetryAttemptsMax + " retries.");
|
||||
Thread.Sleep(RateLimitRecoveryWaitTime);
|
||||
}
|
||||
|
||||
if (InRateLimitAvoidanceMode == true)
|
||||
{
|
||||
// sleep for a moment to help avoid hitting the rate limiter
|
||||
Thread.Sleep(RateLimitAvoidanceWait);
|
||||
}
|
||||
|
||||
Communications comms = new Communications();
|
||||
List<IGDBAPI_ImageSize> imageSizes = new List<IGDBAPI_ImageSize>
|
||||
{
|
||||
size
|
||||
};
|
||||
|
||||
// get the image
|
||||
try
|
||||
{
|
||||
returnPath = Path.Combine(ImagePath, size.ToString(), ImageId + ".jpg");
|
||||
|
||||
// fail early if the file is already downloaded
|
||||
if (!File.Exists(returnPath))
|
||||
{
|
||||
await comms.IGDBAPI_GetImage(imageSizes, ImageId, ImagePath);
|
||||
}
|
||||
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
if (ex.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Image Download", "Image not found, trying a different size.");
|
||||
|
||||
if (FallbackSizes != null)
|
||||
{
|
||||
foreach (Communications.IGDBAPI_ImageSize imageSize in FallbackSizes)
|
||||
{
|
||||
returnPath = await GetSpecificImageFromServer(ImagePath, ImageId, imageSize, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// increment rate limiter avoidance call count
|
||||
RateLimitAvoidanceCallCount += 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return returnPath;
|
||||
}
|
||||
|
||||
public static T? GetSearchCache<T>(string SearchFields, string SearchString)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM SearchCache WHERE SearchFields = @searchfields AND SearchString = @searchstring;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "searchfields", SearchFields },
|
||||
{ "searchstring", SearchString }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
if (data.Rows.Count > 0)
|
||||
{
|
||||
// cache hit
|
||||
string rawString = data.Rows[0]["Content"].ToString();
|
||||
T ReturnValue = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(rawString);
|
||||
if (ReturnValue != null)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Search Cache", "Found search result in cache. Search string: " + SearchString);
|
||||
return ReturnValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Search Cache", "Search result not found in cache.");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// cache miss
|
||||
Logging.Log(Logging.LogType.Information, "Search Cache", "Search result not found in cache.");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetSearchCache<T>(string SearchFields, string SearchString, T SearchResult)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Search Cache", "Storing search results in cache. Search string: " + SearchString);
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO SearchCache (SearchFields, SearchString, Content, LastSearch) VALUES (@searchfields, @searchstring, @content, @lastsearch);";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "searchfields", SearchFields },
|
||||
{ "searchstring", SearchString },
|
||||
{ "content", Newtonsoft.Json.JsonConvert.SerializeObject(SearchResult) },
|
||||
{ "lastsearch", DateTime.UtcNow }
|
||||
};
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See https://api-docs.igdb.com/?javascript#images for more information about the image url structure
|
||||
/// </summary>
|
||||
/// <param name="ImageId"></param>
|
||||
/// <param name="outputPath">The path to save the downloaded files to
|
||||
public async Task IGDBAPI_GetImage(List<IGDBAPI_ImageSize> ImageSizes, string ImageId, string OutputPath)
|
||||
{
|
||||
string urlTemplate = "https://images.igdb.com/igdb/image/upload/t_{size}/{hash}.jpg";
|
||||
|
||||
foreach (IGDBAPI_ImageSize ImageSize in ImageSizes)
|
||||
{
|
||||
string url = urlTemplate.Replace("{size}", Common.GetDescription(ImageSize)).Replace("{hash}", ImageId);
|
||||
string newOutputPath = Path.Combine(OutputPath, Common.GetDescription(ImageSize));
|
||||
string OutputFile = ImageId + ".jpg";
|
||||
string fullPath = Path.Combine(newOutputPath, OutputFile);
|
||||
|
||||
await _DownloadFile(new Uri(url), fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public enum IGDBAPI_ImageSize
|
||||
{
|
||||
/// <summary>
|
||||
/// 90x128 Fit
|
||||
/// </summary>
|
||||
[Description("cover_small")]
|
||||
cover_small,
|
||||
|
||||
/// <summary>
|
||||
/// 264x374 Fit
|
||||
/// </summary>
|
||||
[Description("cover_big")]
|
||||
cover_big,
|
||||
|
||||
/// <summary>
|
||||
/// 165x90 Lfill, Centre gravity - resized by Gaseous and is not a real IGDB size
|
||||
/// </summary>
|
||||
[Description("screenshot_thumb")]
|
||||
screenshot_thumb,
|
||||
|
||||
/// <summary>
|
||||
/// 235x128 Lfill, Centre gravity - resized by Gaseous and is not a real IGDB size
|
||||
/// </summary>
|
||||
[Description("screenshot_small")]
|
||||
screenshot_small,
|
||||
|
||||
/// <summary>
|
||||
/// 589x320 Lfill, Centre gravity
|
||||
/// </summary>
|
||||
[Description("screenshot_med")]
|
||||
screenshot_med,
|
||||
|
||||
/// <summary>
|
||||
/// 889x500 Lfill, Centre gravity
|
||||
/// </summary>
|
||||
[Description("screenshot_big")]
|
||||
screenshot_big,
|
||||
|
||||
/// <summary>
|
||||
/// 1280x720 Lfill, Centre gravity
|
||||
/// </summary>
|
||||
[Description("screenshot_huge")]
|
||||
screenshot_huge,
|
||||
|
||||
/// <summary>
|
||||
/// 284x160 Fit
|
||||
/// </summary>
|
||||
[Description("logo_med")]
|
||||
logo_med,
|
||||
|
||||
/// <summary>
|
||||
/// 90x90 Thumb, Centre gravity
|
||||
/// </summary>
|
||||
[Description("thumb")]
|
||||
thumb,
|
||||
|
||||
/// <summary>
|
||||
/// 35x35 Thumb, Centre gravity
|
||||
/// </summary>
|
||||
[Description("micro")]
|
||||
micro,
|
||||
|
||||
/// <summary>
|
||||
/// 1280x720 Fit, Centre gravity
|
||||
/// </summary>
|
||||
[Description("720p")]
|
||||
r720p,
|
||||
|
||||
/// <summary>
|
||||
/// 1920x1080 Fit, Centre gravity
|
||||
/// </summary>
|
||||
[Description("1080p")]
|
||||
r1080p,
|
||||
|
||||
/// <summary>
|
||||
/// The originally uploaded image
|
||||
/// </summary>
|
||||
[Description("original")]
|
||||
original
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
}
|
||||
|
||||
public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath)
|
||||
public static CompanyLogo? GetCompanyLogo(long? Id, string ImagePath)
|
||||
{
|
||||
if ((Id == 0) || (Id == null))
|
||||
{
|
||||
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
else
|
||||
{
|
||||
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, LogoPath);
|
||||
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, ImagePath);
|
||||
return RetVal.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static CompanyLogo GetCompanyLogo(string Slug, string LogoPath)
|
||||
public static CompanyLogo GetCompanyLogo(string Slug, string ImagePath)
|
||||
{
|
||||
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.slug, Slug, LogoPath);
|
||||
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.slug, Slug, ImagePath);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<CompanyLogo> _GetCompanyLogo(SearchUsing searchUsing, object searchValue, string LogoPath)
|
||||
private static async Task<CompanyLogo> _GetCompanyLogo(SearchUsing searchUsing, object searchValue, string ImagePath)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -64,7 +64,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
if (returnValue != null)
|
||||
{
|
||||
Storage.NewCacheValue(returnValue);
|
||||
@@ -74,7 +74,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
}
|
||||
@@ -91,13 +91,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
throw new Exception("How did you get here?");
|
||||
}
|
||||
|
||||
if (returnValue != null)
|
||||
// check for presence of "original" quality file - download if absent or force download is true
|
||||
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
|
||||
if ((!File.Exists(localFile)) || forceImageDownload == true)
|
||||
{
|
||||
if ((!File.Exists(Path.Combine(LogoPath, "Logo.jpg"))) || forceImageDownload == true)
|
||||
{
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb);
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med);
|
||||
}
|
||||
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Company logo download forced.");
|
||||
|
||||
Communications comms = new Communications();
|
||||
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
@@ -109,65 +110,15 @@ namespace gaseous_server.Classes.Metadata
|
||||
slug
|
||||
}
|
||||
|
||||
private static async Task<CompanyLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
|
||||
private static async Task<CompanyLogo> GetObjectFromServer(string WhereClause, string ImagePath)
|
||||
{
|
||||
// get CompanyLogo metadata
|
||||
// get Artwork metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<CompanyLogo>(IGDBClient.Endpoints.CompanyLogos, fieldList, WhereClause);
|
||||
if (results.Length > 0)
|
||||
{
|
||||
var result = results.First();
|
||||
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb);
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
string fileName = "Logo.jpg";
|
||||
string extension = "jpg";
|
||||
switch (logoSize)
|
||||
{
|
||||
case LogoSize.t_thumb:
|
||||
fileName = "Logo_Thumb";
|
||||
extension = "jpg";
|
||||
break;
|
||||
case LogoSize.t_logo_med:
|
||||
fileName = "Logo_Medium";
|
||||
extension = "png";
|
||||
break;
|
||||
default:
|
||||
fileName = "Logo";
|
||||
extension = "jpg";
|
||||
break;
|
||||
}
|
||||
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
|
||||
|
||||
using (var s = client.GetStreamAsync("https:" + imageUrl))
|
||||
{
|
||||
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
|
||||
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
|
||||
{
|
||||
s.Result.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum LogoSize
|
||||
{
|
||||
t_thumb,
|
||||
t_logo_med
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,21 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using IGDB;
|
||||
using IGDB.Models;
|
||||
using Microsoft.CodeAnalysis.Elfie.Model.Strings;
|
||||
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Covers
|
||||
{
|
||||
const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
|
||||
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 LogoPath)
|
||||
public static Cover? GetCover(long? Id, string ImagePath, bool GetImages)
|
||||
{
|
||||
if ((Id == 0) || (Id == null))
|
||||
{
|
||||
@@ -21,18 +23,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
else
|
||||
{
|
||||
Task<Cover> RetVal = _GetCover(SearchUsing.id, Id, LogoPath);
|
||||
Task<Cover> RetVal = _GetCover(SearchUsing.id, Id, ImagePath, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static Cover GetCover(string Slug, string LogoPath)
|
||||
public static Cover GetCover(string Slug, string ImagePath, bool GetImages)
|
||||
{
|
||||
Task<Cover> RetVal = _GetCover(SearchUsing.slug, Slug, LogoPath);
|
||||
Task<Cover> RetVal = _GetCover(SearchUsing.slug, Slug, ImagePath, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<Cover> _GetCover(SearchUsing searchUsing, object searchValue, string LogoPath)
|
||||
private static async Task<Cover> _GetCover(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -61,17 +63,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
Cover returnValue = new Cover();
|
||||
bool forceImageDownload = false;
|
||||
ImagePath = Path.Combine(ImagePath, "Covers");
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
}
|
||||
@@ -88,11 +91,30 @@ namespace gaseous_server.Classes.Metadata
|
||||
throw new Exception("How did you get here?");
|
||||
}
|
||||
|
||||
if ((!File.Exists(Path.Combine(LogoPath, "Cover.jpg"))) || forceImageDownload == true)
|
||||
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
|
||||
if (GetImages == true)
|
||||
{
|
||||
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb, returnValue.ImageId);
|
||||
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId);
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId);
|
||||
if ((!File.Exists(localFile)) || forceImageDownload == true)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Cover download forced.");
|
||||
|
||||
// check for presence of image file - download if absent or force download is true
|
||||
List<Communications.IGDBAPI_ImageSize> imageSizes = new List<Communications.IGDBAPI_ImageSize>{
|
||||
Communications.IGDBAPI_ImageSize.cover_big,
|
||||
Communications.IGDBAPI_ImageSize.cover_small,
|
||||
Communications.IGDBAPI_ImageSize.original
|
||||
};
|
||||
|
||||
Communications comms = new Communications();
|
||||
foreach (Communications.IGDBAPI_ImageSize size in imageSizes)
|
||||
{
|
||||
localFile = Path.Combine(ImagePath, size.ToString(), returnValue.ImageId + ".jpg");
|
||||
if ((!File.Exists(localFile)) || forceImageDownload == true)
|
||||
{
|
||||
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, size, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
@@ -104,64 +126,15 @@ namespace gaseous_server.Classes.Metadata
|
||||
slug
|
||||
}
|
||||
|
||||
private static async Task<Cover> GetObjectFromServer(string WhereClause, string LogoPath)
|
||||
private static async Task<Cover> GetObjectFromServer(string WhereClause, string ImagePath)
|
||||
{
|
||||
// get Cover metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Cover>(IGDBClient.Endpoints.Covers, fieldList, WhereClause);
|
||||
var result = results.First();
|
||||
|
||||
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);
|
||||
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med, result.ImageId);
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_original, result.ImageId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize, string ImageId)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
string fileName = "Cover.jpg";
|
||||
string extension = "jpg";
|
||||
switch (logoSize)
|
||||
{
|
||||
case LogoSize.t_thumb:
|
||||
fileName = "Cover_Thumb";
|
||||
extension = "jpg";
|
||||
break;
|
||||
case LogoSize.t_logo_med:
|
||||
fileName = "Cover_Medium";
|
||||
extension = "png";
|
||||
break;
|
||||
case LogoSize.t_original:
|
||||
fileName = "Cover";
|
||||
extension = "png";
|
||||
break;
|
||||
default:
|
||||
fileName = "Cover";
|
||||
extension = "jpg";
|
||||
break;
|
||||
}
|
||||
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
|
||||
|
||||
using (var s = client.GetStreamAsync("https:" + imageUrl))
|
||||
{
|
||||
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
|
||||
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
|
||||
{
|
||||
s.Result.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum LogoSize
|
||||
{
|
||||
t_thumb,
|
||||
t_logo_med,
|
||||
t_original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ 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,collection,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;";
|
||||
const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collection,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()
|
||||
{
|
||||
@@ -98,14 +98,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
|
||||
UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
|
||||
return returnValue;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
|
||||
UpdateSubClasses(returnValue, getAllMetadata, followSubGames, forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -115,21 +115,21 @@ namespace gaseous_server.Classes.Metadata
|
||||
return returnValue;
|
||||
case Storage.CacheStatus.Current:
|
||||
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
|
||||
UpdateSubClasses(returnValue, false, false);
|
||||
UpdateSubClasses(returnValue, false, false, false);
|
||||
return returnValue;
|
||||
default:
|
||||
throw new Exception("How did you get here?");
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames)
|
||||
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames, bool forceRefresh)
|
||||
{
|
||||
// required metadata
|
||||
if (Game.Cover != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
|
||||
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -211,7 +211,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
try
|
||||
{
|
||||
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
|
||||
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -285,7 +285,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
try
|
||||
{
|
||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
|
||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -441,9 +441,9 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
Game game = new Game{
|
||||
Id = (long)row["Id"],
|
||||
Name = (string)row["Name"],
|
||||
Slug = (string)row["Slug"],
|
||||
Summary = (string)row["Summary"]
|
||||
Name = (string)Common.ReturnValueIfNull(row["Name"], ""),
|
||||
Slug = (string)Common.ReturnValueIfNull(row["Slug"], ""),
|
||||
Summary = (string)Common.ReturnValueIfNull(row["Summary"], "")
|
||||
};
|
||||
searchResults.Add(game);
|
||||
}
|
||||
@@ -477,17 +477,47 @@ namespace gaseous_server.Classes.Metadata
|
||||
break;
|
||||
}
|
||||
|
||||
// check search cache
|
||||
Game[]? games = Communications.GetSearchCache<Game[]?>(searchFields, searchBody);
|
||||
|
||||
if (games == null)
|
||||
{
|
||||
// cache miss
|
||||
// get Game metadata
|
||||
Communications comms = new Communications();
|
||||
Game[]? results = new Game[0];
|
||||
if (allowSearch == true)
|
||||
{
|
||||
results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
||||
|
||||
Communications.SetSearchCache<Game[]?>(searchFields, searchBody, results);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
else
|
||||
{
|
||||
return games.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<KeyValuePair<long, string>> GetAvailablePlatforms(long GameId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @gameid ORDER BY Platform.`Name`;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("gameid", GameId);
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<KeyValuePair<long, string>> platforms = new List<KeyValuePair<long, string>>();
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
KeyValuePair<long, string> valuePair = new KeyValuePair<long, string>((long)row["PlatformId"], (string)row["Name"]);
|
||||
platforms.Add(valuePair);
|
||||
}
|
||||
|
||||
return platforms;
|
||||
}
|
||||
|
||||
public enum SearchType
|
||||
{
|
||||
@@ -499,10 +529,16 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
public class MinimalGameItem
|
||||
{
|
||||
public MinimalGameItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MinimalGameItem(Game gameObject)
|
||||
{
|
||||
this.Id = gameObject.Id;
|
||||
this.Name = gameObject.Name;
|
||||
this.Slug = gameObject.Slug;
|
||||
this.TotalRating = gameObject.TotalRating;
|
||||
this.TotalRatingCount = gameObject.TotalRatingCount;
|
||||
this.Cover = gameObject.Cover;
|
||||
@@ -526,8 +562,11 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
public long? Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public double? TotalRating { get; set; }
|
||||
public int? TotalRatingCount { get; set; }
|
||||
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; }
|
||||
|
@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
}
|
||||
|
||||
public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath)
|
||||
public static PlatformLogo? GetPlatformLogo(long? Id, string ImagePath)
|
||||
{
|
||||
if ((Id == 0) || (Id == null))
|
||||
{
|
||||
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
else
|
||||
{
|
||||
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, LogoPath);
|
||||
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, ImagePath);
|
||||
return RetVal.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static PlatformLogo GetPlatformLogo(string Slug, string LogoPath)
|
||||
public static PlatformLogo GetPlatformLogo(string Slug, string ImagePath)
|
||||
{
|
||||
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.slug, Slug, LogoPath);
|
||||
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.slug, Slug, ImagePath);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<PlatformLogo> _GetPlatformLogo(SearchUsing searchUsing, object searchValue, string LogoPath)
|
||||
private static async Task<PlatformLogo> _GetPlatformLogo(SearchUsing searchUsing, object searchValue, string ImagePath)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -64,7 +64,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
if (returnValue != null)
|
||||
{
|
||||
Storage.NewCacheValue(returnValue);
|
||||
@@ -74,7 +74,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
}
|
||||
@@ -93,10 +93,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
if (returnValue != null)
|
||||
{
|
||||
if ((!File.Exists(Path.Combine(LogoPath, "Logo.jpg"))) || forceImageDownload == true)
|
||||
// check for presence of "original" quality file - download if absent or force download is true
|
||||
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
|
||||
if ((!File.Exists(localFile)) || forceImageDownload == true)
|
||||
{
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb);
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med);
|
||||
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Platform logo download forced.");
|
||||
|
||||
Communications comms = new Communications();
|
||||
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,65 +113,15 @@ namespace gaseous_server.Classes.Metadata
|
||||
slug
|
||||
}
|
||||
|
||||
private static async Task<PlatformLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
|
||||
private static async Task<PlatformLogo> GetObjectFromServer(string WhereClause, string ImagePath)
|
||||
{
|
||||
// get PlatformLogo metadata
|
||||
// get Artwork metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<PlatformLogo>(IGDBClient.Endpoints.PlatformLogos, fieldList, WhereClause);
|
||||
if (results.Length > 0)
|
||||
{
|
||||
var result = results.First();
|
||||
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb);
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
string fileName = "Logo.jpg";
|
||||
string extension = "jpg";
|
||||
switch (logoSize)
|
||||
{
|
||||
case LogoSize.t_thumb:
|
||||
fileName = "Logo_Thumb";
|
||||
extension = "jpg";
|
||||
break;
|
||||
case LogoSize.t_logo_med:
|
||||
fileName = "Logo_Medium";
|
||||
extension = "png";
|
||||
break;
|
||||
default:
|
||||
fileName = "Logo";
|
||||
extension = "jpg";
|
||||
break;
|
||||
}
|
||||
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
|
||||
|
||||
using (var s = client.GetStreamAsync("https:" + imageUrl))
|
||||
{
|
||||
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
|
||||
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
|
||||
{
|
||||
s.Result.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum LogoSize
|
||||
{
|
||||
t_thumb,
|
||||
t_logo_med
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -93,9 +93,16 @@ namespace gaseous_server.Classes.Metadata
|
||||
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion)
|
||||
{
|
||||
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
|
||||
|
@@ -38,10 +38,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
|
||||
return RetVal.Result;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Metadata", "An error occurred fetching Platform Id " + Id, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Platform GetPlatform(string Slug, bool forceRefresh = false)
|
||||
@@ -123,9 +131,16 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPlatformMapping(Platform platform)
|
||||
|
@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
}
|
||||
|
||||
public static Screenshot? GetScreenshot(long? Id, string LogoPath)
|
||||
public static Screenshot? GetScreenshot(long? Id, string ImagePath, bool GetImages)
|
||||
{
|
||||
if ((Id == 0) || (Id == null))
|
||||
{
|
||||
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
else
|
||||
{
|
||||
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, LogoPath);
|
||||
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, ImagePath, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static Screenshot GetScreenshot(string Slug, string LogoPath)
|
||||
public static Screenshot GetScreenshot(string Slug, string ImagePath, bool GetImages)
|
||||
{
|
||||
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.slug, Slug, LogoPath);
|
||||
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.slug, Slug, ImagePath, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<Screenshot> _GetScreenshot(SearchUsing searchUsing, object searchValue, string LogoPath)
|
||||
private static async Task<Screenshot> _GetScreenshot(SearchUsing searchUsing, object searchValue, string ImagePath, bool GetImages = true)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -61,18 +61,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
Screenshot returnValue = new Screenshot();
|
||||
bool forceImageDownload = false;
|
||||
LogoPath = Path.Combine(LogoPath, "Screenshots");
|
||||
ImagePath = Path.Combine(ImagePath, "Screenshots");
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
}
|
||||
@@ -89,11 +89,17 @@ namespace gaseous_server.Classes.Metadata
|
||||
throw new Exception("How did you get here?");
|
||||
}
|
||||
|
||||
if ((!File.Exists(Path.Combine(LogoPath, "Screenshot.jpg"))) || forceImageDownload == true)
|
||||
// check for presence of "original" quality file - download if absent or force download is true
|
||||
string localFile = Path.Combine(ImagePath, Communications.IGDBAPI_ImageSize.original.ToString(), returnValue.ImageId + ".jpg");
|
||||
if (GetImages == true)
|
||||
{
|
||||
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_thumb, returnValue.ImageId);
|
||||
//GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_logo_med, returnValue.ImageId);
|
||||
GetImageFromServer(returnValue.Url, LogoPath, LogoSize.t_original, returnValue.ImageId);
|
||||
if ((!File.Exists(localFile)) || forceImageDownload == true)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Metadata: " + returnValue.GetType().Name, "Screenshot download forced.");
|
||||
|
||||
Communications comms = new Communications();
|
||||
comms.GetSpecificImageFromServer(ImagePath, returnValue.ImageId, Communications.IGDBAPI_ImageSize.original, null);
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
@@ -105,65 +111,15 @@ namespace gaseous_server.Classes.Metadata
|
||||
slug
|
||||
}
|
||||
|
||||
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string LogoPath)
|
||||
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string ImagePath)
|
||||
{
|
||||
// get Screenshot metadata
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Screenshot>(IGDBClient.Endpoints.Screenshots, fieldList, WhereClause);
|
||||
var result = results.First();
|
||||
|
||||
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_thumb, result.ImageId);
|
||||
//GetImageFromServer(result.Url, LogoPath, LogoSize.t_logo_med, result.ImageId);
|
||||
GetImageFromServer(result.Url, LogoPath, LogoSize.t_original, result.ImageId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void GetImageFromServer(string Url, string LogoPath, LogoSize logoSize, string ImageId)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
string fileName = "Artwork.jpg";
|
||||
string extension = "jpg";
|
||||
switch (logoSize)
|
||||
{
|
||||
case LogoSize.t_thumb:
|
||||
fileName = "_Thumb";
|
||||
extension = "jpg";
|
||||
break;
|
||||
case LogoSize.t_logo_med:
|
||||
fileName = "_Medium";
|
||||
extension = "png";
|
||||
break;
|
||||
case LogoSize.t_original:
|
||||
fileName = "";
|
||||
extension = "png";
|
||||
break;
|
||||
default:
|
||||
fileName = "Artwork";
|
||||
extension = "jpg";
|
||||
break;
|
||||
}
|
||||
fileName = ImageId + fileName;
|
||||
string imageUrl = Url.Replace(LogoSize.t_thumb.ToString(), logoSize.ToString()).Replace("jpg", extension);
|
||||
|
||||
using (var s = client.GetStreamAsync("https:" + imageUrl))
|
||||
{
|
||||
if (!Directory.Exists(LogoPath)) { Directory.CreateDirectory(LogoPath); }
|
||||
using (var fs = new FileStream(Path.Combine(LogoPath, fileName + "." + extension), FileMode.OpenOrCreate))
|
||||
{
|
||||
s.Result.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum LogoSize
|
||||
{
|
||||
t_thumb,
|
||||
t_logo_med,
|
||||
t_original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -59,7 +59,7 @@ 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, forceRefresh);
|
||||
Metadata.Games.GetGame((long)dr["id"], true, false, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -55,12 +55,15 @@ namespace gaseous_server.Classes
|
||||
return GetMediaGroup(mgId);
|
||||
}
|
||||
|
||||
public static GameRomMediaGroupItem GetMediaGroup(long Id)
|
||||
public static GameRomMediaGroupItem GetMediaGroup(long Id, string userid = "")
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM RomMediaGroup WHERE Id=@id;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", Id);
|
||||
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.Id=@id;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", Id },
|
||||
{ "userid", userid }
|
||||
};
|
||||
|
||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
@@ -75,12 +78,15 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId)
|
||||
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId, string userid = "")
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM RomMediaGroup WHERE GameId=@gameid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("gameid", GameId);
|
||||
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.GameId=@gameid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "gameid", GameId },
|
||||
{ "userid", userid }
|
||||
};
|
||||
|
||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
@@ -91,7 +97,7 @@ namespace gaseous_server.Classes
|
||||
mediaGroupItems.Add(BuildMediaGroupFromRow(row));
|
||||
}
|
||||
|
||||
mediaGroupItems.Sort((x, y) => x.PlatformName.CompareTo(y.PlatformName));
|
||||
mediaGroupItems.Sort((x, y) => x.Platform.CompareTo(y.Platform));
|
||||
|
||||
return mediaGroupItems;
|
||||
}
|
||||
@@ -156,7 +162,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;";
|
||||
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id; DELETE FROM GameState WHERE RomId=@id AND IsMediaGroup=1;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", Id);
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
@@ -170,12 +176,23 @@ namespace gaseous_server.Classes
|
||||
|
||||
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
|
||||
{
|
||||
bool hasSaveStates = false;
|
||||
if (row.Table.Columns.Contains("GameStateRomId"))
|
||||
{
|
||||
if (row["GameStateRomId"] != DBNull.Value)
|
||||
{
|
||||
hasSaveStates = true;
|
||||
}
|
||||
}
|
||||
|
||||
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem();
|
||||
mediaGroupItem.Id = (long)row["Id"];
|
||||
mediaGroupItem.Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"];
|
||||
mediaGroupItem.PlatformId = (long)row["PlatformId"];
|
||||
mediaGroupItem.GameId = (long)row["GameId"];
|
||||
mediaGroupItem.RomIds = new List<long>();
|
||||
mediaGroupItem.Roms = new List<Roms.GameRomItem>();
|
||||
mediaGroupItem.HasSaveStates = hasSaveStates;
|
||||
|
||||
// get members
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
@@ -186,6 +203,26 @@ namespace gaseous_server.Classes
|
||||
foreach (DataRow dataRow in data.Rows)
|
||||
{
|
||||
mediaGroupItem.RomIds.Add((long)dataRow["RomId"]);
|
||||
try
|
||||
{
|
||||
mediaGroupItem.Roms.Add(Roms.GetRom((long)dataRow["RomId"]));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Rom Group", "Unable to load ROM data", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// check for a web emulator and update the romItem
|
||||
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
||||
{
|
||||
if (platformMapping.IGDBId == mediaGroupItem.PlatformId)
|
||||
{
|
||||
if (platformMapping.WebEmulator != null)
|
||||
{
|
||||
mediaGroupItem.Emulator = platformMapping.WebEmulator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mediaGroupItem;
|
||||
@@ -360,7 +397,7 @@ namespace gaseous_server.Classes
|
||||
public long Id { get; set; }
|
||||
public long GameId { get; set; }
|
||||
public long PlatformId { get; set; }
|
||||
public string PlatformName {
|
||||
public string Platform {
|
||||
get
|
||||
{
|
||||
try
|
||||
@@ -373,7 +410,10 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
||||
public List<long> RomIds { get; set; }
|
||||
public List<Roms.GameRomItem> Roms { get; set; }
|
||||
public bool HasSaveStates { get; set; } = false;
|
||||
private GroupBuildStatus _Status { get; set; }
|
||||
public GroupBuildStatus Status {
|
||||
get
|
||||
|
@@ -15,30 +15,51 @@ namespace gaseous_server.Classes
|
||||
{}
|
||||
}
|
||||
|
||||
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, int pageNumber = 0, int pageSize = 0)
|
||||
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0, string userid = "")
|
||||
{
|
||||
GameRomObject GameRoms = new GameRomObject();
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
string sqlCount = "";
|
||||
string sqlPlatform = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", GameId);
|
||||
dbDict.Add("userid", userid);
|
||||
|
||||
string NameSearchWhere = "";
|
||||
if (NameSearch.Length > 0)
|
||||
{
|
||||
NameSearchWhere = " AND Games_Roms.`Name` LIKE @namesearch";
|
||||
dbDict.Add("namesearch", '%' + NameSearch + '%');
|
||||
}
|
||||
|
||||
// platform query
|
||||
sqlPlatform = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE GameId = @id ORDER BY Platform.`Name`;";
|
||||
|
||||
if (PlatformId == -1) {
|
||||
sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id ORDER BY Platform.`Name`, Games_Roms.`Name`";
|
||||
// data query
|
||||
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||
|
||||
// count query
|
||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
|
||||
} else {
|
||||
sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid ORDER BY Platform.`Name`, Games_Roms.`Name`";
|
||||
// data query
|
||||
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||
|
||||
// count query
|
||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
||||
|
||||
dbDict.Add("platformid", PlatformId);
|
||||
}
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict)[0];
|
||||
DataTable platformDT = db.ExecuteCMD(sqlPlatform, dbDict);
|
||||
|
||||
if (romDT.Rows.Count > 0)
|
||||
{
|
||||
// set count of roms
|
||||
GameRoms.Count = romDT.Rows.Count;
|
||||
|
||||
// setup platforms list
|
||||
Dictionary<long, string> platformDict = new Dictionary<long, string>();
|
||||
GameRoms.Count = int.Parse((string)rowCount["RomCount"]);
|
||||
|
||||
int pageOffset = pageSize * (pageNumber - 1);
|
||||
for (int i = 0; i < romDT.Rows.Count; i++)
|
||||
@@ -49,18 +70,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
GameRoms.GameRomItems.Add(gameRomItem);
|
||||
}
|
||||
|
||||
if (!platformDict.ContainsKey(gameRomItem.PlatformId))
|
||||
{
|
||||
platformDict.Add(gameRomItem.PlatformId, gameRomItem.Platform);
|
||||
}
|
||||
}
|
||||
|
||||
// get rom media groups
|
||||
GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId);
|
||||
|
||||
// sort the platforms
|
||||
GameRoms.Platforms = platformDict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value).ToList<KeyValuePair<long, string>>();
|
||||
|
||||
return GameRoms;
|
||||
}
|
||||
@@ -122,7 +132,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM Games_Roms WHERE Id = @id";
|
||||
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", RomId);
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
@@ -131,6 +141,15 @@ namespace gaseous_server.Classes
|
||||
|
||||
private static GameRomItem BuildRom(DataRow romDR)
|
||||
{
|
||||
bool hasSaveStates = false;
|
||||
if (romDR.Table.Columns.Contains("SavedStateRomId"))
|
||||
{
|
||||
if (romDR["SavedStateRomId"] != DBNull.Value)
|
||||
{
|
||||
hasSaveStates = true;
|
||||
}
|
||||
}
|
||||
|
||||
GameRomItem romItem = new GameRomItem
|
||||
{
|
||||
Id = (long)romDR["id"],
|
||||
@@ -139,17 +158,18 @@ namespace gaseous_server.Classes
|
||||
GameId = (long)romDR["gameid"],
|
||||
Name = (string)romDR["name"],
|
||||
Size = (long)romDR["size"],
|
||||
CRC = ((string)romDR["crc"]).ToLower(),
|
||||
MD5 = ((string)romDR["md5"]).ToLower(),
|
||||
SHA1 = ((string)romDR["sha1"]).ToLower(),
|
||||
Crc = ((string)romDR["crc"]).ToLower(),
|
||||
Md5 = ((string)romDR["md5"]).ToLower(),
|
||||
Sha1 = ((string)romDR["sha1"]).ToLower(),
|
||||
DevelopmentStatus = (string)romDR["developmentstatus"],
|
||||
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
|
||||
RomType = (int)romDR["romtype"],
|
||||
RomType = (HasheousClient.Models.LookupResponseModel.RomItem.RomTypes)(int)romDR["romtype"],
|
||||
RomTypeMedia = (string)romDR["romtypemedia"],
|
||||
MediaLabel = (string)romDR["medialabel"],
|
||||
Path = (string)romDR["path"],
|
||||
Source = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)romDR["metadatasource"],
|
||||
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
|
||||
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
|
||||
HasSaveStates = hasSaveStates,
|
||||
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
|
||||
};
|
||||
|
||||
@@ -170,136 +190,21 @@ namespace gaseous_server.Classes
|
||||
|
||||
public class GameRomObject
|
||||
{
|
||||
public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>();
|
||||
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>();
|
||||
public int Count { get; set; }
|
||||
public List<KeyValuePair<long, string>> Platforms { get; set; }
|
||||
}
|
||||
|
||||
public class GameRomItem
|
||||
public class GameRomItem : HasheousClient.Models.LookupResponseModel.RomItem
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long PlatformId { get; set; }
|
||||
public string Platform { get; set; }
|
||||
//public Dictionary<string, object>? Emulator { get; set; }
|
||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
||||
public long GameId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public long Size { get; set; }
|
||||
public string? CRC { get; set; }
|
||||
public string? MD5 { get; set; }
|
||||
public string? SHA1 { get; set; }
|
||||
public string? DevelopmentStatus { get; set; }
|
||||
public string[]? Flags { get; set; }
|
||||
public List<KeyValuePair<string, object>>? Attributes { get; set;}
|
||||
public int RomType { get; set; }
|
||||
public string? RomTypeMedia { get; set; }
|
||||
public MediaType? MediaDetail {
|
||||
get
|
||||
{
|
||||
if (RomTypeMedia != null)
|
||||
{
|
||||
return new MediaType(Source, RomTypeMedia);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public string? MediaLabel { get; set; }
|
||||
public string? Path { get; set; }
|
||||
public RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; }
|
||||
public string? SignatureSourceGameTitle { get; set;}
|
||||
public bool HasSaveStates { get; set; } = false;
|
||||
public GameLibrary.LibraryItem Library { get; set; }
|
||||
}
|
||||
|
||||
public class MediaType
|
||||
{
|
||||
public MediaType(RomSignatureObject.Game.Rom.SignatureSourceType Source, string MediaTypeString)
|
||||
{
|
||||
switch (Source)
|
||||
{
|
||||
case RomSignatureObject.Game.Rom.SignatureSourceType.TOSEC:
|
||||
string[] typeString = MediaTypeString.Split(" ");
|
||||
|
||||
string inType = "";
|
||||
foreach (string typeStringVal in typeString)
|
||||
{
|
||||
if (inType == "")
|
||||
{
|
||||
switch (typeStringVal.ToLower())
|
||||
{
|
||||
case "disk":
|
||||
Media = RomSignatureObject.Game.Rom.RomTypes.Disk;
|
||||
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
case "disc":
|
||||
Media = RomSignatureObject.Game.Rom.RomTypes.Disc;
|
||||
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
case "file":
|
||||
Media = RomSignatureObject.Game.Rom.RomTypes.File;
|
||||
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
case "part":
|
||||
Media = RomSignatureObject.Game.Rom.RomTypes.Part;
|
||||
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
case "tape":
|
||||
Media = RomSignatureObject.Game.Rom.RomTypes.Tape;
|
||||
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
case "of":
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
case "side":
|
||||
inType = typeStringVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (inType.ToLower())
|
||||
{
|
||||
case "disk":
|
||||
case "disc":
|
||||
case "file":
|
||||
case "part":
|
||||
case "tape":
|
||||
Number = int.Parse(typeStringVal);
|
||||
break;
|
||||
case "of":
|
||||
Count = int.Parse(typeStringVal);
|
||||
break;
|
||||
case "side":
|
||||
Side = typeStringVal;
|
||||
break;
|
||||
}
|
||||
inType = "";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public RomSignatureObject.Game.Rom.RomTypes? Media { get; set; }
|
||||
|
||||
public int? Number { get; set; }
|
||||
|
||||
public int? Count { get; set; }
|
||||
|
||||
public string? Side { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
81
gaseous-server/Classes/SignatureManagement.cs
Normal file
81
gaseous-server/Classes/SignatureManagement.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System.Data;
|
||||
using gaseous_signature_parser.models.RomSignatureObject;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class SignatureManagement
|
||||
{
|
||||
public List<gaseous_server.Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
|
||||
{
|
||||
if (md5.Length > 0)
|
||||
{
|
||||
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
|
||||
} else
|
||||
{
|
||||
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
|
||||
}
|
||||
}
|
||||
|
||||
public List<gaseous_server.Models.Signatures_Games> GetByTosecName(string TosecName = "")
|
||||
{
|
||||
if (TosecName.Length > 0)
|
||||
{
|
||||
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName);
|
||||
} else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<gaseous_server.Models.Signatures_Games> _GetSignature(string sqlWhere, string searchString)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT view_Signatures_Games.*, Signatures_Roms.Id AS romid, Signatures_Roms.Name AS romname, Signatures_Roms.Size, Signatures_Roms.CRC, Signatures_Roms.MD5, Signatures_Roms.SHA1, Signatures_Roms.DevelopmentStatus, Signatures_Roms.Attributes, Signatures_Roms.RomType, Signatures_Roms.RomTypeMedia, Signatures_Roms.MediaLabel, Signatures_Roms.MetadataSource FROM Signatures_Roms INNER JOIN view_Signatures_Games ON Signatures_Roms.GameId = view_Signatures_Games.Id WHERE " + sqlWhere;
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("searchString", searchString);
|
||||
|
||||
DataTable sigDb = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<gaseous_server.Models.Signatures_Games> GamesList = new List<gaseous_server.Models.Signatures_Games>();
|
||||
|
||||
foreach (DataRow sigDbRow in sigDb.Rows)
|
||||
{
|
||||
gaseous_server.Models.Signatures_Games gameItem = new gaseous_server.Models.Signatures_Games
|
||||
{
|
||||
Game = new gaseous_server.Models.Signatures_Games.GameItem
|
||||
{
|
||||
Id = (Int32)sigDbRow["Id"],
|
||||
Name = (string)sigDbRow["Name"],
|
||||
Description = (string)sigDbRow["Description"],
|
||||
Year = (string)sigDbRow["Year"],
|
||||
Publisher = (string)sigDbRow["Publisher"],
|
||||
Demo = (gaseous_server.Models.Signatures_Games.GameItem.DemoTypes)(int)sigDbRow["Demo"],
|
||||
System = (string)sigDbRow["Platform"],
|
||||
SystemVariant = (string)sigDbRow["SystemVariant"],
|
||||
Video = (string)sigDbRow["Video"],
|
||||
Country = (string)sigDbRow["Country"],
|
||||
Language = (string)sigDbRow["Language"],
|
||||
Copyright = (string)sigDbRow["Copyright"]
|
||||
},
|
||||
Rom = new gaseous_server.Models.Signatures_Games.RomItem
|
||||
{
|
||||
Id = (Int32)sigDbRow["romid"],
|
||||
Name = (string)sigDbRow["romname"],
|
||||
Size = (Int64)sigDbRow["Size"],
|
||||
Crc = (string)sigDbRow["CRC"],
|
||||
Md5 = ((string)sigDbRow["MD5"]).ToLower(),
|
||||
Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
|
||||
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
|
||||
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
|
||||
RomType = (gaseous_server.Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"],
|
||||
RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
|
||||
MediaLabel = (string)sigDbRow["MediaLabel"],
|
||||
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)sigDbRow["MetadataSource"]
|
||||
}
|
||||
};
|
||||
GamesList.Add(gameItem);
|
||||
}
|
||||
return GamesList;
|
||||
}
|
||||
}
|
||||
}
|
99
gaseous-server/Classes/Statistics.cs
Normal file
99
gaseous-server/Classes/Statistics.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System.Data;
|
||||
using gaseous_server.Models;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Statistics
|
||||
{
|
||||
public StatisticsModel RecordSession(Guid SessionId, long GameId, string UserId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql;
|
||||
Dictionary<string, object> dbDict;
|
||||
|
||||
if (SessionId == Guid.Empty)
|
||||
{
|
||||
// new session required
|
||||
SessionId = Guid.NewGuid();
|
||||
|
||||
sql = "INSERT INTO UserTimeTracking (GameId, UserId, SessionId, SessionTime, SessionLength) VALUES (@gameid, @userid, @sessionid, @sessiontime, @sessionlength);";
|
||||
dbDict = new Dictionary<string, object>{
|
||||
{ "gameid", GameId },
|
||||
{ "userid", UserId },
|
||||
{ "sessionid", SessionId },
|
||||
{ "sessiontime", DateTime.UtcNow },
|
||||
{ "sessionlength", 1 }
|
||||
};
|
||||
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
return new StatisticsModel{
|
||||
GameId = GameId,
|
||||
SessionId = SessionId,
|
||||
SessionStart = (DateTime)dbDict["sessiontime"],
|
||||
SessionLength = (int)dbDict["sessionlength"]
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// update existing session
|
||||
sql = "UPDATE UserTimeTracking SET SessionLength = SessionLength + @sessionlength WHERE GameId = @gameid AND UserId = @userid AND SessionId = @sessionid;";
|
||||
dbDict = new Dictionary<string, object>{
|
||||
{ "gameid", GameId },
|
||||
{ "userid", UserId },
|
||||
{ "sessionid", SessionId },
|
||||
{ "sessionlength", 1 }
|
||||
};
|
||||
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
sql = "SELECT * FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid AND SessionId = @sessionid;";
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return new StatisticsModel{
|
||||
GameId = (long)data.Rows[0]["GameId"],
|
||||
SessionId = Guid.Parse(data.Rows[0]["SessionId"].ToString()),
|
||||
SessionStart = (DateTime)data.Rows[0]["SessionTime"],
|
||||
SessionLength = (int)data.Rows[0]["SessionLength"]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public StatisticsModel? GetSession(long GameId, string UserId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT SUM(SessionLength) AS TotalLength FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||
{ "gameid", GameId },
|
||||
{ "userid", UserId }
|
||||
};
|
||||
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data.Rows[0]["TotalLength"] == DBNull.Value)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int TotalTime = int.Parse(data.Rows[0]["TotalLength"].ToString());
|
||||
|
||||
sql = "SELECT * FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid ORDER BY SessionTime DESC LIMIT 1;";
|
||||
data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return new StatisticsModel{
|
||||
GameId = GameId,
|
||||
SessionLength = TotalTime,
|
||||
SessionStart = (DateTime)data.Rows[0]["SessionTime"]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Asp.Versioning;
|
||||
using IGDB;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -97,6 +99,7 @@ namespace gaseous_server.Controllers
|
||||
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
|
||||
profile.SecurityProfile = user.SecurityProfile;
|
||||
profile.UserPreferences = user.UserPreferences;
|
||||
profile.Avatar = user.Avatar;
|
||||
profile.Roles.Sort();
|
||||
|
||||
return Ok(profile);
|
||||
@@ -185,6 +188,7 @@ namespace gaseous_server.Controllers
|
||||
user.LockoutEnabled = rawUser.LockoutEnabled;
|
||||
user.LockoutEnd = rawUser.LockoutEnd;
|
||||
user.SecurityProfile = rawUser.SecurityProfile;
|
||||
user.Avatar = rawUser.Avatar;
|
||||
|
||||
// get roles
|
||||
ApplicationUser? aUser = await _userManager.FindByIdAsync(rawUser.Id);
|
||||
@@ -413,5 +417,115 @@ namespace gaseous_server.Controllers
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[RequestSizeLimit(long.MaxValue)]
|
||||
[Consumes("multipart/form-data")]
|
||||
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
|
||||
[Route("Avatar")]
|
||||
public async Task<IActionResult> UploadAvatar(IFormFile file)
|
||||
{
|
||||
ApplicationUser? user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
else
|
||||
{
|
||||
Guid avatarId = Guid.Empty;
|
||||
|
||||
if (file.Length > 0)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
file.CopyTo(ms);
|
||||
byte[] fileBytes = ms.ToArray();
|
||||
byte[] targetBytes;
|
||||
|
||||
using (var image = new ImageMagick.MagickImage(fileBytes))
|
||||
{
|
||||
ImageMagick.MagickGeometry size = new ImageMagick.MagickGeometry(256, 256);
|
||||
|
||||
// This will resize the image to a fixed size without maintaining the aspect ratio.
|
||||
// Normally an image will be resized to fit inside the specified size.
|
||||
size.IgnoreAspectRatio = true;
|
||||
|
||||
image.Resize(size);
|
||||
var newMs = new MemoryStream();
|
||||
image.Resize(size);
|
||||
image.Strip();
|
||||
image.Write(newMs, ImageMagick.MagickFormat.Jpg);
|
||||
|
||||
targetBytes = newMs.ToArray();
|
||||
}
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||
avatarId = userTable.SetAvatar(user, targetBytes);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(avatarId);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("Avatar/{id}.jpg")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetAvatar(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM UserAvatars WHERE Id = @id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||
{ "id", id }
|
||||
};
|
||||
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count > 0)
|
||||
{
|
||||
string filename = id.ToString() + ".jpg";
|
||||
byte[] filedata = (byte[])data.Rows[0]["Avatar"];
|
||||
string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
};
|
||||
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||
|
||||
return File(filedata, contentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
[Route("Avatar/{id}.jpg")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> DeleteAvatarAsync()
|
||||
{
|
||||
ApplicationUser? user = await _userManager.GetUserAsync(User);
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||
userTable.SetAvatar(user, new byte[0]);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
|
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using gaseous_server.Classes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
|
@@ -3,9 +3,12 @@ using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Authentication;
|
||||
using gaseous_server.Classes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -16,6 +19,17 @@ namespace gaseous_server.Controllers
|
||||
[Authorize]
|
||||
public class CollectionsController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public CollectionsController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all ROM collections
|
||||
/// </summary>
|
||||
@@ -24,9 +38,16 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public List<Classes.Collections.CollectionItem> GetCollections()
|
||||
public async Task<ActionResult> GetCollectionsAsync()
|
||||
{
|
||||
return Classes.Collections.GetCollections();
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
return Ok(Classes.Collections.GetCollections(user.Id));
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -41,22 +62,31 @@ namespace gaseous_server.Controllers
|
||||
[Route("{CollectionId}")]
|
||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetCollection(long CollectionId, bool Build = false)
|
||||
public async Task<ActionResult> GetCollection(long CollectionId, bool Build = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Build == true)
|
||||
{
|
||||
Classes.Collections.StartCollectionItemBuild(CollectionId);
|
||||
Classes.Collections.StartCollectionItemBuild(CollectionId, user.Id);
|
||||
}
|
||||
|
||||
return Ok(Classes.Collections.GetCollection(CollectionId));
|
||||
return Ok(Classes.Collections.GetCollection(CollectionId, user.Id));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents of the specified ROM collection
|
||||
@@ -69,18 +99,27 @@ namespace gaseous_server.Controllers
|
||||
[Route("{CollectionId}/Roms")]
|
||||
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetCollectionRoms(long CollectionId)
|
||||
public async Task<ActionResult> GetCollectionRoms(long CollectionId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId);
|
||||
return Ok(Classes.Collections.GetCollectionContent(collectionItem));
|
||||
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id);
|
||||
return Ok(Classes.Collections.GetCollectionContent(collectionItem, user.Id));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a preview of the provided collection item
|
||||
@@ -94,17 +133,26 @@ namespace gaseous_server.Controllers
|
||||
[Authorize(Roles = "Admin,Gamer")]
|
||||
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item)
|
||||
public async Task<ActionResult> GetCollectionRomsPreview(Classes.Collections.CollectionItem Item)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(Classes.Collections.GetCollectionContent(Item));
|
||||
return Ok(Classes.Collections.GetCollectionContent(Item, user.Id));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return NotFound(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets ROM collection in zip format
|
||||
@@ -117,11 +165,15 @@ namespace gaseous_server.Controllers
|
||||
[Route("{CollectionId}/Roms/Zip")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetCollectionRomsZip(long CollectionId)
|
||||
public async Task<ActionResult> GetCollectionRomsZip(long CollectionId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId);
|
||||
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id);
|
||||
|
||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, CollectionId + ".zip");
|
||||
|
||||
@@ -140,6 +192,11 @@ namespace gaseous_server.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ROM collection
|
||||
@@ -152,17 +209,26 @@ namespace gaseous_server.Controllers
|
||||
[Authorize(Roles = "Admin,Gamer")]
|
||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public ActionResult NewCollection(Classes.Collections.CollectionItem Item)
|
||||
public async Task<ActionResult> NewCollectionAsync(Classes.Collections.CollectionItem Item)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(Classes.Collections.NewCollection(Item));
|
||||
return Ok(Classes.Collections.NewCollection(Item, user.Id));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edits an existing collection
|
||||
@@ -177,17 +243,26 @@ namespace gaseous_server.Controllers
|
||||
[Authorize(Roles = "Admin,Gamer")]
|
||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item)
|
||||
public async Task<ActionResult> EditCollection(long CollectionId, Classes.Collections.CollectionItem Item)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(Classes.Collections.EditCollection(CollectionId, Item, true));
|
||||
return Ok(Classes.Collections.EditCollection(CollectionId, Item, user.Id, true));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edits an existing collection
|
||||
@@ -202,11 +277,15 @@ namespace gaseous_server.Controllers
|
||||
[Route("{CollectionId}/AlwaysInclude")]
|
||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult EditCollectionAlwaysInclude(long CollectionId, [FromQuery]bool Rebuild, [FromBody]Collections.CollectionItem.AlwaysIncludeItem Inclusion)
|
||||
public async Task<ActionResult> EditCollectionAlwaysInclude(long CollectionId, [FromQuery]bool Rebuild, [FromBody]Collections.CollectionItem.AlwaysIncludeItem Inclusion)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId);
|
||||
Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id);
|
||||
bool ItemFound = false;
|
||||
foreach (Collections.CollectionItem.AlwaysIncludeItem includeItem in collectionItem.AlwaysInclude)
|
||||
{
|
||||
@@ -220,13 +299,18 @@ namespace gaseous_server.Controllers
|
||||
collectionItem.AlwaysInclude.Add(Inclusion);
|
||||
}
|
||||
|
||||
return Ok(Classes.Collections.EditCollection(CollectionId, collectionItem, Rebuild));
|
||||
return Ok(Classes.Collections.EditCollection(CollectionId, collectionItem, user.Id, Rebuild));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified ROM collection
|
||||
@@ -239,11 +323,15 @@ namespace gaseous_server.Controllers
|
||||
[Route("{CollectionId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult DeleteCollection(long CollectionId)
|
||||
public async Task<ActionResult> DeleteCollection(long CollectionId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Classes.Collections.DeleteCollection(CollectionId);
|
||||
Classes.Collections.DeleteCollection(CollectionId, user.Id);
|
||||
return Ok();
|
||||
}
|
||||
catch
|
||||
@@ -251,5 +339,10 @@ namespace gaseous_server.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
|
@@ -6,14 +6,18 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Authentication;
|
||||
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.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -24,6 +28,18 @@ namespace gaseous_server.Controllers
|
||||
[ApiController]
|
||||
public class GamesController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public GamesController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager
|
||||
)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
|
||||
@@ -287,15 +303,15 @@ namespace gaseous_server.Controllers
|
||||
[ProducesResponseType(typeof(Game), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ResponseCache(CacheProfileName = "5Minute")]
|
||||
public ActionResult Game(long GameId, bool forceRefresh = false)
|
||||
public ActionResult Game(long GameId)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, forceRefresh, false, forceRefresh);
|
||||
Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject != null)
|
||||
if (game != null)
|
||||
{
|
||||
return Ok(gameObject);
|
||||
return Ok(game);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -374,87 +390,6 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/agerating/{RatingId}/image")]
|
||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GameAgeClassification(long GameId, long RatingId)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameAgeRating gameAgeRating = GetConsolidatedAgeRating(RatingId);
|
||||
|
||||
string fileExtension = "";
|
||||
string fileType = "";
|
||||
switch (gameAgeRating.RatingBoard)
|
||||
{
|
||||
case AgeRatingCategory.ESRB:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
case AgeRatingCategory.PEGI:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
case AgeRatingCategory.ACB:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
case AgeRatingCategory.CERO:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
case AgeRatingCategory.USK:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
case AgeRatingCategory.GRAC:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
case AgeRatingCategory.CLASS_IND:
|
||||
fileExtension = "svg";
|
||||
fileType = "image/svg+xml";
|
||||
break;
|
||||
}
|
||||
|
||||
string resourceName = "gaseous_server.Assets.Ratings." + gameAgeRating.RatingBoard.ToString() + "." + gameAgeRating.RatingTitle.ToString() + "." + fileExtension;
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
string[] resources = assembly.GetManifestResourceNames();
|
||||
if (resources.Contains(resourceName))
|
||||
{
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
byte[] filedata = new byte[stream.Length];
|
||||
stream.Read(filedata, 0, filedata.Length);
|
||||
|
||||
string filename = gameAgeRating.RatingBoard.ToString() + "-" + gameAgeRating.RatingTitle.ToString() + "." + fileExtension;
|
||||
string contentType = fileType;
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
};
|
||||
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||
|
||||
return File(filedata, contentType);
|
||||
}
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
@@ -473,7 +408,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
foreach (long ArtworkId in gameObject.Artworks.Ids)
|
||||
{
|
||||
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
artworks.Add(GameArtwork);
|
||||
}
|
||||
}
|
||||
@@ -501,7 +436,7 @@ namespace gaseous_server.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
if (artworkObject != null)
|
||||
{
|
||||
return Ok(artworkObject);
|
||||
@@ -525,10 +460,11 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/artwork/{ArtworkId}/image")]
|
||||
[Route("{GameId}/artwork/{ArtworkId}/image/{size}")]
|
||||
[Route("{GameId}/artwork/{ArtworkId}/image/{size}/{ImageName}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GameCoverImage(long GameId, long ArtworkId)
|
||||
public ActionResult GameCoverImage(long GameId, long ArtworkId, Communications.IGDBAPI_ImageSize size, string ImageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -536,15 +472,25 @@ namespace gaseous_server.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
|
||||
if (artworkObject != null) {
|
||||
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork", artworkObject.ImageId + ".png");
|
||||
//string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork", size.ToString(), artworkObject.ImageId + ".jpg");
|
||||
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork");
|
||||
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, artworkObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
|
||||
|
||||
if (System.IO.File.Exists(coverFilePath))
|
||||
{
|
||||
string filename = artworkObject.ImageId + ".png";
|
||||
string filename = artworkObject.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
string contentType = "image/png";
|
||||
string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
@@ -592,7 +538,7 @@ namespace gaseous_server.Controllers
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
if (gameObject != null)
|
||||
{
|
||||
IGDB.Models.Cover coverObject = Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
IGDB.Models.Cover coverObject = Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
if (coverObject != null)
|
||||
{
|
||||
return Ok(coverObject);
|
||||
@@ -616,21 +562,33 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/cover/image")]
|
||||
[Route("{GameId}/cover/image/{size}")]
|
||||
[Route("{GameId}/cover/image/{size}/{imagename}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GameCoverImage(long GameId)
|
||||
public ActionResult GameCoverImage(long GameId, Communications.IGDBAPI_ImageSize size, string imagename = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Cover.png");
|
||||
if (gameObject.Cover != null)
|
||||
{
|
||||
if (gameObject.Cover.Id != null)
|
||||
{
|
||||
IGDB.Models.Cover cover = Classes.Metadata.Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Covers");
|
||||
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, cover.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
|
||||
if (System.IO.File.Exists(coverFilePath)) {
|
||||
string filename = "Cover.png";
|
||||
string filename = cover.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
string contentType = "image/png";
|
||||
string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
@@ -643,6 +601,80 @@ namespace gaseous_server.Controllers
|
||||
|
||||
return File(filedata, contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/favourite")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GameGetFavouriteAsync(long GameId)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Favourites favourites = new Favourites();
|
||||
return Ok(favourites.GetFavourite(user.Id, GameId));
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Route("{GameId}/favourite")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GameSetFavouriteAsync(long GameId, bool favourite)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Favourites favourites = new Favourites();
|
||||
return Ok(favourites.SetFavourite(user.Id, GameId, favourite));
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
@@ -825,6 +857,24 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/platforms")]
|
||||
[ProducesResponseType(typeof(List<KeyValuePair<long, string>>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GamePlatforms(long GameId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(Games.GetAvailablePlatforms(GameId));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
@@ -870,13 +920,15 @@ namespace gaseous_server.Controllers
|
||||
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
//[ResponseCache(CacheProfileName = "5Minute")]
|
||||
public ActionResult GameRom(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1)
|
||||
public async Task<ActionResult> GameRomAsync(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1, string NameSearch = "")
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
return Ok(Classes.Roms.GetRoms(GameId, PlatformId, pageNumber, pageSize));
|
||||
return Ok(Classes.Roms.GetRoms(GameId, PlatformId, NameSearch, pageNumber, pageSize, user.Id));
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -1058,13 +1110,15 @@ namespace gaseous_server.Controllers
|
||||
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
//[ResponseCache(CacheProfileName = "5Minute")]
|
||||
public ActionResult GameRomGroup(long GameId, long RomGroupId)
|
||||
public async Task<ActionResult> GameRomGroupAsync(long GameId, long RomGroupId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
|
||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId, user.Id);
|
||||
if (rom.GameId == GameId)
|
||||
{
|
||||
return Ok(rom);
|
||||
@@ -1080,6 +1134,37 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize(Roles = "Admin,Gamer")]
|
||||
[Route("{GameId}/romgroup")]
|
||||
[ProducesResponseType(typeof(List<RomMediaGroup.GameRomMediaGroupItem>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GetGameRomGroupAsync(long GameId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
try
|
||||
{
|
||||
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Rom Group", "An error occurred", ex);
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
@@ -1116,13 +1201,15 @@ namespace gaseous_server.Controllers
|
||||
[Route("{GameId}/romgroup/{RomId}")]
|
||||
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GameRomGroupMembers(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
|
||||
public async Task<ActionResult> GameRomGroupMembersAsync(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
|
||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId, user.Id);
|
||||
if (rom.GameId == GameId)
|
||||
{
|
||||
rom = Classes.RomMediaGroup.EditMediaGroup(RomGroupId, RomIds);
|
||||
@@ -1232,7 +1319,8 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
Classes.Roms.GameRomItem romItem = Classes.Roms.GetRom(RomId);
|
||||
Common.hashObject hash = new Common.hashObject(romItem.Path);
|
||||
Models.Signatures_Games romSig = Classes.ImportGame.GetFileSignature(hash, new FileInfo(romItem.Path), romItem.Path);
|
||||
FileSignature fileSignature = new FileSignature();
|
||||
gaseous_server.Models.Signatures_Games romSig = fileSignature.GetFileSignature(romItem.Library, hash, new FileInfo(romItem.Path), romItem.Path);
|
||||
List<Game> searchResults = Classes.ImportGame.SearchForGame_GetAll(romSig.Game.Name, romSig.Flags.IGDBPlatformId);
|
||||
|
||||
return Ok(searchResults);
|
||||
@@ -1275,7 +1363,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
foreach (long ScreenshotId in gameObject.Screenshots.Ids)
|
||||
{
|
||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
screenshots.Add(GameScreenshot);
|
||||
}
|
||||
}
|
||||
@@ -1301,7 +1389,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
if (gameObject != null) {
|
||||
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
if (screenshotObject != null)
|
||||
{
|
||||
return Ok(screenshotObject);
|
||||
@@ -1325,24 +1413,31 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/screenshots/{ScreenshotId}/image")]
|
||||
[Route("{GameId}/screenshots/{ScreenshotId}/image/{size}")]
|
||||
[Route("{GameId}/screenshots/{ScreenshotId}/image/{size}/{ImageName}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GameScreenshotImage(long GameId, long ScreenshotId)
|
||||
public ActionResult GameScreenshotImage(long GameId, long ScreenshotId, Communications.IGDBAPI_ImageSize Size, string ImageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject));
|
||||
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Screenshots");
|
||||
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, screenshotObject.ImageId, Size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
|
||||
string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Screenshots", screenshotObject.ImageId + ".png");
|
||||
if (System.IO.File.Exists(coverFilePath))
|
||||
{
|
||||
string filename = screenshotObject.ImageId + ".png";
|
||||
string filename = screenshotObject.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
string contentType = "image/png";
|
||||
string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
|
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
|
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using gaseous_server.Classes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
|
@@ -13,6 +13,8 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Text;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -37,6 +39,32 @@ namespace gaseous_server.Controllers
|
||||
return Ok(PlatformMapping.PlatformMap);
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("PlatformMap.json")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult DownloadPlatformMap()
|
||||
{
|
||||
string srcJson = Newtonsoft.Json.JsonConvert.SerializeObject(PlatformMapping.PlatformMap, Newtonsoft.Json.Formatting.Indented);
|
||||
|
||||
string filename = "PlatformMap.json";
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(srcJson);
|
||||
string contentType = "application/json";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
DispositionType = "attachment"
|
||||
};
|
||||
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
|
||||
return File(bytes, contentType);
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
|
@@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
|
@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -77,7 +78,8 @@ namespace gaseous_server.Controllers
|
||||
// Process uploaded files
|
||||
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
|
||||
{
|
||||
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||
Classes.ImportGame uploadImport = new ImportGame();
|
||||
uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||
}
|
||||
|
||||
if (Directory.Exists(workPath))
|
||||
|
@@ -1,15 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using gaseous_server.Models;
|
||||
using IGDB;
|
||||
using IGDB.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NuGet.Common;
|
||||
using static gaseous_server.Classes.Metadata.Games;
|
||||
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -37,36 +40,88 @@ namespace gaseous_server.Controllers
|
||||
string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
|
||||
searchBody += "where name ~ *\"" + SearchString + "\"*;";
|
||||
|
||||
// get Platform metadata
|
||||
List<Platform>? searchCache = Communications.GetSearchCache<List<Platform>>(searchFields, searchBody);
|
||||
|
||||
if (searchCache == null)
|
||||
{
|
||||
// cache miss
|
||||
// get Platform metadata from data source
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
|
||||
|
||||
Communications.SetSearchCache<List<Platform>>(searchFields, searchBody, results.ToList());
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return searchCache;
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("Game")]
|
||||
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(List<GaseousGame>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
|
||||
{
|
||||
List<Game> RetVal = await _SearchForGame(PlatformId, SearchString);
|
||||
List<GaseousGame> RetVal = await _SearchForGame(PlatformId, SearchString);
|
||||
return Ok(RetVal);
|
||||
}
|
||||
|
||||
private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString)
|
||||
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
|
||||
{
|
||||
string searchBody = "";
|
||||
string searchFields = "fields cover.*,first_release_date,name,platforms,slug; ";
|
||||
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
|
||||
searchBody += "search \"" + SearchString + "\";";
|
||||
searchBody += "where platforms = (" + PlatformId + ");";
|
||||
|
||||
// get Platform metadata
|
||||
List<GaseousGame>? searchCache = Communications.GetSearchCache<List<GaseousGame>>(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);
|
||||
|
||||
return results.ToList();
|
||||
List<GaseousGame> games = new List<GaseousGame>();
|
||||
foreach (Game game in results.ToList())
|
||||
{
|
||||
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
|
||||
switch(cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
Storage.NewCacheValue(game, false);
|
||||
break;
|
||||
|
||||
case Storage.CacheStatus.Expired:
|
||||
Storage.NewCacheValue(game, true);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
games.Add(new GaseousGame(game));
|
||||
}
|
||||
|
||||
Communications.SetSearchCache<List<GaseousGame>>(searchFields, searchBody, games);
|
||||
|
||||
return games;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get full version of results from database
|
||||
// this is a hacky workaround due to the readonly nature of IGDB.Model.Game IdentityOrValue fields
|
||||
List<GaseousGame> gamesToReturn = new List<GaseousGame>();
|
||||
foreach (GaseousGame game in searchCache)
|
||||
{
|
||||
Game tempGame = Games.GetGame((long)game.Id, false, false, false);
|
||||
gamesToReturn.Add(new GaseousGame(tempGame));
|
||||
}
|
||||
|
||||
return gamesToReturn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ using gaseous_server.Classes;
|
||||
using gaseous_signature_parser.models.RomSignatureObject;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
@@ -37,82 +38,27 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
|
||||
public List<gaseous_server.Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
|
||||
{
|
||||
if (md5.Length > 0)
|
||||
{
|
||||
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
|
||||
} else
|
||||
{
|
||||
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
|
||||
}
|
||||
SignatureManagement signatureManagement = new SignatureManagement();
|
||||
return signatureManagement.GetSignature(md5, sha1);
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public List<Models.Signatures_Games> GetByTosecName(string TosecName = "")
|
||||
public List<gaseous_server.Models.Signatures_Games> GetByTosecName(string TosecName = "")
|
||||
{
|
||||
if (TosecName.Length > 0)
|
||||
{
|
||||
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName);
|
||||
SignatureManagement signatureManagement = new SignatureManagement();
|
||||
return signatureManagement.GetByTosecName(TosecName);
|
||||
} else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Models.Signatures_Games> _GetSignature(string sqlWhere, string searchString)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT view_Signatures_Games.*, Signatures_Roms.Id AS romid, Signatures_Roms.Name AS romname, Signatures_Roms.Size, Signatures_Roms.CRC, Signatures_Roms.MD5, Signatures_Roms.SHA1, Signatures_Roms.DevelopmentStatus, Signatures_Roms.Attributes, Signatures_Roms.RomType, Signatures_Roms.RomTypeMedia, Signatures_Roms.MediaLabel, Signatures_Roms.MetadataSource FROM Signatures_Roms INNER JOIN view_Signatures_Games ON Signatures_Roms.GameId = view_Signatures_Games.Id WHERE " + sqlWhere;
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("searchString", searchString);
|
||||
|
||||
DataTable sigDb = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<Models.Signatures_Games> GamesList = new List<Models.Signatures_Games>();
|
||||
|
||||
foreach (DataRow sigDbRow in sigDb.Rows)
|
||||
{
|
||||
Models.Signatures_Games gameItem = new Models.Signatures_Games
|
||||
{
|
||||
Game = new Models.Signatures_Games.GameItem
|
||||
{
|
||||
Id = (Int32)sigDbRow["Id"],
|
||||
Name = (string)sigDbRow["Name"],
|
||||
Description = (string)sigDbRow["Description"],
|
||||
Year = (string)sigDbRow["Year"],
|
||||
Publisher = (string)sigDbRow["Publisher"],
|
||||
Demo = (Models.Signatures_Games.GameItem.DemoTypes)(int)sigDbRow["Demo"],
|
||||
System = (string)sigDbRow["Platform"],
|
||||
SystemVariant = (string)sigDbRow["SystemVariant"],
|
||||
Video = (string)sigDbRow["Video"],
|
||||
Country = (string)sigDbRow["Country"],
|
||||
Language = (string)sigDbRow["Language"],
|
||||
Copyright = (string)sigDbRow["Copyright"]
|
||||
},
|
||||
Rom = new Models.Signatures_Games.RomItem
|
||||
{
|
||||
Id = (Int32)sigDbRow["romid"],
|
||||
Name = (string)sigDbRow["romname"],
|
||||
Size = (Int64)sigDbRow["Size"],
|
||||
Crc = (string)sigDbRow["CRC"],
|
||||
Md5 = ((string)sigDbRow["MD5"]).ToLower(),
|
||||
Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
|
||||
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
|
||||
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
|
||||
RomType = (RomSignatureObject.Game.Rom.RomTypes)(int)sigDbRow["RomType"],
|
||||
RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
|
||||
MediaLabel = (string)sigDbRow["MediaLabel"],
|
||||
SignatureSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)sigDbRow["MetadataSource"]
|
||||
}
|
||||
};
|
||||
GamesList.Add(gameItem);
|
||||
}
|
||||
return GamesList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,8 @@ using gaseous_server.Classes.Metadata;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
using RestEase;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -96,7 +98,7 @@ namespace gaseous_server.Controllers
|
||||
|
||||
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
|
||||
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
|
||||
"var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine +
|
||||
"var FirstRunStatus = " + Config.ReadSetting<string>("FirstRunStatus", "0") + ";" + Environment.NewLine +
|
||||
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions{
|
||||
WriteIndented = true
|
||||
}) + ";" + Environment.NewLine +
|
||||
@@ -105,7 +107,8 @@ namespace gaseous_server.Controllers
|
||||
}) + ";" + Environment.NewLine +
|
||||
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
|
||||
WriteIndented = true
|
||||
}) + ";";
|
||||
}) + ";" + Environment.NewLine +
|
||||
"var emulatorDebugMode = " + Config.ReadSetting<string>("emulatorDebugMode", false.ToString()).ToLower() + ";";
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(ver);
|
||||
return File(bytes, "text/javascript");
|
||||
}
|
||||
@@ -113,19 +116,23 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("Settings/BackgroundTasks/Intervals")]
|
||||
[Route("Settings/BackgroundTasks/Configuration")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult GetBackgroundTasks()
|
||||
{
|
||||
Dictionary<string, BackgroundTaskItem> Intervals = new Dictionary<string, BackgroundTaskItem>();
|
||||
Intervals.Add(ProcessQueue.QueueItemType.SignatureIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.SignatureIngestor));
|
||||
Intervals.Add(ProcessQueue.QueueItemType.TitleIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.TitleIngestor));
|
||||
Intervals.Add(ProcessQueue.QueueItemType.MetadataRefresh.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.MetadataRefresh));
|
||||
Intervals.Add(ProcessQueue.QueueItemType.OrganiseLibrary.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.OrganiseLibrary));
|
||||
Intervals.Add(ProcessQueue.QueueItemType.LibraryScan.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.LibraryScan));
|
||||
Intervals.Add(ProcessQueue.QueueItemType.Rematcher.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Rematcher));
|
||||
Intervals.Add(ProcessQueue.QueueItemType.Maintainer.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Maintainer));
|
||||
foreach (ProcessQueue.QueueItemType itemType in Enum.GetValues(typeof(ProcessQueue.QueueItemType)))
|
||||
{
|
||||
BackgroundTaskItem taskItem = new BackgroundTaskItem(itemType);
|
||||
if (taskItem.UserManageable == true)
|
||||
{
|
||||
if (!Intervals.ContainsKey(itemType.ToString()))
|
||||
{
|
||||
Intervals.Add(itemType.ToString(), taskItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Intervals);
|
||||
}
|
||||
@@ -133,45 +140,102 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Route("Settings/BackgroundTasks/Intervals")]
|
||||
[Route("Settings/BackgroundTasks/Configuration")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult SetBackgroundTasks(Dictionary<string, int> Intervals)
|
||||
public ActionResult SetBackgroundTasks([FromBody] List<BackgroundTaskSettingsItem> model)
|
||||
{
|
||||
foreach (KeyValuePair<string, int> Interval in Intervals)
|
||||
foreach (BackgroundTaskSettingsItem TaskConfiguration in model)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(ProcessQueue.QueueItemType), Interval.Key))
|
||||
if (Enum.IsDefined(typeof(ProcessQueue.QueueItemType), TaskConfiguration.Task))
|
||||
{
|
||||
try
|
||||
{
|
||||
BackgroundTaskItem taskItem = new BackgroundTaskItem(
|
||||
(ProcessQueue.QueueItemType)Enum.Parse(typeof(ProcessQueue.QueueItemType), Interval.Key)
|
||||
(ProcessQueue.QueueItemType)Enum.Parse(typeof(ProcessQueue.QueueItemType), TaskConfiguration.Task)
|
||||
);
|
||||
|
||||
if (Interval.Value >= taskItem.MinimumAllowedValue)
|
||||
if (taskItem.UserManageable == true)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + Interval.Key + " with new interval " + Interval.Value);
|
||||
// update task enabled
|
||||
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with enabled value " + TaskConfiguration.Enabled.ToString());
|
||||
|
||||
Config.SetSetting("Interval_" + Interval.Key, Interval.Value.ToString());
|
||||
Config.SetSetting<string>("Enabled_" + TaskConfiguration.Task, TaskConfiguration.Enabled.ToString());
|
||||
|
||||
// update existing process
|
||||
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
{
|
||||
if (item.ItemType.ToString().ToLower() == Interval.Key.ToLower())
|
||||
if (item.ItemType.ToString().ToLower() == TaskConfiguration.Task.ToLower())
|
||||
{
|
||||
item.Interval = Interval.Value;
|
||||
item.Enabled(Boolean.Parse(TaskConfiguration.Enabled.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
// update task interval
|
||||
if (TaskConfiguration.Interval >= taskItem.MinimumAllowedInterval)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with new interval " + TaskConfiguration.Interval);
|
||||
|
||||
Config.SetSetting<string>("Interval_" + TaskConfiguration.Task, TaskConfiguration.Interval.ToString());
|
||||
|
||||
// update existing process
|
||||
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
{
|
||||
if (item.ItemType.ToString().ToLower() == TaskConfiguration.Task.ToLower())
|
||||
{
|
||||
item.Interval = TaskConfiguration.Interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Interval " + Interval.Value + " for task " + Interval.Key + " is below the minimum allowed value of " + taskItem.MinimumAllowedValue + ". Skipping.");
|
||||
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Interval " + TaskConfiguration.Interval.ToString() + " for task " + TaskConfiguration.Task + " is below the minimum allowed value of " + taskItem.MinimumAllowedInterval + ". Skipping.");
|
||||
}
|
||||
|
||||
// update task weekdays
|
||||
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with new weekdays " + String.Join(", ", TaskConfiguration.AllowedDays));
|
||||
|
||||
Config.SetSetting<string>("AllowedDays_" + TaskConfiguration.Task, Newtonsoft.Json.JsonConvert.SerializeObject(TaskConfiguration.AllowedDays));
|
||||
|
||||
// update existing process
|
||||
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
{
|
||||
if (item.ItemType.ToString().ToLower() == TaskConfiguration.Task.ToLower())
|
||||
{
|
||||
item.AllowedDays = TaskConfiguration.AllowedDays;
|
||||
}
|
||||
}
|
||||
|
||||
// update task hours
|
||||
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with new hours " + TaskConfiguration.AllowedStartHours + ":" + TaskConfiguration.AllowedStartMinutes.ToString("00") + " to " + TaskConfiguration.AllowedEndHours + ":" + TaskConfiguration.AllowedEndMinutes.ToString("00"));
|
||||
|
||||
Config.SetSetting<string>("AllowedStartHours_" + TaskConfiguration.Task, TaskConfiguration.AllowedStartHours.ToString());
|
||||
Config.SetSetting<string>("AllowedStartMinutes_" + TaskConfiguration.Task, TaskConfiguration.AllowedStartMinutes.ToString());
|
||||
Config.SetSetting<string>("AllowedEndHours_" + TaskConfiguration.Task, TaskConfiguration.AllowedEndHours.ToString());
|
||||
Config.SetSetting<string>("AllowedEndMinutes_" + TaskConfiguration.Task, TaskConfiguration.AllowedEndMinutes.ToString());
|
||||
|
||||
// update existing process
|
||||
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
{
|
||||
if (item.ItemType.ToString().ToLower() == TaskConfiguration.Task.ToLower())
|
||||
{
|
||||
item.AllowedStartHours = TaskConfiguration.AllowedStartHours;
|
||||
item.AllowedStartMinutes = TaskConfiguration.AllowedStartMinutes;
|
||||
item.AllowedEndHours = TaskConfiguration.AllowedEndHours;
|
||||
item.AllowedEndMinutes = TaskConfiguration.AllowedEndMinutes;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Unable to update non-user manageable task " + TaskConfiguration.Task + ". Skipping.");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// task name not defined
|
||||
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Task " + Interval.Key + " is not user definable. Skipping.");
|
||||
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Task " + TaskConfiguration.Task + " is not user definable. Skipping.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +253,8 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
SystemSettingsModel systemSettingsModel = new SystemSettingsModel{
|
||||
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
|
||||
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention
|
||||
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention,
|
||||
EmulatorDebugMode = Boolean.Parse(Config.ReadSetting<string>("emulatorDebugMode", false.ToString()))
|
||||
};
|
||||
|
||||
return Ok(systemSettingsModel);
|
||||
@@ -207,6 +272,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
|
||||
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
|
||||
Config.SetSetting<string>("emulatorDebugMode", model.EmulatorDebugMode.ToString());
|
||||
Config.UpdateConfig();
|
||||
}
|
||||
|
||||
@@ -256,66 +322,393 @@ namespace gaseous_server.Controllers
|
||||
|
||||
public class BackgroundTaskItem
|
||||
{
|
||||
public BackgroundTaskItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BackgroundTaskItem(ProcessQueue.QueueItemType TaskName)
|
||||
{
|
||||
this.Task = TaskName.ToString();
|
||||
this.TaskEnum = TaskName;
|
||||
|
||||
switch (TaskName)
|
||||
{
|
||||
case ProcessQueue.QueueItemType.SignatureIngestor:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 60;
|
||||
this.MinimumAllowedValue = 20;
|
||||
this.MinimumAllowedInterval = 20;
|
||||
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;
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.TitleIngestor:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 1;
|
||||
this.MinimumAllowedValue = 1;
|
||||
this.MinimumAllowedInterval = 1;
|
||||
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
|
||||
};
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.MetadataRefresh:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 360;
|
||||
this.MinimumAllowedValue = 360;
|
||||
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;
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.OrganiseLibrary:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 1440;
|
||||
this.MinimumAllowedValue = 120;
|
||||
this.MinimumAllowedInterval = 120;
|
||||
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.LibraryScan,
|
||||
ProcessQueue.QueueItemType.LibraryScanWorker,
|
||||
ProcessQueue.QueueItemType.TitleIngestor,
|
||||
ProcessQueue.QueueItemType.Rematcher
|
||||
};
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.LibraryScan:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 1440;
|
||||
this.MinimumAllowedValue = 120;
|
||||
this.MinimumAllowedInterval = 120;
|
||||
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.Rematcher
|
||||
};
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.Rematcher:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 1440;
|
||||
this.MinimumAllowedValue = 360;
|
||||
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
|
||||
};
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.Maintainer:
|
||||
case ProcessQueue.QueueItemType.DailyMaintainer:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 1440;
|
||||
this.MinimumAllowedInterval = 1440;
|
||||
this.DefaultAllowedDays = new List<DayOfWeek>{
|
||||
DayOfWeek.Sunday,
|
||||
DayOfWeek.Monday,
|
||||
DayOfWeek.Tuesday,
|
||||
DayOfWeek.Wednesday,
|
||||
DayOfWeek.Thursday,
|
||||
DayOfWeek.Friday,
|
||||
DayOfWeek.Saturday
|
||||
};
|
||||
this.DefaultAllowedStartHours = 1;
|
||||
this.DefaultAllowedStartMinutes = 0;
|
||||
this.DefaultAllowedEndHours = 5;
|
||||
this.DefaultAllowedEndMinutes = 59;
|
||||
this._Blocks = new List<ProcessQueue.QueueItemType>{
|
||||
ProcessQueue.QueueItemType.All
|
||||
};
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.WeeklyMaintainer:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 10080;
|
||||
this.MinimumAllowedValue = 10080;
|
||||
this.MinimumAllowedInterval = 10080;
|
||||
this.DefaultAllowedDays = new List<DayOfWeek>{
|
||||
DayOfWeek.Monday
|
||||
};
|
||||
this.DefaultAllowedStartHours = 1;
|
||||
this.DefaultAllowedStartMinutes = 0;
|
||||
this.DefaultAllowedEndHours = 5;
|
||||
this.DefaultAllowedEndMinutes = 59;
|
||||
this._Blocks = new List<ProcessQueue.QueueItemType>{
|
||||
ProcessQueue.QueueItemType.All
|
||||
};
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade:
|
||||
this._UserManageable = false;
|
||||
this.DefaultInterval = 1;
|
||||
this.MinimumAllowedInterval = 1;
|
||||
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.Add(ProcessQueue.QueueItemType.All);
|
||||
break;
|
||||
|
||||
case ProcessQueue.QueueItemType.TempCleanup:
|
||||
this._UserManageable = true;
|
||||
this.DefaultInterval = 1;
|
||||
this.MinimumAllowedInterval = 1;
|
||||
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;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("Invalid task");
|
||||
this._UserManageable = false;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public string Task { get; set; }
|
||||
public ProcessQueue.QueueItemType TaskEnum { get; set; }
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_UserManageable == true)
|
||||
{
|
||||
return bool.Parse(Config.ReadSetting<string>("Enabled_" + Task, true.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_UserManageable == true)
|
||||
{
|
||||
Config.SetSetting<string>("Enabled_" + Task, value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _UserManageable;
|
||||
public bool UserManageable => _UserManageable;
|
||||
public int Interval {
|
||||
get
|
||||
{
|
||||
return int.Parse(Config.ReadSetting("Interval_" + Task, DefaultInterval.ToString()));
|
||||
return int.Parse(Config.ReadSetting<string>("Interval_" + Task, DefaultInterval.ToString()));
|
||||
}
|
||||
}
|
||||
public int DefaultInterval { get; set; }
|
||||
public int MinimumAllowedValue { get; set; }
|
||||
public int MinimumAllowedInterval { get; set; }
|
||||
public List<DayOfWeek> AllowedDays
|
||||
{
|
||||
get
|
||||
{
|
||||
string jsonDefaultAllowedDays = Newtonsoft.Json.JsonConvert.SerializeObject(DefaultAllowedDays);
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<DayOfWeek>>(Config.ReadSetting<string>("AllowedDays_" + Task, jsonDefaultAllowedDays));
|
||||
}
|
||||
}
|
||||
public int AllowedStartHours
|
||||
{
|
||||
get
|
||||
{
|
||||
return int.Parse(Config.ReadSetting<string>("AllowedStartHours_" + Task, DefaultAllowedStartHours.ToString()));
|
||||
}
|
||||
}
|
||||
public int AllowedStartMinutes
|
||||
{
|
||||
get
|
||||
{
|
||||
return int.Parse(Config.ReadSetting<string>("AllowedStartMinutes_" + Task, DefaultAllowedStartMinutes.ToString()));
|
||||
}
|
||||
}
|
||||
public int AllowedEndHours
|
||||
{
|
||||
get
|
||||
{
|
||||
return int.Parse(Config.ReadSetting<string>("AllowedEndHours_" + Task, DefaultAllowedEndHours.ToString()));
|
||||
}
|
||||
}
|
||||
public int AllowedEndMinutes
|
||||
{
|
||||
get
|
||||
{
|
||||
return int.Parse(Config.ReadSetting<string>("AllowedEndMinutes_" + Task, DefaultAllowedEndMinutes.ToString()));
|
||||
}
|
||||
}
|
||||
public List<DayOfWeek> DefaultAllowedDays { get; set; }
|
||||
public int DefaultAllowedStartHours { get; set; }
|
||||
public int DefaultAllowedStartMinutes { get; set; }
|
||||
public int DefaultAllowedEndHours { get; set; }
|
||||
public int DefaultAllowedEndMinutes { get; set; }
|
||||
private List<ProcessQueue.QueueItemType> _Blocks = new List<ProcessQueue.QueueItemType>();
|
||||
public List<ProcessQueue.QueueItemType> Blocks
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Blocks.Contains(ProcessQueue.QueueItemType.All))
|
||||
{
|
||||
List<ProcessQueue.QueueItemType> blockList = new List<ProcessQueue.QueueItemType>();
|
||||
List<ProcessQueue.QueueItemType> skipBlockItems = new List<ProcessQueue.QueueItemType>{
|
||||
ProcessQueue.QueueItemType.All,
|
||||
ProcessQueue.QueueItemType.NotConfigured,
|
||||
this.TaskEnum
|
||||
};
|
||||
foreach (ProcessQueue.QueueItemType blockType in Enum.GetValues(typeof(ProcessQueue.QueueItemType)))
|
||||
{
|
||||
if (!skipBlockItems.Contains(blockType))
|
||||
{
|
||||
blockList.Add(blockType);
|
||||
}
|
||||
}
|
||||
return blockList;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _Blocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
public List<ProcessQueue.QueueItemType> BlockedBy
|
||||
{
|
||||
get
|
||||
{
|
||||
List<ProcessQueue.QueueItemType> blockedBy = new List<ProcessQueue.QueueItemType>();
|
||||
|
||||
List<BackgroundTaskItem> backgroundTaskItems = new List<BackgroundTaskItem>();
|
||||
foreach (ProcessQueue.QueueItemType blockType in Enum.GetValues(typeof(ProcessQueue.QueueItemType)))
|
||||
{
|
||||
if (blockType != this.TaskEnum)
|
||||
{
|
||||
BackgroundTaskItem taskItem = new BackgroundTaskItem(blockType);
|
||||
if (taskItem.Blocks.Contains(this.TaskEnum))
|
||||
{
|
||||
if (!blockedBy.Contains(blockType))
|
||||
{
|
||||
blockedBy.Add(blockType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blockedBy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BackgroundTaskSettingsItem
|
||||
{
|
||||
public string Task { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public int Interval { get; set; }
|
||||
public List<DayOfWeek> AllowedDays { get; set; }
|
||||
public int AllowedStartHours { get; set; }
|
||||
public int AllowedStartMinutes { get; set; }
|
||||
public int AllowedEndHours { get; set; }
|
||||
public int AllowedEndMinutes { get; set; }
|
||||
}
|
||||
|
||||
public class SystemSettingsModel
|
||||
{
|
||||
public bool AlwaysLogToDisk { get; set; }
|
||||
public int MinimumLogRetentionPeriod { get; set; }
|
||||
public bool EmulatorDebugMode { get; set; }
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ using gaseous_server.Classes.Metadata;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -40,7 +41,7 @@ namespace gaseous_server.Controllers
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> CreateAdminAccount(Authentication.RegisterViewModel model)
|
||||
{
|
||||
if (Config.ReadSetting("FirstRunStatus", "0") == "0")
|
||||
if (Config.ReadSetting<string>("FirstRunStatus", "0") == "0")
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
@@ -68,7 +69,16 @@ namespace gaseous_server.Controllers
|
||||
await _signInManager.SignInAsync(user, isPersistent: true);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "First Run", "Setting first run state to 1");
|
||||
Config.SetSetting("FirstRunStatus", "1");
|
||||
Config.SetSetting<string>("FirstRunStatus", "1");
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "First Run", "Migrating existing collections to newly created user (for upgrades from v1.6.1 and earlier)");
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "UPDATE RomCollections SET OwnedBy=@userid WHERE OwnedBy IS NULL;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "userid", user.Id }
|
||||
};
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||
using Asp.Versioning;
|
||||
using Humanizer;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
@@ -85,7 +87,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
model.GameAgeRating.IncludeUnrated = false;
|
||||
}
|
||||
|
||||
return Ok(GetGames(model, pageNumber, pageSize));
|
||||
return Ok(GetGames(model, user.Id, pageNumber, pageSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -141,9 +143,13 @@ namespace gaseous_server.Controllers.v1_1
|
||||
public List<string> GameMode { get; set; }
|
||||
public List<string> PlayerPerspective { get; set; }
|
||||
public List<string> Theme { get; set; }
|
||||
public int MinimumReleaseYear { get; set; } = -1;
|
||||
public int MaximumReleaseYear { get; set; } = -1;
|
||||
public GameRatingItem GameRating { get; set; } = new GameRatingItem();
|
||||
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
|
||||
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
|
||||
public bool HasSavedGame { get; set; }
|
||||
public bool IsFavourite { get; set; }
|
||||
|
||||
|
||||
public class GameRatingItem
|
||||
@@ -181,11 +187,12 @@ namespace gaseous_server.Controllers.v1_1
|
||||
}
|
||||
}
|
||||
|
||||
public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
|
||||
public static GameReturnPackage GetGames(GameSearchModel model, string userid, int pageNumber = 0, int pageSize = 0)
|
||||
{
|
||||
string whereClause = "";
|
||||
string havingClause = "";
|
||||
Dictionary<string, object> whereParams = new Dictionary<string, object>();
|
||||
whereParams.Add("userid", userid);
|
||||
|
||||
List<string> whereClauses = new List<string>();
|
||||
List<string> havingClauses = new List<string>();
|
||||
@@ -202,6 +209,32 @@ namespace gaseous_server.Controllers.v1_1
|
||||
whereParams.Add("@Name", "(*" + model.Name + "*) (" + model.Name + ") ");
|
||||
}
|
||||
|
||||
if (model.HasSavedGame == true)
|
||||
{
|
||||
string hasSavesTemp = "(RomSavedStates.RomSaveCount IS NOT NULL OR RomGroupSavedStates.MediaGroupSaveCount IS NOT NULL)";
|
||||
whereClauses.Add(hasSavesTemp);
|
||||
}
|
||||
|
||||
if (model.IsFavourite == true)
|
||||
{
|
||||
string isFavTemp = "Favourite = 1";
|
||||
havingClauses.Add(isFavTemp);
|
||||
}
|
||||
|
||||
if (model.MinimumReleaseYear != -1)
|
||||
{
|
||||
string releaseTempMinVal = "FirstReleaseDate >= @minreleasedate";
|
||||
whereParams.Add("minreleasedate", new DateTime(model.MinimumReleaseYear, 1, 1));
|
||||
havingClauses.Add(releaseTempMinVal);
|
||||
}
|
||||
|
||||
if (model.MaximumReleaseYear != -1)
|
||||
{
|
||||
string releaseTempMaxVal = "FirstReleaseDate <= @maxreleasedate";
|
||||
whereParams.Add("maxreleasedate", new DateTime(model.MaximumReleaseYear, 12, 31, 23, 59, 59));
|
||||
havingClauses.Add(releaseTempMaxVal);
|
||||
}
|
||||
|
||||
if (model.GameRating != null)
|
||||
{
|
||||
List<string> ratingClauses = new List<string>();
|
||||
@@ -444,11 +477,78 @@ namespace gaseous_server.Controllers.v1_1
|
||||
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
//string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
|
||||
string sql = "SELECT DISTINCT Game.Id, Game.`Name`, Game.NameThe, Game.PlatformId, Game.TotalRating, Game.TotalRatingCount, Game.Cover, Game.Artworks, Game.FirstReleaseDate, Game.Category, Game.ParentGame, Game.AgeRatings, Game.AgeGroupId, Game.RomCount FROM (SELECT DISTINCT Game.*, CASE WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The') ELSE Game.`Name` END AS NameThe, Games_Roms.PlatformId, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + " LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + " GROUP BY Game.Id HAVING RomCount > 0) Game LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
|
||||
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
|
||||
string sql = @"
|
||||
SELECT DISTINCT
|
||||
Game.Id,
|
||||
Game.`Name`,
|
||||
Game.NameThe,
|
||||
Game.Slug,
|
||||
Game.PlatformId,
|
||||
Game.TotalRating,
|
||||
Game.TotalRatingCount,
|
||||
Game.Cover,
|
||||
Game.Artworks,
|
||||
Game.FirstReleaseDate,
|
||||
Game.Category,
|
||||
Game.ParentGame,
|
||||
Game.AgeRatings,
|
||||
Game.AgeGroupId,
|
||||
Game.RomCount,
|
||||
RomSavedStates.RomSaveCount,
|
||||
RomGroupSavedStates.MediaGroupSaveCount,
|
||||
CASE
|
||||
WHEN Favourites.UserId IS NULL THEN 0
|
||||
ELSE 1
|
||||
END AS Favourite
|
||||
FROM
|
||||
(SELECT DISTINCT
|
||||
Game.*,
|
||||
CASE
|
||||
WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The')
|
||||
ELSE Game.`Name`
|
||||
END AS NameThe,
|
||||
Games_Roms.PlatformId,
|
||||
AgeGroup.AgeGroupId,
|
||||
COUNT(Games_Roms.Id) AS RomCount
|
||||
FROM
|
||||
Game
|
||||
LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId
|
||||
LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + @"
|
||||
LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + @"
|
||||
GROUP BY Game.Id
|
||||
HAVING RomCount > 0) Game
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
|
||||
FROM
|
||||
GameState
|
||||
JOIN Games_Roms ON GameState.RomId = Games_Roms.Id
|
||||
WHERE
|
||||
GameState.IsMediaGroup = 0
|
||||
AND GameState.UserId = @userid
|
||||
GROUP BY Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
RomMediaGroup.GameId,
|
||||
COUNT(RomMediaGroup.GameId) AS MediaGroupSaveCount
|
||||
FROM
|
||||
RomMediaGroup
|
||||
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
|
||||
LEFT JOIN
|
||||
Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId
|
||||
LEFT JOIN
|
||||
Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId
|
||||
LEFT JOIN
|
||||
Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId
|
||||
LEFT JOIN
|
||||
Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId
|
||||
LEFT JOIN
|
||||
Favourites ON Game.Id = Favourites.GameId AND Favourites.UserId = @userid " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
List<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>();
|
||||
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
|
||||
|
||||
@@ -458,17 +558,65 @@ namespace gaseous_server.Controllers.v1_1
|
||||
// compile data for return
|
||||
int pageOffset = pageSize * (pageNumber - 1);
|
||||
for (int i = pageOffset; i < dbResponse.Rows.Count; i++)
|
||||
{
|
||||
if (pageNumber != 0 && pageSize != 0)
|
||||
{
|
||||
if (i >= (pageOffset + pageSize))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
|
||||
RetVal.Add(retGame);
|
||||
}
|
||||
|
||||
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);
|
||||
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
|
||||
Games.MinimalGameItem retMinGame = new Games.MinimalGameItem(retGame);
|
||||
if (dbResponse.Rows[i]["RomSaveCount"] != DBNull.Value || dbResponse.Rows[i]["MediaGroupSaveCount"] != DBNull.Value)
|
||||
{
|
||||
retMinGame.HasSavedGame = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
retMinGame.HasSavedGame = false;
|
||||
}
|
||||
if ((int)dbResponse.Rows[i]["Favourite"] == 0)
|
||||
{
|
||||
retMinGame.IsFavourite = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
retMinGame.IsFavourite = true;
|
||||
}
|
||||
|
||||
RetVal.Add(retMinGame);
|
||||
}
|
||||
|
||||
// build alpha list
|
||||
Dictionary<string, int> AlphaList = new Dictionary<string, int>();
|
||||
int CurrentPage = 0;
|
||||
int NextPageIndex = 0;
|
||||
for (int i = 0; i < dbResponse.Rows.Count; i++)
|
||||
{
|
||||
string firstChar = dbResponse.Rows[i]["NameThe"].ToString().Substring(0, 1).ToUpperInvariant();
|
||||
if (!"ABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(firstChar))
|
||||
{
|
||||
firstChar = "#";
|
||||
}
|
||||
if (!AlphaList.ContainsKey(firstChar))
|
||||
{
|
||||
AlphaList.Add(firstChar, CurrentPage);
|
||||
}
|
||||
if (NextPageIndex == i)
|
||||
{
|
||||
NextPageIndex += pageSize;
|
||||
CurrentPage += 1;
|
||||
}
|
||||
}
|
||||
|
||||
GameReturnPackage gameReturn = new GameReturnPackage
|
||||
{
|
||||
Count = RecordCount,
|
||||
Games = RetVal,
|
||||
AlphaList = AlphaList
|
||||
};
|
||||
|
||||
return gameReturn;
|
||||
}
|
||||
@@ -495,6 +643,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
|
||||
public int Count { get; set; }
|
||||
public List<Games.MinimalGameItem> Games { get; set; } = new List<Games.MinimalGameItem>();
|
||||
public Dictionary<string, int> AlphaList { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
|
300
gaseous-server/Controllers/V1.1/StateManagerController.cs
Normal file
300
gaseous-server/Controllers/V1.1/StateManagerController.cs
Normal file
@@ -0,0 +1,300 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using gaseous_server.Models;
|
||||
using gaseous_server.Classes;
|
||||
using Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Data;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiVersion("1.1")]
|
||||
[ApiController]
|
||||
public class StateManagerController: ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public StateManagerController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager
|
||||
)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.GameStateItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}")]
|
||||
public async Task<ActionResult> SaveStateAsync(long RomId, UploadStateModel uploadState, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
byte[] CompressedState = Common.Compress(uploadState.StateByteArray);
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State, Zipped) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state, @zipped); SELECT LAST_INSERT_ID();";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "userid", user.Id },
|
||||
{ "romid", RomId },
|
||||
{ "ismediagroup", IsMediaGroup },
|
||||
{ "statedatetime", DateTime.UtcNow },
|
||||
{ "name", "" },
|
||||
{ "screenshot", uploadState.ScreenshotByteArray },
|
||||
{ "state", CompressedState },
|
||||
{ "zipped", true }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (IsMediaGroup == false)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Save State", "Saved state for rom id " + RomId + ". State size: " + uploadState.StateByteArrayBase64.Length + " Compressed size: " + CompressedState.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Save State", "Saved state for media group id " + RomId + ". State size: " + uploadState.StateByteArrayBase64.Length + " Compressed size: " + CompressedState.Length);
|
||||
}
|
||||
|
||||
return Ok(await GetStateAsync(RomId, (long)(ulong)data.Rows[0][0], IsMediaGroup));
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(List<Models.GameStateItem>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}")]
|
||||
public async Task<ActionResult> GetAllStateAsync(long RomId, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Id, StateDateTime, `Name`, Screenshot FROM GameState WHERE RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid ORDER BY StateDateTime DESC;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "romid", RomId },
|
||||
{ "userid", user.Id },
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<Models.GameStateItem> gameStates = new List<GameStateItem>();
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
gameStates.Add(BuildGameStateItem(row));
|
||||
}
|
||||
|
||||
return Ok(gameStates);
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.GameStateItem), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}/{StateId}")]
|
||||
public async Task<ActionResult> GetStateAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Id, StateDateTime, `Name`, Screenshot FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", StateId },
|
||||
{ "romid", RomId },
|
||||
{ "userid", user.Id },
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// invalid match - return not found
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameStateItem stateItem = BuildGameStateItem(data.Rows[0]);
|
||||
|
||||
return Ok(stateItem);
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpDelete]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}/{StateId}")]
|
||||
public async Task<ActionResult> DeleteStateAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", StateId },
|
||||
{ "romid", RomId },
|
||||
{ "userid", user.Id },
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPut]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}/{StateId}")]
|
||||
public async Task<ActionResult> EditStateAsync(long RomId, long StateId, GameStateItemUpdateModel model, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "UPDATE GameState SET `Name` = @name WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", StateId },
|
||||
{ "romid", RomId },
|
||||
{ "userid", user.Id },
|
||||
{ "ismediagroup", IsMediaGroup },
|
||||
{ "name", model.Name }
|
||||
};
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}/{StateId}/Screenshot/")]
|
||||
[Route("{RomId}/{StateId}/Screenshot/image.png")]
|
||||
public async Task<ActionResult> GetStateScreenshotAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Screenshot FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", StateId },
|
||||
{ "romid", RomId },
|
||||
{ "userid", user.Id },
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// invalid match - return not found
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
string filename = "image.jpg";
|
||||
byte[] bytes = (byte[])data.Rows[0][0];
|
||||
string contentType = "image/png";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
};
|
||||
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||
|
||||
return File(bytes, contentType);
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}/{StateId}/State/")]
|
||||
[Route("{RomId}/{StateId}/State/savestate.state")]
|
||||
public async Task<ActionResult> GetStateDataAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Zipped, State FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", StateId },
|
||||
{ "romid", RomId },
|
||||
{ "userid", user.Id },
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// invalid match - return not found
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
string filename = "savestate.state";
|
||||
byte[] bytes;
|
||||
if ((bool)data.Rows[0]["Zipped"] == false)
|
||||
{
|
||||
bytes = (byte[])data.Rows[0]["State"];
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = Common.Decompress((byte[])data.Rows[0]["State"]);
|
||||
}
|
||||
string contentType = "application/octet-stream";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
};
|
||||
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||
|
||||
return File(bytes, contentType);
|
||||
}
|
||||
}
|
||||
|
||||
private Models.GameStateItem BuildGameStateItem(DataRow dr)
|
||||
{
|
||||
bool HasScreenshot = true;
|
||||
if (dr["Screenshot"] == DBNull.Value)
|
||||
{
|
||||
HasScreenshot = false;
|
||||
}
|
||||
GameStateItem stateItem = new GameStateItem
|
||||
{
|
||||
Id = (long)dr["Id"],
|
||||
Name = (string)dr["Name"],
|
||||
SaveTime = DateTime.Parse(((DateTime)dr["StateDateTime"]).ToString("yyyy-MM-ddThh:mm:ss") + 'Z'),
|
||||
HasScreenshot = HasScreenshot
|
||||
};
|
||||
|
||||
return stateItem;
|
||||
}
|
||||
}
|
||||
}
|
104
gaseous-server/Controllers/V1.1/StatisticsController.cs
Normal file
104
gaseous-server/Controllers/V1.1/StatisticsController.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using gaseous_server.Models;
|
||||
using gaseous_server.Classes;
|
||||
using Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiVersion("1.1")]
|
||||
[ApiController]
|
||||
public class StatisticsController: ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public StatisticsController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager
|
||||
)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("Games/{GameId}/")]
|
||||
public async Task<ActionResult> NewRecordStatistics(long GameId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Statistics statistics = new Statistics();
|
||||
return Ok(statistics.RecordSession(Guid.Empty, GameId, user.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPut]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("Games/{GameId}/{SessionId}")]
|
||||
public async Task<ActionResult> SubsequentRecordStatistics(long GameId, Guid SessionId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Statistics statistics = new Statistics();
|
||||
return Ok(statistics.RecordSession(SessionId, GameId, user.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("Games/{GameId}")]
|
||||
public async Task<ActionResult> GetStatistics(long GameId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Statistics statistics = new Statistics();
|
||||
StatisticsModel? model = statistics.GetSession(GameId, user.Id);
|
||||
if (model == null)
|
||||
{
|
||||
return NoContent();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ok(model);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
gaseous-server/Models/GameState.cs
Normal file
35
gaseous-server/Models/GameState.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class UploadStateModel
|
||||
{
|
||||
public string ScreenshotByteArrayBase64 { get; set; }
|
||||
public string StateByteArrayBase64 { get; set; }
|
||||
public byte[] ScreenshotByteArray
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.FromBase64String(ScreenshotByteArrayBase64);
|
||||
}
|
||||
}
|
||||
public byte[] StateByteArray
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.FromBase64String(StateByteArrayBase64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GameStateItem
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public DateTime SaveTime { get; set; }
|
||||
public bool HasScreenshot { get; set; }
|
||||
}
|
||||
|
||||
public class GameStateItemUpdateModel
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
}
|
52
gaseous-server/Models/GaseousGame.cs
Normal file
52
gaseous-server/Models/GaseousGame.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Reflection;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class GaseousGame : IGDB.Models.Game
|
||||
{
|
||||
public GaseousGame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GaseousGame(IGDB.Models.Game game)
|
||||
{
|
||||
var targetType = this.GetType();
|
||||
var sourceType = game.GetType();
|
||||
foreach(var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public| BindingFlags.SetProperty))
|
||||
{
|
||||
// check whether source object has the the property
|
||||
var sp = sourceType.GetProperty(prop.Name);
|
||||
if (sp != null)
|
||||
{
|
||||
// if yes, copy the value to the matching property
|
||||
var value = sp.GetValue(game, null);
|
||||
prop.SetValue(this, value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
|
||||
return cover;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -42,6 +42,7 @@ namespace gaseous_server.Models
|
||||
// exists
|
||||
if (ResetToDefault == false)
|
||||
{
|
||||
WriteAvailableEmulators(mapItem);
|
||||
Logging.Log(Logging.LogType.Information, "Platform Map", "Skipping import of " + mapItem.IGDBName + " - already in database.");
|
||||
}
|
||||
else
|
||||
@@ -106,11 +107,19 @@ namespace gaseous_server.Models
|
||||
long mapId = (long)row["Id"];
|
||||
if (PlatformMapCache.ContainsKey(mapId.ToString()))
|
||||
{
|
||||
platformMaps.Add(PlatformMapCache[mapId.ToString()]);
|
||||
PlatformMapItem mapItem = PlatformMapCache[mapId.ToString()];
|
||||
if (mapItem != null)
|
||||
{
|
||||
platformMaps.Add(mapItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
platformMaps.Add(BuildPlatformMapItem(row));
|
||||
PlatformMapItem mapItem = BuildPlatformMapItem(row);
|
||||
if (mapItem != null)
|
||||
{
|
||||
platformMaps.Add(mapItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +254,30 @@ namespace gaseous_server.Models
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteAvailableEmulators (PlatformMapItem item)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core, AvailableWebEmulators=@AvailableWebEmulators WHERE Id = @Id; ";
|
||||
|
||||
dbDict.Add("Id", item.IGDBId);
|
||||
dbDict.Add("RetroPieDirectoryName", item.RetroPieDirectoryName);
|
||||
if (item.WebEmulator != null)
|
||||
{
|
||||
dbDict.Add("WebEmulator_Type", item.WebEmulator.Type);
|
||||
dbDict.Add("WebEmulator_Core", item.WebEmulator.Core);
|
||||
dbDict.Add("AvailableWebEmulators", Newtonsoft.Json.JsonConvert.SerializeObject(item.WebEmulator.AvailableWebEmulators));
|
||||
}
|
||||
else
|
||||
{
|
||||
dbDict.Add("WebEmulator_Type", "");
|
||||
dbDict.Add("WebEmulator_Core", "");
|
||||
dbDict.Add("AvailableWebEmulators", "");
|
||||
}
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
}
|
||||
|
||||
static PlatformMapItem BuildPlatformMapItem(DataRow row)
|
||||
{
|
||||
long IGDBId = (long)row["Id"];
|
||||
@@ -253,8 +286,10 @@ namespace gaseous_server.Models
|
||||
string sql = "";
|
||||
|
||||
// get platform data
|
||||
IGDB.Models.Platform platform = Platforms.GetPlatform(IGDBId);
|
||||
IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId);
|
||||
|
||||
if (platform != null)
|
||||
{
|
||||
// get platform alternate names
|
||||
sql = "SELECT * FROM PlatformMap_AlternateNames WHERE Id = @Id ORDER BY Name";
|
||||
dbDict.Clear();
|
||||
@@ -358,14 +393,22 @@ namespace gaseous_server.Models
|
||||
return mapItem;
|
||||
}
|
||||
|
||||
public static void GetIGDBPlatformMapping(ref Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName)
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void GetIGDBPlatformMapping(ref gaseous_server.Models.Signatures_Games Signature, string ImageExtension, bool SetSystemName)
|
||||
{
|
||||
if (Signature.Game != null)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Determining platform based on extension " + ImageExtension + " or \"" + Signature.Game.System + "\"");
|
||||
}
|
||||
|
||||
bool PlatformFound = false;
|
||||
foreach (Models.PlatformMapping.PlatformMapItem PlatformMapping in Models.PlatformMapping.PlatformMap)
|
||||
{
|
||||
if (PlatformMapping.Extensions != null)
|
||||
{
|
||||
if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(RomFileInfo.Extension, StringComparer.OrdinalIgnoreCase))
|
||||
if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(ImageExtension, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
if (SetSystemName == true)
|
||||
{
|
||||
@@ -375,6 +418,8 @@ namespace gaseous_server.Models
|
||||
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
|
||||
|
||||
PlatformFound = true;
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from file extension");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -397,10 +442,17 @@ namespace gaseous_server.Models
|
||||
Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName;
|
||||
|
||||
PlatformFound = true;
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Platform id " + PlatformMapping.IGDBId + " determined from signature system to platform map");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PlatformFound == false)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Platform Mapping", "Unable to determine platform");
|
||||
}
|
||||
}
|
||||
|
||||
public class PlatformMapItem
|
||||
|
@@ -4,197 +4,19 @@ using gaseous_signature_parser.models.RomSignatureObject;
|
||||
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class Signatures_Games
|
||||
public class Signatures_Games : HasheousClient.Models.LookupResponseModel
|
||||
{
|
||||
public Signatures_Games()
|
||||
{
|
||||
}
|
||||
|
||||
public GameItem? Game { get; set; }
|
||||
public RomItem? Rom { get; set; }
|
||||
|
||||
//[JsonIgnore]
|
||||
public int Score
|
||||
{
|
||||
get
|
||||
{
|
||||
int _score = 0;
|
||||
|
||||
if (Game != null)
|
||||
{
|
||||
_score = _score + Game.Score;
|
||||
}
|
||||
|
||||
if (Rom != null)
|
||||
{
|
||||
_score = _score + Rom.Score;
|
||||
}
|
||||
|
||||
return _score;
|
||||
}
|
||||
}
|
||||
|
||||
public SignatureFlags Flags = new SignatureFlags();
|
||||
|
||||
public class GameItem
|
||||
{
|
||||
public Int32? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Year { get; set; }
|
||||
public string? Publisher { get; set; }
|
||||
public DemoTypes Demo { get; set; }
|
||||
public string? System { get; set; }
|
||||
public string? SystemVariant { get; set; }
|
||||
public string? Video { get; set; }
|
||||
public string? Country { get; set; }
|
||||
public string? Language { get; set; }
|
||||
public string? Copyright { get; set; }
|
||||
|
||||
public enum DemoTypes
|
||||
{
|
||||
NotDemo = 0,
|
||||
demo = 1,
|
||||
demo_kiosk = 2,
|
||||
demo_playable = 3,
|
||||
demo_rolling = 4,
|
||||
demo_slideshow = 5
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int Score
|
||||
{
|
||||
get
|
||||
{
|
||||
// calculate a score based on the availablility of data
|
||||
int _score = 0;
|
||||
var properties = this.GetType().GetProperties();
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
if (prop.GetGetMethod() != null)
|
||||
{
|
||||
switch (prop.Name.ToLower())
|
||||
{
|
||||
case "id":
|
||||
case "score":
|
||||
break;
|
||||
case "name":
|
||||
case "year":
|
||||
case "publisher":
|
||||
case "system":
|
||||
if (prop.PropertyType == typeof(string))
|
||||
{
|
||||
if (prop.GetValue(this) != null)
|
||||
{
|
||||
string propVal = prop.GetValue(this).ToString();
|
||||
if (propVal.Length > 0)
|
||||
{
|
||||
_score = _score + 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (prop.PropertyType == typeof(string))
|
||||
{
|
||||
if (prop.GetValue(this) != null)
|
||||
{
|
||||
string propVal = prop.GetValue(this).ToString();
|
||||
if (propVal.Length > 0)
|
||||
{
|
||||
_score = _score + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RomItem
|
||||
{
|
||||
public Int32? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public Int64? Size { get; set; }
|
||||
public string? Crc { get; set; }
|
||||
public string? Md5 { get; set; }
|
||||
public string? Sha1 { get; set; }
|
||||
|
||||
public string? DevelopmentStatus { get; set; }
|
||||
|
||||
public List<KeyValuePair<string, object>> Attributes { get; set; } = new List<KeyValuePair<string, object>>();
|
||||
|
||||
public RomSignatureObject.Game.Rom.RomTypes RomType { get; set; }
|
||||
public string? RomTypeMedia { get; set; }
|
||||
public string? MediaLabel { get; set; }
|
||||
|
||||
public RomSignatureObject.Game.Rom.SignatureSourceType SignatureSource { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public int Score
|
||||
{
|
||||
get
|
||||
{
|
||||
// calculate a score based on the availablility of data
|
||||
int _score = 0;
|
||||
var properties = this.GetType().GetProperties();
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
if (prop.GetGetMethod() != null)
|
||||
{
|
||||
switch (prop.Name.ToLower())
|
||||
{
|
||||
case "name":
|
||||
case "size":
|
||||
case "crc":
|
||||
case "developmentstatus":
|
||||
case "flags":
|
||||
case "attributes":
|
||||
case "romtypemedia":
|
||||
case "medialabel":
|
||||
if (prop.PropertyType == typeof(string) || prop.PropertyType == typeof(Int64) || prop.PropertyType == typeof(List<string>))
|
||||
{
|
||||
if (prop.GetValue(this) != null)
|
||||
{
|
||||
string propVal = prop.GetValue(this).ToString();
|
||||
if (propVal.Length > 0)
|
||||
{
|
||||
_score = _score + 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (prop.PropertyType == typeof(string))
|
||||
{
|
||||
if (prop.GetValue(this) != null)
|
||||
{
|
||||
string propVal = prop.GetValue(this).ToString();
|
||||
if (propVal.Length > 0)
|
||||
{
|
||||
_score = _score + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SignatureFlags
|
||||
{
|
||||
public long IGDBPlatformId { get; set; }
|
||||
public string IGDBPlatformName { get; set; }
|
||||
public long IGDBGameId { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
gaseous-server/Models/StatisticsModel.cs
Normal file
17
gaseous-server/Models/StatisticsModel.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class StatisticsModel
|
||||
{
|
||||
public Guid SessionId { get; set; } = Guid.Empty;
|
||||
public long GameId { get; set; }
|
||||
public DateTime SessionStart { get; set; }
|
||||
public int SessionLength { get; set; }
|
||||
public DateTime SessionEnd
|
||||
{
|
||||
get
|
||||
{
|
||||
return SessionStart.AddMinutes(SessionLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,6 +2,10 @@
|
||||
using System.ComponentModel.Design.Serialization;
|
||||
using System.Data;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Controllers;
|
||||
using Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal;
|
||||
using NuGet.Common;
|
||||
using NuGet.Packaging;
|
||||
|
||||
namespace gaseous_server
|
||||
{
|
||||
@@ -11,25 +15,63 @@ namespace gaseous_server
|
||||
|
||||
public class QueueItem
|
||||
{
|
||||
public QueueItem(QueueItemType ItemType, bool AllowManualStart = true, bool RemoveWhenStopped = false)
|
||||
{
|
||||
_ItemType = ItemType;
|
||||
_ItemState = QueueItemState.NeverStarted;
|
||||
_LastRunTime = Config.ReadSetting<DateTime>("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.AddMinutes(-5));
|
||||
_AllowManualStart = AllowManualStart;
|
||||
_RemoveWhenStopped = RemoveWhenStopped;
|
||||
|
||||
// load queueitem configuration
|
||||
BackgroundTaskItem defaultItem = new BackgroundTaskItem(ItemType);
|
||||
Enabled(defaultItem.Enabled);
|
||||
_Interval = defaultItem.Interval;
|
||||
_AllowedDays = defaultItem.AllowedDays;
|
||||
AllowedStartHours = defaultItem.AllowedStartHours;
|
||||
AllowedStartMinutes = defaultItem.AllowedStartMinutes;
|
||||
AllowedEndHours = defaultItem.AllowedEndHours;
|
||||
AllowedEndMinutes = defaultItem.AllowedEndMinutes;
|
||||
_Blocks = defaultItem.Blocks;
|
||||
}
|
||||
|
||||
public QueueItem(QueueItemType ItemType, int ExecutionInterval, bool AllowManualStart = true, bool RemoveWhenStopped = false)
|
||||
{
|
||||
_ItemType = ItemType;
|
||||
_ItemState = QueueItemState.NeverStarted;
|
||||
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))).AddMinutes(-5);
|
||||
_LastRunTime = Config.ReadSetting<DateTime>("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.AddMinutes(-5));
|
||||
_Interval = ExecutionInterval;
|
||||
_AllowManualStart = AllowManualStart;
|
||||
_RemoveWhenStopped = RemoveWhenStopped;
|
||||
|
||||
// load timing defaults
|
||||
BackgroundTaskItem defaultItem = new BackgroundTaskItem(ItemType);
|
||||
Enabled(defaultItem.Enabled);
|
||||
_AllowedDays = defaultItem.AllowedDays;
|
||||
AllowedStartHours = defaultItem.AllowedStartHours;
|
||||
AllowedStartMinutes = defaultItem.AllowedStartMinutes;
|
||||
AllowedEndHours = defaultItem.AllowedEndHours;
|
||||
AllowedEndMinutes = defaultItem.AllowedEndMinutes;
|
||||
}
|
||||
|
||||
public QueueItem(QueueItemType ItemType, int ExecutionInterval, List<QueueItemType> Blocks, bool AllowManualStart = true, bool RemoveWhenStopped = false)
|
||||
{
|
||||
_ItemType = ItemType;
|
||||
_ItemState = QueueItemState.NeverStarted;
|
||||
_LastRunTime = DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ"))).AddMinutes(-5);
|
||||
_LastRunTime = Config.ReadSetting<DateTime>("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.AddMinutes(-5));
|
||||
_Interval = ExecutionInterval;
|
||||
_AllowManualStart = AllowManualStart;
|
||||
_RemoveWhenStopped = RemoveWhenStopped;
|
||||
_Blocks = Blocks;
|
||||
|
||||
// load timing defaults
|
||||
BackgroundTaskItem defaultItem = new BackgroundTaskItem(ItemType);
|
||||
Enabled(defaultItem.Enabled);
|
||||
_AllowedDays = defaultItem.AllowedDays;
|
||||
AllowedStartHours = defaultItem.AllowedStartHours;
|
||||
AllowedStartMinutes = defaultItem.AllowedStartMinutes;
|
||||
AllowedEndHours = defaultItem.AllowedEndHours;
|
||||
AllowedEndMinutes = defaultItem.AllowedEndMinutes;
|
||||
}
|
||||
|
||||
private QueueItemType _ItemType = QueueItemType.NotConfigured;
|
||||
@@ -40,13 +82,15 @@ namespace gaseous_server
|
||||
{
|
||||
get
|
||||
{
|
||||
return DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
|
||||
// return DateTime.Parse(Config.ReadSetting("LastRun_" + _ItemType.ToString(), DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")));
|
||||
return Config.ReadSetting<DateTime>("LastRun_" + _ItemType.ToString(), DateTime.UtcNow);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_SaveLastRunTime == true)
|
||||
{
|
||||
Config.SetSetting("LastRun_" + _ItemType.ToString(), value.ToString("yyyy-MM-ddThh:mm:ssZ"));
|
||||
//Config.SetSetting("LastRun_" + _ItemType.ToString(), value.ToString("yyyy-MM-ddThh:mm:ssZ"));
|
||||
Config.SetSetting<DateTime>("LastRun_" + _ItemType.ToString(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,8 +103,33 @@ namespace gaseous_server
|
||||
private bool _RemoveWhenStopped = false;
|
||||
private bool _IsBlocked = false;
|
||||
private string _CorrelationId = "";
|
||||
private List<DayOfWeek> _AllowedDays = new List<DayOfWeek>
|
||||
{
|
||||
DayOfWeek.Sunday,
|
||||
DayOfWeek.Monday,
|
||||
DayOfWeek.Tuesday,
|
||||
DayOfWeek.Wednesday,
|
||||
DayOfWeek.Thursday,
|
||||
DayOfWeek.Friday,
|
||||
DayOfWeek.Saturday
|
||||
};
|
||||
private List<QueueItemType> _Blocks = new List<QueueItemType>();
|
||||
|
||||
public List<DayOfWeek> AllowedDays
|
||||
{
|
||||
get
|
||||
{
|
||||
return _AllowedDays;
|
||||
}
|
||||
set
|
||||
{
|
||||
_AllowedDays = value;
|
||||
}
|
||||
}
|
||||
public int AllowedStartHours { get; set; } = 0;
|
||||
public int AllowedStartMinutes { get; set; } = 0;
|
||||
public int AllowedEndHours { get; set; } = 23;
|
||||
public int AllowedEndMinutes { get; set; } = 59;
|
||||
public QueueItemType ItemType => _ItemType;
|
||||
public QueueItemState ItemState => _ItemState;
|
||||
public DateTime LastRunTime => _LastRunTime;
|
||||
@@ -70,9 +139,56 @@ namespace gaseous_server
|
||||
{
|
||||
get
|
||||
{
|
||||
return LastRunTime.AddMinutes(Interval);
|
||||
// next run time
|
||||
DateTime tempNextRun = LastRunTime.ToLocalTime().AddMinutes(Interval);
|
||||
// if (tempNextRun < DateTime.Now)
|
||||
// {
|
||||
// tempNextRun = DateTime.Now;
|
||||
// }
|
||||
DayOfWeek nextWeekDay = tempNextRun.DayOfWeek;
|
||||
|
||||
// create local start and end times
|
||||
DateTime tempStartTime = new DateTime(tempNextRun.Year, tempNextRun.Month, tempNextRun.Day, AllowedStartHours, AllowedStartMinutes, 0, DateTimeKind.Local);
|
||||
DateTime tempEndTime = new DateTime(tempNextRun.Year, tempNextRun.Month, tempNextRun.Day, AllowedEndHours, AllowedEndMinutes, 0, DateTimeKind.Local);
|
||||
|
||||
// bump the next run time to the next allowed day and hour range
|
||||
if (AllowedDays.Contains(nextWeekDay))
|
||||
{
|
||||
// next run day is allowed, nothing to do
|
||||
}
|
||||
else
|
||||
{
|
||||
// keep bumping the day forward until the a weekday is found
|
||||
do
|
||||
{
|
||||
tempNextRun = tempNextRun.AddDays(1);
|
||||
nextWeekDay = tempNextRun.DayOfWeek;
|
||||
}
|
||||
while (!AllowedDays.Contains(nextWeekDay));
|
||||
|
||||
// update windows
|
||||
tempStartTime = new DateTime(tempNextRun.Year, tempNextRun.Month, tempNextRun.Day, AllowedStartHours, AllowedStartMinutes, 0, DateTimeKind.Local);
|
||||
tempEndTime = new DateTime(tempNextRun.Year, tempNextRun.Month, tempNextRun.Day, AllowedEndHours, AllowedEndMinutes, 0, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
// are the hours in the right range
|
||||
TimeSpan spanNextRun = tempNextRun.TimeOfDay;
|
||||
if (LastRunTime.ToLocalTime().AddMinutes(Interval) < tempStartTime)
|
||||
{
|
||||
return tempStartTime.ToUniversalTime();
|
||||
}
|
||||
else if (spanNextRun >= tempStartTime.TimeOfDay && spanNextRun <= tempEndTime.TimeOfDay)
|
||||
{
|
||||
// all good - return nextRun
|
||||
return tempNextRun.ToUniversalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
return tempStartTime.ToUniversalTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Interval
|
||||
{
|
||||
get
|
||||
@@ -144,10 +260,11 @@ namespace gaseous_server
|
||||
|
||||
case QueueItemType.TitleIngestor:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
|
||||
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory)
|
||||
Classes.ImportGame import = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
import.ProcessDirectory(Config.LibraryConfiguration.LibraryImportDirectory);
|
||||
|
||||
// clean up
|
||||
Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory);
|
||||
@@ -170,24 +287,39 @@ namespace gaseous_server
|
||||
|
||||
case QueueItemType.OrganiseLibrary:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser");
|
||||
Classes.ImportGame.OrganiseLibrary();
|
||||
Classes.ImportGame importLibraryOrg = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
importLibraryOrg.OrganiseLibrary();
|
||||
|
||||
_SaveLastRunTime = true;
|
||||
|
||||
break;
|
||||
|
||||
case QueueItemType.LibraryScan:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner");
|
||||
Classes.ImportGame import = new ImportGame
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanners");
|
||||
Classes.ImportGame libScan = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
import.LibraryScan();
|
||||
libScan.LibraryScan();
|
||||
|
||||
_SaveLastRunTime = true;
|
||||
|
||||
break;
|
||||
|
||||
case QueueItemType.LibraryScanWorker:
|
||||
GameLibrary.LibraryItem library = (GameLibrary.LibraryItem)Options;
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanner worker for library " + library.Name);
|
||||
Classes.ImportGame importLibraryScan = new ImportGame
|
||||
{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
importLibraryScan.LibrarySpecificScan(library);
|
||||
|
||||
break;
|
||||
|
||||
case QueueItemType.Rematcher:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Rematch");
|
||||
Classes.ImportGame importRematch = new ImportGame
|
||||
@@ -202,7 +334,8 @@ namespace gaseous_server
|
||||
|
||||
case QueueItemType.CollectionCompiler:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler");
|
||||
Classes.Collections.CompileCollections((long)Options);
|
||||
Dictionary<string, object> collectionOptions = (Dictionary<string, object>)Options;
|
||||
Classes.Collections.CompileCollections((long)collectionOptions["Id"], (string)collectionOptions["UserId"]);
|
||||
break;
|
||||
|
||||
case QueueItemType.MediaGroupCompiler:
|
||||
@@ -215,12 +348,51 @@ namespace gaseous_server
|
||||
DatabaseMigration.UpgradeScriptBackgroundTasks();
|
||||
break;
|
||||
|
||||
case QueueItemType.Maintainer:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Maintenance");
|
||||
case QueueItemType.DailyMaintainer:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Daily Maintenance");
|
||||
Classes.Maintenance maintenance = new Maintenance{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
maintenance.RunMaintenance();
|
||||
maintenance.RunDailyMaintenance();
|
||||
|
||||
_SaveLastRunTime = true;
|
||||
|
||||
break;
|
||||
|
||||
case QueueItemType.WeeklyMaintainer:
|
||||
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Weekly Maintenance");
|
||||
Classes.Maintenance weeklyMaintenance = new Maintenance{
|
||||
CallingQueueItem = this
|
||||
};
|
||||
weeklyMaintenance.RunWeeklyMaintenance();
|
||||
|
||||
_SaveLastRunTime = true;
|
||||
break;
|
||||
|
||||
case QueueItemType.TempCleanup:
|
||||
try
|
||||
{
|
||||
foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries)
|
||||
{
|
||||
string rootPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, libraryItem.Id.ToString());
|
||||
if (Directory.Exists(rootPath))
|
||||
{
|
||||
foreach (string directory in Directory.GetDirectories(rootPath))
|
||||
{
|
||||
DirectoryInfo info = new DirectoryInfo(directory);
|
||||
if (info.LastWriteTimeUtc.AddMinutes(5) < DateTime.UtcNow)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Get Signature", "Deleting temporary decompress folder: " + directory);
|
||||
Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception tcEx)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Get Signature", "An error occurred while cleaning temporary files", tcEx);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -233,7 +405,14 @@ namespace gaseous_server
|
||||
}
|
||||
|
||||
_ForceExecute = false;
|
||||
if (_DisableWhenComplete == false)
|
||||
{
|
||||
_ItemState = QueueItemState.Stopped;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ItemState = QueueItemState.Disabled;
|
||||
}
|
||||
_LastFinishTime = DateTime.UtcNow;
|
||||
_LastRunDuration = Math.Round((DateTime.UtcNow - _LastRunTime).TotalSeconds, 2);
|
||||
|
||||
@@ -252,6 +431,26 @@ namespace gaseous_server
|
||||
_IsBlocked = BlockState;
|
||||
}
|
||||
|
||||
private bool _DisableWhenComplete = false;
|
||||
public void Enabled(bool Enabled)
|
||||
{
|
||||
_DisableWhenComplete = !Enabled;
|
||||
if (Enabled == true)
|
||||
{
|
||||
if (_ItemState == QueueItemState.Disabled)
|
||||
{
|
||||
_ItemState = QueueItemState.Stopped;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_ItemState == QueueItemState.Stopped || _ItemState == QueueItemState.NeverStarted)
|
||||
{
|
||||
_ItemState = QueueItemState.Disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HasErrorsItem HasErrors
|
||||
{
|
||||
get
|
||||
@@ -350,6 +549,11 @@ namespace gaseous_server
|
||||
/// </summary>
|
||||
LibraryScan,
|
||||
|
||||
/// <summary>
|
||||
/// Performs the work for the LibraryScan task
|
||||
/// </summary>
|
||||
LibraryScanWorker,
|
||||
|
||||
/// <summary>
|
||||
/// Looks for roms in the library that have an unknown platform or game match
|
||||
/// </summary>
|
||||
@@ -371,9 +575,19 @@ namespace gaseous_server
|
||||
BackgroundDatabaseUpgrade,
|
||||
|
||||
/// <summary>
|
||||
/// Performs a clean up of old files, and optimises the database
|
||||
/// Performs a clean up of old files, and purge old logs
|
||||
/// </summary>
|
||||
Maintainer
|
||||
DailyMaintainer,
|
||||
|
||||
/// <summary>
|
||||
/// Performs more intensive cleanups and optimises the database
|
||||
/// </summary>
|
||||
WeeklyMaintainer,
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up marked paths in the temporary directory
|
||||
/// </summary>
|
||||
TempCleanup
|
||||
}
|
||||
|
||||
public enum QueueItemState
|
||||
|
@@ -3,18 +3,14 @@ using System.Text.Json.Serialization;
|
||||
using gaseous_server;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Models;
|
||||
using gaseous_server.SignatureIngestors.XML;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using IGDB.Models;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using Asp.Versioning;
|
||||
|
||||
Logging.WriteToDiskOnly = true;
|
||||
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
|
||||
@@ -50,16 +46,10 @@ Config.InitSettings();
|
||||
Config.UpdateConfig();
|
||||
|
||||
// set api metadata source from config
|
||||
Communications.MetadataSource = Config.MetadataConfiguration.Source;
|
||||
Communications.MetadataSource = Config.MetadataConfiguration.MetadataSource;
|
||||
|
||||
// set initial values
|
||||
Guid APIKey = Guid.NewGuid();
|
||||
if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
|
||||
{
|
||||
// it's a new api key save it
|
||||
Logging.Log(Logging.LogType.Information, "Startup", "Setting initial API key");
|
||||
Config.SetSetting("API Key", APIKey.ToString());
|
||||
}
|
||||
// set up hasheous client
|
||||
HasheousClient.WebApp.HttpHelper.BaseUri = Config.MetadataConfiguration.HasheousHost;
|
||||
|
||||
// clean up storage
|
||||
if (Directory.Exists(Config.LibraryConfiguration.LibraryTempDirectory))
|
||||
@@ -106,6 +96,11 @@ builder.Services.AddControllers().AddJsonOptions(x =>
|
||||
builder.Services.AddResponseCaching();
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
options.CacheProfiles.Add("None",
|
||||
new CacheProfile()
|
||||
{
|
||||
Duration = 1
|
||||
});
|
||||
options.CacheProfiles.Add("Default30",
|
||||
new CacheProfile()
|
||||
{
|
||||
@@ -132,12 +127,7 @@ builder.Services.AddApiVersioning(config =>
|
||||
config.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
|
||||
new HeaderApiVersionReader("x-api-version"),
|
||||
new MediaTypeApiVersionReader("x-api-version"));
|
||||
});
|
||||
// builder.Services.AddApiVersioning(setup =>
|
||||
// {
|
||||
// setup.ApiVersionReader = new UrlSegmentApiVersionReader();
|
||||
// });
|
||||
builder.Services.AddVersionedApiExplorer(setup =>
|
||||
}).AddApiExplorer(setup =>
|
||||
{
|
||||
setup.GroupNameFormat = "'v'VVV";
|
||||
setup.SubstituteApiVersionInUrl = true;
|
||||
@@ -234,15 +224,6 @@ builder.Services.ConfigureApplicationCookie(options =>
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||
options.Cookie.SameSite = SameSiteMode.Strict;
|
||||
});
|
||||
// builder.Services.AddIdentityCore<ApplicationUser>(options => {
|
||||
// options.SignIn.RequireConfirmedAccount = false;
|
||||
// options.User.RequireUniqueEmail = true;
|
||||
// options.Password.RequireDigit = false;
|
||||
// options.Password.RequiredLength = 10;
|
||||
// options.Password.RequireNonAlphanumeric = false;
|
||||
// options.Password.RequireUppercase = false;
|
||||
// options.Password.RequireLowercase = false;
|
||||
// });
|
||||
builder.Services.AddScoped<UserStore>();
|
||||
builder.Services.AddScoped<RoleStore>();
|
||||
|
||||
@@ -269,8 +250,16 @@ var app = builder.Build();
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.SwaggerEndpoint($"/swagger/v1/swagger.json", "v1.0");
|
||||
options.SwaggerEndpoint($"/swagger/v1.1/swagger.json", "v1.1");
|
||||
// options.SwaggerEndpoint($"/swagger/v1/swagger.json", "v1.0");
|
||||
// options.SwaggerEndpoint($"/swagger/v1.1/swagger.json", "v1.1");
|
||||
|
||||
var descriptions = app.DescribeApiVersions();
|
||||
foreach (var description in descriptions)
|
||||
{
|
||||
var url = $"/swagger/{description.GroupName}/swagger.json";
|
||||
var name = description.GroupName.ToUpperInvariant();
|
||||
options.SwaggerEndpoint(url, name);
|
||||
}
|
||||
}
|
||||
);
|
||||
//}
|
||||
@@ -413,60 +402,39 @@ var platformMap = PlatformMapping.PlatformMap;
|
||||
|
||||
// add background tasks
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.SignatureIngestor,
|
||||
int.Parse(Config.ReadSetting("Interval_SignatureIngestor", "60"))
|
||||
)
|
||||
ProcessQueue.QueueItemType.SignatureIngestor)
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.TitleIngestor,
|
||||
int.Parse(Config.ReadSetting("Interval_TitleIngestor", "1")),
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
ProcessQueue.QueueItemType.LibraryScan
|
||||
})
|
||||
ProcessQueue.QueueItemType.TitleIngestor)
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.MetadataRefresh,
|
||||
int.Parse(Config.ReadSetting("Interval_MetadataRefresh", "360"))
|
||||
)
|
||||
ProcessQueue.QueueItemType.MetadataRefresh)
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
int.Parse(Config.ReadSetting("Interval_OrganiseLibrary", "1440")),
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.LibraryScan,
|
||||
ProcessQueue.QueueItemType.TitleIngestor,
|
||||
ProcessQueue.QueueItemType.Rematcher
|
||||
})
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary)
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.LibraryScan,
|
||||
int.Parse(Config.ReadSetting("Interval_LibraryScan", "1440")),
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
ProcessQueue.QueueItemType.Rematcher
|
||||
})
|
||||
ProcessQueue.QueueItemType.LibraryScan)
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.Rematcher,
|
||||
int.Parse(Config.ReadSetting("Interval_Rematcher", "1440")),
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
ProcessQueue.QueueItemType.LibraryScan
|
||||
})
|
||||
ProcessQueue.QueueItemType.Rematcher)
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.Maintainer,
|
||||
int.Parse(Config.ReadSetting("Interval_Maintainer", "10080")),
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.All
|
||||
})
|
||||
|
||||
// maintenance tasks
|
||||
ProcessQueue.QueueItem dailyMaintenance = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.DailyMaintainer
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(dailyMaintenance);
|
||||
|
||||
ProcessQueue.QueueItem weeklyMaintenance = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.WeeklyMaintainer
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(weeklyMaintenance);
|
||||
|
||||
ProcessQueue.QueueItem tempCleanup = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.TempCleanup
|
||||
);
|
||||
ProcessQueue.QueueItems.Add(tempCleanup);
|
||||
|
||||
Logging.WriteToDiskOnly = false;
|
||||
|
||||
|
@@ -1,41 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:38715",
|
||||
"sslPort": 44314
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"launchUrl": "/",
|
||||
"applicationUrl": "http://localhost:5198",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7282;http://localhost:5198",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,18 +26,17 @@ SELECT
|
||||
*
|
||||
FROM
|
||||
(SELECT DISTINCT
|
||||
row_number() over (partition by Id order by AgeGroupId desc) as seqnum, view_GamesWithRoms.*,
|
||||
(SELECT
|
||||
AgeGroupId
|
||||
FROM
|
||||
ClassificationMap
|
||||
WHERE
|
||||
RatingId = AgeRating.Rating
|
||||
ORDER BY AgeGroupId DESC) AgeGroupId
|
||||
row_number() over (partition by Id order by AgeGroup.AgeGroupId desc) as seqnum, view_GamesWithRoms.*,
|
||||
AgeGroup.AgeGroupId AS AgeGroupId
|
||||
FROM
|
||||
view_GamesWithRoms
|
||||
LEFT JOIN Relation_Game_AgeRatings ON view_GamesWithRoms.Id = Relation_Game_AgeRatings.GameId
|
||||
LEFT JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id
|
||||
LEFT JOIN (SELECT
|
||||
AgeGroupId, RatingId
|
||||
FROM
|
||||
ClassificationMap
|
||||
ORDER BY AgeGroupId DESC) AgeGroup ON AgeRating.Rating = AgeGroup.RatingId
|
||||
) g
|
||||
WHERE g.seqnum = 1;
|
||||
|
||||
|
5
gaseous-server/Support/Database/MySQL/gaseous-1012.sql
Normal file
5
gaseous-server/Support/Database/MySQL/gaseous-1012.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE `Games_Roms`
|
||||
ADD INDEX `id_IdAndLibraryId` (`Id` ASC, `LibraryId` ASC) VISIBLE;
|
||||
|
||||
ALTER TABLE `ServerLogs`
|
||||
ADD INDEX `idx_EventDate` (`EventTime` ASC) VISIBLE;
|
9
gaseous-server/Support/Database/MySQL/gaseous-1013.sql
Normal file
9
gaseous-server/Support/Database/MySQL/gaseous-1013.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE `SearchCache` (
|
||||
`SearchFields` varchar(384) NOT NULL,
|
||||
`SearchString` varchar(128) NOT NULL,
|
||||
`Content` longtext DEFAULT NULL,
|
||||
`LastSearch` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`SearchFields`,`SearchString`),
|
||||
KEY `idx_SearchString` (`SearchFields`,`SearchString`),
|
||||
KEY `idx_LastSearch` (`LastSearch`)
|
||||
);
|
2
gaseous-server/Support/Database/MySQL/gaseous-1014.sql
Normal file
2
gaseous-server/Support/Database/MySQL/gaseous-1014.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `RomCollections`
|
||||
ADD COLUMN `ArchiveType` INT NULL AFTER `IncludeBIOSFiles`;
|
11
gaseous-server/Support/Database/MySQL/gaseous-1015.sql
Normal file
11
gaseous-server/Support/Database/MySQL/gaseous-1015.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE `GameState` (
|
||||
`Id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`UserId` VARCHAR(45) NULL,
|
||||
`RomId` BIGINT NULL,
|
||||
`IsMediaGroup` INT NULL,
|
||||
`StateDateTime` DATETIME NULL,
|
||||
`Name` VARCHAR(100) NULL,
|
||||
`Screenshot` LONGBLOB NULL,
|
||||
`State` LONGBLOB NULL,
|
||||
PRIMARY KEY (`Id`),
|
||||
INDEX `idx_UserId` (`UserId` ASC) VISIBLE);
|
6
gaseous-server/Support/Database/MySQL/gaseous-1016.sql
Normal file
6
gaseous-server/Support/Database/MySQL/gaseous-1016.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE `Settings`
|
||||
ADD COLUMN `ValueType` INT NULL DEFAULT 0 AFTER `Setting`,
|
||||
ADD COLUMN `ValueDate` DATETIME NULL DEFAULT NULL AFTER `Value`;
|
||||
|
||||
ALTER TABLE `GameState`
|
||||
ADD COLUMN `Zipped` BOOLEAN NOT NULL DEFAULT 0;
|
4
gaseous-server/Support/Database/MySQL/gaseous-1017.sql
Normal file
4
gaseous-server/Support/Database/MySQL/gaseous-1017.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE `RomCollections`
|
||||
ADD COLUMN `OwnedBy` VARCHAR(45) NULL,
|
||||
ADD COLUMN `AgeGroup` INT NULL,
|
||||
ADD COLUMN `AgeGroupUnclassified` BOOLEAN NULL;
|
17
gaseous-server/Support/Database/MySQL/gaseous-1018.sql
Normal file
17
gaseous-server/Support/Database/MySQL/gaseous-1018.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
ALTER TABLE `Collection`
|
||||
ADD COLUMN `AsParentRelations` LONGTEXT NULL AFTER `Id`,
|
||||
ADD COLUMN `AsChildRelations` LONGTEXT NULL AFTER `AsParentRelations`,
|
||||
ADD COLUMN `Type` INT NULL AFTER `UpdatedAt`;
|
||||
|
||||
ALTER TABLE `Cover`
|
||||
ADD COLUMN `GameLocalization` BIGINT NULL AFTER `Game`;
|
||||
|
||||
ALTER TABLE `Game`
|
||||
ADD COLUMN `Collections` LONGTEXT NULL AFTER `Collection`,
|
||||
ADD COLUMN `ExpandedGames` LONGTEXT NULL AFTER `Dlcs`,
|
||||
ADD COLUMN `Forks` LONGTEXT NULL AFTER `Follows`,
|
||||
ADD COLUMN `GameLocalizations` LONGTEXT NULL AFTER `GameEngines`,
|
||||
ADD COLUMN `LanguageSupports` LONGTEXT NULL AFTER `Keywords`,
|
||||
ADD COLUMN `Ports` LONGTEXT NULL AFTER `PlayerPerspectives`,
|
||||
ADD COLUMN `Remakes` LONGTEXT NULL AFTER `ReleaseDates`,
|
||||
ADD COLUMN `Remasters` LONGTEXT NULL AFTER `Remakes`;
|
11
gaseous-server/Support/Database/MySQL/gaseous-1019.sql
Normal file
11
gaseous-server/Support/Database/MySQL/gaseous-1019.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE `UserAvatars` (
|
||||
`UserId` VARCHAR(128) NOT NULL,
|
||||
`Id` VARCHAR(45) NOT NULL,
|
||||
`Avatar` LONGBLOB NULL,
|
||||
PRIMARY KEY (`UserId`),
|
||||
INDEX `idx_AvatarId` (`Id` ASC) VISIBLE,
|
||||
CONSTRAINT `ApplicationUser_Avatar`
|
||||
FOREIGN KEY (`UserId`)
|
||||
REFERENCES `Users` (`Id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION);
|
13
gaseous-server/Support/Database/MySQL/gaseous-1020.sql
Normal file
13
gaseous-server/Support/Database/MySQL/gaseous-1020.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE `UserTimeTracking` (
|
||||
`GameId` BIGINT NULL,
|
||||
`UserId` VARCHAR(45) NULL,
|
||||
`SessionId` VARCHAR(45) NULL,
|
||||
`SessionTime` DATETIME NULL,
|
||||
`SessionLength` INT NULL,
|
||||
INDEX `UserId_idx` (`UserId` ASC) VISIBLE,
|
||||
INDEX `SessionId_idx` (`SessionId` ASC) VISIBLE,
|
||||
CONSTRAINT `UserId`
|
||||
FOREIGN KEY (`UserId`)
|
||||
REFERENCES `Users` (`Id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION);
|
8
gaseous-server/Support/Database/MySQL/gaseous-1021.sql
Normal file
8
gaseous-server/Support/Database/MySQL/gaseous-1021.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE `Favourites` (
|
||||
`UserId` varchar(45) NOT NULL,
|
||||
`GameId` bigint(20) NOT NULL,
|
||||
PRIMARY KEY (`UserId`,`GameId`),
|
||||
KEY `idx_GameId` (`GameId`),
|
||||
KEY `idx_UserId` (`UserId`),
|
||||
CONSTRAINT `ApplicationUser_Favourite` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
);
|
49
gaseous-server/Support/Database/MySQL/gaseous-fix-1005.sql
Normal file
49
gaseous-server/Support/Database/MySQL/gaseous-fix-1005.sql
Normal file
@@ -0,0 +1,49 @@
|
||||
CREATE TABLE `Relation_Game_AgeRatings` (
|
||||
`GameId` BIGINT NOT NULL,
|
||||
`AgeRatingsId` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `AgeRatingsId`),
|
||||
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
|
||||
);
|
||||
|
||||
CREATE TABLE `Relation_Game_Genres` (
|
||||
`GameId` BIGINT NOT NULL,
|
||||
`GenresId` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `GenresId`),
|
||||
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
|
||||
);
|
||||
|
||||
CREATE TABLE `Relation_Game_GameModes` (
|
||||
`GameId` BIGINT NOT NULL,
|
||||
`GameModesId` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `GameModesId`),
|
||||
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
|
||||
);
|
||||
|
||||
CREATE TABLE `Relation_Game_PlayerPerspectives` (
|
||||
`GameId` BIGINT NOT NULL,
|
||||
`PlayerPerspectivesId` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `PlayerPerspectivesId`),
|
||||
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
|
||||
);
|
||||
|
||||
CREATE TABLE `Relation_Game_Themes` (
|
||||
`GameId` BIGINT NOT NULL,
|
||||
`ThemesId` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `ThemesId`),
|
||||
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
|
||||
);
|
||||
|
||||
ALTER TABLE `Games_Roms`
|
||||
ADD COLUMN `LastMatchAttemptDate` DATETIME NULL AFTER `LibraryId`;
|
||||
|
||||
CREATE TABLE `RomMediaGroup` (
|
||||
`Id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`Status` INT NULL,
|
||||
`PlatformId` BIGINT NULL,
|
||||
`GameId` BIGINT NULL,
|
||||
PRIMARY KEY (`Id`));
|
||||
|
||||
CREATE TABLE `RomMediaGroup_Members` (
|
||||
`GroupId` BIGINT NOT NULL,
|
||||
`RomId` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`GroupId`, `RomId`));
|
@@ -188,6 +188,91 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 114,
|
||||
"igdbName": "Amiga CD32",
|
||||
"igdbSlug": "amiga-cd32",
|
||||
"alternateNames": [
|
||||
"Amiga CD32",
|
||||
"Commodore Amiga CD32"
|
||||
],
|
||||
"extensions": {
|
||||
"supportedFileExtensions": [
|
||||
".ZIP"
|
||||
],
|
||||
"uniqueFileExtensions": [
|
||||
]
|
||||
},
|
||||
"retroPieDirectoryName": "amiga",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "amiga",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "amiga",
|
||||
"alternateCoreName": "puae",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": [
|
||||
{
|
||||
"hash": "85ad74194e87c08904327de1a9443b7a",
|
||||
"description": "Kickstart v1.2 rev 33.180",
|
||||
"filename": "kick33180.A500"
|
||||
},
|
||||
{
|
||||
"hash": "82a21c1890cae844b3df741f2762d48d",
|
||||
"description": "Kickstart v1.3 rev 34.005",
|
||||
"filename": "kick34005.A500"
|
||||
},
|
||||
{
|
||||
"hash": "89da1838a24460e4b93f4f0c5d92d48d",
|
||||
"description": "CDTV extended ROM v1.00",
|
||||
"filename": "kick34005.CDTV"
|
||||
},
|
||||
{
|
||||
"hash": "dc10d7bdd1b6f450773dfb558477c230",
|
||||
"description": "Kickstart v2.04 rev 37.175",
|
||||
"filename": "kick37175.A500"
|
||||
},
|
||||
{
|
||||
"hash": "5f8924d013dd57a89cf349f4cdedc6b1",
|
||||
"description": "CD32 Kickstart v3.1 rev 40.060",
|
||||
"filename": "kick40060.CD32"
|
||||
},
|
||||
{
|
||||
"hash": "f2f241bf094168cfb9e7805dc2856433",
|
||||
"description": "CD32 KS + extended v3.1 rev 40.060",
|
||||
"filename": "kick40060.CD32"
|
||||
},
|
||||
{
|
||||
"hash": "bb72565701b1b6faece07d68ea5da639",
|
||||
"description": "CD32 extended ROM rev 40.060",
|
||||
"filename": "kick40060.CD32.ext"
|
||||
},
|
||||
{
|
||||
"hash": "e40a5dfb3d017ba8779faba30cbd1c8e",
|
||||
"description": "Kickstart v3.1 rev 40.063",
|
||||
"filename": "kick40063.A600"
|
||||
},
|
||||
{
|
||||
"hash": "646773759326fbac3b2311fd8c8793ee",
|
||||
"description": "Kickstart v3.1 rev 40.068",
|
||||
"filename": "kick40068.A1200"
|
||||
},
|
||||
{
|
||||
"hash": "9bdedde6a4f33555b4a270c8ca53297d",
|
||||
"description": "Kickstart v3.1 rev 40.068",
|
||||
"filename": "kick40068.A4000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 25,
|
||||
"igdbName": "Amstrad CPC",
|
||||
@@ -267,6 +352,9 @@
|
||||
},
|
||||
{
|
||||
"core": "fbalpha2012_cps2"
|
||||
},
|
||||
{
|
||||
"core": "mame"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -543,6 +631,48 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 68,
|
||||
"igdbName": "ColecoVision",
|
||||
"igdbSlug": "colecovision",
|
||||
"alternateNames": [
|
||||
"ColecoVision"
|
||||
],
|
||||
"extensions": {
|
||||
"supportedFileExtensions": [
|
||||
".BIN",
|
||||
".COL",
|
||||
".ROM",
|
||||
".ZIP"
|
||||
],
|
||||
"uniqueFileExtensions": [
|
||||
]
|
||||
},
|
||||
"retroPieDirectoryName": "coleco",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "coleco",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "coleco",
|
||||
"alternateCoreName": "coleco",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": [
|
||||
{
|
||||
"hash": "2c66f5911e5b42b8ebe113403548eee7",
|
||||
"description": "ColecoVision BIOS - Mandatory",
|
||||
"filename": "colecovision.rom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 15,
|
||||
"igdbName": "Commodore C64/128/MAX",
|
||||
@@ -590,8 +720,11 @@
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "c64",
|
||||
"alternateCoreName": "vice_x64",
|
||||
"alternateCoreName": "vice_x64sc",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"core": "vice_x64"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -620,6 +753,255 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 90,
|
||||
"igdbName": "Commodore PET",
|
||||
"igdbSlug": "cpet",
|
||||
"alternateNames": [
|
||||
"cpet",
|
||||
"PET",
|
||||
"Commodore PET"
|
||||
],
|
||||
"extensions": {
|
||||
"supportedFileExtensions": [
|
||||
".CRT",
|
||||
".D64",
|
||||
".D80",
|
||||
".D81",
|
||||
".G64",
|
||||
".M3U",
|
||||
".PRG",
|
||||
".T64",
|
||||
".TAP",
|
||||
".X64",
|
||||
".ZIP"
|
||||
],
|
||||
"uniqueFileExtensions": [
|
||||
".CRT",
|
||||
".D64",
|
||||
".D80",
|
||||
".D81",
|
||||
".G64",
|
||||
".PRG",
|
||||
".T64",
|
||||
".X64"
|
||||
]
|
||||
},
|
||||
"retroPieDirectoryName": "c64",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "vice_xpet",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "pet",
|
||||
"alternateCoreName": "vice_xpet",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 94,
|
||||
"igdbName": "Commodore Plus/4",
|
||||
"igdbSlug": "c-plus-4",
|
||||
"alternateNames": [
|
||||
"C+4",
|
||||
"c-plus-4",
|
||||
"Plus/4",
|
||||
"Commodore Plus/4"
|
||||
],
|
||||
"extensions": {
|
||||
"supportedFileExtensions": [
|
||||
".CRT",
|
||||
".D64",
|
||||
".D80",
|
||||
".D81",
|
||||
".G64",
|
||||
".M3U",
|
||||
".PRG",
|
||||
".T64",
|
||||
".TAP",
|
||||
".X64",
|
||||
".ZIP"
|
||||
],
|
||||
"uniqueFileExtensions": [
|
||||
".CRT",
|
||||
".D64",
|
||||
".D80",
|
||||
".D81",
|
||||
".G64",
|
||||
".PRG",
|
||||
".T64",
|
||||
".X64"
|
||||
]
|
||||
},
|
||||
"retroPieDirectoryName": "c64",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "vice_xplus4",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "plus4",
|
||||
"alternateCoreName": "vice_xplus4",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 71,
|
||||
"igdbName": "Commodore VIC-20",
|
||||
"igdbSlug": "vic-20",
|
||||
"alternateNames": [
|
||||
"VIC20",
|
||||
"VIC-20",
|
||||
"Commodore VIC-20"
|
||||
],
|
||||
"extensions": {
|
||||
"supportedFileExtensions": [
|
||||
".CRT",
|
||||
".D64",
|
||||
".D80",
|
||||
".D81",
|
||||
".G64",
|
||||
".M3U",
|
||||
".PRG",
|
||||
".T64",
|
||||
".TAP",
|
||||
".X64",
|
||||
".ZIP"
|
||||
],
|
||||
"uniqueFileExtensions": [
|
||||
".CRT",
|
||||
".D64",
|
||||
".D80",
|
||||
".D81",
|
||||
".G64",
|
||||
".PRG",
|
||||
".T64",
|
||||
".X64"
|
||||
]
|
||||
},
|
||||
"retroPieDirectoryName": "c64",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "vice_xvic",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "vic20",
|
||||
"alternateCoreName": "vice_xvic",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 158,
|
||||
"igdbName": "Commodore CDTV",
|
||||
"igdbSlug": "commodore-cdtv",
|
||||
"alternateNames": [
|
||||
"Commodore CDTV",
|
||||
"Amiga CDTV",
|
||||
"Commodore Amiga CDTV"
|
||||
],
|
||||
"extensions": {
|
||||
"supportedFileExtensions": [
|
||||
".ZIP"
|
||||
],
|
||||
"uniqueFileExtensions": [
|
||||
]
|
||||
},
|
||||
"retroPieDirectoryName": "amiga",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "amiga",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "amiga",
|
||||
"alternateCoreName": "puae",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": [
|
||||
{
|
||||
"hash": "85ad74194e87c08904327de1a9443b7a",
|
||||
"description": "Kickstart v1.2 rev 33.180",
|
||||
"filename": "kick33180.A500"
|
||||
},
|
||||
{
|
||||
"hash": "82a21c1890cae844b3df741f2762d48d",
|
||||
"description": "Kickstart v1.3 rev 34.005",
|
||||
"filename": "kick34005.A500"
|
||||
},
|
||||
{
|
||||
"hash": "89da1838a24460e4b93f4f0c5d92d48d",
|
||||
"description": "CDTV extended ROM v1.00",
|
||||
"filename": "kick34005.CDTV"
|
||||
},
|
||||
{
|
||||
"hash": "dc10d7bdd1b6f450773dfb558477c230",
|
||||
"description": "Kickstart v2.04 rev 37.175",
|
||||
"filename": "kick37175.A500"
|
||||
},
|
||||
{
|
||||
"hash": "5f8924d013dd57a89cf349f4cdedc6b1",
|
||||
"description": "CD32 Kickstart v3.1 rev 40.060",
|
||||
"filename": "kick40060.CD32"
|
||||
},
|
||||
{
|
||||
"hash": "f2f241bf094168cfb9e7805dc2856433",
|
||||
"description": "CD32 KS + extended v3.1 rev 40.060",
|
||||
"filename": "kick40060.CD32"
|
||||
},
|
||||
{
|
||||
"hash": "bb72565701b1b6faece07d68ea5da639",
|
||||
"description": "CD32 extended ROM rev 40.060",
|
||||
"filename": "kick40060.CD32.ext"
|
||||
},
|
||||
{
|
||||
"hash": "e40a5dfb3d017ba8779faba30cbd1c8e",
|
||||
"description": "Kickstart v3.1 rev 40.063",
|
||||
"filename": "kick40063.A600"
|
||||
},
|
||||
{
|
||||
"hash": "646773759326fbac3b2311fd8c8793ee",
|
||||
"description": "Kickstart v3.1 rev 40.068",
|
||||
"filename": "kick40068.A1200"
|
||||
},
|
||||
{
|
||||
"hash": "9bdedde6a4f33555b4a270c8ca53297d",
|
||||
"description": "Kickstart v3.1 rev 40.068",
|
||||
"filename": "kick40068.A4000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"igdbId": 33,
|
||||
"igdbName": "Game Boy",
|
||||
@@ -1208,15 +1590,18 @@
|
||||
"retroPieDirectoryName": "mastersystem",
|
||||
"webEmulator": {
|
||||
"type": "EmulatorJS",
|
||||
"core": "segaMS",
|
||||
"core": "picodrive",
|
||||
"availableWebEmulators": [
|
||||
{
|
||||
"emulatorType": "EmulatorJS",
|
||||
"availableWebEmulatorCores": [
|
||||
{
|
||||
"core": "segaMS",
|
||||
"alternateCoreName": "genesis_plus_gx",
|
||||
"core": "picodrive",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"core": "segaMS",
|
||||
"alternateCoreName": "genesis_plus_gx"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -20,8 +20,8 @@ namespace gaseous_server
|
||||
//_logger.LogInformation("Timed Hosted Service running.");
|
||||
Logging.Log(Logging.LogType.Debug, "Background", "Starting background task monitor");
|
||||
|
||||
_timer = new Timer(DoWork, null, TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(5));
|
||||
_timer = new Timer(DoWork, null, TimeSpan.FromSeconds(10),
|
||||
TimeSpan.FromSeconds(30));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -36,6 +36,8 @@ namespace gaseous_server
|
||||
List<ProcessQueue.QueueItem> ActiveList = new List<ProcessQueue.QueueItem>();
|
||||
ActiveList.AddRange(ProcessQueue.QueueItems);
|
||||
foreach (ProcessQueue.QueueItem qi in ActiveList) {
|
||||
if (qi.ItemState != ProcessQueue.QueueItemState.Disabled)
|
||||
{
|
||||
if (CheckIfProcessIsBlockedByOthers(qi) == false) {
|
||||
qi.BlockedState(false);
|
||||
if (DateTime.UtcNow > qi.NextRunTime || qi.Force == true)
|
||||
@@ -53,6 +55,7 @@ namespace gaseous_server
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
|
17
gaseous-server/appsettings.Production.json
Normal file
17
gaseous-server/appsettings.Production.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"Production": {
|
||||
"Url": "http://*:80"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,36 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>gaseous_server</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(RunConfiguration)' == 'https' " />
|
||||
<PropertyGroup Condition=" '$(RunConfiguration)' == 'http' " />
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\net7.0\gaseous-server.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net8.0\gaseous-server.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Release\net7.0\gaseous-server.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Release\net8.0\gaseous-server.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="gaseous-signature-parser" Version="2.0.0" />
|
||||
<PackageReference Include="gaseous.IGDB" Version="1.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0" />
|
||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.0.0" />
|
||||
<PackageReference Include="gaseous-signature-parser" Version="2.1.0" />
|
||||
<PackageReference Include="gaseous.IGDB" Version="1.0.2" />
|
||||
<PackageReference Include="hasheous-client" Version="0.2.0" />
|
||||
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
|
||||
<PackageReference Include="sharpcompress" Version="0.36.0" />
|
||||
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.1.23" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.3.1" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.3.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Controllers\" />
|
||||
<None Remove="Models\" />
|
||||
@@ -45,6 +46,7 @@
|
||||
<None Remove="Support\Database\MySQL\gaseous-1002.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1003.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1004.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-fix-1005.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1005.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1006.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1007.sql" />
|
||||
@@ -52,60 +54,17 @@
|
||||
<None Remove="Support\Database\MySQL\gaseous-1009.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1010.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1011.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1012.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1013.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1014.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1015.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1016.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1017.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1018.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1019.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1020.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1021.sql" />
|
||||
<None Remove="Classes\Metadata\" />
|
||||
<None Remove="Assets\" />
|
||||
<None Remove="Assets\Ratings\" />
|
||||
<None Remove="Assets\Ratings\ESRB\" />
|
||||
<None Remove="Assets\Ratings\ACB\" />
|
||||
<None Remove="Assets\Ratings\PEGI\" />
|
||||
<None Remove="Assets\Ratings\ESRB\AO.svg" />
|
||||
<None Remove="Assets\Ratings\ESRB\E.svg" />
|
||||
<None Remove="Assets\Ratings\ESRB\E10plus.svg" />
|
||||
<None Remove="Assets\Ratings\ESRB\M.svg" />
|
||||
<None Remove="Assets\Ratings\ESRB\RP.svg" />
|
||||
<None Remove="Assets\Ratings\ESRB\RP-LM17-English.svg" />
|
||||
<None Remove="Assets\Ratings\ESRB\T.svg" />
|
||||
<None Remove="Assets\Ratings\CERO\" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_A.png" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_B.png" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_C.png" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_D.png" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_Z.png" />
|
||||
<None Remove="Assets\Ratings\USK\" />
|
||||
<None Remove="Assets\Ratings\USK\USK_0.svg" />
|
||||
<None Remove="Assets\Ratings\USK\USK_12.svg" />
|
||||
<None Remove="Assets\Ratings\USK\USK_16.svg" />
|
||||
<None Remove="Assets\Ratings\USK\USK_18.svg" />
|
||||
<None Remove="Assets\Ratings\USK\USK_6.svg" />
|
||||
<None Remove="Assets\Ratings\ACB\ACB_G.svg" />
|
||||
<None Remove="Assets\Ratings\ACB\ACB_M.svg" />
|
||||
<None Remove="Assets\Ratings\ACB\ACB_MA15.svg" />
|
||||
<None Remove="Assets\Ratings\ACB\ACB_PG.svg" />
|
||||
<None Remove="Assets\Ratings\ACB\ACB_R18.svg" />
|
||||
<None Remove="Assets\Ratings\ACB\ACB_RC.svg" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_A.svg" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_B.svg" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_C.svg" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_D.svg" />
|
||||
<None Remove="Assets\Ratings\CERO\CERO_Z.svg" />
|
||||
<None Remove="Assets\Ratings\PEGI\Eighteen.svg" />
|
||||
<None Remove="Assets\Ratings\PEGI\Seven.svg" />
|
||||
<None Remove="Assets\Ratings\PEGI\Sixteen.svg" />
|
||||
<None Remove="Assets\Ratings\PEGI\Three.svg" />
|
||||
<None Remove="Assets\Ratings\PEGI\Twelve.svg" />
|
||||
<None Remove="Assets\Ratings\GRAC\" />
|
||||
<None Remove="Assets\Ratings\GRAC\GRAC_All.svg" />
|
||||
<None Remove="Assets\Ratings\GRAC\GRAC_Eighteen.svg" />
|
||||
<None Remove="Assets\Ratings\GRAC\GRAC_Fifteen.svg" />
|
||||
<None Remove="Assets\Ratings\GRAC\GRAC_Testing.svg" />
|
||||
<None Remove="Assets\Ratings\GRAC\GRAC_Twelve.svg" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Eighteen.svg" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Fourteen.svg" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_L.svg" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Sixteen.svg" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Ten.svg" />
|
||||
<None Remove="Assets\Ratings\CLASS_IND\CLASS_IND_Twelve.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\" />
|
||||
@@ -114,15 +73,6 @@
|
||||
<Folder Include="Classes\SignatureIngestors\" />
|
||||
<Folder Include="Support\" />
|
||||
<Folder Include="Classes\Metadata\" />
|
||||
<Folder Include="Assets\" />
|
||||
<Folder Include="Assets\Ratings\" />
|
||||
<Folder Include="Assets\Ratings\ESRB\" />
|
||||
<Folder Include="Assets\Ratings\ACB\" />
|
||||
<Folder Include="Assets\Ratings\PEGI\" />
|
||||
<Folder Include="Assets\Ratings\CERO\" />
|
||||
<Folder Include="Assets\Ratings\USK\" />
|
||||
<Folder Include="Assets\Ratings\GRAC\" />
|
||||
<Folder Include="Assets\Ratings\CLASS_IND\" />
|
||||
<Folder Remove="Reference" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -135,50 +85,12 @@
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\AO.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\E.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\E10.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\M.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\RP.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\RP-LM17-English.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ESRB\T.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\USK\USK_0.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\USK\USK_12.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\USK\USK_16.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\USK\USK_18.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\USK\USK_6.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_G.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_M.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_MA15.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_PG.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_R18.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\ACB\ACB_RC.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_A.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_B.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_C.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_D.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CERO\CERO_Z.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\PEGI\Eighteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\PEGI\Seven.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\PEGI\Sixteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\PEGI\Three.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\PEGI\Twelve.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_All.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Eighteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Fifteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Testing.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\GRAC\GRAC_Twelve.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Eighteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Fourteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_L.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Sixteen.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Ten.svg" />
|
||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Twelve.svg" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1000.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-fix-1005.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1006.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" />
|
||||
@@ -186,5 +98,15 @@
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1009.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1010.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1011.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1012.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1013.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1014.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1015.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1016.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1017.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1018.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1019.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1021.sql" />
|
||||
</ItemGroup>
|
||||
</Project>
|
BIN
gaseous-server/wwwroot/.DS_Store
vendored
BIN
gaseous-server/wwwroot/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 22 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user